New upstream version 18.11-rc1
[deb_dpdk.git] / doc / guides / sample_app_ug / kernel_nic_interface.rst
index 580d06b..6acdf0f 100644 (file)
@@ -31,18 +31,27 @@ This is done by creating one or more kernel net devices for each of the DPDK por
 The application allows the use of standard Linux tools (ethtool, ifconfig, tcpdump) 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
+:doc:`../prog_guide/kernel_nic_interface` for more information on loading
+the ``rte_kni`` kernel module.
+
 Overview
 --------
 
-The Kernel NIC Interface sample application uses two threads in user space for each physical NIC port being used,
-and allocates one or more KNI device for each physical NIC port with kernel module's support.
-For a physical NIC port, one thread reads from the port and writes to KNI devices,
-and another thread reads from KNI devices and writes the data unmodified to the physical NIC port.
-It is recommended to configure one KNI device for each physical NIC port.
-If configured with more than one KNI devices for a physical NIC port,
-it is just for performance testing, or it can work together with VMDq support in future.
+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.
+The packet flow through the Kernel NIC Interface application is as shown
+in the following figure.
 
 .. _figure_kernel_nic:
 
@@ -50,226 +59,236 @@ The packet flow through the Kernel NIC Interface application is as shown in the
 
    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 :ref:`default carrier state <kni_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 :ref:`default carrier state <kni_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.
+
 Compiling the Application
 -------------------------
 
 To compile the sample application see :doc:`compiling`.
 
-The application is located in the ``kni`` sub-directory.
+The application is located in the ``examples/kni`` sub-directory.
 
 .. note::
 
         This application is intended as a linuxapp only.
 
-Loading the Kernel Module
--------------------------
+Running the kni Example Application
+-----------------------------------
 
-Loading the KNI kernel module without any parameter is the typical way a DPDK application
-gets packets into and out of the kernel net stack.
-This way, only one kernel thread is created for all KNI devices for packet receiving in kernel side:
+The ``kni`` example application requires a number of command line options:
 
 .. code-block:: console
 
-    #insmod rte_kni.ko
+    kni [EAL options] -- -p PORTMASK --config="(port,lcore_rx,lcore_tx[,lcore_kthread,...])[,(port,lcore_rx,lcore_tx[,lcore_kthread,...])]" [-P] [-m]
 
-Pinning the kernel thread to a specific core can be done using a taskset command such as following:
+Where:
 
-.. code-block:: console
+*   ``-p PORTMASK``:
 
-    #taskset -p 100000 `pgrep --fl kni_thread | awk '{print $1}'`
+    Hexadecimal bitmask of ports to configure.
 
-This command line tries to pin the specific kni_thread on the 20th lcore (lcore numbering starts at 0),
-which means it needs to check if that lcore is available on the board.
-This command must be sent after the application has been launched, as insmod does not start the kni thread.
+*   ``--config="(port,lcore_rx,lcore_tx[,lcore_kthread,...])[,(port,lcore_rx,lcore_tx[,lcore_kthread,...])]"``:
 
-For optimum performance,
-the lcore in the mask must be selected to be on the same socket as the lcores used in the KNI application.
+    Determines which lcores the Rx and Tx DPDK tasks, and (optionally)
+    the KNI kernel thread(s) are bound to for each physical port.
 
-To provide flexibility of performance, the kernel module of the KNI,
-located in the kmod sub-directory of the DPDK target directory,
-can be loaded with parameter of kthread_mode as follows:
+*   ``-P``:
 
-*   #insmod rte_kni.ko kthread_mode=single
+    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.
 
-    This mode will create only one kernel thread for all KNI devices for packet receiving in kernel side.
-    By default, it is in this single kernel thread mode.
-    It can set core affinity for this kernel thread by using Linux command taskset.
+*   ``-m``:
 
-*   #insmod rte_kni.ko kthread_mode =multiple
+    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.
 
-    This mode will create a kernel thread for each KNI device for packet receiving in kernel side.
-    The core affinity of each kernel thread is set when creating the KNI device.
-    The lcore ID for each kernel thread is provided in the command line of launching the application.
-    Multiple kernel thread mode can provide scalable higher performance.
+Refer to *DPDK Getting Started Guide* for general information on running
+applications and the Environment Abstraction Layer (EAL) options.
 
