karma303 发表于 2016-06-18 09:32

papaya内核笔记 之二 网卡发包

本帖最后由 karma303 于 2016-06-18 09:39 编辑

刚写完了RTL8139网卡的驱动的发送部分,记下来一些笔记,跟朋友们分享。
笔记可能会比较杂,因为8139的资料已经很多了,我还是想到什么写什么吧。


【bochs不能模拟8139】
google上搜(bochs, RTL8139)是能找到一些item的,但都是10多年前的帖子。我的bochs2.6里,.bochsrc里找不到配置选项,只有e1000,ne2k。其实就是不支持。那些google条目里讨论的,应该是由bochs驱动本机的真实网卡。bochs是有这个能力的。
只好用qemu。

【tips for QUEMU】
我也是才转到qemu上去的。暂时总结了几点:
1, 从bochs迁移到quemu,原先的虚拟磁盘可以原封不动的继续用,bochs下用的是plain disk,其实就是个普通文件,在qemu里,对应raw类型。
2, 安装quemu后,系统上没有qemu这个命令。在/usr/bin里有qemu-system-i386,还有个x86_64的,任选其一。btw,同目录下还有个qemu-i386,你要是用了这个命令就等死吧。linux就这德行。
3, 如果你的qemu跟gdb连不上,加这个选项tcp:127.0.0.1:1234。像我用的是:
   qemu-system-i386 -S -net nic,model=rtl8139 -gdb tcp:127.0.0.1:1234 400m.img

【怎么看网卡的irq】
网卡的irq是在BIOS阶段所分配的,据说过程复杂,涉及routine table等等。ibm有篇长文,PCI Interrupt Routing (Navigating the Maze)。可怜我在osdev上问,还被嘲讽了一顿。
其实不用懂它,只管读pci配置空间的PCI_LINE字段。在我的qemu和机器上,分别是11和10。

【伪中断(spurious IRQ)】
我在测试网卡发包中断时,发现qemu上出现了15号中断。但我确定我的IMR屏蔽了15号中断。最后才知道是spurios IRQ:
8259A往CPU送一个中断信号 ====> CPU acknowledge it并等着8259A再送上来中断号 ====> 8259A发现身上的那个irq消失了(程序员ACK不当,或者信号噪声),但cpu还在那儿等着呢。只好捏造一个假的给cpu。

怎么识别伪中断呢,在我的内核里,是看到15或7就直接当做伪中断处理了,因为我不准备支持打印机或启用第二个IDE硬盘。------------irq.c @do_IRQ()------------
        switch(irq){
                case 15:
                        out_byte(0x20, 0x20);
                case 7:
                        return 0;
                default:
                        desc->hw_handler->ack(irq);       
        }
-------------------------------------更好的的方法是,看看ISR上相应的位是否为1。
解释一下上面的代码:原则上,这种中断,一定不要ACK它(就是送EOI)。但如果是15号,就需要(且只需要)给master芯片发一个EOI。因为是slave芯片撒的谎,master不知道。

【netif_wake_queue()当然要直接启动发送队列】
本来没什么好说的,但在CU上看到一个帖子,说它只是设置了启动标志位。当然不是。
这根IDE磁盘的request队列一样,它们的发送过程,都是靠中断来”驱动“的。TOK中断,证明一个包发出去了,一个DESC可用了,所以要启动下一个发送。此时不做,就再没有机会了。


【怎么清零ISR的一个位】
writing a 1 to any bit will reset this bit, but writing a 0 has no effect。手册上的话,就是这样。
我写这,是因为看到早期linux源码的一段注释,大意是,“按照手册上的话读一个bit,并不能清0,只好写入1",不知道那个作者看的是哪个手册?呵呵。(善意。此处声明,本系列的呵呵都是善意的:)
对了,这个ISR,说的是8139的ISR。

【IMR的名字起的有问题吧】
8139手册上的Interrupt Mask Register,是置位则允许相应的中断,reset则屏蔽。
那就叫做Interrupt Enable Register好啦。

【发包队列里的sk_buff可以尽早释放】
我开始觉得,要等到TOK中断时,再释放相应的sk_buff,因为没发送成功的话,还可以重发。实际上网卡是自带重发功能的,sk_buff可以尽早释放。把里面的data拷贝到tx_buf后就可以释放了。


【8139为什么有4个DESC寄存器】
我现在还是不清楚。如果网卡朝网线上送包的速度是确定的:假如每秒能发1000个包,那1个寄存器和4个寄存器有什么区别呢?
我猜是为了减少cpu中断的次数,如果只有一个desc的话,cpu就得中断1000次;现在有了4个,最少中断250次就好了。

【start_xmit()里会关中断】
发包函数常是在开中断的大环境下被调用的,例如bootom half。
但发包函数里,会cli,核心操作完了,再sti。因为start_xmit()除了要完成关键的寄存器操作(发包),还要更新net_device结构体的相关信息,例如bsy_desc,touse_desc(papaya内核的里的变量名,对应linux的dirty_tx, cur_tx)。 如果关掉硬中断,很有可能半路被TOK中断(网卡说,不久前发的包已经出送上网线了),在TOK中断里,也会更新dirty_tx等成员。
这是老生常谈的问题,只要在开中断的大环境里写代码,就要考虑中断routine对数据的竞争访问。

