也谈UDP GSO和GRO

文章详细探讨了Linux中的TSO/GRO技术,强调了它们与硬件的关系,以及在TCP和UDP支持、抓包分析、多层网络和wireguard-go应用中的表现。特别指出UDPGRO在内核支持和应用透明度方面的局限性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

shixudong@163.com

近期偶而关注了一下Linux的网卡卸载技术,无奈网上相关资料大多已经过时,如同鸡肋。几经筛选,终于发现tailscale有三篇文章结合实际应用对TSO/GRO介绍得比较深入,最主要的还是紧密结合linux新内核特性,更有实用价值。本人也结合自己的理解做一些解读,对TSO/GRO的基本概念就不再展开,以供随时备查。

一、背景

TSO/LRO/UFO需要硬件支持,TSO针对TCP发送,UFO针对UDP发送(基本没有物理网卡支持,自内核v4.14起废弃),LRO针对数据接收(从未进入主流,事实上已废弃,被内核实现的GRO所取代)。GSO/GRO由内核实现,GRO是早期硬件LRO的软件升级版,虽然GRO是由内核实现,但仍需修改网卡驱动以调用内核GRO接口,网卡才能真正支持GRO功能,目前绝大部分在用网卡驱动都已完成支持GRO的升级。

GSO/GRO号称更加通用,主要是两个特征,一是因为纯软件实现,不需要专用硬件支持,并且GRO实现较硬件来说,因身处内核,掌握信息更多,接收卸载更为准确,这也直接导致了纯硬件接收卸载(LRO)的没落;二是不仅支持TCP,未来还能支持UDP,然而GSO/GRO对UDP的支持迟迟没有实现。随着QUIC逐步被数据中心采用,内核从v4.18才开始支持UDP GSO,对UDP GRO的支持更是到v5.0才提供。尽管如此,GSO/GRO对UDP的支持力度还是不够,一是不像TSO,对网络应用是透明的,UDP GSO/GRO对网络应用不透明,需要应用程序重新编码并设置相应的socket选项以启用GSO/GRO。其次目前流行的各种虚拟网络通常建立在UDP隧道虚拟设备之上,而最初的UDP GSO/GRO实现对虚拟设备的支持还不够完善。

二、TCP卸载抓包效果

网上资料虽然带有丰富的TCP卸载效果图,但通过tcpdump抓包实际观察各种卸载技术并进行对比分析更有意义,有助于进一步加深印象,此处不描述具体的抓包和分析过程,而是结合卸载技术的实现逻辑分析不同因素对抓包效果的影响。

1、抓包位置影响

根据tcpdump在内核的抓包位置,由于GRO在网卡驱动层面就被调用,GRO收包发生在抓包位置之前,可以观察到GRO开关效果。鉴于GRO开关在网络驱动调用的GRO接口里判断,如某个逻辑网卡驱动(如bridge)没有必要调用GRO,自然缺乏GRO判断机制,该网卡对应的GRO开关就无实际意义。

在早期内核,GSO拆包发生在抓包位置之后,抓包工具能观察到GSO开关效果,较新内核将GSO拆包调整到了抓包位置之前,就再也不能通过抓包观察GSO的开关效果了。

TSO开关在内核实际执行GSO拆包动作前判断,无论GSO开关与否,如TSO关或不支持TSO,tcpdump只能抓到分段后的小包(也就是说,无法通过抓包观察GSO开关效果);如TSO为on,总能抓到未分段的大包(也就是说,如TSO为on,GSO总是开启)。对于逻辑网卡,TSO与硬件特性无关,表示其透传大包能力,只要ethtool能控制开关,也能被抓包观察。

2、多层网络影响

多层网络环境下(如中间有bridge),每层都是一个网络驱动程序,有自己的抓包位置和ethtool控制开关,每层抓包位置的逻辑不变,网络卸载则按如下逻辑发挥作用:在发送路径包含多个网络驱动时,如离内核更近的环节TSO为off,对大包进行了拆分操作,那么后续环节的TSO开关将失去实际意义,再也不能将其合并为大包。与之相对应,在接收路径包含多个网络驱动时,如离外部网络更近的环节GRO为on,对小包进行了合并操作,那么后续环节的GRO开关将失去实际意义,再也不能将其恢复小包。

