免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 1408 | 回复: 8
打印 上一主题 下一主题

[内核入门] papaya内核笔记 3 再谈网卡发包 [复制链接]

论坛徽章:
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
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2016-06-27 00:40 |只看该作者 |倒序浏览
本帖最后由 karma303 于 2016-07-05 05:18 编辑

  RTL8139驱动的发包部分原先写好了,也测过了,只是觉得脏,改来改去,今天才得到一个比较舒服的版本。 不过跟linux内核的实现已经相差甚远了。
  先贴上一篇写的不错的入门文章
  1. m.blog.csdn.net/article/details?id=9191011
复制代码
美中不足的是,它有一段代码分析错了:
  1.     static void rtl8139_interrupt (int irq, void *dev_instance, struct pt_regs *regs)  
  2.     {  
  3.         ...
  4.         if((isr & TxOK) || (isr & TxErr))  
  5.         {  
  6.             while((tp->dirty_tx != tp->cur_tx) || netif_queue_stopped(dev))  
  7.             {  
  8.                 unsigned int txstatus =  
  9.                 readl(ioaddr + TSD0 + tp->dirty_tx * sizeof(int));  
  10.       
  11.                 if(!(txstatus & (TxStatOK | TxAborted | TxUnderrun)))  
  12.                     break; /* yet not transmitted */  
  13.       
  14.                 if(txstatus & TxStatOK) {  
  15.                     LOG_MSG("Transmit OK interrupt\n");  
  16.                     tp->stats.tx_bytes += (txstatus & 0x1fff);  
  17.                     tp->stats.tx_packets++;  
  18.                 }  
  19.                 else {  
  20.                     LOG_MSG("Transmit Error interrupt\n");  
  21.                     tp->stats.tx_errors++;  
  22.             }  
  23.       
  24.             tp->dirty_tx++;  
  25.             tp->dirty_tx = tp->dirty_tx % NUM_TX_DESC;  
  26.       
  27.             if((tp->dirty_tx == tp->cur_tx) & netif_queue_stopped(dev))  
  28.             {  
  29.                 LOG_MSG("waking up queue\n");  
  30.                 netif_wake_queue(dev);  
  31.             }  
  32.             }  
  33.         }  
  34.     .......  
  35.     }  
复制代码
先不管那篇文章怎么分析的,我们自己来看看。
  截取的这段代码,是RTL8139发包中断的handler,其中有一个while循环,它是在尽可能多的探测已经空闲(发包完成)的descriptor。
  按理说,一个TOK中断,对应着一个descriptor重新可用,为什么要探测更多呢?
  很简单,设想这样的情景:我们一口气写了4个descriptor,就是说,一口气启动了4个包的发送,过了一会儿,descriptor 1发包完成了,上报给CPU一个TOK中断,但是,CPU很忙(可能正陷在别的硬中断里),就在这一小会儿,descriptor2,3,4也完成了发包,他们的TOK接踵而至。 只有一个ISR,当然是前浪死在沙滩上,等CPU缓过神,转向网卡的TOK中断routine,好几个TOK中断已经丢失了。
  但这不妨事,因为网卡上就4个descriptor,我们逐个检查一下它们各自TSR的某些位,若为1,则表明这个descriptor已经完成发包了。
  这就是上面的while循环的目的。在文档里是有说明的。
  
  再说那篇文章,他写的是,“那是因为8139不是在成功发送一个包后发出中断,而是将缓冲区上所有包发走后才发出中断的”。
  我不是想接人家的短,只是看到他的错,觉得很欣慰。那个作者心里肯定对"8139为什么有4个descriptor”有一些猜想,所以分析问题时尝试往上靠。 我也一直没明白8139为什么有4个descriptor。

  刚才说到文档,其实官网上我没找到,google到的两篇很好的,竟然来自于美国两所大学的网站。
  1.   phy.duke.edu/~rgb/brahma/Resources/beowulf/linux/drivers/rtl8139.html
  2.   cs.usfca.edu/~cruse/cs326/RTL8139_ProgrammersGuide.pdf
