目录

Seastar和DPDK的开发总结

DPDKData Plane Development Kit是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函数调用与内核进行交互。

/images/seastar-dpdk/kni.png

创建KNI虚拟网络接口,正确配置ip/netmask/boardcast/hwaddr/gateway,向KNI接口写入的数据可以顺利到达内核。

网卡和网关配置。必须与机器原有配置保持一致

VLAN

VLAN(Virtual LAN,虚拟局域网)用于局域网分割广播域,减少网络带宽和CPU消耗,提高网络处理能力。

VLAN环境下的机器需要配置vlan网络接口,接口命名规则是${interface}.${vlan_id},vlan_id范围是0-4095。

以某物理机为例,eth0eth1是物理网卡,bond0是在它们之上做的网卡bonding,bond0.107bond0的vlan网络接口(107是vlan_id)。

1
2
3
4
5
6
7
[root@~]# lshw -businfo -class network
Bus info          Device     Class          Description
=======================================================
pci@0000:06:00.0  eth0       network        Ethernet Controller 10-Gigabit X540-AT2
pci@0000:06:00.1  eth1       network        Ethernet Controller 10-Gigabit X540-AT2
                  bond0      network        Ethernet interface
                  bond0.107  network        Ethernet interface

查看配置文件/etc/sysconfig/network-scripts/ifcfg-bond0.107,VLAN="yes"表示其为vlan接口

IEEE 802.1Q标准封装的vlan数据帧在传统以太网数据帧上附加了vlan-tag,机器与与交换机的数据传输是否带vlan-tag,可以在机器上抓包分析;在此机器上测试,收到和发出的数据包均带有vlan-tag。

/images/seastar-dpdk/Ethernet_802.1Q_Insert.jpg

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。

/images/seastar-dpdk/rss.png

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

至此,网络初始化完成。

1
2
smp::configure();
engine().when_started().then([] {});

参考资料