3、同机转发路径影响

主机上层应用程序根据应用层逻辑,发出去的TCP包可为大包,也可为小包,对于TCP大包,由内核根据TSO开关控制发送到网卡驱动的数据包拆分与否。对于转发包,入口处是否合并取决于入口网卡GRO开关,转发环节上不做拆分与合并动作(二层桥转发也允许GSO大包通过),出口处是否拆分取决于出口网卡TSO开关(此处对转发出口处的分析不够全面,详见《再谈UDP GSO和GRO》)。如三层转发出口网卡在桥上,bridge和物理网卡的TSO都能影响出口包是否拆分。

三、UDP GSO/GRO

如前所述,自v4.18以来,内核开始支持UDP GSO,鉴于启用UDP GSO和传统GSO的方式不一致,需要在应用程序设置相应的socket选项显式启用,所以UDP GSO不用受ethtool的GSO开关控制。v4.18还新引入了与UDP GSO相配套的硬件特性tx-udp-segmentation,类似TSO,可通过硬件卸载UDP大包发送,该特性刚发布时,仅个别物理网卡支持。对于逻辑网卡来说,该特性表示其UDP大包透传能力,从 v4.18到v5.10,部分逻辑网卡如bridge能充分利用该特性,透明转发UDP大包。自v5.11起,内核优化了逻辑网卡支持该特性的方式,独立虚拟网卡如wireguard等也开始支持该特性。然而美中不足的是,v5.11的修改,导致先前已支持该特性的部分逻辑网卡如bridge反而不再支持该特性(除非下挂的物理网卡也支持该特性)。UDP GSO虽然不受GSO开关控制,但同样使用了传统GSO的底层框架和机制,所以仅在网卡支持并开启tx-udp-segmentation特性时,才能使用抓包工具直观验证UDP GSO效果。

自v5.0起,内核开始支持UDP GRO,不过也需要应用程序显式启用UDP GRO,同时因UDP GRO收包动作和传统GRO一致,由网卡驱动调用GRO接口实现,所以同样受到网卡GRO开关的控制,这点和UDP GSO稍有区别。自v5.6起,支持UDP fraglist GRO/GSO(入口网卡rx-gro-list为on,此处GSO主要配套用于转发环节),自v5.12起,支持普通(non-fraglisted)UDP GRO大包的接收和转发(入口网卡rx-udp-gro-forwarding为on,同时rx-gro-list必须为off)。以上两种情形,UDP GRO大包接收都对应用程序透明,无需显式启用UDP GRO功能,大大拓展了UDP GRO的使用场景。

四、wireguard-go的UDP GSO/GRO

本文开头提到的Tailscale关于wireguard-go优化的三篇文章,第一篇实现了wireguard-go对上层TCP应用的TSO/GRO支持,第二篇基于v4.18/v5.0的UDP GSO/GRO功能,实现了对底层UDP GSO/GRO支持,用于卸载wireguard协议本身。第三篇依赖v6.2内核tun驱动对UDP GSO/GRO的全面支持,自行实现了wg网卡对上层UDP GRO应用的透明支持(上层应用无需显式启用UDP GRO)。Wireguard-go在实现上述TCP/UDP GRO支持时,除了对底层UDP GRO的支持需要依托底层网卡的GRO机制外,对上层应用的GRO支持(包括TCP/UDP),无需依赖内核TCP/UDP GRO接口调用,所以该wg网卡与GRO相关的ethtool控制开关(如gro、rx-gro-list、rx-udp-gro-forwarding等)并无实际作用。

