新客网WWW.XKER.COM:致力做中国最专业的网络学院!
学院: 操作系统 - 网络应用 - 服务器 - 网络安全 - 工具软件 - 办公软件 - Web开发 - 数据库 - 网页设计 - 图形图像 - 媒体动画 - 硬件学堂 - 存储频道 - QQ专区
您的位置:首页 > 网络学院 > 操作系统 > Unix系统 > FreeBSD教程 > 正文:浅谈watchdog timeout出现的原因

浅谈watchdog timeout出现的原因

新客网 XKER.COM 2008-01-19 来源:新客网编辑整理 佚名 收藏本文

【新客网FreeBSD教程】最近有比较多的人谈到网卡的”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);
/*打印出是哪块网卡出现问题。
sc->rl_unit代表该种网卡的第几快。我们知道在一个机器里同样的网卡可能有几块,当然此参数是由网卡驱动程序的初始化程序填充*/

ifp->if_oerrors ;
/*累计输出出现的错误包数量(o代表输出)*/

rl_txeof(sc);
/*这里是每个驱动程序不同的,此处为8139的,不过我觉得用rl_stop(sc)更好*/

rl_rxeof(sc);
/*这里也是每个驱动程序不同。我觉得来一个rl_reset(sc)也不错。*/

rl_init(sc);/*这里大家都一样,重新初始芯片。*/

return;
}

好了,到这我们知道是XX_watchdog函数发出了watchdog timeout信息。那么是谁来调用该函数呢? 我们接着来看另一个函数:if_slowtimo函数,该函数在if.c中。if.c是interface(接口的简称), 即系统在启动过程中初始化时必须调用其中的一些函数。ifinit函数是其中被调用的一个,这个函数很简单:
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)
{/*搜索每一个接口设备,TAILQ_FOREACH实际上是for(...)*/

  if (ifp->if_timer == 0 || --ifp->if_timer)

  continue;
/*假如是if_timer为0或if_timer减1以后还为真,实际上是对每块网卡的计数器if_timer减1后判定他是否还大于0,小于0就调用watchdog 函数。*/

  if (ifp->if_watchdog) /*不过调用之前看看该卡有没有watchdog函数*/

  (*ifp->if_watchdog)(ifp);

}

splx(s);

timeout_add(to, hz /
IFNET_SLOWHZ);
/*每次计时器完成后都会清除,你不得不又加上去。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))
{/*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_WR99vE_4(sc, RL_CUR_TXADDR(sc),/* 4:以下为硬件输出的IO指令*/

     vtophys(mtod(RL_CUR_TXMBUF(sc), caddr_t)));

  CSR_WR99vE_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;

}

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"*/

...

}

共2页: 上一页 [1] [2] 下一页
收藏】 【评论】 【推荐】 【投稿】 【打印】 【关闭
发表评论
要记得去论坛讨论,点击注册新会员匿名评论
评论内容:不能超过250字,需审核后才会公布,请自觉遵守互联网相关政策法规。
阅读排行
随机推荐
实用信息推荐