-To measure the throughput in a loopback mode, the kernel module of the KNI,
-located in the kmod sub-directory of the DPDK target directory,
-can be loaded with parameters as follows:
+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.
 
-*   #insmod rte_kni.ko lo_mode=lo_mode_fifo
+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.
 
-    This loopback mode will involve ring enqueue/dequeue operations in kernel space.
+The optional ``lcore_kthread`` lcore ID parameter in ``--config`` can be
+specified zero, one or more times for each physical port.
 
-*   #insmod rte_kni.ko lo_mode=lo_mode_fifo_skb
+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.
 
-    This loopback mode will involve ring enqueue/dequeue operations and sk buffer copies in kernel space.
+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 :ref:`multiple
+kernel thread <kni_kernel_thread_mode>` 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 :ref:`single kernel thread <kni_kernel_thread_mode>`
+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.
 
-Running the Application
------------------------
+Example Configurations
+~~~~~~~~~~~~~~~~~~~~~~~
 
-The application requires a number of command line options:
+The following commands will first load the ``rte_kni`` kernel module in
+:ref:`multiple kernel thread <kni_kernel_thread_mode>` 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.
 
 .. code-block:: console
 
-    kni [EAL options] -- -P -p PORTMASK --config="(port,lcore_rx,lcore_tx[,lcore_kthread,...])[,port,lcore_rx,lcore_tx[,lcore_kthread,...]]"
+    # 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)"
 
-Where:
-
-*   -P: 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.
+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.
 
-*   -p PORTMASK: Hexadecimal bitmask of ports to configure.
+The kernel thread for each interface will be bound as follows:
 
-*   --config="(port,lcore_rx, lcore_tx[,lcore_kthread, ...]) [, port,lcore_rx, lcore_tx[,lcore_kthread, ...]]":
-    Determines which lcores of RX, TX, kernel thread are mapped to which ports.
+    * ``vEth0_0`` - bound to lcore 8.
+    * ``vEth0_1`` - bound to lcore 10.
+    * ``vEth1_0`` - bound to lcore 9.
+    * ``vEth1_1`` - bound to lcore 11
 
-Refer to *DPDK Getting Started Guide* for general information on running applications and the Environment Abstraction Layer (EAL) options.
+.. code-block:: console
 