【netif_wake_queue()里用不用要关中断】
netif_wake_queue()是被推迟到bottom_half里执行的,到目前为止,我还没关硬中断,不能关啊,关了还呆在bottom half里干什么。
netif_wake_queue会操作发包队列skb_queue,这个结构体队列是安全的,因为中断routine不会碰它。但它还要读取”队列启动标志位", 这个flag是会被中断routine修改的。我在想,遇到这种临界资源时,是不是也做短暂的cli/sti ?
反正现在内核测试是没问题。再说。

【机器测试的细节】
1, 如果你的网卡没插网线,也会有发包中断,并且是TOK中断。
2, 发包的时候,如果你的网线连在联通的光猫上,记得等光猫启动好之后再测试,这时候网卡的红色指示灯会随着发包闪,光猫的lan口也会闪。

先写这么多吧,之前说要把BH的代码贴上,现在测试过了,就贴上吧。
超简单的,连mask变量都没用。所以测试时几乎没遇到什么bug。呵呵,我就是爱写这样的代码。
-----------------kernel/bh.c--------------------
#include<asm/bit.h>
#include<linux/bh.h>
#include<linux/slab.h>
#include<utils.h>

#define NUM_BH 32
unsigned active;        /* bitmap for active bh */
unsigned using;                /* bitmap for allocated/free bh slot */
static struct bh{
        bh_fn routine;
        void *data;
}bh_strus;

void bh_init(void){
        assert(active * using == 0);
}

void do_bh(void){
        int id;
        unsigned shadow;

        bh_disable();
        cli();

        repeat:
        shadow = active;
        active = 0;
        sti();
        while( (id = __bs(shadow)) != -1 ){                /* bit scan forward */
                __btr(&shadow, id);                /* clear active bit, for later scanning*/
                struct bh *bh = bh_strus + id;                assert( bh->routine );
                bh->routine( bh->data );
        }
        cli();
        if(active){
                oprintf(" repeat for pending\n");
                goto repeat;
        }
        bh_enable();
}

int alloc_bh(bh_fn routine, void *data){
        oprintf(">");
        int id = __bs0s(&using);               
        if(id == -1) spin("bh allocation failed");
        assert(!__bt(&active, id));
        bh_strus.routine = routine;
        bh_strus.data = data;
        return id;
}

void free_bh(int id){
        if(id < 0 || id > NUM_BH) spin(" illegal bh id to free");       
        if(!__bt(&using, id))         spin("attempt to free a non-using bh");
        if(!__bt(&active, id)) spin("attempt to free an active bh");

        __btr(&using, id);                /*bit test and reset */
}

void mark_bh(int id){
        if(id < 0 || id > NUM_BH) spin(" illegal bh id to mark");       
        if(!__bt(&using, id))         spin("attempt to mark a non-using bh");
        //if(!__bt(&active, id)) spin("this bh is already active");

        __bts(&active, id);                /* bit test and set */
}

Godbach 发表于 2016-06-22 14:19

回复 1# karma303
感谢分享!


   

nswcfd 发表于 2016-06-22 18:44

关于xmit里是否关中断的事情(好吧,老话题了……),貌似有些网卡是不关的?

比如e1000,xmit更新ring->next_to_use,tx irq更新ring->next_to_clean,一个管头,一个管尾,似乎没有必要互斥起来(就像一些lockless队列一样)?

当然以上讨论未必适合8139。

PS,如果8139真的需要关闭中断的话,那么这两类网卡的(中断行为的)本质区别在哪里?

PS2,回溯了一下更早期的e1000驱动,发现老版本的xmit是关闭irq的,为什么新版本的就不关闭了呢?是什么原因导致了这个区别?

nswcfd 发表于 2016-06-22 18:45

搜了一下kernel git,e1000 tx去掉irq大概是从这个版本开始的
https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/drivers/net/e1000/e1000_main.c?id=8017943e6b177f117e4be71f09a38e2c9fd56193

weiweishuo 发表于 2016-06-22 22:06

回复 3# nswcfd

【PS,如果8139真的需要关闭中断的话,那么这两类网卡的(中断行为的)本质区别在哪里?】
e1000没写过。 要不要关中断,估计还是软件层次上的问题。8139的private里有一个dirty_tx,这个变量显然要关中断才能操作的,因为Tx中断会修改它。


【比如e1000,xmit更新ring->next_to_use,tx irq更新ring->next_to_clean,一个管头,一个管尾,似乎没有必要互斥起来(就像一些lockless队列一样)?】
我最近在写net_wake_queue的时候,有一个想法,就是新的skb总是追加(常常发生在中断里)到“处理队列”的末尾;处理(在bottom half里)的话,总是从队首开始。这样,操作这个队列的时候,就不用cli了。 我还没实施,因为我还没想通一点,如果队列只剩一个node的时候,还会竞争访问,怎么办。   谢谢你这段话,"lockless"这个词真好。

PS:我看的内核源码都是早期版本的。
页: [1]
查看完整版本: papaya内核笔记 之二 网卡发包