DPDK Programmer’s Guide(34)内核网卡接口[KNI]

官方文档查看地址:
http://doc.dpdk.org/guides/prog_guide/kernel_nic_interface.html
PDF下载地址:
https://www.intel.com/content/www/us/en/embedded/technology/packet-processing/dpdk/dpdk-programmers-guide.html

本篇难度系数:
翻译:☆☆☆☆☆
理解:★★☆☆☆

34. 内核网卡接口

DPDK内核网卡接口(KNI:Kernel NIC Interface)允许用户空间应用程序访问Linux*控制平面。

使用DPDK KNI的好处如下:

  • 比现有的Linux TUN/TAP接口更快(通过消除系统调用和copy_to_user()/copy_from_user()操作)。
  • 允许使用标准的Linux net工具(如ethtool、ifconfig和tcpdump)管理DPDK端口。
  • 允许与内核网络堆栈的接口。

使用DPDK内核NIC接口的应用程序的组件如图34.2所示。
在这里插入图片描述
图34.2 DPDK KNI应用程序的组件 Fig. 34.2 Components of a DPDK KNI Application

34.1DPDK KNI内核模块

KNI内核可加载模块rte_kni为DPDK应用程序提供内核接口。

加载rte_kni模块后,它将创建一个设备/dev/kni, DPDK KNI API函数使用该设备来控制内核模块并与内核模块通信。

rte_kni内核模块包含几个可选参数,可以在加载模块时指定这些参数来控制其行为:

# modinfo rte_kni.ko
<snip>
parm:           lo_mode: KNI loopback mode (default=lo_mode_none):
                lo_mode_none        Kernel loopback disabled
                lo_mode_fifo        Enable kernel loopback with fifo
                lo_mode_fifo_skb    Enable kernel loopback with fifo and skb buffer
                 (charp)
parm:           kthread_mode: Kernel thread mode (default=single):
                single    Single kernel thread mode enabled.
                multiple  Multiple kernel thread mode enabled.
                 (charp)
parm:           carrier: Default carrier state for KNI interface (default=off):
                off   Interfaces will be created with carrier state set to off.
                on    Interfaces will be created with carrier state set to on.
                 (charp)

在没有任何可选参数的情况下加载rte_kni内核模块是DPDK应用程序将数据包进出内核网络堆栈的典型方式。在没有任何参数的情况下,仅为内核端接收数据包的所有KNI设备创建一个内核线程,禁用环回模式,并将KNI接口的默认载波状态设置为off。

# insmod kmod/rte_kni.ko

34.1.1环回模式
测试时,可以通过指定lo_mode参数,在loopback模式下加载rte_kni内核模块:

# insmod kmod/rte_kni.ko lo_mode=lo_mode_fifo

lo_mode_fifo loopback选项将在内核空间中循环回ring入/出队列操作。

# insmod kmod/rte_kni.ko lo_mode=lo_mode_fifo_skb

lo_mode_fifo_skb loopback选项将回ring入/出队列操作和内核空间中的sk缓冲区副本。

如果没有指定lo_mode参数,则禁用环回模式。

34.1.2内核线程模式
为了提供性能的灵活性,可以使用kthread_mode参数加载rte_kni内核模块。rte_kni内核模块支持两个选项:“单内核线程”模式和“多内核线程”模式。

启用单内核线程模式如下:

# insmod kmod/rte_kni.ko kthread_mode=single

此模式将仅为所有KNI接口创建一个内核线程,以便在内核端接收数据。默认情况下,这个内核线程不绑定到任何特定的内核,但是当创建第一个KNI接口时,用户可以通过在struct rte_kni_conf中设置core_idforce_bind参数来为这个内核线程设置核心关联:

为了获得最佳性能,内核线程应该绑定到与应用程序中使用的DPDK lcore相同套接字中的CPU核心。

还可以将KNI内核模块配置为为DPDK应用程序创建的每个KNI接口启动单独的内核线程。启用多内核线程模式如下:

# insmod kmod/rte_kni.ko kthread_mode=multiple

该模式将为每个KNI接口创建一个单独的内核线程,以便在内核端接收数据。创建每个KNI接口时,可以通过在struct rte_kni_conf中设置core_id和force_bind参数来指定每个kni_thread内核线程的核心关联性。

如果主机系统上有足够的未使用内核可用,多内核线程模式可以提供可伸缩的更高性能。

如果没有指定kthread_mode参数,则使用“单内核线程”模式。

34.1.3默认的载体(载波?)状态
rte_kni内核模块创建的KNI接口的默认载波状态在加载模块时通过载波选项进行控制。

如果指定载波=off,内核模块将在启用接口管理时关闭接口的载波状态。DPDK应用程序可以使用rte_kni_update_link()函数设置KNI接口的载波状态。这对于DPDK应用程序非常有用,因为DPDK应用程序要求KNI接口的载波状态反映相应物理NIC端口的实际链路状态。

