用户工具

站点工具


hacking:w:watchdog_timeout_bce

bce(4) 在多处理器环境的 watchdog timeout 问题修正

– delphij

bce(4) 是一款常见的千兆网卡。我们在使用过程中发现它在高负载、多处理器环境下会出现 watchdog timeout 的现象。查看代码发现它使用的是旧式的 if_watchdog KPI。由于这个变动对于编写其他网卡驱动程序具有一定的参考意义,因此我把具体的修改过程写出来。

改写

传统的旧式 if_watchdog KPI 在网卡注册的过程中会由网卡驱动注册一个回调函数 if_watchdog,它是一个 void f(struct ifnet *ifp) 函数指针,会在内核发现网卡出现长时间不产生 tx/rx 中断时调用。旧式 if_watchdog 回调时并不会上 softclock 的锁(因为内核并不知道该上哪个锁)。传统回调函数需要做的事情大致如下:

  1. 显示“Watchdog timeout occurred, resetting!”。
  2. 向网卡发出 RESET 请求。

与此相反,新式的 watchdog KPI 写法,不再由内核统一进行 watchdog 计数,而是将这一工作交给网卡驱动来进行。也就是说,网卡需要在自己的 softclock 结构中维护一个计数(为了一致起见,我们通常将其命名为 watchdog_timer),而不再维护先前在 ifnet 结构中的 if_timer 计数(这个计数是由内核的旧式 if_watchdog KPI 使用的)。对于 bce(4) 而言,这主要是一个 s/ifp→if_timer/sc→watchdog_timer/g 的替换。

新式的 watchdog 需要挂在网卡的 tick callout 上。因此,我们在 bce_tick_locked() 函数进行 callout_reset 之前调用一次 watchdog 函数:

       /* Check that chip hasn't hang. */
       bce_watchdog(sc);

注意,由于内核不再帮我们维护 if_timer 计数,因此 watchdog 函数必须自行维护:

       if (sc->watchdog_timer == 0 || --sc->watchdog_timer)
               return;

其他变动

Jack Vagel 在 Intel 的 em(4) 网卡驱动中新增了一个特性,即,如果网卡收到了停止帧,则此时 watchdog timeout 不对网卡进行 reset。我也为 bce(4) 驱动增加了这项能力:

       /*
        * If we are in this routine because of pause frames, then
        * don't reset the hardware.
        */
       if (REG_RD(sc, BCE_EMAC_TX_STATUS) & BCE_EMAC_TX_STATUS_XOFFED) 
               return;

修改之后的 watchdog 函数:

bce_watchdog(struct bce_softc *sc)
{
 
        BCE_LOCK_ASSERT(sc);
 
        if (sc->watchdog_timer == 0 || --sc->watchdog_timer)
                return;
 
        /*
         * If we are in this routine because of pause frames, then
         * don't reset the hardware.
         */
        if (REG_RD(sc, BCE_EMAC_TX_STATUS) & BCE_EMAC_TX_STATUS_XOFFED) 
                return;
 
        BCE_PRINTF("%s(%d): Watchdog timeout occurred, resetting!\n", 
                __FILE__, __LINE__);
 
        DBRUN(BCE_VERBOSE_SEND, 
                bce_dump_driver_state(sc);
                bce_dump_status_block(sc));
 
        /* DBRUN(BCE_FATAL, bce_breakpoint(sc)); */
 
        sc->bce_ifp->if_drv_flags &= ~IFF_DRV_RUNNING;
 
        bce_init_locked(sc);
        sc->bce_ifp->if_oerrors++;
 
}

修改过程将 bce_tick_locked 改为了 bce_tick,同时对这一 callout 进行了修改以适应这一变动,也就是将 callout_init(&sc→bce_stat_ch, CALLOUT_MPSAFE); 改成了 callout_init_mtx(&sc→bce_stat_ch, &sc→bce_mtx, 0);。

具体变动请参见 sys/dev/bce/if_bce.c:1.23 和 sys/dev/bce/if_bcereg.h:1.11。

/data/vhosts/wiki-data/pages/hacking/w/watchdog_timeout_bce.txt · 最后更改: 2008/02/01 06:48 由 delphij