DPDK内核的NIC接口(KNI)允许用户空间的应用程序访问Linux的控制面。使用DPDK的KNI的好处有如下三点:
- 通过消除系统调用以及对copy_to_user()/copy_from_user()操作而获得比现有的Linux TUN/TAP接口更快的速度。
- 允许使用标准的Linux网络工具来管理DPDK端口,如tcpdump、ifconfig等。
- 允许使用内核网络栈接口。
1、DPDK KNI内核模块
KNI内核的可加载模块支持两种类型的设备:
- 网络设备:
- 接口名称由用户空间提供。
- MAC地址可以是真实的NIC MAC地址或者随意的一个MAC地址。
- 诸如像netdev_ops,header_ops,ethtool_ops所实现的网络功能由struct net_device来定义,这其中也包含mbuf和FIFO。
- 其他设备:
- 通过调用ioctl创建网络设备。
- 维护被所有KNI实例所共享的内核线程上下文。
- 对于单线程内核模型,维护所有KNI实例共享的内核线程上下文。
- 对于多线程内核模型,为每个KNI实例维护一个内核线程上下文。
DPDK的应用程序动态创建KNI接口。接口的名称和FIFO信息使用结构体rte_kni_device_info表示,并且通过调用ioctl来获取,结构体rte_kni_device_info包含以下内容:
3.DPDK的mbuf流更详细的内容请参考DPDK源代码中的rte_kni_common.h。
- 接口名称。
- 与FIFO相关联内存区的物理地址。
- Mbuf内存池的详细信息,包括物理的和虚拟的。
- PCI的信息。
- 内核的亲和性。
物理地址会从映射到内核地址空间,并且存储在单独的KNI上下文中。
内核的RX线程的亲和性由force_bind和core_id的配置参数控制。
KNI接口在创建之后可以在DPDK应用程序中动态删除。此外,当DPDK应用程序关闭时,其他设备的释放操作会删除所有未删除的KNI接口。
为了最小化在内核空间运行的DPDK代码数量,只在用户空间对mbuf内存池进行管理。内核模块知道mbuf的存在,但所有mbuf的分配和释放操作都只由DPDK应用程序来处理。下图展示了在双向上发送数据包的典型场景。
4、实例:入口
在DPDK的RX侧的mbuf是由处在RX线程上下文下的PMD所分配的,该线程把mbuf放在rx_q的FIFO中排队。KNI线程轮询rx_q中所有活动的KNI设备。如果一个mbuf出队,其会被转化为sk_buff并且通过netif_rx()发送到网络栈。出队的mbuf必须被释放,所以这个指针会发送到free_q FIFO中。5、实例:出口
在同一主循环中,RX线程会轮询free_q FIFO,并且释放出队的mbuf。
对于数据包的出口侧,DPDK应用程序必须先入队几个mbuf以便在内核侧创建一个mbuf缓存。6、链路状态和MTU的改变
通过调用kni_net_tx(),从Linux网络堆栈接收数据包。由于存在mbuf缓存,故mbuf无需等待便可出队并且用sk_buff中的数据填充mbuf。之后,sk_buff被释放并且将mbuf送入tx_q FIFO.
DPDKde TX线程将mbuf出队,并通过rte_eth_tx_burst()将mbuf发送到PMD。之后将mbuf返回给内核端的缓存。
链路状态和MTU的改变通常是通过ifconfig来完成的特定操作。该请求从内核端发起(在ifconfig进程的上下文中),由用户空间的DPDK应用程序处理。应用程序会轮询请求,调用应用程序的处理函数并将响应结果返回到内核空间。
应用程序的处理函数可以在接口创建的时候注册,也可以在运行期间注册和注销。这为多进程场景下的应用提供了灵活性。其约束是单个进程来注册和处理请求。