GRO (generic receive offload)

GRO是在协议栈接收报文时进行减负的一种处理方式,该方式在设计上考虑了多种协议报文。主要原理是在接收端通过把多个相关的报文(比如TCP分段报文)组装成一个大的报文后再传送给协议栈进行处理,因为内核协议栈对报文的处理都是对报文头部进行处理,如果相关的多个报文合并后只有一个报文头,这样就减少了协议栈处理报文个数,加快协议栈对报文的处理速度。

GRO功能对到本机的报文能起到一定的加速作用,但如果linux 运行在转发设备上,一般不需要使用GRO功能,这时使用GRO功能反而会降低处理速度。

GRO功能和只是针对NAPI类型的驱动,网卡驱动支持GRO要调用内核提供的GRO函数进行收包。并且该功能和网络设备硬件无关,是纯软件实现的。

设计需求:

1、需要根据一定的规则进行报文的合并。需要支持各种协议的合并规则。linux实现中是要求支持GRO功能的协议自己实现自己的合并函数,gro根据报文类型调用相应的合并函数。

2、对等待合并的报文应该进行缓存。等合并好后再送进协议栈进行处理。linux实现中在每个NAPI实例中放有一个等待合并的skb队列gro_list。

具体实现:

数据结构:

1、NAPI中GRO相关的字段:

struct napi_struct

{

unsigned int    gro_count;  //gro_list 上挂的skb 个数。

struct sk_buff  *gro_list; //等待合并的skb 链表

}

2、每个协议中定义自己的GRO接收合并函数和合并后处理函数。

接收合并函数定义:

struct sk_buff**(*gro_receive)(struct sk_buff **head,struct sk_buff *skb);

参数:

head:等待合并的skb链表头

skb:接收到的skb。

返回值:

如果为空,表示报文被合并后不需要现在送入协议栈。

如果不为空,表示返回的报文需要立即送入协议栈。

合并后处理函数定义:

int(*gro_complete)(struct sk_buff *skb);

该函数对合并好的报文进行进一步加工,比如更新校验和。

3、GRO功能使用skb结构体内私有空间cb[48]来存放gro所用到的一些信息。

定义结构体struct napi_gro_cb

struct   napi_gro_cb

{

/*指向存在skb_shinfo(skb)->frag[0].page页的数据的头部,

GRO使用过程中,如果skb是线性的,就置为空。

如果是非线性的并且报文头部全部存在非线性区中,

就指向页中的数据起始部分

*/

void *frag0;

/*第一页中数据的长度,如果frag0 字段不为空,

就设置该字段,否则为0。( Length of frag0.)

*/

unsigned int frag0_len;

/*This indicates where we are processing

relative to skb->data.

表明skb->data到GRO需要处理的数据区的偏移量。

因为在GRO合并处理过程中skb->data是不能被改变的,

所以需要使用该字段来记录一下偏移量。

GRO处理过程中根据该记录值快速找到要处理的数据部分。

比如进入ip层进行GRO处理,这时skb->data指向ip 头,

而ip层的gro 正好要处理ip头,这时偏移量就为0.

进入传输层后进行GRO处理,这时skb->data还指向ip头,

而tcp层gro要处理tcp头,这时偏移量就是ip头部长度。

*/

int data_offset;

/*This is non-zero if the packet may be of the same flow.

标记挂在napi->gro_list上的报文是否跟现在的报文进行匹配。

每层的gro_receive都设置该标记位。

接收到一个报文后,使用该报文和挂在napi->gro_list 上的报文进行匹配。

在链路层,使用dev 和 mac头进行匹配,如果一样表示两个报文是通一个

设备发过来的,就标记napi->gro_list上对应的skb的same为1.

到网络层,再进一步进行匹配时,只需跟napi->list上刚被链路层标记

same为1的报文进行网络层的匹配即可,不需再跟每个报文进行匹配。

如果网络层不匹配,就清除该标记。

到传输层,也是只配置被网络层标记same为1 的报文即可。

这样设计为的是减少没必要的匹配操作

*/

int same_flow;

/*This is non-zero if the packet cannot be merged

with the new skb.

如果该字段不为0,表示该数据报文没必要再等待合并,

可以直接送进协议栈进行处理了

*/

int flush;

/*该报文被合并过的次数 ,Number of segments aggregated. */

int count;

/* Free the skb? ,是否该被丢弃*/

int free;

};

