| 最近有比较多的人谈到网卡的”watchdog timeout“问题,究竟是什么原因造成的,大多数人都把网卡的性能不佳做为问题的根源所在。我认为网卡的性能只是一方面的因素,他还涉及到缓冲的大小、单位时间内的包的数量、及网卡驱动程序等一系列因素。以下将从源代码的角度来对他进行分析。 首先,我们看看到底是哪个函数发出了“watchdog timeout”字符串,只要你查一下源代码不难看出,在各网卡的驱动程序里的XX_watchdog(XX是各网卡的名称,如:8139是rl,AMD7990是pcn,Inter是fxp等等)函数发出的。函数比较简单: static void rl_watchdog(ifp) struct ifnet *ifp; /*申明ifp是一个ifnet结构,结构存放了该网卡的输入输 出的函数指针和一些重要参数,当然也包括rl_watchdog函数的 指针*/ { struct rl_softc *sc; sc = ifp->if_softc; /*ifnet是softc结构的一个子集,softc包含了更多的该网卡的参数*/ printf("rl%d: watchdog timeout\n", sc->rl_unit); ifp->if_oerrors++; rl_txeof(sc); rl_rxeof(sc); rl_init(sc);/*这里大家都一样,重新初始芯片。*/ return; void ifinit() { static struct timeout if_slowtim; timeout_set(&if_slowtim, if_slowtimo, &if_slowtim); /*简单的说就是设置一定时器*/ if_slowtimo(&if_slowtim); /*哈哈,等不急了,先调用了再说*/ } 这样一来,if_slowtimo就成了一个一定时间内就要执行的一个函数了,此时候,大家也知道了if_slowtimo 的大概功能,无非是定时查看各网卡的发送数据的情况,如果没发送完成,就给该卡加一个计数器,到计数器达到一定的值时还没发送出去就调用该卡的XX_watchdog函数。下面我们来看看if_slowtimo函数。 void if_slowtimo(arg) void *arg; { struct timeout *to = (struct timeout *)arg; struct ifnet *ifp; int s = splimp(); TAILQ_FOREACH(ifp, &ifnet, if_list) if (ifp->if_timer == 0 || --ifp->if_timer) continue; if (ifp->if_watchdog) /*不过调用之前看看该卡有没有watchdog函数*/ (*ifp->if_watchdog)(ifp); } splx(s); timeout_add(to, hz / } 到这里一切都很明白了,我们只要在驱动程序的输出包时给定一个值,而输出函数是一个直到输出成功才跳出的循环,不成功他就一直重试来输出此包,而我们上面的程序就会时间一到就给你的值减1,如果减到小于0了,就watchdog timeout。我们还是来看一看程序吧: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)) 到数据必须重新搬迁。花时间啊!*/ 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) != ifp->if_flags |= IFF_OACTIVE;/*加上正在传标志*/ ifp->if_timer = 5;/*设定计数器为5*/ return; } 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 = ... } 综上所述:引起watchdog timeout的主要原因为:1、缓冲区不够大,前面的没发完后面的又跟的来了。2、内核的内存分配出现问题,此情况比较少发生。3、卡的质量(在IO时的吞吐量)。如何解决些问题:首先我们必须查出导致出现该问题的原因,即是这问题中的哪个引起的,我们来修改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); 当然这只是我个人的见解,可能有许多不足或没考虑到的地方,也希望大家能提出更好、更容易的方法。 |








