Monday June 06, 2005 |
相信多数在Solaris内核上工作的工程师都象我一样,工作中很大的乐趣来自修改Bug。尤其对于我来说,追根溯源找到导致问题的原因是其中最有满足感的部分(对我,后面的修改,测试验证过程的确相对平淡很多)。尤其是某些Bug,当你最初看到它的描述,第一印象就是"这绝不可能!",然后就是试图重现,将描述中的片断拼凑成真正的问题,最后通过某种手段找到元凶--其中的精彩有时真的可以和侦探探案相比--而且,的确如此,好的工程师与精明的侦探有一样灵敏的嗅觉。或许某一天,我应该谈一谈将我带到Solaris内核世界的第一个bug,讲讲当时我是如何地束手无策,同事的一个资深工程师又是如何地与我坐在一起帮助我,我是如何惊叹于他的思维模式和mdb工具的强大,从毫无头绪中抽丝拨茧,找到问题的根本原因。 不过今天,我要讲的是这个星期我正在解决的bug 6272300。问题其实很简单,新引入的Gldv3(Generic Lan Driver v3)构架中就应该确保如果在一个接口(例如bge1)上已经创建了链路聚合(aggregation),那么后续ifconfig再尝试 plumb该接口,应该返回设备忙,并显示操作失败 (exclusive active原则). 现在你们所能看到bug的问题描述已经被重新编辑过,简化了很多。最初的描述里,提到测试版本是Solaris snv-14,但同时打印出来的栈信息却显示了根本在snv-14中不存在的函数e1000g_m_resources(存在这个函数,说明e1000g 网卡驱动已经移植到Gldv3,但事实e1000g当前仍然是最传统的DLPI驱动),这是之一;之二,按照其中描述的步骤,在我的实验机器上却无法重现(当然只能基于已经基于GLDv3的BGE网卡)。所以,我最初的反应就是,这应该是网卡驱动组自己编译的Solaris版本,问题也可能出自那个项目的代码中。 (当我刚到Sun时,我的Sunvisor就告诉我,永远不要相信bug report里的描述,那里表面描述的可能不够准确,有时甚至根本不是问题,但往往真正的问题确实存在,只是隐藏在后面,需要我们去挖掘 -至理名言 这个bug被搁置了几天后,终于又被转发过来,还同时传递了几个信息-1). 此问题只能在特定机器上重现;2) 已经确认此问题在Snv14的BGE网卡上也能重现。第二条信息说明当前的Solaris版本的确存在问题,第一个信息则比较奇怪了,为什么只能在特定机器上重现(发现问题的两台机器都是x64系统,而我的实验机器则是Sparc,但相关的代码不应该也不可能与机器的体系结构有关。- 哈哈,有趣的东西来了。 痛苦的是,bug的提交者反复强调这个问题很可能在即使是相同的体系结构其他机器上也不能重现,但调试这个问题需要使用kmdb,而这两台机器都没有console连接,但是让我坐在实验室里瑟瑟发抖(实验室的温度简直变态),而且通过主机调试,手边又不再有别的机器可以参照代码,简直再没有比这更"完美"的调试环境了。 还是没有勇气坐在实验室里忍受冰山一样的寒冷,我要了问题机器的IP地址,回到了自己的座位上。先碰碰运气吧尝试复现吧。比较幸运,很快我就在我们自己的实验室里找到了有console接口,具有bge接口,同时又是相同体系结构的机器,更幸运的是,它已经装了snv-14,我甚至不需要重装系统或者bfu;最最幸运的是,它上面的snv-14是非调试版本的内核!(你们很快就会知道为什么这一点这么重要。) 重现意想不到地顺利,说实话,对那段代码的自信(虽然不直接是我本人的代码,但是相关代码的review, 修改,让我对那段代码我太熟悉了,绝对不可能有这么简单的逻辑问题!)使我对能够复现问题颇为吃惊。接下来的工作就比较简单了,创建链路聚合,在处理DLPI 消息的函数(处理IP plumb的主要函数)入口设置断点,plumb接口,单步跟踪... 相关的代码在函数dld_proto中:
/*
* If this primitive causes the data-link channel used by this
* object to become active, then we need to notify dls. Note that
* if we're already passive by having succesfully processed a
* DL_PASSIVE_REQ, then active primitives do not cause us to become
* active.
*/
if (prip->pri_active && dsp->ds_passivestate == DLD_UNINITIALIZED) {
if (!dls_active_set(dsp->ds_dc)) {
dlerrorack(dsp->ds_wq, mp, prim, DL_SYSERR, EBUSY);
return;
}
}
当处理DL_BIND_REQ DLPI消息时,prip->pri_active为TRUE,表示该DLPI请求是一个activation请求,此时的dsp刚刚被初始化,其 ds_passivestate为UNINITIALIZED状态,dls_active_set函数调用mac_active_set,发现相应的 mac(bge1接口)已经处于active状态(其mi_activelink在创建aggregation时设置为1),返回失败,回应EBUSY DL_ERROR_ACK消息,通知上层应用程序该操作失败。- 一切看上去都很完美。 可kmdb的结果却出人意料 但不要忘了这是非调试版本的内核,创建对象时直接使用缓冲的实例,str_constructor函数并没有被调用,所以此时的 ds_passivestate继承了上个实例释放时的数值(DLD_ACTIVE);而调试版本的内核,总是删除缓冲对象,每次创建对象时重新构造,所以不能重现相同的问题。 哈,对应的修改只需一行,在dld_str_destroy函数中重新初始化ds_passivestate为DLD_UNINITIALIZED。 就这么简单。 |
Calendar
LinksReferersToday's Page Hits: 10 |
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||

