DPDK KNI的几个问题解决和后续发展

2021-02-20 20:48:24 浏览数 (1)

前言

DPDK KNI是什么

KNI全称为Kernel NIC Interface,是DPDK框架下实现的DPDK与内核的高性能通信方案。

KNI解决什么问题

主要解决物理网卡被DPDK接管后,仍然需要使用内核协议栈的问题;

此外,相对于TUN/TAP方式,减少一次拷贝,性能更高。

KNI如何使用

  • 加载rte_kni.ko模块
  • 整体初始化:rte_kni_init
  • 创建/释放一个KNI接口:rte_kni_alloc/rte_kni_release
  • 报文收发:rte_kni_rx_burst/rte_kni_tx_burst

几个问题

KNI是DPDK引入的技术,早期实现上为了高性能有不少问题,以下逐个分析(注:部分问题在最新版本中已经解决)

KNI FIFO内存一致性问题

KNI实现中,DPDK程序与内核间,一般都是两个线程在处理,通信方式是FIFO,收发方向都有单独的FIFO;

早期实现仅考虑了strongly-ordered systems,典型的x86,因为未触发乱序问题(buffer与read/write指针的乱序行为)。

后续版本中,ARM环境的引入暴露了此问题,导致mbuf被复用等问题发生。

解决方式是引入合适的memory barrier。

KNI 巨帧包(scattered packets)处理问题

之前提到KNI相对于TUN/TAP方式,减少了一次拷贝。

  • TUN/TAP方式
    • 第一次拷贝:DPDK程序将报文发往内核,此时write系统调用拷贝;
    • 第二次拷贝:内核收到消息后,拷贝到skb中。
  • KNI方式
    • DPDK程序将报文发往内核,通过共享大页内存实现,避免了此次拷贝;然后将报文从大页拷出到skb中。

从上面的对比可以看出,KNI核心是共享大页内存,FIFO传递物理地址,内核收到后转换为内核地址进行处理。

但是在网络中,存在一种mbuf chain情况(典型的,mbuf本身2k,收包9k,会串成5个mbuf构成的chain),在用户态这些mbuf都是通过虚拟地址方式连结,往KNI传递时会遇到地址转换问题。

分两种情况:

  • chain上所有mbuf属于同一块大页内存,此时通过首片的偏移能够推算出后续的偏移;KNI默认采用此方式。
  • chain上的mbuf属于不同大页内存,此时就不能通过首片来计算。KNI内核会得到错误地址,严重时导致crash。 修复方式:提前将chain上mbuf地址转换为物理地址。 https://git.dpdk.org/dpdk/commit/?id=5eb1708ec1db1f2d644f44a42468139df0b0ad6c

KNI释放时mbuf泄露问题

KNI早期实现未考虑KNI释放时,FIFO中mbuf的释放问题,反复创建/释放KNI口的情况下可能会导致mbuf泄露。

修复方式:KNI释放时转换未处理的mbuf

https://git.dpdk.org/dpdk/commit/?id=e77fec694936ce067153c5a6596c6bc818baaa5c

后续发展

evendfd epoll workqueue方式处理报文

当前KNI内核部分,是通过启动一个内核线程轮询收包实现,为了避免占用CPU过多,又加入了sleep机制,导致KNI的CPU/时延/吞吐等都不是很理想。更合理的方式是通过eventfd机制,用户态发包后唤醒kernel处理,采用NAPI类似方式,使得CPU/时延/吞吐达到理想状态。

多队列支持

当前KNI不支持多队列,极限性能受限(single线程模式不超过1GBps,mutiple线程模式又占用过多资源)。

在实现eventfd基础上,再实现多队列机制,可以让性能更加,同时在闲时不占用额外资源。

参考连接

https://doc.dpdk.org/guides/prog_guide/kernel_nic_interface.html

http://doc.dpdk.org/api/rte__kni_8h.html

0 人点赞