14. Kernel NIC Interface Sample Application

The Kernel NIC Interface (KNI) is a DPDK control plane solution that allows userspace applications to exchange packets with the kernel networking stack. To accomplish this, DPDK userspace applications use an IOCTL call to request the creation of a KNI virtual device in the Linux* kernel. The IOCTL call provides interface information and the DPDK’s physical address space, which is re-mapped into the kernel address space by the KNI kernel loadable module that saves the information to a virtual device context. The DPDK creates FIFO queues for packet ingress and egress to the kernel module for each device allocated.

The KNI kernel loadable module is a standard net driver, which upon receiving the IOCTL call access the DPDK’s FIFO queue to receive/transmit packets from/to the DPDK userspace application. The FIFO queues contain pointers to data packets in the DPDK. This:

  • Provides a faster mechanism to interface with the kernel net stack and eliminates system calls
  • Facilitates the DPDK using standard Linux* userspace net tools (tshark, rsync, and so on)
  • Eliminate the copy_to_user and copy_from_user operations on packets.

The Kernel NIC Interface sample application is a simple example that demonstrates the use of the DPDK to create a path for packets to go through the Linux* kernel. This is done by creating one or more kernel net devices for each of the DPDK ports. The application allows the use of standard Linux tools (ethtool, iproute, tshark) with the DPDK ports and also the exchange of packets between the DPDK application and the Linux* kernel.

The Kernel NIC Interface sample application requires that the KNI kernel module rte_kni be loaded into the kernel. See Kernel NIC Interface for more information on loading the rte_kni kernel module.

14.1. Overview

The Kernel NIC Interface sample application kni allocates one or more KNI interfaces for each physical NIC port. For each physical NIC port, kni uses two DPDK threads in user space; one thread reads from the port and writes to the corresponding KNI interfaces and the other thread reads from the KNI interfaces and writes the data unmodified to the physical NIC port.

It is recommended to configure one KNI interface for each physical NIC port. The application can be configured with more than one KNI interface for each physical NIC port for performance testing or it can work together with VMDq support in future.

The packet flow through the Kernel NIC Interface application is as shown in the following figure.

../_images/kernel_nic.png

Fig. 14.1 Kernel NIC Application Packet Flow

If link monitoring is enabled with the -m command line flag, one additional pthread is launched which will check the link status of each physical NIC port and will update the carrier status of the corresponding KNI interface(s) to match the physical NIC port’s state. This means that the KNI interface(s) will be disabled automatically when the Ethernet link goes down and enabled when the Ethernet link goes up.

If link monitoring is enabled, the rte_kni kernel module should be loaded such that the default carrier state is set to off. This ensures that the KNI interface is only enabled after the Ethernet link of the corresponding NIC port has reached the linkup state.

If link monitoring is not enabled, the rte_kni kernel module should be loaded with the default carrier state set to on. This sets the carrier state of the KNI interfaces to on when the KNI interfaces are enabled without regard to the actual link state of the corresponding NIC port. This is useful for testing in loopback mode where the NIC port may not be physically connected to anything.

14.2. Compiling the Application

To compile the sample application see Compiling the Sample Applications.

The application is located in the examples/kni sub-directory.

Note

This application is intended as a linux only.

14.3. Running the kni Example Application

The kni example application requires a number of command line options:

kni [EAL options] -- -p PORTMASK --config="(port,lcore_rx,lcore_tx[,lcore_kthread,...])[,(port,lcore_rx,lcore_tx[,lcore_kthread,...])]" [-P] [-m]

Where:

  • -p PORTMASK:

    Hexadecimal bitmask of ports to configure.

  • --config="(port,lcore_rx,lcore_tx[,lcore_kthread,...])[,(port,lcore_rx,lcore_tx[,lcore_kthread,...])]":

    Determines which lcores the Rx and Tx DPDK tasks, and (optionally) the KNI kernel thread(s) are bound to for each physical port.

  • -P:

    Optional flag to set all ports to promiscuous mode so that packets are accepted regardless of the packet’s Ethernet MAC destination address. Without this option, only packets with the Ethernet MAC destination address set to the Ethernet address of the port are accepted.

  • -m:

    Optional flag to enable monitoring and updating of the Ethernet carrier state. With this option set, a thread will be started which will periodically check the Ethernet link status of the physical Ethernet ports and set the carrier state of the corresponding KNI network interface to match it. This means that the KNI interface will be disabled automatically when the Ethernet link goes down and enabled when the Ethernet link goes up.

Refer to DPDK Getting Started Guide for general information on running applications and the Environment Abstraction Layer (EAL) options.

The -c coremask or -l corelist parameter of the EAL options must include the lcores specified by lcore_rx and lcore_tx for each port, but does not need to include lcores specified by lcore_kthread as those cores are used to pin the kernel threads in the rte_kni kernel module.

The --config parameter must include a set of (port,lcore_rx,lcore_tx,[lcore_kthread,...]) values for each physical port specified in the -p PORTMASK parameter.

The optional lcore_kthread lcore ID parameter in --config can be specified zero, one or more times for each physical port.

If no lcore ID is specified for lcore_kthread, one KNI interface will be created for the physical port port and the KNI kernel thread(s) will have no specific core affinity.