#define NAPI_GRO_CB(skb) ((struct napi_gro_cb *)(skb)->cb)

NAPI_GRO_CB(skb) 的初始化:

skb_reset_offset() 来重置gro 的 cb区域。如果是skb非线性的,并且本身不包含数据(包括头也没有),而所有的数据都保存在skb_shared_info中(支持S/G的网卡有可能会这么做)。

因为合并报文时需要报文头的信息,这时报文头是存在skb_shared_info的frags[0]中的,我们使用指针指向正确的报文头部。

void skb_gro_reset_offset(struct sk_buff *skb)

{

NAPI_GRO_CB(skb)->data_offset = 0;

NAPI_GRO_CB(skb)->frag0 = NULL;

NAPI_GRO_CB(skb)->frag0_len = 0;

/*如果skb 不包括数据并且skb_shinfo(skb)->frags[0].page 不在

高端内存中,表示报文头存在skb_shinfo(skb)->frags[0].page中

*/

if (skb->mac_header == skb->tail &&

!PageHighMem(skb_shinfo(skb)->frags[0].page))

{

NAPI_GRO_CB(skb)->frag0 =

page_address(skb_shinfo(skb)->frags[0].page) +

skb_shinfo(skb)->frags[0].page_offset;

NAPI_GRO_CB(skb)->frag0_len =

skb_shinfo(skb)->frags[0].size;

}

}

GRO在如下地方将报文送进协议栈进行处理:

1、当napi的循环执行完毕时,也就是执行napi_complete的时候,调用napi_gro_flush来把gro_list上的报文送给协议栈。一般调用napi_complete时,是NAPI一次轮询就处理完了全部的报文,这时短期内网卡可能不会进行报文的接收,所以要把napi->gro_list上的报文都送到协议栈,不用再等待合并后再送了。

void napi_complete(struct napi_struct *n)

{

......

/*把napi->gro_list上的所有报文调用napi_gro_complete都

送给协议栈,并清空grp_list

*/

napi_gro_flush(n);

......

}

void napi_gro_flush(struct napi_struct *napi)

{

struct sk_buff *skb, *next;

for (skb = napi->gro_list; skb; skb = next)

{

next = skb->next;

skb->next = NULL;

/*链路层实现的gro完成函数 ,详见后文*/

napi_gro_complete(skb);

}

napi->gro_count = 0;

napi->gro_list = NULL;

}

2、在napi_skb_finish里,他会通过判断__napi_gro_receive的返回值,来决定是需要将数据包立即送进进协议栈还是保存起来。

int napi_skb_finish(int ret, struct sk_buff *skb)

{

int err = NET_RX_SUCCESS;

switch (ret)

{

/*如果返回NORMAL,就把报文送给协议栈进行处理*/

case GRO_NORMAL:

return netif_receive_skb(skb);

/*如果报文经过检查被丢弃了,释放内存并直接返回*/

case GRO_DROP:

err = NET_RX_DROP;

/*如果报文被合并了,这时报文已经被copy走了,

释放该报文占用的内存

*/

case GRO_MERGED_FREE:

kfree_skb(skb);

break;

}

return err;

}

GRO 的收包函数:

支持GRO功能的网卡驱动必须支持NAPI接口并调用GRO的专用接收函数napi_gro_receive()来把报文送给协议栈进行处理。

int napi_gro_receive(struct napi_struct *napi,struct sk_buff *skb)

{

skb_gro_reset_offset(skb);

return napi_skb_finish(__napi_gro_receive(napi, skb), skb);

}

napi_skb_finish根据__napi_gro_receive(napi, skb)函数返回的结果,来处理报文。如果合并完成或不需要gro处理,返回GRO_NORMAL。

__napi_gro_receive()算是链路层上实现的gro_receive函数,详解见下文。

(未完待续。。。。。。)

