- 论坛徽章:
- 13
|
本帖最后由 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不会碰它。但它还要读取”队列启动标志位[ND_QUEUE_STOOPED]", 这个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[NUM_BH];
- 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[id].routine = routine;
- bh_strus[id].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 */
- }
复制代码 |
评分
-
查看全部评分
|