【转】linux kernel 网络协议栈之GRO(Generic receive offload)
转自http://abcdxyzk.github.io/blog/2015/04/18/kernel-net-gro/
Attention: gro会合并多个gso_size不同的包, 会将gso_size设置成第一个包的gso_size.
http://www.pagefault.info/?p=159
GRO(Generic receive offload)在内核2.6.29之后合并进去的,作者是一个华裔Herbert Xu ,GRO的简介可以看这里:
http://lwn.net/Articles/358910/
先来描述一下GRO的作用,GRO是针对网络接受包的处理的,并且只是针对NAPI类型的驱动,因此如果要支持GRO,不仅要内核支持,而且驱动也必须调用相应的借口,用ethtool -K gro on来设置,如果报错就说明网卡驱动本身就不支持GRO。
GRO类似tso,可是tso只支持发送数据包,这样你tcp层大的段会在网卡被切包,然后再传递给对端,而如果没有gro,则小的段会被一个个送到协议栈,有了gro之后,就会在接收端做一个反向的操作(想对于tso).也就是将tso切好的数据包组合成大包再传递给协议栈。
如果实现了GRO支持的驱动是这样子处理数据的,在NAPI的回调poll方法中读取数据包,然后调用GRO的接口napi_gro_receive或者napi_gro_frags来将数据包feed进协议栈。而具体GRO的工作就是在这两个函数中进行的,他们最终都会调用__napi_gro_receive
。下面就是napi_gro_receive,它最终会调用napi_skb_finish以及__napi_gro_receive
。
1 2 3 4 5 6 |
|
然后GRO什么时候会将数据feed进协议栈呢,这里会有两个退出点,一个是在napi_skb_finish里,他会通过判断__napi_gro_receive
的返回值,来决定是需要将数据包立即feed进协议栈还是保存起来,还有一个点是当napi的循环执行完毕时,也就是执行napi_complete的时候,先来看napi_skb_finish,napi_complete我们后面会详细介绍。
在NAPI驱动中,直接调用netif_receive_skb会将数据feed 进协议栈,因此这里如果返回值是NORMAL,则直接调用netif_receive_skb来将数据送进协议栈。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
|
GRO的主要思想就是,组合一些类似的数据包(基于一些数据域,后面会介绍到)为一个大的数据包(一个skb),然后feed给协议栈,这里主要是利用Scatter-gather IO,也就是skb的struct skb_shared_info域(我前面的blog讲述ip分片的时候有详细介绍这个域)来合并数据包。
在每个NAPI的实例都会包括一个域叫gro_list,保存了我们积攒的数据包(将要被merge的).然后每次进来的skb都会在这个链表里面进行查找,看是否需要merge。而gro_count表示当前的gro_list中的skb的个数。
1 2 3 4 5 6 7 8 9 |
|
紧接着是gro最核心的一个数据结构napi_gro_cb,它是保存在skb的cb域中,它保存了gro要使用到的一些上下文,这里每个域kernel的注释都比较清楚。到后面我们会看到这些域的具体用途。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
|
每一层协议都实现了自己的gro回调函数,gro_receive和gro_complete,gro系统会根据协议来调用对应回调函数,其中gro_receive是将输入skb尽量合并到我们gro_list中。而gro_complete则是当我们需要提交gro合并的数据包到协议栈时被调用的。
下面就是ip层和tcp层对应的回调方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
|
gro的入口函数是napi_gro_receive,它的实现很简单,就是将skb包含的gro上下文reset,然后调用__napi_gro_receive
,最终通过napi_skb_finis来判断是否需要讲数据包feed进协议栈。
1 2 3 4 5 6 7 |
|
napi_skb_finish一开始已经介绍过了,这个函数主要是通过判断传递进来的ret(__napi_gro_receive
的返回值),来决定是否需要feed数据进协议栈。它的第二个参数是前面处理过的skb。
这里再来看下skb_gro_reset_offset,首先要知道一种情况,那就是skb本身不包含数据(包括头也没有),而所有的数据都保存在skb_shared_info中(支持S/G的网卡有可能会这么做).此时我们如果想要合并的话,就需要将包头这些信息取出来,也就是从skb_shared_info的frags[0]中去的,在 skb_gro_reset_offset中就有做这个事情,而这里就会把头的信息保存到napi_gro_cb 的frags0中。并且此时frags必然不会在high mem,要么是线性区,要么是dma(S/G io)。 来看skb_gro_reset_offset。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
|
接下来就是__napi_gro_receive
,它主要是遍历gro_list,然后给same_flow赋值,这里要注意,same_flow是一个标记,表示某个skb是否有可能会和当前要处理的skb是相同的流,而这里的相同会在每层都进行判断,也就是在设备层,ip层,tcp层都会判断,这里就是设备层的判断了。这里的判断很简单,有2个条件:
1 设备是否相同
2 mac的头必须相等
如果上面两个条件都满足,则说明两个skb有可能是相同的flow,所以设置same_flow,以便与我们后面合并。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
|
接下来来看dev_gro_receive,这个函数我们分做两部分来看,第一部分是正常处理部分,第二部份是处理frag0的部分。
来看如何判断是否支持GRO,这里每个设备的features会在驱动初始化的时候被初始化,然后如果支持GRO,则会包括NETIF_F_GRO。 还有要注意的就是,gro不支持切片的ip包,因为ip切片的组包在内核的ip会做一遍,因此这里gro如果合并的话,没有多大意义,而且还增加复杂度。
在dev_gro_receive中会遍历对应的ptype(也就是协议的类链表,以前的blog有详细介绍),然后调用对应的回调函数,一般来说这里会调用文章开始说的ip_packet_type,也就是 inet_gro_receive。
而 inet_gro_receive的返回值表示我们需要立刻feed 进协议栈的数据包,如果为空,则说明不需要feed数据包进协议栈。后面会分析到这里他的详细算法。
而如果当inet_gro_receive正确返回后,如果same_flow没有被设置,则说明gro list中不存在能和当前的skb合并的项,因此此时需要将skb插入到gro list中。这个时候的返回值就是HELD。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 |
|
然后就是处理frag0的部分,以及不支持gro的处理。 frag0的作用是: 有些包的包头会存在skb->frag[0]里面,gro合并时会调用skb_gro_header_slow将包头拉到线性空间中,那么在非线性skb->frag[0]中的包头部分就应该删掉。
这里要需要对skb_shinfo的结构比较了解,我在以前的blog对这个有很详细的介绍,可以去查阅。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
|
接下来就是inet_gro_receive,这个函数是ip层的gro receive回调函数,函数很简单,首先取得ip头,然后判断是否需要从frag复制数据,如果需要则复制数据
1 2 3 4 5 6 7 8 9 10 11 12 |
|
然后就是一些校验工作,比如协议是否支持gro_reveive,ip头是否合法等等
1 2 3 4 5 6 7 8 9 10 11 12 13 |
|
然后就是核心的处理部分,它会遍历整个gro_list,然后进行same_flow和是否需要flush的判断。
这里ip层设置same_flow是根据下面的规则的:
1 4层的协议必须相同
2 tos域必须相同
3 源,目的地址必须相同
如果3个条件一个不满足,则会设置same_flow为0。 这里还有一个就是判断是否需要flush 对应的skb到协议栈,这里的判断条件是这样子的。
1 ip包的ttl不一样
2 ip包的id顺序不对
3 如果是切片包
如果上面两个条件某一个满足,则说明skb需要被flush出gro。
不过这里要注意只有两个数据包是same flow的情况下,才会进行flush判断。原因很简单,都不是有可能进行merge的包,自然没必要进行flush了。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 |
|
然后就是tcp层的gro方法,它的主要实现函数是tcp_gro_receive,他的流程和inet_gro_receiv类似,就是取得tcp的头,然后对gro list进行遍历,最终会调用合并方法。
首先来看gro list遍历的部分,它对same flow的要求就是source必须相同,如果不同则设置same flow为0.如果相同则跳到found部分,进行合并处理。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
|
接下来就是当找到能够合并的skb的时候的处理,这里首先来看flush的设置,这里会有4个条件:
1 拥塞状态被设置(TCP_FLAG_CWR).
2 tcp的ack的序列号不匹配 (这是肯定的,因为它只是对tso或者说gso进行反向操作)
3 skb的flag和从gro list中查找到要合并skb的flag 如果他们中的不同位 不包括TCP_FLAG_CWR | TCP_FLAG_FIN | TCP_FLAG_PSH,这三个任意一个域。
4 tcp的option域不同
如果上面4个条件有一个满足,则会设置flush为1,也就是找到的这个skb(gro list中)必须被刷出到协议栈。
这里谈一下flags域的设置问题首先如果当前的skb设置了cwr,也就是发生了拥塞,那么自然前面被缓存的数据包需要马上被刷到协议栈,以便与tcp的拥塞控制马上进行。
而FIN和PSH这两个flag自然不需要一致,因为这两个和其他的不是互斥的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
|
从上面我们可以看到如果tcp的包被设置了一些特殊的flag比如PSH,SYN这类的就必须马上把数据包刷出到协议栈。
下面就是最终的一些flags判断,比如第一个数据包进来都会到这里来判断。
1 2 3 4 5 6 7 8 9 10 11 12 |
|
这里要知道每次我们只会刷出gro list中的一个skb节点,这是因为每次进来的数据包我们也只会匹配一个。因此如果遇到需要刷出的数据包,会在dev_gro_receive中先刷出gro list中的,然后再将当前的skb feed进协议栈。
最后就是gro最核心的一个函数skb_gro_receive,它的主要工作就是合并,它有2个参数,第一个是gro list中和当前处理的skb是same flow的skb,第二个就是我们需要合并的skb。
这里要注意就是farg_list,其实gro对待skb_shared_info和ip层切片,组包很类似,就是frags放Scatter-Gather I/O的数据包,frag_list放线性数据。这里gro 也是这样的,如果过来的skb支持Scatter-Gather I/O并且数据是只放在frags中,则会合并frags,如果过来的skb不支持Scatter-Gather I/O(数据头还是保存在skb中),则合并很简单,就是新建一个skb然后拷贝当前的skb,并将gro list中的skb直接挂载到farg_list。
先来看支持Scatter-Gather I/O的处理部分。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 |
|
这里gro list中的要被合并的skb我们叫做skb_s.
接下来就是不支持支持Scatter-Gather I/O(skb的头放在skb中)的处理。这里处理也比较简单,就是复制一个新的nskb,然后它的头和skb_s一样,然后将skb_s挂载到nskb的frag_list上,并且把新建的nskb挂在到gro list中,代替skb_s的位置,而当前的skb
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 |
|
Posted by kk 2015-04-18 15:48:00
转载于:https://www.cnblogs.com/qxxnxxFight/p/11001900.html
【转】linux kernel 网络协议栈之GRO(Generic receive offload)相关推荐
- linux kernel 网络协议栈之GRO(Generic receive offload)
linux kernel 网络协议栈之GRO(Generic receive offload) 2010年11月26日 Simon Liu 发表评论 阅读评论 原创文章,转载请注明: 转载自pagef ...
- linux进程的使用xps,linux kernel 网络协议栈之xps特性详解
全称是Transmit Packet Steering,是rfs/rps的作者Tom Herbert提交的又一个patch,预计会在2.6.37进入内核. 这个patch主要是针对多队列的网卡发送时的 ...
- linux 网卡gso,linux内核网络协议栈学习笔记:关于GRO/GSO/LRO/TSO等patch的分析和测试...
TSO,全称是TCP Segmentation Offload,我们知道通常以太网的MTU是1500,除去TCP/IP的包头,TCP的MSS (Max Segment Size)大小是1460,通常情 ...
- Generic receive offload
GRO(Generic receive offload)在内核2.6.29之后合并进去的,作者是一个华裔Herbert Xu ,GRO的简介可以看这里: http://lwn.net/Articles ...
- linux内核网络协议栈--网卡报文收发(十六)
版本说明 Linux版本: 3.10.103 网卡驱动: ixgbev 报文收发简单流程 网卡驱动默认采用的是NAPI的报文处理方式.即中断+轮询的方式,网卡收到一个报文之后会产生接收中断,并且屏蔽中 ...
- Linux内核网络协议栈:udp数据包发送(源码解读)
<监视和调整Linux网络协议栈:接收数据> <监控和调整Linux网络协议栈的图解指南:接收数据> <Linux网络 - 数据包的接收过程> <Linux网 ...
- linux内核网络协议栈--linux网络设备理解(十三)
网络层次 linux网络设备驱动与字符设备和块设备有很大的不同. 字符设备和块设备对应/dev下的一个设备文件.而网络设备不存在这样的设备文件.网络设备使用套接字socket访问,虽然也使用read, ...
- 深入浅出Linux内核网络协议栈|结构sk_buff|Iptables|Netfilter丨内核源码丨驱动开发丨内核开发丨C/C++Linux服务器开发
深入浅出Linux内核网络协议栈 视频讲解如下,点击观看: 深入浅出Linux内核网络协议栈|结构sk C/C++Linux服务器开发高级架构师知识点精彩内容包括:C/C++,Linux,Nginx, ...
- Linux内核网络协议栈流程及架构
文章目录 Linux内核网络报文处理流程 Linux内核网络协议栈架构 Linux内核网络报文处理流程 linux网络协议栈是由若干个层组成的,网络数据的处理流程主要是指在协议栈的各个层之间的传递. ...
- linux内核网络协议栈--监控和调优:接收数据(十五)
译者序 本文翻译自 2016 年的一篇英文博客 Monitoring and Tuning the Linux Networking Stack: Receiving Data.如果能看懂英文,建议阅 ...
最新文章
- mongodb权限管理02
- .NET编码解码(HtmlEncode与HtmlEncode)
- 手机mvno怎么设置_微信透明背景壁纸怎么弄 手机设置方法教程分享
- poj2516 最小费用最大流
- 洛谷P1202 [USACO1.1]黑色星期五Friday the Thirteenth
- linux系统死机窗口移动不了怎么办,Linux 操作系统死机故障处理方法总结
- 怎么才能在百度上看到自己发布的博文?
- HEVC学习 —— HM的使用
- PostgreSQL 优势,MySQL 数据库自身的特性并不十分丰富,触发器和存储过程的支持较弱,Greenplum、AWS 的 Redshift 等都是基于 PostgreSQL 开发的...
- dbf文件转excel_Excel批量转PDF,关键一步不能忘
- Ubuntu18版本安装ROS
- Latex转word的一款软件-Pandoc
- ipad怎样和计算机连接网络,ipad怎样连接电脑itunes
- 音频/视频标签的使用
- JAVA小实验——接口与继承
- 2019年985院校计算机专业排名,2019年985大学名单排名,985大学详解(附全榜单)
- 【学习笔记】Golang之Gorm学习笔记
- 时间序列分析软件Hector用户手册(二)
- 雅思大作文 城市 练习+词汇
- 没有kindle 但要接收mox moe的邮箱推送
热门文章
- nc文件服务器配置教程,nc文件服务器配置
- snmp有android代理端吗,GitHub - wosika/SNMP4Android: 简易使用于安卓的SNMP工具类,基于snmp4j...
- javascript实现划词搜索功能(兼容IE,firefox,opera)
- 对有序特征进行离散化(继承Spark的机器学习Estimator类)
- spring实现定时任务的两种方式
- Linux rhel7 下MySQL5.7.18详细安装文档
- 美化下拉框select箭头部分(不彻底)
- Swarm Mode服务管理
- 面试题总结——JAVA高级工程师
- struts2的package和result的标签的属性