-The -c coremask or -l corelist parameter of the EAL options should include the lcores indicated by the lcore_rx and lcore_tx,
-but does not need to include lcores indicated by lcore_kthread as they are used to pin the kernel thread on.
-The -p PORTMASK parameter should include the ports indicated by the port in --config, neither more nor less.
+    # 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 lcore_kthread in --config can be configured none, one or more lcore IDs.
-In multiple kernel thread mode, if configured none, a KNI device will be allocated for each port,
-while no specific lcore affinity will be set for its kernel thread.
-If configured one or more lcore IDs, one or more KNI devices will be allocated for each port,
-while specific lcore affinity will be set for its kernel thread.
-In single kernel thread mode, if configured none, a KNI device will be allocated for each port.
-If configured one or more lcore IDs,
-one or more KNI devices will be allocated for each port while
-no lcore affinity will be set as there is only one kernel thread for all KNI devices.
+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 :ref:`single kernel thread
+mode <kni_kernel_thread_mode>`, :ref:`loopback mode <kni_loopback_mode>`
+enabled, and the :ref:`default carrier state <kni_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.
 
-For example, to run the application with two ports served by six lcores, one lcore of RX, one lcore of TX,
-and one lcore of kernel thread for each port:
+Since the physical NIC ports are not being used, link monitoring can be
+disabled by **not** specifying the ``-m`` flag to ``kni``:
 
 .. code-block:: console
 
-    ./build/kni -l 4-7 -n 4 -- -P -p 0x3 --config="(0,4,6,8),(1,5,7,9)"
+    # 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)"
 
 KNI Operations
 --------------
 
-Once the KNI application is started, one can use different Linux* commands to manage the net interfaces.
-If more than one KNI devices configured for a physical port,
-only the first KNI device will be paired to the physical device.
-Operations on other KNI devices will not affect the physical port handled in user space application.
-
-Assigning an IP address:
-
-.. code-block:: console
-
-    #ifconfig vEth0_0 192.168.0.1
+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.
 
-Displaying the NIC registers:
+Enable KNI interface and assign an IP address:
 
 .. code-block:: console
 
-    #ethtool -d vEth0_0
+    # ifconfig vEth0_0 192.168.0.1
 
-Dumping the network traffic:
+Show KNI interface configuration and statistics:
 
 .. code-block:: console
 
-    #tcpdump -i vEth0_0
+    # ifconfig vEth0_0
 
-Change the MAC address:
+The user can also check and reset the packet statistics inside the ``kni``
+application by sending the app the USR1 and USR2 signals:
 
 .. code-block:: console
 
-    #ifconfig vEth0_0 hw ether 0C:01:02:03:04:08
+    # Print statistics
+    # kill -SIGUSR1 `pidof kni`
 
-When the DPDK userspace application is closed, all the KNI devices are deleted from Linux*.
+    # Zero statistics
+    # kill -SIGUSR2 `pidof kni`
 
-Explanation
------------
-
-The following sections provide some explanation of code.
-
-Initialization
-~~~~~~~~~~~~~~
+Dump network traffic:
 
-Setup of mbuf pool, driver and queues is similar to the setup done in the :doc:`l2_forward_real_virtual`..
-In addition, one or more kernel NIC interfaces are allocated for each
-of the configured ports according to the command line parameters.
+.. code-block:: console
 
-The code for allocating the kernel NIC interfaces for a specific port is as follows:
+    # tcpdump -i vEth0_0
 
-.. code-block:: c
+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.
 
-    static int
-    kni_alloc(uint16_t port_id)
-    {
-        uint8_t i;
-        struct rte_kni *kni;
-        struct rte_kni_conf conf;
-        struct kni_port_params **params = kni_port_params_array;
+Change the MAC address:
 
-        if (port_id >= RTE_MAX_ETHPORTS || !params[port_id])
-            return -1;
+.. code-block:: console
 
-        params[port_id]->nb_kni = params[port_id]->nb_lcore_k ? params[port_id]->nb_lcore_k : 1;
+    # ifconfig vEth0_0 hw ether 0C:01:02:03:04:08
 
-        for (i = 0; i < params[port_id]->nb_kni; i++) {
+Change the MTU size:
 
-            /* Clear conf at first */
+.. code-block:: console
 
-            memset(&conf, 0, sizeof(conf));
-            if (params[port_id]->nb_lcore_k) {
-                snprintf(conf.name, RTE_KNI_NAMESIZE, "vEth%u_%u", port_id, i);
-                conf.core_id = params[port_id]->lcore_k[i];
-                conf.force_bind = 1;
-            } else
-                snprintf(conf.name, RTE_KNI_NAMESIZE, "vEth%u", port_id);
-                conf.group_id = (uint16_t)port_id;
-                conf.mbuf_size = MAX_PACKET_SZ;
+    # ifconfig vEth0_0 mtu 1450
 
-                /*
-                 *   The first KNI device associated to a port
-                 *   is the master, for multiple kernel thread
-                 *   environment.
-                 */
+If DPDK is compiled with ``CONFIG_RTE_KNI_KMOD_ETHTOOL=y`` and an Intel
+NIC is used, the user can use ``ethtool`` on the KNI interface as if it
+were a normal Linux kernel interface.
 
-                if (i == 0) {
-                    struct rte_kni_ops ops;
-                    struct rte_eth_dev_info dev_info;
+Displaying the NIC registers:
 
-                    memset(&dev_info, 0, sizeof(dev_info)); rte_eth_dev_info_get(port_id, &dev_info);
+.. code-block:: console
 
-                    conf.addr = dev_info.pci_dev->addr;
-                    conf.id = dev_info.pci_dev->id;
+    # ethtool -d vEth0_0
 
-                    /* Get the interface default mac address */
-                    rte_eth_macaddr_get(port_id, (struct ether_addr *)&conf.mac_addr);
+When the ``kni`` application is closed, all the KNI interfaces are deleted
+from the Linux kernel.
 
-                    memset(&ops, 0, sizeof(ops));
+Explanation
+-----------
 
-                    ops.port_id = port_id;
-                    ops.change_mtu = kni_change_mtu;
-                    ops.config_network_if = kni_config_network_interface;
-                    ops.config_mac_address = kni_config_mac_address;
+The following sections provide some explanation of code.
 
-                    kni = rte_kni_alloc(pktmbuf_pool, &conf, &ops);
-                } else
-                    kni = rte_kni_alloc(pktmbuf_pool, &conf, NULL);
+Initialization
+~~~~~~~~~~~~~~
 
-                if (!kni)
-                    rte_exit(EXIT_FAILURE, "Fail to create kni for "
-                            "port: %d\n", port_id);
+Setup of mbuf pool, driver and queues is similar to the setup done in the :doc:`l2_forward_real_virtual`..
+In addition, one or more kernel NIC interfaces are allocated for each
+of the configured ports according to the command line parameters.
 
-                params[port_id]->kni[i] = kni;
-            }
-        return 0;
-   }
+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.
@@ -280,105 +299,8 @@ is the association of each port with lcores for RX, TX and kernel threads.
 
 *   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 as follows:
-
-.. code-block:: console
-
-    static int
-    parse_config(const char *arg)
-    {
-        const char *p, *p0 = arg;
-        char s[256], *end;
-        unsigned size;
-        enum fieldnames {
-            FLD_PORT = 0,
-            FLD_LCORE_RX,
-            FLD_LCORE_TX,
-            _NUM_FLD = KNI_MAX_KTHREAD + 3,
-        };
-        int i, j, nb_token;
-        char *str_fld[_NUM_FLD];
-        unsigned long int_fld[_NUM_FLD];
-        uint16_t port_id, nb_kni_port_params = 0;
-
-        memset(&kni_port_params_array, 0, sizeof(kni_port_params_array));
-
-        while (((p = strchr(p0, '(')) != NULL) && nb_kni_port_params < RTE_MAX_ETHPORTS) {
-            p++;
-            if ((p0 = strchr(p, ')')) == NULL)
-                goto fail;
-
-            size = p0 - p;
-
-            if (size >= sizeof(s)) {
-                printf("Invalid config parameters\n");
-                goto fail;
-            }
-
-            snprintf(s, sizeof(s), "%.*s", size, p);
-            nb_token = rte_strsplit(s, sizeof(s), str_fld, _NUM_FLD, ',');
-
-            if (nb_token <= FLD_LCORE_TX) {
-                printf("Invalid config parameters\n");
-                goto fail;
-            }
-
-            for (i = 0; i < nb_token; i++) {
-                errno = 0;
-                int_fld[i] = strtoul(str_fld[i], &end, 0);
-                if (errno != 0 || end == str_fld[i]) {
-                    printf("Invalid config parameters\n");
-                    goto fail;
-                }
-            }
-
-            i = 0;
-            port_id = (uint8_t)int_fld[i++];
-
-            if (port_id >= RTE_MAX_ETHPORTS) {
-                printf("Port ID %u could not exceed the maximum %u\n", port_id, RTE_MAX_ETHPORTS);
-                goto fail;
-            }
-
-            if (kni_port_params_array[port_id]) {
-                printf("Port %u has been configured\n", port_id);
-                goto fail;
-            }
-
-            kni_port_params_array[port_id] = (struct kni_port_params*)rte_zmalloc("KNI_port_params", sizeof(struct kni_port_params), RTE_CACHE_LINE_SIZE);
-            kni_port_params_array[port_id]->port_id = port_id;
-            kni_port_params_array[port_id]->lcore_rx = (uint8_t)int_fld[i++];
-            kni_port_params_array[port_id]->lcore_tx = (uint8_t)int_fld[i++];
-
-            if (kni_port_params_array[port_id]->lcore_rx >= RTE_MAX_LCORE || kni_port_params_array[port_id]->lcore_tx >= RTE_MAX_LCORE) {
-                printf("lcore_rx %u or lcore_tx %u ID could not "
-                        "exceed the maximum %u\n",
-                        kni_port_params_array[port_id]->lcore_rx, kni_port_params_array[port_id]->lcore_tx, RTE_MAX_LCORE);
-                goto fail;
-           }
-
-        for (j = 0; i < nb_token && j < KNI_MAX_KTHREAD; i++, j++)
-            kni_port_params_array[port_id]->lcore_k[j] = (uint8_t)int_fld[i];
-            kni_port_params_array[port_id]->nb_lcore_k = j;
-        }
-
-        print_config();
-
-        return 0;
-
-    fail:
-
-        for (i = 0; i < RTE_MAX_ETHPORTS; i++) {
-            if (kni_port_params_array[i]) {
-                rte_free(kni_port_params_array[i]);
-                kni_port_params_array[i] = NULL;
-            }
-        }
-
-        return -1;
-
-    }
+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``.
 
 Packet Forwarding
 ~~~~~~~~~~~~~~~~~
@@ -387,211 +309,14 @@ After the initialization steps are completed, the main_loop() function is run on
 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,
+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 :ref:`l2_fwd_app_rx_tx_packets`).
-The packet transmission is done by sending mbufs into the kernel NIC interfaces by rte_kni_tx_burst().
+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.
 
-.. code-block:: c
-
-    /**
-     *   Interface to burst rx and enqueue mbufs into rx_q
-     */
-
-    static void
-    kni_ingress(struct kni_port_params *p)
-    {
-        uint8_t i, nb_kni, port_id;
-        unsigned nb_rx, num;
-        struct rte_mbuf *pkts_burst[PKT_BURST_SZ];
-
-        if (p == NULL)
-            return;
-
-        nb_kni = p->nb_kni;
-        port_id = p->port_id;
-
-        for (i = 0; i < nb_kni; i++) {
-            /* Burst rx from eth */
-            nb_rx = rte_eth_rx_burst(port_id, 0, pkts_burst, PKT_BURST_SZ);
-            if (unlikely(nb_rx > PKT_BURST_SZ)) {
-                RTE_LOG(ERR, APP, "Error receiving from eth\n");
-                return;
-            }
-
-            /* Burst tx to kni */
-            num = rte_kni_tx_burst(p->kni[i], pkts_burst, nb_rx);
-            kni_stats[port_id].rx_packets += num;
-            rte_kni_handle_request(p->kni[i]);
-
-            if (unlikely(num < nb_rx)) {
-                /* Free mbufs not tx to kni interface */
-                kni_burst_free_mbufs(&pkts_burst[num], nb_rx - num);
-                kni_stats[port_id].rx_dropped += nb_rx - num;
-            }
-        }
-    }
-
-For the other case that reads from kernel NIC interfaces and writes to a physical NIC port, packets are retrieved by reading
-mbufs from kernel NIC interfaces by `rte_kni_rx_burst()`.
+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 :ref:`l2_fwd_app_rx_tx_packets`).
-
-.. code-block:: c
-
-    /**
-     *   Interface to dequeue mbufs from tx_q and burst tx
-     */
-
-    static void
-
-    kni_egress(struct kni_port_params *p)
-    {
-        uint8_t i, nb_kni, port_id;
-        unsigned nb_tx, num;
-        struct rte_mbuf *pkts_burst[PKT_BURST_SZ];
-
-        if (p == NULL)
-            return;
-
-        nb_kni = p->nb_kni;
-        port_id = p->port_id;
-
-        for (i = 0; i < nb_kni; i++) {
-            /* Burst rx from kni */
-            num = rte_kni_rx_burst(p->kni[i], pkts_burst, PKT_BURST_SZ);
-            if (unlikely(num > PKT_BURST_SZ)) {
-                RTE_LOG(ERR, APP, "Error receiving from KNI\n");
-                return;
-            }
-
-            /* Burst tx to eth */
-
-            nb_tx = rte_eth_tx_burst(port_id, 0, pkts_burst, (uint16_t)num);
-
-            kni_stats[port_id].tx_packets += nb_tx;
-
-            if (unlikely(nb_tx < num)) {
-                /* Free mbufs not tx to NIC */
-                kni_burst_free_mbufs(&pkts_burst[nb_tx], num - nb_tx);
-                kni_stats[port_id].tx_dropped += num - nb_tx;
-            }
-        }
-    }
-
-Callbacks for Kernel Requests
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-To execute specific PMD operations in user space requested by some Linux* commands,
-callbacks must be implemented and filled in the struct rte_kni_ops structure.
-Currently, setting a new MTU, change in MAC address, configuring promiscusous mode and
-configuring the network interface(up/down) re supported.
-Default implementation for following is available in rte_kni library.
-Application may choose to not implement following callbacks:
-
-- ``config_mac_address``
-- ``config_promiscusity``
-
-
-.. code-block:: c
-
-    static struct rte_kni_ops kni_ops = {
-        .change_mtu = kni_change_mtu,
-        .config_network_if = kni_config_network_interface,
-        .config_mac_address = kni_config_mac_address,
-        .config_promiscusity = kni_config_promiscusity,
-    };
-
-    /* Callback for request of changing MTU */
-
-    static int
-    kni_change_mtu(uint16_t port_id, unsigned new_mtu)
-    {
-        int ret;
-        struct rte_eth_conf conf;
-
-        if (port_id >= rte_eth_dev_count()) {
-            RTE_LOG(ERR, APP, "Invalid port id %d\n", port_id);
-            return -EINVAL;
-        }
-
-        RTE_LOG(INFO, APP, "Change MTU of port %d to %u\n", port_id, new_mtu);
-
-        /* Stop specific port */
-
-        rte_eth_dev_stop(port_id);
-
-        memcpy(&conf, &port_conf, sizeof(conf));
-
-        /* Set new MTU */
-
-        if (new_mtu > ETHER_MAX_LEN)
-            conf.rxmode.jumbo_frame = 1;
-        else
-            conf.rxmode.jumbo_frame = 0;
-
-        /* mtu + length of header + length of FCS = max pkt length */
-
-        conf.rxmode.max_rx_pkt_len = new_mtu + KNI_ENET_HEADER_SIZE + KNI_ENET_FCS_SIZE;
-
-        ret = rte_eth_dev_configure(port_id, 1, 1, &conf);
-        if (ret < 0) {
-            RTE_LOG(ERR, APP, "Fail to reconfigure port %d\n", port_id);
-            return ret;
-        }
-
-        /* Restart specific port */
-
-        ret = rte_eth_dev_start(port_id);
-        if (ret < 0) {
-             RTE_LOG(ERR, APP, "Fail to restart port %d\n", port_id);
-            return ret;
-        }
-
-        return 0;
-    }
-
-    /* Callback for request of configuring network interface up/down */
-
-    static int
-    kni_config_network_interface(uint16_t port_id, uint8_t if_up)
-    {
-        int ret = 0;
-
-        if (port_id >= rte_eth_dev_count() || port_id >= RTE_MAX_ETHPORTS) {
-            RTE_LOG(ERR, APP, "Invalid port id %d\n", port_id);
-            return -EINVAL;
-        }
-
-        RTE_LOG(INFO, APP, "Configure network interface of %d %s\n",
-
-        port_id, if_up ? "up" : "down");
-
-        if (if_up != 0) {
-            /* Configure network interface up */
-            rte_eth_dev_stop(port_id);
-            ret = rte_eth_dev_start(port_id);
-        } else /* Configure network interface down */
-            rte_eth_dev_stop(port_id);
-
-        if (ret < 0)
-            RTE_LOG(ERR, APP, "Failed to start port %d\n", port_id);
-        return ret;
-    }
-
-    /* Callback for request of configuring device mac address */
-
-    static int
-    kni_config_mac_address(uint16_t port_id, uint8_t mac_addr[])
-    {
-        .....
-    }
-
-    /* Callback for request of configuring promiscuous mode */
-
-    static int
-    kni_config_promiscusity(uint16_t port_id, uint8_t to_on)
-    {
-        .....
-    }