如果指定carrier=on,内核模块将在启用接口管理时自动将接口的载波状态设置为up。这对于DPDK应用程序非常有用,这些应用程序使用KNI接口作为一个纯虚拟接口,它不对应于任何物理硬件,并且不希望使用rte_kni_update_link()显式地设置接口的载波状态。在NIC端口可能没有物理连接到任何东西的环回模式下进行测试也非常有用。

将默认载波状态设为on:

# insmod kmod/rte_kni.ko carrier=on

要将默认载波状态设置为off:

# insmod kmod/rte_kni.ko carrier=off

如果没有指定载波参数,KNI接口的默认载波状态将被设置为off。

34.2创建和删除KNI

在创建任何KNI接口之前,必须将rte_kni内核模块加载到内核中,并使用rte_kni_init()函数进行配置。

KNI接口由DPDK应用程序通过rte_kni_alloc()函数动态创建。

struct rte_kni_conf结构包含一些字段,允许用户指定接口名称、设置MTU大小、设置显式或随机的MAC地址,并控制内核Rx线程的关联性(单线程和多线程模式)。默认情况下,KNI示例示例从匹配的设备获取MTU,在KNI PMD的情况下,它是从mbuf缓冲区长度派生的。

struct rte_kni_ops结构包含指向处理rte_kni内核模块请求的函数的指针。当KNI接口由应用程序外部的控制命令或函数操作时,这些函数允许DPDK应用程序执行操作。

例如,DPDK应用程序可能希望在用户启用/禁用带有ip link set [up|down] dev <ifaceX>的KNI接口时启用/禁用物理NIC端口。DPDK应用程序可以为config_network_if注册一个回调函数,当接口管理状态发生更改时将调用这个回调函数。

目前有四个回调函数可供用户注册应用程序功能:

  • config_network_if:
    当KNI接口的管理状态发生更改时调用。例如,当用户运行ip link set [up|down] dev <ifaceX>
  • change_mtu:
    当用户更改KNI接口的MTU大小时调用。例如,当用户运行ip link set [up|down] dev <ifaceX>
  • config_mac_address:
    当用户更改KNI接口的MAC地址时调用。例如,当用户运行ip link set [up|down] dev <ifaceX>时。如果用户将这个回调函数设置为NULL,但是将port_id字段设置为-1以外的值,那么将调用rte_knikni_config_mac_address()中的一个默认回调处理程序,它将在指定的port_id上调用rte_eth_dev_default_mac_addr_set()

为了运行这些回调,应用程序必须定期调用rte_kni_handle_request()函数。任何已注册的用户回调函数都将直接从rte_kni_handle_request()中调用,因此必须注意防止死锁和不阻塞任何DPDK fastpath任务。通常,使用这些回调的DPDK应用程序需要创建一个单独的线程或辅助进程来定期调用rte_kni_handle_request()

使用rte_kni_release()的DPDK应用程序可以删除KNI接口。当/dev/kni设备关闭时,所有未显式删除的KNI接口都将被删除,要么使用rte_kni_close()显式删除,要么在DPDK应用程序关闭时删除。

34.3.DPDK mbuf Flow
为了最小化内核空间中运行的DPDK代码量,mbuf mempool只在用户空间中管理。内核模块将知道mbuf,但是所有mbuf分配和自由操作将只由DPDK应用程序处理

Fig. 34.3给出了数据包双向发送的典型场景。
在这里插入图片描述Fig. 34.3 Packet Flow via mbufs in the DPDK KNI

34.4用例:入口

在DPDK RX端,mbuf由PMD在RX线程上下文中分配。这个线程将在rx_q FIFO中对mbuf进行排队。KNI线程将轮询rx_q的所有KNI活动设备。如果mbuf退出队列,它将被转换为sk_buff并通过netif_rx()发送到网络堆栈。离开队列的mbuf必须被释放,因此在free_q FIFO中返回相同的指针。

在同一个主循环中,RX线程轮询这个FIFO,并在退出队列后释放mbuf。

34.5用例:出口

为了使数据包退出,DPDK应用程序必须首先对几个mbuf进行排队,以便在内核端创建一个mbuf缓存。

通过调用kni_net_tx()回调函数,可以从Linux网络堆栈接收数据包。mbuf被排出队列(由于缓存而不用等待),并填充来自sk_buff的数据。然后释放sk_buff,并在tx_q FIFO中发送mbuf。

DPDK TX线程退出mbuf队列,并通过rte_eth_tx_burst()将其发送到PMD。然后将mbuf放回缓存中。

34.6. Ethtool

Ethtool是一个特定于linux的工具,在内核中有相应的支持,每个网络设备必须为支持的操作注册自己的回调。当前的实现使用igb/ixgbe修改后的Linux驱动程序来支持ethtool。在i40e和VMs (VF或EM设备)中不支持Ethtool。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章