复制代码
原先学init进程时,看到一篇国内大学网站上的文章,也很惊艳,是个老师写的。能在大学里学内核,或者教内核,都是挺幸福的。
  回到正题。刚才说到那个while循环,手册上有提醒。而且手册上还提醒了第二点:
  Case 2: No packet TOK need to be handled, but ISR routine called
  就是提醒驱动编写者,有些个TOK中断送到CPU上时,中断例程其实无事可做
  什么原因呢?想象这样一个情景:Descriptor A的TOK routine正在运行,此时Descriptor B的TOK发生了,但CPU听不到,因为还在Descriptor A的中断例程里,但是,就在这个例程里,我们能检测到Descriptor B已经可用了(你还记得那个while循环吗),并做相关的处理。等从这次中断routine出去,回立刻陷入Descriptor B的TOK的中断例程。但是,已经无事可做了。驱动编写者要考虑到这一点。

  以上是本文的上半部分。

  在papaya内核里,我主要是去掉了队列的"STOPPED"标志位,相应的,netif_start_queue, netif_stop_queue等一些函数也就删掉了。因为我想了很久,觉得没必要给tx_queue设置一个"running/stopped"的属性。
  那网卡忙碌需要暂停发包时,该怎么通知内核的framework呢。我给start_xmit设计了一个返回值,用来反映当前网卡是否忙碌(是否还有发包的能力)。
  1. void nic_wake_queue(struct net_device *netdev){

  2.         cli();
  3.         if((netdev->flags & IN_WAKE_QUEUE) == 1) return sti();
  4.         if(!netdev->tx_queue.root) return sti();               
  5.         if(netdev->tx_busy(netdev)) return sti();
  6.         netdev->flags |= IN_WAKE_QUEUE;
  7.         sti();
  8.        
  9.         struct skb_queue *list = &netdev->tx_queue;
  10.         struct sk_buff * one;
  11.         int code = 0;
  12.         while(1){
  13.                 oprintf(" %u done  ", netdev->tx_count);
  14.                 one = list->root;
  15.                 LL2_POP(list);
  16.                 cli();
  17.                 code = netdev->start_xmit(one, netdev);
  18.                 if(code == -1 || list->root == 0){                //-1 indicates busy
  19.                         netdev->flags &= ~IN_WAKE_QUEUE;
  20.                         break;
  21.                 }
  22.                 sti();
  23.         }

  24.         sti();               
  25. }
复制代码
因为不用维护任何全局的flags,代码很简单。
  一开始三个if语句,分别是防止自身重入,检测队列空,检测网卡忙。
  为什么nic_wake_queue不允许自身重入呢。我其实没有想“自身重入会带来哪些坏处”(我没有脑细胞去想了),因为这个函数没有任何重入的理由,他能在队列异步生长(因为中断尾随的bottom half)的同时很好的工作。
  这个函数可以直接由用户调用(通过syscall),也可以在bottom half里由内核routine调用。BH与BH之间不存在重入的可能,因为papaya内核里,BH是严禁嵌套的,连不同channel上的BH都不嵌套。这根早期的linux很像。
  重入发生的情形是:用户调用nic_wake_queue,运行期间被硬中断,其尾随的bottom half里,可能会调用nic_wake_queue。像比TOK中断。我们的if就是防止这个。

  第二个话题,我在调用start_xmit的前后,用cli()/sti()把它包围起来,这个cli()来的似乎有些早,以后可以优化,但sti()一点儿也不早。start_xmit的返回值非常重要,必须保证它平安的返回。(即,在cli模式下顺利的ret回来)

  接下来说说tx_queue的“运行”靠什么维持。
  tx_queue会经常暂停,因为网卡忙碌,或者是自身空了。那再次启动靠什么呢?
  我的设计是,提交skb到发包队列时,如果发现队列是空的,就手动调用一下nic_wake_queue()。否则,就不用管它,待会儿肯定有TOK中断发生。TOK中断是“驱动”发包队列的主力。
  往发包队列里提交skb是个很常用的操作:
  1.         void waiting_for_transmit(struct sk_buff *skb){
  2.                 cli();
  3.                 bool tx_queue_empty = !(bool)skb->dev->tx_queue.root;
  4.                 LL2_A( &skb->dev->tx_queue, skb);
  5.                 sti();

  6.                 if(tx_queue_empty)                nic_wake_queue(skb->dev);
  7.         }