至于第三篇介绍的wireguard-go对上层应用的UDP GSO支持,亮点就是借助v6.2内核tun驱动,实现了wg网卡的tx-udp-segmentation特性(默认打开),允许wg网卡透明发送UDP GSO大包(内核wireguard自v5.11就支持了)。然而对于上层应用来说,依然需要显式启用UDP GSO功能才能享受wg网卡透明发送UDP大包的能力。此外第二篇介绍的wireguard-go在实现底层UDP GSO支持时,主要目标还是为了给上层TSO/UDP GSO锦上添花,当上层不使用TSO或不支持UDP GSO功能时,底层即使支持UDP GSO也无法将上层多个TCP/UDP包合并为底层一个UDP大包。这个不足实际上是由GSO/GRO的内在机制造成的,GSO只负责硬件不支持时的数据包拆分,不负责发送路径上的数据包合并,而GRO则正好相反,只负责接收路径上的数据包合并。

五、rx-gro-list与rx-udp-gro-forwarding的区别

UDP GSO由于无法实现对UDP应用的透明支持,大规模推广存在一定困难,然而内核对UDP GRO的透明支持却越加完善,如前所述,rx-gro-list和rx-udp-gro-forwarding都支持接收和转发UDP GRO大包。用户空间wireguard-go更是如此,并且接收和转发UDP GRO大包不用受相关gro开关的控制。在提供UDP GRO大包转发服务时,与出口网卡的tx-udp-segmentation特性相结合,可大大提高UDP大包的转发效率(如出口网卡为物理网卡,可实现硬件卸载;如出口网卡为逻辑网卡,可实现透明转发)。

虽然rx-gro-list和rx-udp-gro-forwarding都能支持UDP GRO大包转发,但他们的处理方式有所区别,前者采用fraglist方式,后者采用non-fraglisted方式。针对处理方式的不同,转发UDP GRO大包时对出口网卡的特性要求也是不同的,rx-udp-gro-forwarding只要求出口网卡支持tx-udp-segmentation即可,而rx-gro-list要求出口网卡除了支持该特性外,还得同时支持tx-gso-list和tx-scatter-gather-fraglist。目前基本上没有硬件网卡能支持tx-scatter-gather-fraglist,对于逻辑网卡来说,估计是历史遗留问题,目前尚不能同时支持这三种特性,v5.11起的wireguard,目前还不支持tx-scatter-gather-fraglist,v6.2起的tun(wireguard-go),目前还不支持tx-gso-list。因此如出口网卡具备tx-udp-segmentation特性时,为确保能硬件卸载(物理网卡)或透明转发(逻辑网卡)UDP GRO大包,入口网卡应启用rx-udp-gro-forwarding而非rx-gro-list开关(不能同时启用,否则rx-gro-list优先起作用)。

Tailscale的第三篇文章也专门指出,将wireguard-go用于转发出口网卡时,为了充分利用其tx-udp-segmentation特性,入口网卡应只启用rx-udp-gro-forwarding:“We recommend leaving rx-gro-list disabled. With rx-gro-list taking precedence over rx-udp-gro-forwarding, the effects of UDP GRO will be limited, reducing UDP throughput. rx-gro-list is a compelling, performance-enhancing feature, but the Linux kernel does not currently support a method to carry its benefits through the TUN driver. Instead, packets are segmented before being transmitted out a TUN device.”说了一大通貌似深奥的理由,然而其实质乃是v6.2内核的TUN驱动在实现UDP GSO支持时忘了同时支持NETIF_F_GSO_FRAGLIST属性而已(对应tx-gso-list),虽然TUN已支持tx-udp-segmentation和tx-scatter-gather-fraglist,但三者缺一不可,导致TUN无法透明转发fraglist方式的UDP GRO大包。

同理将内核wireguard网卡用于转发出口网卡时,虽然wg网卡已支持tx-udp-segmentation和tx-gso-list,但并没有同时支持tx-scatter-gather-fraglist,因而入口网卡同样也应只启用rx-udp-gro-forwarding,才能真正发挥wg网卡透明转发UDP GRO大包的能力。

