- 论坛徽章:
- 13
|
早起捉到一只8139驱动的bug。
先说一下测试的环境,是一根网线连着linux机器(也就是我写程序的)和装有papaya内核的机器(下面简称papaya机器)。
是在测试tcp的时候,内核收到第一次tcp syn,没有响应。
但当这个syn重发到内核(netcat会自动重发的),也就是内核第二次收到这个syn,就响应了。
而且是响应两次:它ack了`第一次的tcp syn,并且忽略了第二次的syn(内核当然该这么做)。
我发现,如果我手速快的话,在netcat向内核重发tcp syn 之前,ctrl-C掉它,也就是不让内核接受到第二次tcp syn。
然后,我ping一下ppaaya机器,这时输出较少,很清楚的看到,内核被第一个icmp包中断后,驱动在一个收包中断里读出了两个包。第一个包就是tcp syn。
这样就把bug定位到了8139的驱动,它为什么没能读出早先的那个syn包?而是让它停在网卡上?
这时,我留意到,屏幕上打印的与网卡中断相关的信息,是以发包(Tx)中断结束的,在Tx中断之后,网卡就“沉默”了,它本来还应该有一次Rx中断。
于是想到了,可能这次Rx中断发生在Tx中断的routine里,但是Tx的routine把8139的ISR寄存器整个清零了。包括ROK位。
那,等从这次Tx中断出去,期间的这次Rx中断也就丢了。
都是因为这句代码:
RTL_writew(netdev, ISR, 0xffff);
改成:
RTL_writew(netdev, ISR, TxOK);
就好了。只清除ISR上TxOK这个bit。
其实这个bug只是偶尔出现: 在papaya机器启动之前,我不把linux机器的ip配置成192.168.0.22, 它才会出现。
为什么是这样呢:
papaya内核在启动的时候,默认会向192.168.0.22(linux机器)发送一个ARP询问。一旦把linux的ip修改掉,那这次开机询问就无功而返(其实是无功无返)。
于是,当linux机器向192.168.0.9(papaya机器)发送tcp syn包时,它不得不先做一次ARP询问,获取相应的mac。
我们从papaya机器的角度看:
(收包)收到ARP inquiry ====> (发包)发送ARP reply ====> (收包)收到TCP syn
发送ARP reply之后,很快网卡产生Tx中断。 而linux收到了这个arp reply,会立刻把syn包发过来,因为网线很短,在Tx的中断例程里,这个syn包就过来了。然后就。。。
如果在papaya机器开机时的ARP询问成功了呢?
那linux机器发送tcp syn包之前,就不需要再做一次ARP询问,也就没有接下来的事情了。
这个bug只在收发包密集交错的时候才会出现。
到现在我似乎又明白了一个问题,之前经常出现的spurious irq;或者陷入网卡中断后,驱动却发现ISR是0 。可能都跟这个bug有关。
PS:
只在具体的中断分支里清除ISR上相应的bit,是可以解决这个bug。但linux的做法更好,它基本像这样:
int isr = RTL_readw(netdev, ISR);
RTL_writew(netdev, ISR, 0xfffff);
if(isr & RxOK) ...
if(isr & RxErr) ...
if(isr & TxOK) ...
...
注意,上面是一系列的if,而不是if, else if, else if .. else。
linux是在一次中断里,处理ISR上indicate的所有中断。而且在更外围,是一个while循环,它应该是试图处理在这次中断例程里又发生的中断。
另一方面,linux很早的就一次性清除了ISR的所有位, 这样就ISR就可以尽快的indicate接下来的中断。
这是两个很常规的提升性能的举措。附: linux2.038内核里的rtl8139.c ,自己改一下后缀。
rtl8139.zip
(43.69 KB, 下载次数: 15)
|
评分
-
查看全部评分
|