复制代码
这段代码不很优雅,就是队列为空,不代表待会儿就没有TOK中断发生了。所以应该再做进一步的检测,不到万不得已,不手动调用。

  【关于tx_queue的”排队“】
  很多人写了netif_wake_queue之后,大概都希望看到很多skb在tx_queue里排队的情形。实际上,8139发包的速度是很快的,不容易看到”排队“。
  要想让包排队,要求是,在你依次写完1,2,3,4号descriptor之后,1号descriptor的包还没发出去。这时候,你再提交的包就只能排队等候了。
  我今天是把发包的大小从60byte增加到1500byte,才第一次捕捉到”排队“:因为包大了,发送的相对就慢了。
  我测试发送100个包,指定不同的发包长度,看分别有多少个包排队。
  1.   发包长度(byte)  1000   800   700  670  650   600
  2.   排队个数(个/100) 94    92    89    87   0    0
复制代码
以上仅限于真实机器,如果你在QEMU上测,估计永远是瞬间发出去,什么也看不到。我对QEMU是越来越失望了。在考虑是不是写一个e1000的驱动,这样可以回归bochs。

  【关于OWN位】
  写8139驱动的一个细节。如果你想检测一个descriptor的包是否发出去了,记住,不要检测TSR的OWN位,它置1,仅代表这个包从内存DMA到FIFO里了。并不一定到网线上了。
  要检测的是 if(!TSR.TOK && !TSR.TUN && !TSR.TABT)。这三个bit分别代表”发包成功“,"FIFO exhausted", "发包终止"。 如果这三个bit都为0,那说明结果还没出来,包还没发出去。

【8139芯片的word aligned】
  驱动提供给8139的内存buffer的起始地址,无论是用来接收,还是发送,都必须4字节对齐。 这是8139芯片要求的。
  我开始只是在收包时注意它,发包时没管。
  后来发现,我发出去的包,每间隔一个包就是坏包:mac头都是错的。
  因为我申请4个发包缓冲区时,不是一个个申请的,而是:
  1.   
  2.         private->tx_buf = kmalloc2( TX_BUF_SIZE * NUM_TX_DESC, __GFP_DMA);
  3.         for(int i = 0; i < NUM_TX_DESC; i++){
  4.                 private->tx_bufs[i] = private->tx_buf + i * TX_BUF_SIZE;
  5.         }
复制代码
而我把TX_BUF_SIZE定义成8139发送单包的体积上线:1762。
  改成 #define TX_BUF_SIZE (1762 & ~3)就好了。

论坛徽章:
146
2015年亚洲杯之日本
日期:2015-04-28 13:32:012015年亚洲杯之朝鲜
日期:2015-05-06 10:16:442015年亚洲杯之日本
日期:2015-05-06 10:21:342015年亚洲杯纪念徽章
日期:2015-05-13 17:16:442015亚冠之北京国安
日期:2015-05-13 17:18:292015亚冠之鹿岛鹿角
日期:2015-05-13 17:19:062015亚冠之德黑兰石油
日期:2015-05-27 16:47:402015亚冠之塔什干棉农
日期:2015-05-28 15:24:122015亚冠之卡尔希纳萨夫
日期:2015-06-01 13:52:392015亚冠之柏斯波利斯
日期:2015-06-04 17:37:292015亚冠之阿尔纳斯尔
日期:2015-06-16 11:31:202015亚冠之塔什干火车头
日期:2015-06-23 10:12:33
2 [报告]
发表于 2016-06-27 09:15 |只看该作者
期待文章更新,咱们社区先加了精华鼓励奖,加油