linux 网络 指示灯 亮,Linux网络子系统中GRO的实现相关推荐

  1. u盘插在电脑上灯亮没有反应_Win7系统插入U盘指示灯亮但电脑不显示的解决方法【图】...

    Win7系统插入U盘指示灯亮但电脑不显示的解决方法分享给大家,u盘可以非常方便的传输和储存文件,深受广大用户的喜爱,几乎人手必备一个!但是关于u盘的问题也有很多,比如有些用户反映,当在win7系统中插 ...

  2. 【Linux】一步一步学Linux网络编程教程汇总(更新中......)

    00. 目录 文章目录 00. 目录 01. 基础理论知识 02. 初级编程 03. 高级编程 04. LibEvent库 05. 06. 07. 01. 基础理论知识 [Linux网络编程]网络协议 ...

  3. 【Linux网络编程】TCP网络编程中connect listen和accept三者之间的关系

    00. 目录 文章目录 00. 目录 01. TCP服务端和客户端流程 02. connect函数 03. listen函数 04. 三次握手 05. accept函数 06. 附录 01. TCP服 ...

  4. 【Linux网络编程】TCP网络编程中connect()、listen()和accept()三者之间的关系

    基于 TCP 的网络编程开发分为服务器端和客户端两部分,常见的核心步骤和流程如下: connect()函数 对于客户端的 connect() 函数,该函数的功能为客户端主动连接服务器,建立连接是通过三 ...

  5. 【网络编程】Linux tcpdump命令详解---编辑中

    目录 即看即用 详细说明 简介 输出信息含义 链路层头 TCP 数据包 UDP 数据包 SMB/CIFS 解码 AFS 请求和回应 KIP AppleTalk协议 IP 数据包破碎 时间戳 反向过滤 ...

  6. TCP/IP网络协议栈在Linux内核中的如何使用丨内核开发丨驱动开发丨操作系统丨内核源码

    TCP/IP网络协议栈在Linux内核中的如何使用 视频讲解如下,点击观看: TCP/IP网络协议栈在Linux内核中的如何使用丨内核开发丨驱动开发丨操作系统丨内核源码 C/C++Linux服务器开发 ...

  7. linux网络驱动架构,Linux网络体系架构和网卡驱动设计

    Linux网络体系架构 1.Linux的协议栈层次 2.Linux的网络子系统架构 Linux的协议栈层次 Linux的优点之一在于它丰富而稳定的网络协议栈.其范围从协议无关层(例如通用socket层 ...

  8. Windows10系统的Linux子系统中安装MySQL数据库心得

    后端开发童鞋们, 自己开发机用的是Windows系统电脑(台式机或笔记本), 而开发的程序和使用的数据库等要运行在Linux服务器上, 这种情况有木有? 提前声明: 本文并不讨论操作系统的比较, 以及 ...

  9. 【学习点滴】linux调试工具、cmake和网络抓包

    目录 gdb 多进程调试 多线程调试: gdb底层原理 Linux下查看服务器端的并发连接个数: Valgrind memcheck strace Linux下,绑定1024以下的端口需要root权限 ...

最新文章

  1. 导入特征怎么实体化_幼儿教师开展集体活动开场导入方法 看过的都说很实用!...
  2. Hadoop安装及eclipse配置
  3. AspNet MVC与T4,我定制的视图模板
  4. css 盒子有内容 盒子往下掉_css盒子模型与文本溢出学习笔记
  5. 【转】C#格式化字符串
  6. Linux中shell变量作用域笔记
  7. android webview 劫持,安卓包风险安全监测提示存在Activity劫持、WebView远程代码执行,请问怎么解决?...
  8. Python: PS滤镜--径向模糊
  9. 文献阅读 Linear Regression for Face Recognition
  10. NRPE: Unable to read output 问题处理总结
  11. Python学习-第一天-函数和模块的使用
  12. 调试器如何工作(2)
  13. ANDROID仿淘宝商品浏览滑(拖)动查看详情界面
  14. Excel如何锁定首列,教程来啦!怎样将excel的列锁定冻结
  15. 位运算实现加减乘除运算——超详细C语言描述
  16. 移动端h5头像上传、头像裁切、上传图片
  17. vue2.0引入antd
  18. 【Jenkins】Jenkins容器构建脚本以及容器瘦身docker-slim使用
  19. 武汉第一职业教育中心计算机技能高考,武汉第一职业教育中心2021年招生简章...
  20. 最大流为什么会等于最小割

热门文章

  1. 保险基本概念测试人员须知(一)
  2. 练习题:千克转换成磅
  3. 军队文职(数学2+物理)——高等数学 3、求极限(一)
  4. TYPORA的使用手册
  5. 数据库基础---选择,投影,连接,除法运算
  6. 阿里巴巴字体图标使用方法
  7. 论文查重一般包括哪些部分呢?
  8. gdpr通用数据保护条例_从信息安全角度看通用数据保护条例(GDPR)
  9. MPU6050配置低功耗和中断
  10. java聊天系统异常问题_聊天室bug问题