实现一个自定义的Linux bond tx_hash_policy

19 阅读4分钟

边学边做,法力无边

背景知识

Linux bond 可以将多个口合并为一个虚拟口使用,其好处取决于bond的mode。通常是可以充分利用多个口带来的带宽增加,实现负载均衡,failover能力等等。

着重介绍下几个balance模式,借用下DPDK社区的图片

mode 0: balance rr

mode 2: balance xor

mode 4: 802.3AD Link Aggregation

实际场景通常 mode 4模式非常受欢迎,因其和交换机双侧支持LACP协议可以保证快速的failover切换。

上面的图片可以看到发送的时候,RR模式下,报文不是按照自身的特点,是按照顺序逐个选择口发送,XOR模式下,报文依据自身报文特点利用xor运算计算hash值然后选择端口发送。同时观察可以知道mode 2与 mode 4在发送特征上没有区别。

另外上面的图没有正确表示接收,图片都是把接收放在了第一个成员口。实际是因为接收不是由bond成员设备决定的,实际上是对端交换机的发送策略决定的。对于Balance RR,这种模式肯定不是同流同口。

如果Linux设备和交换机都是用XOR来实现,那么需要注意它们的hash实现,比如linux选择使用layer3+4,即使用ip的源目的地址,udp、tcp的源目的port来做hash,然后选择口,如果交换机用的是layer2,也就是mac地址来做hash,这也会造成一条流会落在不同的口上,存在乱序风险。

一个应用场景

在嵌入式场景下比如车载,switch 通常需要接比较多的外设,混合100M和1G等不同速率,内部主要计算设备使用1G口接多个到switch。部分场合使用多个口是通过分配不同IP段,不同的业务走不同的IP,提高设备利用率。这实际上也是一种人为实现的负载分摊。

如果简化设备网络架构,那么bonding是比较合适的,单ip配置,bonding实现负载均衡,并且支持failover能力,保证所有流量即使某个成员口link down也能切换到另一条路。但是嵌入式switch的lag能力和服务器端存在很大差异,比如其设计的tx hash算法可能是厂商独有的。如果配置后,会发现Linux内核里面任何实现都无法保证在和其共同工作时达成同流同口的算法效果。

实现方式

主要是修改bonding.ko 的代码,增删在内不超过50行代码实现。

  • 实现 tx hash 函数。 模拟switch实现一个tx hash 实现,比如 myhash,这个不难吧,只要厂商文档描述提供实现细节就都能做。主要实现时候要注意两点,一个是计算hash的时候需要交换源目的地址,以及端口才能实现类似Toeplitz的效果,出入才能相同,另一个就是主要大小端。

  • 实现配置方式 这也比较好理解,类似的xmit_hash_policy下新enum一个代表着自定义的hash策略。然后将bond_xmit_hashtype_tbl里面的参数按照策略名称,枚举顺序添加一个。

{ "myhash3+4", BOND_XMIT_POLICY_MYHASH34, 0},

同时MODULE_PARM_DESC里面找到xmit_hash_policy然后也加上策略名称,至此实现的参数,modinfo里面就能看到了。

  • 解析逻辑 这里和我们自定义的策略生效在哪一层有关,比如是希望生效在Layer3+4,那么就放在原生layer3+4的位置, 主要修改的位置在__bond_xmit_hash,bond_flow_dissect等位置,并且在__bond_xmit_hash中选择合适位置调用我们实现的tx hash函数。

编译make M=drivers/net/bonding/ modules -j8 得到bonding.ko

由于自定义的策略iproute2是没有的,用ip link方式配置会被拦截报错,可以用文件系统绕开

echo myhash3+4 > /sys/class/net/bond0/bonding/xmit_hash_policy

自此就完成了,验证 用iperf3 打流,加上iperf3 -c ip地址 --cport xxx 可以固定一条流,Linux验证sar -n DEV 1 观察是否同流同口,多看几个口即可。

回顾小结

  • 理解bond的原理
  • 了解内核模块开发
  • 了解一个switch lag的hash算法,并仿造实现。