论坛徽章:
9
程序设计版块每日发帖之星
日期:2016-02-13 06:20:00数据库技术版块每日发帖之星
日期:2016-06-15 06:20:00数据库技术版块每日发帖之星
日期:2016-06-16 06:20:00数据库技术版块每日发帖之星
日期:2016-06-18 06:20:00程序设计版块每日发帖之星
日期:2016-06-27 06:20:00程序设计版块每日发帖之星
日期:2016-07-09 06:20:00IT运维版块每日发帖之星
日期:2016-07-15 06:20:00IT运维版块每日发帖之星
日期:2016-07-27 06:20:00程序设计版块每日发帖之星
日期:2016-08-18 06:20:00
3 [报告]
发表于 2016-06-27 14:14 |只看该作者
现在大量的中断的话,都是轮询+中断,对于网卡来讲是NAPI

论坛徽章:
9
程序设计版块每日发帖之星
日期:2016-02-13 06:20:00数据库技术版块每日发帖之星
日期:2016-06-15 06:20:00数据库技术版块每日发帖之星
日期:2016-06-16 06:20:00数据库技术版块每日发帖之星
日期:2016-06-18 06:20:00程序设计版块每日发帖之星
日期:2016-06-27 06:20:00程序设计版块每日发帖之星
日期:2016-07-09 06:20:00IT运维版块每日发帖之星
日期:2016-07-15 06:20:00IT运维版块每日发帖之星
日期:2016-07-27 06:20:00程序设计版块每日发帖之星
日期:2016-08-18 06:20:00
4 [报告]
发表于 2016-06-27 14:17 |只看该作者
本帖最后由 mordorwww 于 2016-06-27 14:28 编辑
mordorwww 发表于 2016-06-27 14:14
现在大量的中断的话,都是轮询+中断,对于网卡来讲是NAPI



另外内核里同一个中断是不会嵌套的 ,否则内核栈很容易溢出。
所以在网卡发送中断里,是不会处理下一个descriptor的发送中断的。
内核里同样类型的发送中断是串行处理的,是在软中断里排队串行处理各个descriptor的,此时并不需要中断,所以就没有中断, 也不是中断来同步CPU和网卡,而是由 ring buffer上各个descriptor上的标志位来同步CPU和网卡的。不知道对于其它的高速设备,例如sata驱动和sas驱动是不是也是这样同步的
实际上,同一类型的硬中断也是串行处理的

论坛徽章:
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
5 [报告]
发表于 2016-06-28 20:10 |只看该作者
allan cruse, 貌似有专门讲intel网卡编程的课。

论坛徽章:
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
6 [报告]
发表于 2016-06-28 20:14 |只看该作者
While the chip is a bus master, it's not a descriptor-based bus master.

这句话怎么理解?

论坛徽章:
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
7 [报告]
发表于 2016-06-28 20:17 |只看该作者
The result is that all IP packets must be copied to an alignment buffer before being queued for transmit.

这个copy是软件行为还是硬件行为?

论坛徽章:
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
8 [报告]
发表于 2016-06-28 23:02 |只看该作者
本帖最后由 karma303 于 2016-06-28 23:18 编辑

回复 6# nswcfd
While the chip is a bus master, it's not a descriptor-based bus master.
while表转折吧。

【bus master】
传统的PC体系中,CPU以外的设备是没有“主动权”的,即使它往内存DMA,也是由PC上的DMA控制器主导。
PCI总线标准允许挂在它上面的设备称为"bus master",这种芯片上有专门的电路用来控制DMA过程及对内存总线的征用。

【not descriptor based】
它大概是跟intel 8254x这样的网卡芯片比较,觉得RTL8139太low了。
其实RTL8139的tx部分是基于descriptor的。

   

论坛徽章:
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
9 [报告]
发表于 2016-06-28 23:29 |只看该作者
回复 7# nswcfd
The result is that all IP packets must be copied to an alignment buffer before being queued for transmit.

整个这段话我都没看懂。只好以后看了。

   
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

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

清除 Cookies - ChinaUnix - Archiver - WAP - TOP