1..  BSD LICENSE
2    Copyright(c) 2010-2014 Intel Corporation. All rights reserved.
3    All rights reserved.
4
5    Redistribution and use in source and binary forms, with or without
6    modification, are permitted provided that the following conditions
7    are met:
8
9    * Redistributions of source code must retain the above copyright
10    notice, this list of conditions and the following disclaimer.
11    * Redistributions in binary form must reproduce the above copyright
12    notice, this list of conditions and the following disclaimer in
13    the documentation and/or other materials provided with the
14    distribution.
15    * Neither the name of Intel Corporation nor the names of its
16    contributors may be used to endorse or promote products derived
17    from this software without specific prior written permission.
18
19    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20    "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21    LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22    A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23    OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24    SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25    LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26    DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27    THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28    (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29    OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30
31Kernel NIC Interface Sample Application
32=======================================
33
34The Kernel NIC Interface (KNI) is a DPDK control plane solution that
35allows userspace applications to exchange packets with the kernel networking stack.
36To accomplish this, DPDK userspace applications use an IOCTL call
37to request the creation of a KNI virtual device in the Linux* kernel.
38The IOCTL call provides interface information and the DPDK's physical address space,
39which is re-mapped into the kernel address space by the KNI kernel loadable module
40that saves the information to a virtual device context.
41The DPDK creates FIFO queues for packet ingress and egress
42to the kernel module for each device allocated.
43
44The KNI kernel loadable module is a standard net driver,
45which upon receiving the IOCTL call access the DPDK's FIFO queue to
46receive/transmit packets from/to the DPDK userspace application.
47The FIFO queues contain pointers to data packets in the DPDK. This:
48
49*   Provides a faster mechanism to interface with the kernel net stack and eliminates system calls
50
51*   Facilitates the DPDK using standard Linux* userspace net tools (tcpdump, ftp, and so on)
52
53*   Eliminate the copy_to_user and copy_from_user operations on packets.
54
55The Kernel NIC Interface sample application is a simple example that demonstrates the use
56of the DPDK to create a path for packets to go through the Linux* kernel.
57This is done by creating one or more kernel net devices for each of the DPDK ports.
58The application allows the use of standard Linux tools (ethtool, ifconfig, tcpdump) with the DPDK ports and
59also the exchange of packets between the DPDK application and the Linux* kernel.
60
61Overview
62--------
63
64The Kernel NIC Interface sample application uses two threads in user space for each physical NIC port being used,
65and allocates one or more KNI device for each physical NIC port with kernel module's support.
66For a physical NIC port, one thread reads from the port and writes to KNI devices,
67and another thread reads from KNI devices and writes the data unmodified to the physical NIC port.
68It is recommended to configure one KNI device for each physical NIC port.
69If configured with more than one KNI devices for a physical NIC port,
70it is just for performance testing, or it can work together with VMDq support in future.
71
72The packet flow through the Kernel NIC Interface application is as shown in the following figure.
73
74.. _figure_kernel_nic:
75
76.. figure:: img/kernel_nic.*
77
78   Kernel NIC Application Packet Flow
79
80
81Compiling the Application
82-------------------------
83
84Compile the application as follows:
85
86#.  Go to the example directory:
87
88    .. code-block:: console
89
90        export RTE_SDK=/path/to/rte_sdk cd
91        ${RTE_SDK}/examples/kni
92
93#.  Set the target (a default target is used if not specified)
94
95    .. note::
96
97        This application is intended as a linuxapp only.
98
99    .. code-block:: console
100
101        export RTE_TARGET=x86_64-native-linuxapp-gcc
102
103#.  Build the application:
104
105    .. code-block:: console
106
107        make
108
109Loading the Kernel Module
110-------------------------
111
112Loading the KNI kernel module without any parameter is the typical way a DPDK application
113gets packets into and out of the kernel net stack.
114This way, only one kernel thread is created for all KNI devices for packet receiving in kernel side:
115
116.. code-block:: console
117
118    #insmod rte_kni.ko
119
120Pinning the kernel thread to a specific core can be done using a taskset command such as following:
121
122.. code-block:: console
123
124    #taskset -p 100000 `pgrep --fl kni_thread | awk '{print $1}'`
125
126This command line tries to pin the specific kni_thread on the 20th lcore (lcore numbering starts at 0),
127which means it needs to check if that lcore is available on the board.
128This command must be sent after the application has been launched, as insmod does not start the kni thread.
129
130For optimum performance,
131the lcore in the mask must be selected to be on the same socket as the lcores used in the KNI application.
132
133To provide flexibility of performance, the kernel module of the KNI,
134located in the kmod sub-directory of the DPDK target directory,
135can be loaded with parameter of kthread_mode as follows:
136
137*   #insmod rte_kni.ko kthread_mode=single
138
139    This mode will create only one kernel thread for all KNI devices for packet receiving in kernel side.
140    By default, it is in this single kernel thread mode.
141    It can set core affinity for this kernel thread by using Linux command taskset.
142
143*   #insmod rte_kni.ko kthread_mode =multiple
144
145    This mode will create a kernel thread for each KNI device for packet receiving in kernel side.
146    The core affinity of each kernel thread is set when creating the KNI device.
147    The lcore ID for each kernel thread is provided in the command line of launching the application.
148    Multiple kernel thread mode can provide scalable higher performance.
149
150To measure the throughput in a loopback mode, the kernel module of the KNI,
151located in the kmod sub-directory of the DPDK target directory,
152can be loaded with parameters as follows:
153
154*   #insmod rte_kni.ko lo_mode=lo_mode_fifo
155
156    This loopback mode will involve ring enqueue/dequeue operations in kernel space.
157
158*   #insmod rte_kni.ko lo_mode=lo_mode_fifo_skb
159
160    This loopback mode will involve ring enqueue/dequeue operations and sk buffer copies in kernel space.
161
162Running the Application
163-----------------------
164
165The application requires a number of command line options:
166
167.. code-block:: console
168
169    kni [EAL options] -- -P -p PORTMASK --config="(port,lcore_rx,lcore_tx[,lcore_kthread,...])[,port,lcore_rx,lcore_tx[,lcore_kthread,...]]"
170
171Where:
172
173*   -P: Set all ports to promiscuous mode so that packets are accepted regardless of the packet's Ethernet MAC destination address.
174    Without this option, only packets with the Ethernet MAC destination address set to the Ethernet address of the port are accepted.
175
176*   -p PORTMASK: Hexadecimal bitmask of ports to configure.
177
178*   --config="(port,lcore_rx, lcore_tx[,lcore_kthread, ...]) [, port,lcore_rx, lcore_tx[,lcore_kthread, ...]]":
179    Determines which lcores of RX, TX, kernel thread are mapped to which ports.
180
181Refer to *DPDK Getting Started Guide* for general information on running applications and the Environment Abstraction Layer (EAL) options.
182
183The -c coremask parameter of the EAL options should include the lcores indicated by the lcore_rx and lcore_tx,
184but does not need to include lcores indicated by lcore_kthread as they are used to pin the kernel thread on.
185The -p PORTMASK parameter should include the ports indicated by the port in --config, neither more nor less.
186
187The lcore_kthread in --config can be configured none, one or more lcore IDs.
188In multiple kernel thread mode, if configured none, a KNI device will be allocated for each port,
189while no specific lcore affinity will be set for its kernel thread.
190If configured one or more lcore IDs, one or more KNI devices will be allocated for each port,
191while specific lcore affinity will be set for its kernel thread.
192In single kernel thread mode, if configured none, a KNI device will be allocated for each port.
193If configured one or more lcore IDs,
194one or more KNI devices will be allocated for each port while
195no lcore affinity will be set as there is only one kernel thread for all KNI devices.
196
197For example, to run the application with two ports served by six lcores, one lcore of RX, one lcore of TX,
198and one lcore of kernel thread for each port:
199
200.. code-block:: console
201
202    ./build/kni -c 0xf0 -n 4 -- -P -p 0x3 -config="(0,4,6,8),(1,5,7,9)"
203
204KNI Operations
205--------------
206
207Once the KNI application is started, one can use different Linux* commands to manage the net interfaces.
208If more than one KNI devices configured for a physical port,
209only the first KNI device will be paired to the physical device.
210Operations on other KNI devices will not affect the physical port handled in user space application.
211
212Assigning an IP address:
213
214.. code-block:: console
215
216    #ifconfig vEth0_0 192.168.0.1
217
218Displaying the NIC registers:
219
220.. code-block:: console
221
222    #ethtool -d vEth0_0
223
224Dumping the network traffic:
225
226.. code-block:: console
227
228    #tcpdump -i vEth0_0
229
230When the DPDK userspace application is closed, all the KNI devices are deleted from Linux*.
231
232Explanation
233-----------
234
235The following sections provide some explanation of code.
236
237Initialization
238~~~~~~~~~~~~~~
239
240Setup of mbuf pool, driver and queues is similar to the setup done in the L2 Forwarding sample application
241(see Chapter 9 "L2 Forwarding Sample Application (in Real and Virtualized Environments" for details).
242In addition, one or more kernel NIC interfaces are allocated for each
243of the configured ports according to the command line parameters.
244
245The code for creating the kernel NIC interface for a specific port is as follows:
246
247.. code-block:: c
248
249    kni = rte_kni_create(port, MAX_PACKET_SZ, pktmbuf_pool, &kni_ops);
250    if (kni == NULL)
251        rte_exit(EXIT_FAILURE, "Fail to create kni dev "
252           "for port: %d\n", port);
253
254The code for allocating the kernel NIC interfaces for a specific port is as follows:
255
256.. code-block:: c
257
258    static int
259    kni_alloc(uint8_t port_id)
260    {
261        uint8_t i;
262        struct rte_kni *kni;
263        struct rte_kni_conf conf;
264        struct kni_port_params **params = kni_port_params_array;
265
266        if (port_id >= RTE_MAX_ETHPORTS || !params[port_id])
267            return -1;
268
269        params[port_id]->nb_kni = params[port_id]->nb_lcore_k ? params[port_id]->nb_lcore_k : 1;
270
271        for (i = 0; i < params[port_id]->nb_kni; i++) {
272
273            /* Clear conf at first */
274
275            memset(&conf, 0, sizeof(conf));
276            if (params[port_id]->nb_lcore_k) {
277                rte_snprintf(conf.name, RTE_KNI_NAMESIZE, "vEth%u_%u", port_id, i);
278                conf.core_id = params[port_id]->lcore_k[i];
279                conf.force_bind = 1;
280            } else
281                rte_snprintf(conf.name, RTE_KNI_NAMESIZE, "vEth%u", port_id);
282                conf.group_id = (uint16_t)port_id;
283                conf.mbuf_size = MAX_PACKET_SZ;
284
285                /*
286                 *   The first KNI device associated to a port
287                 *   is the master, for multiple kernel thread
288                 *   environment.
289                 */
290
291                if (i == 0) {
292                    struct rte_kni_ops ops;
293                    struct rte_eth_dev_info dev_info;
294
295                    memset(&dev_info, 0, sizeof(dev_info)); rte_eth_dev_info_get(port_id, &dev_info);
296
297                    conf.addr = dev_info.pci_dev->addr;
298                    conf.id = dev_info.pci_dev->id;
299
300                    memset(&ops, 0, sizeof(ops));
301
302                    ops.port_id = port_id;
303                    ops.change_mtu = kni_change_mtu;
304                    ops.config_network_if = kni_config_network_interface;
305
306                    kni = rte_kni_alloc(pktmbuf_pool, &conf, &ops);
307                } else
308                    kni = rte_kni_alloc(pktmbuf_pool, &conf, NULL);
309
310                if (!kni)
311                    rte_exit(EXIT_FAILURE, "Fail to create kni for "
312                            "port: %d\n", port_id);
313
314                params[port_id]->kni[i] = kni;
315            }
316        return 0;
317   }
318
319The other step in the initialization process that is unique to this sample application
320is the association of each port with lcores for RX, TX and kernel threads.
321
322*   One lcore to read from the port and write to the associated one or more KNI devices
323
324*   Another lcore to read from one or more KNI devices and write to the port
325
326*   Other lcores for pinning the kernel threads on one by one
327
328This is done by using the`kni_port_params_array[]` array, which is indexed by the port ID.
329The code is as follows:
330
331.. code-block:: console
332
333    static int
334    parse_config(const char *arg)
335    {
336        const char *p, *p0 = arg;
337        char s[256], *end;
338        unsigned size;
339        enum fieldnames {
340            FLD_PORT = 0,
341            FLD_LCORE_RX,
342            FLD_LCORE_TX,
343            _NUM_FLD = KNI_MAX_KTHREAD + 3,
344        };
345        int i, j, nb_token;
346        char *str_fld[_NUM_FLD];
347        unsigned long int_fld[_NUM_FLD];
348        uint8_t port_id, nb_kni_port_params = 0;
349
350        memset(&kni_port_params_array, 0, sizeof(kni_port_params_array));
351
352        while (((p = strchr(p0, '(')) != NULL) && nb_kni_port_params < RTE_MAX_ETHPORTS) {
353            p++;
354            if ((p0 = strchr(p, ')')) == NULL)
355                goto fail;
356
357            size = p0 - p;
358
359            if (size >= sizeof(s)) {
360                printf("Invalid config parameters\n");
361                goto fail;
362            }
363
364            rte_snprintf(s, sizeof(s), "%.*s", size, p);
365            nb_token = rte_strsplit(s, sizeof(s), str_fld, _NUM_FLD, ',');
366
367            if (nb_token <= FLD_LCORE_TX) {
368                printf("Invalid config parameters\n");
369                goto fail;
370            }
371
372            for (i = 0; i < nb_token; i++) {
373                errno = 0;
374                int_fld[i] = strtoul(str_fld[i], &end, 0);
375                if (errno != 0 || end == str_fld[i]) {
376                    printf("Invalid config parameters\n");
377                    goto fail;
378                }
379            }
380
381            i = 0;
382            port_id = (uint8_t)int_fld[i++];
383
384            if (port_id >= RTE_MAX_ETHPORTS) {
385                printf("Port ID %u could not exceed the maximum %u\n", port_id, RTE_MAX_ETHPORTS);
386                goto fail;
387            }
388
389            if (kni_port_params_array[port_id]) {
390                printf("Port %u has been configured\n", port_id);
391                goto fail;
392            }
393
394            kni_port_params_array[port_id] = (struct kni_port_params*)rte_zmalloc("KNI_port_params", sizeof(struct kni_port_params), RTE_CACHE_LINE_SIZE);
395            kni_port_params_array[port_id]->port_id = port_id;
396            kni_port_params_array[port_id]->lcore_rx = (uint8_t)int_fld[i++];
397            kni_port_params_array[port_id]->lcore_tx = (uint8_t)int_fld[i++];
398
399            if (kni_port_params_array[port_id]->lcore_rx >= RTE_MAX_LCORE || kni_port_params_array[port_id]->lcore_tx >= RTE_MAX_LCORE) {
400                printf("lcore_rx %u or lcore_tx %u ID could not "
401                        "exceed the maximum %u\n",
402                        kni_port_params_array[port_id]->lcore_rx, kni_port_params_array[port_id]->lcore_tx, RTE_MAX_LCORE);
403                goto fail;
404           }
405
406        for (j = 0; i < nb_token && j < KNI_MAX_KTHREAD; i++, j++)
407            kni_port_params_array[port_id]->lcore_k[j] = (uint8_t)int_fld[i];
408            kni_port_params_array[port_id]->nb_lcore_k = j;
409        }
410
411        print_config();
412
413        return 0;
414
415    fail:
416
417        for (i = 0; i < RTE_MAX_ETHPORTS; i++) {
418            if (kni_port_params_array[i]) {
419                rte_free(kni_port_params_array[i]);
420                kni_port_params_array[i] = NULL;
421            }
422        }
423
424        return -1;
425
426    }
427
428Packet Forwarding
429~~~~~~~~~~~~~~~~~
430
431After the initialization steps are completed, the main_loop() function is run on each lcore.
432This function first checks the lcore_id against the user provided lcore_rx and lcore_tx
433to see if this lcore is reading from or writing to kernel NIC interfaces.
434
435For the case that reads from a NIC port and writes to the kernel NIC interfaces,
436the packet reception is the same as in L2 Forwarding sample application
437(see Section 9.4.6 "Receive, Process  and Transmit Packets").
438The packet transmission is done by sending mbufs into the kernel NIC interfaces by rte_kni_tx_burst().
439The KNI library automatically frees the mbufs after the kernel successfully copied the mbufs.
440
441.. code-block:: c
442
443    /**
444     *   Interface to burst rx and enqueue mbufs into rx_q
445     */
446
447    static void
448    kni_ingress(struct kni_port_params *p)
449    {
450        uint8_t i, nb_kni, port_id;
451        unsigned nb_rx, num;
452        struct rte_mbuf *pkts_burst[PKT_BURST_SZ];
453
454        if (p == NULL)
455            return;
456
457        nb_kni = p->nb_kni;
458        port_id = p->port_id;
459
460        for (i = 0; i < nb_kni; i++) {
461            /* Burst rx from eth */
462            nb_rx = rte_eth_rx_burst(port_id, 0, pkts_burst, PKT_BURST_SZ);
463            if (unlikely(nb_rx > PKT_BURST_SZ)) {
464                RTE_LOG(ERR, APP, "Error receiving from eth\n");
465                return;
466            }
467
468            /* Burst tx to kni */
469            num = rte_kni_tx_burst(p->kni[i], pkts_burst, nb_rx);
470            kni_stats[port_id].rx_packets += num;
471            rte_kni_handle_request(p->kni[i]);
472
473            if (unlikely(num < nb_rx)) {
474                /* Free mbufs not tx to kni interface */
475                kni_burst_free_mbufs(&pkts_burst[num], nb_rx - num);
476                kni_stats[port_id].rx_dropped += nb_rx - num;
477            }
478        }
479    }
480
481For the other case that reads from kernel NIC interfaces and writes to a physical NIC port, packets are retrieved by reading
482mbufs from kernel NIC interfaces by `rte_kni_rx_burst()`.
483The packet transmission is the same as in the L2 Forwarding sample application
484(see Section 9.4.6 "Receive, Process and Transmit Packet's").
485
486.. code-block:: c
487
488    /**
489     *   Interface to dequeue mbufs from tx_q and burst tx
490     */
491
492    static void
493
494    kni_egress(struct kni_port_params *p)
495    {
496        uint8_t i, nb_kni, port_id;
497        unsigned nb_tx, num;
498        struct rte_mbuf *pkts_burst[PKT_BURST_SZ];
499
500        if (p == NULL)
501            return;
502
503        nb_kni = p->nb_kni;
504        port_id = p->port_id;
505
506        for (i = 0; i < nb_kni; i++) {
507            /* Burst rx from kni */
508            num = rte_kni_rx_burst(p->kni[i], pkts_burst, PKT_BURST_SZ);
509            if (unlikely(num > PKT_BURST_SZ)) {
510                RTE_LOG(ERR, APP, "Error receiving from KNI\n");
511                return;
512            }
513
514            /* Burst tx to eth */
515
516            nb_tx = rte_eth_tx_burst(port_id, 0, pkts_burst, (uint16_t)num);
517
518            kni_stats[port_id].tx_packets += nb_tx;
519
520            if (unlikely(nb_tx < num)) {
521                /* Free mbufs not tx to NIC */
522                kni_burst_free_mbufs(&pkts_burst[nb_tx], num - nb_tx);
523                kni_stats[port_id].tx_dropped += num - nb_tx;
524            }
525        }
526    }
527
528Callbacks for Kernel Requests
529~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
530
531To execute specific PMD operations in user space requested by some Linux* commands,
532callbacks must be implemented and filled in the struct rte_kni_ops structure.
533Currently, setting a new MTU and configuring the network interface (up/ down) are supported.
534
535.. code-block:: c
536
537    static struct rte_kni_ops kni_ops = {
538        .change_mtu = kni_change_mtu,
539        .config_network_if = kni_config_network_interface,
540    };
541
542    /* Callback for request of changing MTU */
543
544    static int
545    kni_change_mtu(uint8_t port_id, unsigned new_mtu)
546    {
547        int ret;
548        struct rte_eth_conf conf;
549
550        if (port_id >= rte_eth_dev_count()) {
551            RTE_LOG(ERR, APP, "Invalid port id %d\n", port_id);
552            return -EINVAL;
553        }
554
555        RTE_LOG(INFO, APP, "Change MTU of port %d to %u\n", port_id, new_mtu);
556
557        /* Stop specific port */
558
559        rte_eth_dev_stop(port_id);
560
561        memcpy(&conf, &port_conf, sizeof(conf));
562
563        /* Set new MTU */
564
565        if (new_mtu > ETHER_MAX_LEN)
566            conf.rxmode.jumbo_frame = 1;
567        else
568            conf.rxmode.jumbo_frame = 0;
569
570        /* mtu + length of header + length of FCS = max pkt length */
571
572        conf.rxmode.max_rx_pkt_len = new_mtu + KNI_ENET_HEADER_SIZE + KNI_ENET_FCS_SIZE;
573
574        ret = rte_eth_dev_configure(port_id, 1, 1, &conf);
575        if (ret < 0) {
576            RTE_LOG(ERR, APP, "Fail to reconfigure port %d\n", port_id);
577            return ret;
578        }
579
580        /* Restart specific port */
581
582        ret = rte_eth_dev_start(port_id);
583        if (ret < 0) {
584             RTE_LOG(ERR, APP, "Fail to restart port %d\n", port_id);
585            return ret;
586        }
587
588        return 0;
589    }
590
591    /* Callback for request of configuring network interface up/down */
592
593    static int
594    kni_config_network_interface(uint8_t port_id, uint8_t if_up)
595    {
596        int ret = 0;
597
598        if (port_id >= rte_eth_dev_count() || port_id >= RTE_MAX_ETHPORTS) {
599            RTE_LOG(ERR, APP, "Invalid port id %d\n", port_id);
600            return -EINVAL;
601        }
602
603        RTE_LOG(INFO, APP, "Configure network interface of %d %s\n",
604
605        port_id, if_up ? "up" : "down");
606
607        if (if_up != 0) {
608            /* Configure network interface up */
609            rte_eth_dev_stop(port_id);
610            ret = rte_eth_dev_start(port_id);
611        } else /* Configure network interface down */
612            rte_eth_dev_stop(port_id);
613
614        if (ret < 0)
615            RTE_LOG(ERR, APP, "Failed to start port %d\n", port_id);
616        return ret;
617    }
618