本人在wsl环境下重新编译wsl内核,优化了逻辑网卡的NETIF_F_GSO_SOFTWARE定义和一般网卡的NETIF_F_SOFT_FEATURES定义,为wireguard增加了tx-scatter-gather-fraglist特性,为tap/tun增加了tx-gso-list特性后,无论入口网卡选择rx-gro-list还是rx-udp-gro-forwarding,内核和用户空间的wg网卡都能透明转发UDP GRO大包而无需交由GSO重新拆分为小包。
补充和小结一下,UDP GSO/GRO使用的底层框架和传统GSO/GRO完全一致,故TCP卸载抓包验证效果同样适用于UDP(实际上可以通过抓包工具直观感受UDP GSO的效果,详见《再谈UDP GSO和GRO》)。内核引入的UDP GSO/GRO新特性,如网卡支持的话,tx-udp-segmentation将默认开启,其他特性默认关闭,需要手动开启。wireguard-go针对上层应用实现的TCP/UDP GRO,默认启用,相应开关无实际作用,至于TCP/UDP GSO的实现,完全基于内核,tx-udp-segmentation也是默认开启,可以手动关闭。
最后顺便提一下,对于业务实时性要求高、但吞吐量要求不高的场合,开启UDP GRO将引入不必要的延时,建议同时关闭UDP GRO的本机接收和转发功能。

                
<think>嗯,用户让我介绍一下LinuxUDP的性能调优。好的,首先我需要回忆一下UDPTCP的区别。UDP是无连接的,所以它更注重速度低延迟,但不像TCP那样有拥塞控制重传机制。所以性能调优方面可能需要注意不同的方面。 用户可能是系统管理员或者开发人员,他们可能在处理高吞吐量的应用,比如实时视频流、在线游戏或者VoIP。这些场景下,UDP的性能问题可能会更明显,比如丢包、延迟或者吞吐量不足。需要针对这些方面进行优化。 首先,我应该考虑系统层面的参数调整。比如,增大UDP缓冲区的大小。默认的缓冲区可能比较小,当数据量大的时候容易丢包。所以需要调整net.core.rmem_maxnet.core.wmem_max,还有具体的参数比如net.ipv4.udp_mem。这部分可能需要用sysctl命令来设置。 然后,可能需要提到多队列网卡中断亲性。现在的服务器通常有多核CPU,正确配置网卡的中断分配到不同的CPU核心,可以减少竞争,提高处理效率。比如使用ethtool配置IRQ亲性,或者启用RSS(接收端缩放)。 接下来,应用程序本身的优化也很重要。比如使用recvmmsgsendmmsg这样的系统调用来批量处理数据包,减少系统调用的次数。还有绑定特定的CPU核心,利用亲性减少上下文切换。 另外,可能要考虑内核版本的升级。新的内核通常对网络栈有优化,比如更高效的NAPI机制,或者GRO(Generic Receive Offload)之类的功能,可以提升处理效率。 还有SO_REUSEPORT选项,允许多个套接字监听同一个端口,内核会自动分配连接,提高多核利用率。这对于多线程应用来说可能很有用。 硬件方面也不能忽视,比如使用更快的网卡,支持DPDK或者XDP的技术,可以绕过内核直接处理数据包,大幅提升性能。不过这可能涉及更复杂的配置,用户可能需要根据实际情况选择。 可能还需要注意防火墙规则,过多的iptables或者nftables规则会增加处理时间,导致性能下降。简化规则或者使用更高效的数据结构(比如ipset)可能会有帮助。 另外,监控工具的使用也很重要,比如用sar、netstat或者ss查看统计信息,发现是否有丢包或者错误。ethtool可以查看网卡统计信息,帮助诊断问题。 用户可能遇到的常见问题包括缓冲区溢出导致的丢包,这时候需要检查缓冲区大小是否足够。或者CPU负载不均衡,导致某个核心成为瓶颈,这时候调整中断亲性可能会有帮助。 还有,分片的问题。UDP数据包过大导致分片,会增加丢包的概率,所以应用程序最好控制包的大小在MTU以内,避免分片。 总之,调优UDP性能需要从系统参数、应用程序设计、硬件配置等多个方面入手,根据具体的应用场景瓶颈进行针对性调整。可能需要用户一步步测试验证,才能找到最适合自己环境的配置。</think>在Linux系统中,UDP性能调优主要围绕提升吞吐量、降低延迟减少丢包率展开。以下是一些关键优化方向及实践方法: --- ### **一、系统级参数调优** 1. **增大UDP缓冲区** UDP是无连接的协议,若接收/发送缓冲区不足会导致丢包。 ```bash # 调整最大缓冲区大小(单位:字节) sysctl -w net.core.rmem_max=268435456 # 接收缓冲区 sysctl -w net.core.wmem_max=268435456 # 发送缓冲区 sysctl -w net.ipv4.udp_mem="min 4096000 max 268435456" # 全局内存限制 ``` 永久生效需写入 `/etc/sysctl.conf`。 2. **优化网络队列长度** 增加网络接口的发送/接收队列长度,减少丢包风险: ```bash ifconfig eth0 txqueuelen 10000 ``` 3. **启用GRO/GSO(通用分段/接收卸载)** 利用网卡硬件特性批量处理数据包,降低CPU负载: ```bash ethtool -K eth0 gro on # 接收方向 ethtool -K eth0 gso on # 发送方向 ``` 4. **多队列网卡(RSS)与中断亲性** 为多核CPU分配独立的网卡队列中断,避免竞争: ```bash ethtool -L eth0 combined 8 # 启用8个队列(需网卡支持) echo 1 > /proc/irq/${IRQ_NUM}/smp_affinity_list # 绑定中断到特定CPU ``` --- ### **二、应用程序优化** 1. **批量处理数据包** 使用 `recvmmsg`/`sendmmsg` 替代 `recvmsg`/`sendmsg`,减少系统调用次数。 2. **绑定CPU亲性** 通过 `pthread_setaffinity_np` 或 `taskset` 绑定线程到特定CPU核心,减少上下文切换。 3. **启用SO_REUSEPORT** 允许多个套接字监听同一端口,内核自动分配连接,提升多核利用率: ```c int opt = 1; setsockopt(sock_fd, SOL_SOCKET, SO_REUSEPORT, &opt, sizeof(opt)); ``` 4. **避免分片(MTU优化)** UDP包大小应控制在 **MTU - IP头 - UDP头 = 1500 - 20 - 8 = 1472字节** 以内,避免分片导致的丢包。 --- ### **三、内核与硬件优化** 1. **升级内核** 新版本内核(如5.x+)通常对网络协议栈有优化,例如更高效的NAPI机制。 2. **使用高性能网卡** 支持DPDK(用户态驱动)或XDP(eXpress Data Path)的网卡可绕过内核协议栈,显著提升吞吐量。 3. **禁用无关功能** 关闭无关的防火墙规则、QoS策略或调试日志,减少CPU开销: ```bash sysctl -w net.core.netdev_budget=6000 # 提升单次NAPI处理包数 ``` --- ### **四、监控与诊断工具** 1. **查看丢包统计** ```bash netstat -su # UDP丢包统计 ethtool -S eth0 # 网卡详细统计 ``` 2. **追踪瓶颈** - `sar -n UDP 1`:实时监控UDP流量。 - `perf` 或 `bpftrace`:分析内核协议栈瓶颈。 --- ### **五、典型场景调优** - **低延迟场景**:禁用 `net.ipv4.udp_l3mdev_accept`,减少路由查找开销。 - **高吞吐场景**:结合 `XDP` 或 `AF_XDP` 实现内核旁路。 - **容器环境**:调整容器的网络命名空间参数(如 `net.core.somaxconn`)。 --- ### **总结** UDP调优需结合具体场景,通过 **系统参数调优 + 应用程序优化 + 硬件升级** 多维度改进。建议逐步测试调整,并通过监控工具验证效果。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值