If one or more lcore IDs are specified for lcore_kthread, a KNI interface will be created for each lcore ID specified, bound to the physical port port. If the rte_kni kernel module is loaded in multiple kernel thread mode, a kernel thread will be created for each KNI interface and bound to the specified core. If the rte_kni kernel module is loaded in single kernel thread mode, only one kernel thread is started for all KNI interfaces. The kernel thread will be bound to the first lcore_kthread lcore ID specified.

14.3.1. Example Configurations

The following commands will first load the rte_kni kernel module in multiple kernel thread mode. The kni application is then started using two ports; Port 0 uses lcore 4 for the Rx task, lcore 6 for the Tx task, and will create a single KNI interface vEth0_0 with the kernel thread bound to lcore 8. Port 1 uses lcore 5 for the Rx task, lcore 7 for the Tx task, and will create a single KNI interface vEth1_0 with the kernel thread bound to lcore 9.

# rmmod rte_kni
# insmod kmod/rte_kni.ko kthread_mode=multiple
# ./build/kni -l 4-7 -n 4 -- -P -p 0x3 -m --config="(0,4,6,8),(1,5,7,9)"

The following example is identical, except an additional lcore_kthread core is specified per physical port. In this case, kni will create four KNI interfaces: vEth0_0/vEth0_1 bound to physical port 0 and vEth1_0/vEth1_1 bound to physical port 1.

The kernel thread for each interface will be bound as follows:

  • vEth0_0 - bound to lcore 8.
  • vEth0_1 - bound to lcore 10.
  • vEth1_0 - bound to lcore 9.
  • vEth1_1 - bound to lcore 11
# rmmod rte_kni
# insmod kmod/rte_kni.ko kthread_mode=multiple
# ./build/kni -l 4-7 -n 4 -- -P -p 0x3 -m --config="(0,4,6,8,10),(1,5,7,9,11)"

The following example can be used to test the interface between the kni test application and the rte_kni kernel module. In this example, the rte_kni kernel module is loaded in single kernel thread mode, loopback mode enabled, and the default carrier state is set to on so that the corresponding physical NIC port does not have to be connected in order to use the KNI interface. One KNI interface vEth0_0 is created for port 0 and one KNI interface vEth1_0 is created for port 1. Since rte_kni is loaded in “single kernel thread” mode, the one kernel thread is bound to lcore 8.

Since the physical NIC ports are not being used, link monitoring can be disabled by not specifying the -m flag to kni:

# rmmod rte_kni
# insmod kmod/rte_kni.ko lo_mode=lo_mode_fifo carrier=on
# ./build/kni -l 4-7 -n 4 -- -P -p 0x3 --config="(0,4,6,8),(1,5,7,9)"

14.4. KNI Operations

Once the kni application is started, the user can use the normal Linux commands to manage the KNI interfaces as if they were any other Linux network interface.

Enable KNI interface and assign an IP address:

# ip addr add dev vEth0_0 192.168.0.1

Show KNI interface configuration and statistics:

# ip -s -d addr show vEth0_0

The user can also check and reset the packet statistics inside the kni application by sending the app the USR1 and USR2 signals:

# Print statistics
# pkill -USR1 kni

# Zero statistics
# pkill -USR2 kni

Dump network traffic:

# tshark -n -i vEth0_0

The normal Linux commands can also be used to change the MAC address and MTU size used by the physical NIC which corresponds to the KNI interface. However, if more than one KNI interface is configured for a physical port, these commands will only work on the first KNI interface for that port.

Change the MAC address:

# ip link set dev vEth0_0 lladdr 0C:01:02:03:04:08

Change the MTU size:

# ip link set dev vEth0_0 mtu 1450

Limited ethtool support:

# ethtool -i vEth0_0

When the kni application is closed, all the KNI interfaces are deleted from the Linux kernel.

14.5. Explanation

The following sections provide some explanation of code.

14.5.1. Initialization

Setup of mbuf pool, driver and queues is similar to the setup done in the L2 Forwarding Sample Application (in Real and Virtualized Environments).. In addition, one or more kernel NIC interfaces are allocated for each of the configured ports according to the command line parameters.

The code for allocating the kernel NIC interfaces for a specific port is in the function kni_alloc.

The other step in the initialization process that is unique to this sample application is the association of each port with lcores for RX, TX and kernel threads.

  • One lcore to read from the port and write to the associated one or more KNI devices
  • Another lcore to read from one or more KNI devices and write to the port
  • Other lcores for pinning the kernel threads on one by one

This is done by using the kni_port_params_array[] array, which is indexed by the port ID. The code is in the function parse_config.

14.5.2. Packet Forwarding

After the initialization steps are completed, the main_loop() function is run on each lcore. This function first checks the lcore_id against the user provided lcore_rx and lcore_tx to see if this lcore is reading from or writing to kernel NIC interfaces.

For the case that reads from a NIC port and writes to the kernel NIC interfaces (kni_ingress), the packet reception is the same as in L2 Forwarding sample application (see Receive, Process and Transmit Packets). The packet transmission is done by sending mbufs into the kernel NIC interfaces by rte_kni_tx_burst(). The KNI library automatically frees the mbufs after the kernel successfully copied the mbufs.

For the other case that reads from kernel NIC interfaces and writes to a physical NIC port (kni_egress), packets are retrieved by reading mbufs from kernel NIC interfaces by rte_kni_rx_burst(). The packet transmission is the same as in the L2 Forwarding sample application (see Receive, Process and Transmit Packets).