static void rl_start(ifp)
struct ifnet *ifp;
{
struct rl_softc *sc;
struct mbuf *m_head = NULL;
sc = ifp->if_softc;
while(RL_CUR_TXMBUF(sc) == NULL) {/* 1:当输出缓冲区为空时才进行新的输出*/
IF_DEQUEUE(&ifp->if_snd, m_head);/*把将要输出的数据加入到输出队列中。*/
if (m_head == NULL)/* 2:申请内存失败*/
break;
if (rl_encap(sc, m_head)) {/*8139卡的弱智表现在此,多加一个头部,还要长字节对齐,影响到数据必须重新搬迁。花时间啊!*/
IF_PREPEND(&ifp->if_snd, m_head);
ifp->if_flags |= IFF_OACTIVE;
break;
}
if (ifp->if_bpf)/* 3:假如包过滤存在就进行过滤*/
bpf_mtap(ifp, RL_CUR_TXMBUF(sc));
CSR_WRITE_4(sc, RL_CUR_TXADDR(sc),/* 4:以下为硬件输出的IO指令*/
vtophys(mtod(RL_CUR_TXMBUF(sc), caddr_t)));
CSR_WRITE_4(sc, RL_CUR_TXSTAT(sc),
RL_TXTHRESH(sc->rl_txthresh) |
RL_CUR_TXMBUF(sc)->m_pkthdr.len);
RL_INC(sc->rl_cdata.cur_tx);
}
if (RL_CUR_TXMBUF(sc) != NULL)/*假如传送缓冲不为空,说明数据放到缓冲中已经预备传了*/
ifp->if_flags |= IFF_OACTIVE;/*加上正在传标志*/
ifp->if_timer = 5;/*设定计数器为5*/
return;
}
大家注重看我注释中的1,2,3,4标号,即是4个有可能使传送出现问题的地方。但大家也会问:即使我传送成功了也会if_timer变成5啊,那么成功了后是怎么消除这个5的呢,答案在当传送完一个包后,网卡将产生一个中断,我们来看中断程序:
static void rl_intr(arg)
{
... 这中间我就不写了
if ((status & RL_ISR_TX_OK) || (status & RL_ISR_TX_ERR)) /*假如中断后状态寄存器的标识是成功或出错,就调用下面的程序。*/
rl_txeof(sc);
...
}
再看rl_txeof:
static void rl_txeof(sc)
{
...
ifp->if_timer = 0;/*哈哈,在这清0了,也就是说,只要你不是反复在那传,不过错误和传输正确都不会出现"watchdog timeout"*/
...
}
综上所述:引起watchdog timeout的主要原因为:1、缓冲区不够大,前面的没发完后面的又跟的来了。2、内核的内存分配出现问题,此情况比较少发生。3、卡的质量(在IO时的吞吐量)。4、bpf过滤过于复杂引起。
如何解决这个问题:
首先我们必须查出导致出现该问题的原因,即是4个问题中的哪个引起的,我们来修改驱动程序和相关的程序:
在if.h中定义一全局变量:
u_int8_t myerror; /*意思是出错的原因代码,按我列的来吧,1是缓冲区不够...*/
在函数static void rl_start(ifp)中加入:
static void rl_start(ifp)
struct ifnet *ifp;
{
struct rl_softc *sc;
struct mbuf *m_head = NULL;
sc = ifp->if_softc;
u_int8_t tmperror;
if (RL_CUR_TXMBUF(sc) != NULL) {/*新加,假如是缓冲区不够问题*/
myerror=1;
}
while(RL_CUR_TXMBUF(sc) == NULL) {
IF_DEQUEUE(&ifp->if_snd, m_head);
if (m_head == NULL)
{
myerror=2; /*内存分配出错*/
break;
}
if (rl_encap(sc, m_head)) {
IF_PREPEND(&ifp->if_snd, m_head);
ifp->if_flags |= IFF_OACTIVE;
break;
}
if (ifp->if_bpf)
bpf_mtap(ifp, RL_CUR_TXMBUF(sc));
tmperror=myerror;/*在进行写IO口前先保存前面出错的原因*/
CSR_WRITE_4(sc, RL_CUR_TXADDR(sc),
vtophys(mtod(RL_CUR_TXMBUF(sc), caddr_t)));
CSR_WRITE_4(sc, RL_CUR_TXSTAT(sc),
RL_TXTHRESH(sc->rl_txthresh) |
RL_CUR_TXMBUF(sc)->m_pkthdr.len);
myerror=tmperror;/*上面两句没问题的话再还原前面的出错原因*/
RL_INC(sc->rl_cdata.cur_tx);
}
if (RL_CUR_TXMBUF(sc) != NULL)
ifp->if_flags |= IFF_OACTIVE;
ifp->if_timer = 5;
return;
}
最后再改一下rl_watchdog中的显示部分
printf("rl%d: watchdog timeout:error number is %x\n", sc->rl_unit,myerror);
当然这只是我个人的见解,可能有许多不足或没考虑到的地方,也希望大家能提出更好、更轻易的方法。
最新相关文章
发表评论