–delphij
本文介绍的是一个个案:由于一个较大的变动集引入了一个不太容易被注意到的问题,随后在大约一个月之后被人发现,以及其中测试、查找并修正问题的过程。个人认为这个过程(在缺少注释、datasheet、有效的变动集的情况下缩小问题范围并最终加以解决的调试过程)对于类似的问题具有一定的参考意义。
Broadcom NetXtreme II 系列(DragonFlyBSD和FreeBSD称为bce,OpenBSD称为bnx,Linux称为bnx2)是Broadcom公司最近几年来推出的一系列千兆以太网控制器,与早期的NetXtreme系列相比,增加了包括TCP/IP/UDP校验和offload、2.5Gbps介质支持等能力。FreeBSD的驱动 bce(4) 由 Broadcom 的 David Christensen 撰写和维护。
今年 bce(4) 进行了一系列改进而没有合并到 7-STABLE 中,因此在 7.1-RELEASE 的发布版本准备过程中,我向 re@ 提议将适用的变动全部合并到 7-STABLE 分支。这个变动集(184826)于UTC时间2008年11月10日22:40:16提交到svn,包含了-HEAD分支的变动集176448、178132、178853、179436、179695、179771和182293;随后,根据用户之前的反馈,又进一步增加了185082(合并为185161)。
第一个变动集commit后大约一个月,有细心的用户发现中断异常,即,每个bce设备每秒钟会产生4-5万个中断,无论是否真的有流量。
因为一时没有头绪,我们首先尝试将问题尽量缩小和简化。由于 7.0-RELEASE 上面正常,而最近对 bce(4) 驱动做过修改,尽管系统的其他组件也做过改动,但是单纯将 bce(4) 回退,如果问题消失,就可以知道是否是新版 bce(4) 引入的回归问题。首先,我們回退 185161 和 184826,问题消失(我們将这个版本记为7-STABLE,-185161,-184826)。185161是一个很容易理解的修正逻辑问题的补丁,而184826则是一个多达两万六千多行增删的大型patchset,因此它的嫌疑较大。测试7-STABLE-185161发现问题依然存在,那么,至少184826是非常可能有问题的:
| 版本 | 状态 | ||
| 7-STABLE | 有问题 | ||
| 7-STABLE, -185161 | 有问题 | ||
| 7-STABLE, -185161, -184826 | 无问题 |
184826是一个大型patchset,包含了176448、178132、178853、179436、179695、179771和182293的积累结果。其中,179771是最大的一个patchset,这次commit改掉了两万多行代码。
想要单纯通过比对来找到这么大的changeset中的问题是很麻烦的,因为手头没有资料,在死磕代码之前,首先试试看屏蔽一部分变动。将新增的 BCE_USE_SPLIT_HEADER 特性禁止掉,发现问题依然存在。
DragonFlyBSD 的 sephe@ 对 bce(4) 的一些参数进行了修改,对FreeBSD而言,这些参数位于if_bce.c 927行开始的bce_tx_quick_cons_trip_int等,修改这些参数并重启,观察vmstat -i发现中断数量明显减少。由于这些参数决定了收发包时产生中断的频率,因此,直觉上看,问题应该与中断处理函数有一些关系。
在 bce_intr 中发现:
/* Was it a link change interrupt? */ if ((status_attn_bits & STATUS_ATTN_BITS_LINK_STATE) != (sc->status_block->status_attn_bits_ack & STATUS_ATTN_BITS_LINK_STATE)) bce_phy_intr(sc);
下面新增了一段代码:
/* Clear any transient status updates during link state change. */ REG_WR(sc, BCE_HC_COMMAND, sc->hc_command | BCE_HC_COMMAND_COAL_NOW_WO_INT); REG_RD(sc, BCE_HC_COMMAND);
从注释上看,这个操作只有在发生了链路状态变化的时候才应该进行。增加一对 { },改为:
/* Was it a link change interrupt? */ if ((status_attn_bits & STATUS_ATTN_BITS_LINK_STATE) != (sc->status_block->status_attn_bits_ack & STATUS_ATTN_BITS_LINK_STATE)) { bce_phy_intr(sc); /* Clear any transient status updates during link state change. */ REG_WR(sc, BCE_HC_COMMAND, sc->hc_command | BCE_HC_COMMAND_COAL_NOW_WO_INT); REG_RD(sc, BCE_HC_COMMAND); }
联编并测试,果然,大量中断的现象消失了。
(说明:从公开的资料看,执行这个操作不应产生新的中断。由于没有 datasheet,因此这个问题还需要继续研究。)