Seastar和DPDK的开发总结
DPDK是Intel提供的数据平面开发套件,为用户空间高效的数据包处理提供驱动和函数库支持。 使用DPDK开发的应用程序,需要自行实现网络协议栈,使用门槛相对较高。
Seastar是一个面向现代硬件多核架构的高性能应用程序开发框架,它实现了native网络协议栈,同时集成DPDK。
最近使用DPDK对Seastar进行扩展,支持网卡bonding、Linux控制平面访问等,在此做一个小结。
Linux控制平面访问
KNI(Kernel NIC Interface)允许DPDK应用程序访问Linux控制平面,比如使用SSH远程访问机器,KNI能够让ssh连接发送的数据到达sshd进程。
KNI有以下优势:
- 相比TUN/TAP接口,KNI避免两次内核态与用户态间的数据拷贝,更加高效
- 支持使用Linux标准网络工具(ifconfig/ethtool/tcpdump等)操作DPDK网络接口
- 支持在内核网络协议栈上创建网络接口
启用KNI,首先加载KNI内核模块rte_kni
,该模块自动创建一个特殊设备/dev/kni
,而后通过KNI的API函数调用与内核进行交互。
创建KNI虚拟网络接口,正确配置ip/netmask/boardcast/hwaddr/gateway,向KNI接口写入的数据可以顺利到达内核。
网卡和网关配置。必须与机器原有配置保持一致
VLAN
VLAN(Virtual LAN,虚拟局域网)用于局域网分割广播域,减少网络带宽和CPU消耗,提高网络处理能力。
VLAN环境下的机器需要配置vlan网络接口,接口命名规则是${interface}.${vlan_id}
,vlan_id范围是0-4095。
以某物理机为例,eth0
和eth1
是物理网卡,bond0
是在它们之上做的网卡bonding,bond0.107
是bond0
的vlan网络接口(107是vlan_id)。
|
|
查看配置文件/etc/sysconfig/network-scripts/ifcfg-bond0.107,
VLAN="yes"
表示其为vlan接口
IEEE 802.1Q标准封装的vlan数据帧在传统以太网数据帧上附加了vlan-tag,机器与与交换机的数据传输是否带vlan-tag,可以在机器上抓包分析;在此机器上测试,收到和发出的数据包均带有vlan-tag。
DPDK应用程序直接操作网卡队列,不需要vlan网络接口,收包时进行untagging(利用网卡自身offload功能将vlan-tag剥离至rte_mbuf),发包时进行tagging(为rte_mbuf设置vlan-tag)。
RSS和多网卡队列
RSS(Receive Side Scaling)是一种网卡驱动技术,能够让网卡的数据流量分布在多个CPU上。
启用DPDK的RSS功能,需要预设Hash Function、Hash Key和Hash Types,其中Hash Function一般是Toeplitz哈希算法,Hash Types是一个标记位组合,标识哪类数据包接受RSS计算。
网卡收到数据包后,以数据包的IP端口等信息作为输入(根据packet类型,输入可能为二/三/四元组),经过Hash Function计算出一个哈希值,哈希值的LSBs(最低有效位)用于索引RETA
(即indirection table
),对应的值是网卡队列ID,即数据包将发往该网卡队列。
INTEL 82576/82599网卡的RETA是位宽为4的128项的索引映射表,取哈希值低7位(LSBs)作为索引;Seastar默认的RETA填充策略是按照网络队列ID从小到大依次填充,另外,RETA支持运行时动态调整;Seastar将每个网卡队列绑定到一个核,网卡队列ID即CPU ID。
Seastar应用程序与外部进行连接时,connect函数随机选择一个本地端口(local_port),然后对四元组(dst_ip, dst_port, local_ip, local_port)
做RSS计算,如果结果与当前CPU ID一致,则使用该端口发起连接,目的是对端发送的数据由当前CPU接收和处理。
数据包分发
Seastar的native协议栈实现了ARP/ICMP/TCP/UDP等协议,DPDK模式下收到的数据包全部进入native协议栈;在引入KNI后,需要对数据包进行解析,根据规则进入不同的协议栈。
-
ARP数据包
ARP数据包按类型分为request和Reply,request只需进入native协议栈;reply则需要知道request由哪个协议栈发起,进入该协议栈。
鉴于ARP请求处理开销不大,当前实现不管request还是reply,同时进入native协议栈和kernel协议栈。
-
ICMP数据包
ICMP请求的数据包进入native协议栈,当然也可以进入kernel协议栈,但需要经过KNI这一层的处理,会增加些许延迟。
同时进入两个协议栈,对端会出现
ping (DUP!)
错误 -
应用程序数据包
应用程序数据包根据目标端口区分,进入native协议栈,端口包括两类:
- server端口 - 用于accept客户端连接,在程序启动前已经确定
- client端口 - connect函数调用所使用的本地端口,与CPU ID相关的随机端口
Seastar与外部建立连接后返回一个
connected_socket
,但无法确定所用的本地端口,当前实现是对应的dpdk_qp
(相当于网卡队列)维护一个本地端口表,执行tcp::tcb::connect()
建立连接前注册端口,执行tcp::tcb::cleanup()
关闭连接后注销端口。 -
其他
上述三类之外的数据包,全部进入kernel协议栈。
Seastar网络初始化
create_dpdk_net_device()
函数创建DPDK设备:
- 初始化物理网卡(slave),根据网卡的物理特性启用offload,设置网卡队列长度,设置RSS
- 创建bond-port(名称是dpdk_bond,模式是8023AD),添加slave-port
- 初始化bond-port,配置读写队列数量、xmit_policy
- 创建kni(名字是dpdk_bond0)
- 创建rte_ring,用于多核通信(多写一读)的kni数据包队列
- 在每个core上,初始化对应的dpdk_qp,创建rx/tx队列
- 启动slave-ports和bond-port,设置reta,网卡均启动后,配置kni的IP/netmask/boardcast/hw/gateway
至此,网络初始化完成。
|
|
参考资料
- https://doc.dpdk.org/guides/prog_guide/kernel_nic_interface.html
- https://en.wikipedia.org/wiki/IEEE_802.1Q
- https://docs.microsoft.com/en-us/windows-hardware/drivers/network/introduction-to-receive-side-scaling
- https://docs.microsoft.com/en-us/windows-hardware/drivers/network/rss-hashing-functions
- https://docs.microsoft.com/en-us/windows-hardware/drivers/network/rss-hashing-types
- http://galsagie.github.io/2015/02/26/dpdk-tips-1/