免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 2081 | 回复: 4

[实践] papaya内核笔记 之二 网卡发包 [复制链接]

论坛徽章:
13
程序设计版块每日发帖之星
日期:2016-06-29 06:20:00每日论坛发贴之星
日期:2016-08-14 06:20:00操作系统版块每日发帖之星
日期:2016-08-14 06:20:00每日论坛发贴之星
日期:2016-08-13 06:20:00数据库技术版块每日发帖之星
日期:2016-08-13 06:20:00程序设计版块每日发帖之星
日期:2016-08-13 06:20:00IT运维版块每日发帖之星
日期:2016-08-13 06:20:00每日论坛发贴之星
日期:2016-08-12 06:20:00数据库技术版块每日发帖之星
日期:2016-08-12 06:20:00程序设计版块每日发帖之星
日期:2016-08-12 06:20:00操作系统版块每日发帖之星
日期:2016-08-12 06:20:00综合交流区版块每日发帖之星
日期:2016-08-09 06:20:00
发表于 2016-06-18 09:32 |显示全部楼层
本帖最后由 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硬盘。
  1.   ------------irq.c @do_IRQ()------------
  2.         switch(irq){
  3.                 case 15:
  4.                         out_byte(0x20, 0x20);
  5.                 case 7:
  6.                         return 0;
  7.                 default:
  8.                         desc->hw_handler->ack(irq);       
  9.         }
  10. -------------------------------------
复制代码
更好的的方法是,看看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。呵呵,我就是爱写这样的代码。

  1. -----------------kernel/bh.c--------------------
  2. #include<asm/bit.h>
  3. #include<linux/bh.h>
  4. #include<linux/slab.h>
  5. #include<utils.h>

  6. #define NUM_BH 32
  7. unsigned active;        /* bitmap for active bh */
  8. unsigned using;                /* bitmap for allocated/free bh slot */
  9. static struct bh{
  10.         bh_fn routine;
  11.         void *data;
  12. }bh_strus[NUM_BH];

  13. void bh_init(void){
  14.         assert(active * using == 0);
  15. }

  16. void do_bh(void){
  17.         int id;
  18.         unsigned shadow;

  19.         bh_disable();
  20.         cli();

  21.         repeat:
  22.         shadow = active;
  23.         active = 0;
  24.         sti();
  25.         while( (id = __bs(shadow)) != -1 ){                /* bit scan forward */
  26.                 __btr(&shadow, id);                /* clear active bit, for later scanning*/
  27.                 struct bh *bh = bh_strus + id;                assert( bh->routine );
  28.                 bh->routine( bh->data );
  29.         }
  30.         cli();
  31.         if(active){
  32.                 oprintf(" repeat for pending\n");
  33.                 goto repeat;
  34.         }
  35.         bh_enable();
  36. }

  37. int alloc_bh(bh_fn routine, void *data){
  38.         oprintf(">");
  39.         int id = __bs0s(&using);               
  40.         if(id == -1) spin("bh allocation failed");
  41.         assert(!__bt(&active, id));
  42.         bh_strus[id].routine = routine;
  43.         bh_strus[id].data = data;
  44.         return id;
  45. }

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

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

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

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

复制代码

评分

参与人数 1可用积分 +10 收起 理由
Godbach + 10 赞一个!

查看全部评分

论坛徽章:
36
IT运维版块每日发帖之星
日期:2016-04-10 06:20:00IT运维版块每日发帖之星
日期:2016-04-16 06:20:0015-16赛季CBA联赛之广东
日期:2016-04-16 19:59:32IT运维版块每日发帖之星
日期:2016-04-18 06:20:00IT运维版块每日发帖之星
日期:2016-04-19 06:20:00每日论坛发贴之星
日期:2016-04-19 06:20:00IT运维版块每日发帖之星
日期:2016-04-25 06:20:00IT运维版块每日发帖之星
日期:2016-05-06 06:20:00IT运维版块每日发帖之星
日期:2016-05-08 06:20:00IT运维版块每日发帖之星
日期:2016-05-13 06:20:00IT运维版块每日发帖之星
日期:2016-05-28 06:20:00每日论坛发贴之星
日期:2016-05-28 06:20:00
发表于 2016-06-22 14:19 |显示全部楼层
回复 1# karma303
感谢分享!


   

论坛徽章:
20
程序设计版块每日发帖之星
日期:2015-08-17 06:20:00程序设计版块每日发帖之星
日期:2016-07-16 06:20:00程序设计版块每日发帖之星
日期:2016-07-18 06:20:00每日论坛发贴之星
日期:2016-07-18 06:20:00黑曼巴
日期:2016-12-26 16:00:3215-16赛季CBA联赛之江苏
日期:2017-06-26 11:05:5615-16赛季CBA联赛之上海
日期:2017-07-21 18:12:5015-16赛季CBA联赛之青岛
日期:2017-09-04 17:32:0515-16赛季CBA联赛之吉林
日期:2018-03-26 10:02:16程序设计版块每日发帖之星
日期:2016-07-15 06:20:0015-16赛季CBA联赛之江苏
日期:2016-07-07 18:37:512015亚冠之萨济拖拉机
日期:2015-08-17 12:21:08
发表于 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的,为什么新版本的就不关闭了呢?是什么原因导致了这个区别?

论坛徽章:
20
程序设计版块每日发帖之星
日期:2015-08-17 06:20:00程序设计版块每日发帖之星
日期:2016-07-16 06:20:00程序设计版块每日发帖之星
日期:2016-07-18 06:20:00每日论坛发贴之星
日期:2016-07-18 06:20:00黑曼巴
日期:2016-12-26 16:00:3215-16赛季CBA联赛之江苏
日期:2017-06-26 11:05:5615-16赛季CBA联赛之上海
日期:2017-07-21 18:12:5015-16赛季CBA联赛之青岛
日期:2017-09-04 17:32:0515-16赛季CBA联赛之吉林
日期:2018-03-26 10:02:16程序设计版块每日发帖之星
日期:2016-07-15 06:20:0015-16赛季CBA联赛之江苏
日期:2016-07-07 18:37:512015亚冠之萨济拖拉机
日期:2015-08-17 12:21:08
发表于 2016-06-22 18:45 |显示全部楼层
搜了一下kernel git,e1000 tx去掉irq大概是从这个版本开始的
https://git.kernel.org/cgit/linu ... 71f09a38e2c9fd56193

论坛徽章:
0
发表于 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:我看的内核源码都是早期版本的。
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP