2014-11-11 12:27:01 +00:00
|
|
|
.. BSD LICENSE
|
|
|
|
Copyright(c) 2010-2014 Intel Corporation. All rights reserved.
|
|
|
|
All rights reserved.
|
|
|
|
|
|
|
|
Redistribution and use in source and binary forms, with or without
|
|
|
|
modification, are permitted provided that the following conditions
|
|
|
|
are met:
|
|
|
|
|
|
|
|
* Redistributions of source code must retain the above copyright
|
|
|
|
notice, this list of conditions and the following disclaimer.
|
|
|
|
* Redistributions in binary form must reproduce the above copyright
|
|
|
|
notice, this list of conditions and the following disclaimer in
|
|
|
|
the documentation and/or other materials provided with the
|
|
|
|
distribution.
|
|
|
|
* Neither the name of Intel Corporation nor the names of its
|
|
|
|
contributors may be used to endorse or promote products derived
|
|
|
|
from this software without specific prior written permission.
|
|
|
|
|
|
|
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
|
|
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
|
|
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
|
|
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
|
|
|
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
|
|
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
|
|
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
|
|
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
|
|
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
|
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
|
|
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
|
|
|
|
|
|
Kernel NIC Interface Sample Application
|
|
|
|
=======================================
|
|
|
|
|
2014-12-18 10:51:19 +00:00
|
|
|
The Kernel NIC Interface (KNI) is a DPDK control plane solution that
|
2014-11-11 12:27:01 +00:00
|
|
|
allows userspace applications to exchange packets with the kernel networking stack.
|
2014-12-18 10:51:19 +00:00
|
|
|
To accomplish this, DPDK userspace applications use an IOCTL call
|
2014-11-11 12:27:01 +00:00
|
|
|
to request the creation of a KNI virtual device in the Linux* kernel.
|
2014-12-18 10:51:19 +00:00
|
|
|
The IOCTL call provides interface information and the DPDK's physical address space,
|
2014-11-11 12:27:01 +00:00
|
|
|
which is re-mapped into the kernel address space by the KNI kernel loadable module
|
|
|
|
that saves the information to a virtual device context.
|
2014-12-18 10:51:19 +00:00
|
|
|
The DPDK creates FIFO queues for packet ingress and egress
|
2014-11-11 12:27:01 +00:00
|
|
|
to the kernel module for each device allocated.
|
|
|
|
|
|
|
|
The KNI kernel loadable module is a standard net driver,
|
2014-12-18 10:51:19 +00:00
|
|
|
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:
|
2014-11-11 12:27:01 +00:00
|
|
|
|
|
|
|
* Provides a faster mechanism to interface with the kernel net stack and eliminates system calls
|
|
|
|
|
2014-12-18 10:51:19 +00:00
|
|
|
* Facilitates the DPDK using standard Linux* userspace net tools (tcpdump, ftp, and so on)
|
2014-11-11 12:27:01 +00:00
|
|
|
|
|
|
|
* 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
|
2014-12-18 10:51:19 +00:00
|
|
|
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, ifconfig, tcpdump) with the DPDK ports and
|
|
|
|
also the exchange of packets between the DPDK application and the Linux* kernel.
|
2014-11-11 12:27:01 +00:00
|
|
|
|
|
|
|
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 packet flow through the Kernel NIC Interface application is as shown in the following figure.
|
|
|
|
|
2015-05-18 11:34:06 +00:00
|
|
|
.. _figure_kernel_nic:
|
2014-11-11 12:27:01 +00:00
|
|
|
|
2015-05-18 11:34:06 +00:00
|
|
|
.. figure:: img/kernel_nic.*
|
2014-11-11 12:27:01 +00:00
|
|
|
|
2015-05-18 11:34:06 +00:00
|
|
|
Kernel NIC Application Packet Flow
|
2014-11-11 12:27:01 +00:00
|
|
|
|
|
|
|
Compiling the Application
|
|
|
|
-------------------------
|
|
|
|
|
2017-10-25 15:50:59 +00:00
|
|
|
To compile the sample application see :doc:`compiling`.
|
2014-11-11 12:27:01 +00:00
|
|
|
|
2017-10-25 15:50:59 +00:00
|
|
|
The application is located in the ``kni`` sub-directory.
|
2014-11-11 12:27:01 +00:00
|
|
|
|
2017-10-25 15:50:59 +00:00
|
|
|
.. note::
|
2014-11-11 12:27:01 +00:00
|
|
|
|
|
|
|
This application is intended as a linuxapp only.
|
|
|
|
|
|
|
|
Loading the Kernel Module
|
|
|
|
-------------------------
|
|
|
|
|
2014-12-18 10:51:19 +00:00
|
|
|
Loading the KNI kernel module without any parameter is the typical way a DPDK application
|
2014-11-11 12:27:01 +00:00
|
|
|
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:
|
|
|
|
|
|
|
|
.. code-block:: console
|
|
|
|
|
|
|
|
#insmod rte_kni.ko
|
|
|
|
|
|
|
|
Pinning the kernel thread to a specific core can be done using a taskset command such as following:
|
|
|
|
|
|
|
|
.. code-block:: console
|
|
|
|
|
|
|
|
#taskset -p 100000 `pgrep --fl kni_thread | awk '{print $1}'`
|
|
|
|
|
|
|
|
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.
|
|
|
|
|
|
|
|
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.
|
|
|
|
|
|
|
|
To provide flexibility of performance, the kernel module of the KNI,
|
2014-12-18 10:51:19 +00:00
|
|
|
located in the kmod sub-directory of the DPDK target directory,
|
2014-11-11 12:27:01 +00:00
|
|
|
can be loaded with parameter of kthread_mode as follows:
|
|
|
|
|
|
|
|
* #insmod rte_kni.ko kthread_mode=single
|
|
|
|
|
|
|
|
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.
|
|
|
|
|
|
|
|
* #insmod rte_kni.ko kthread_mode =multiple
|
|
|
|
|
|
|
|
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.
|
|
|
|
|
|
|
|
To measure the throughput in a loopback mode, the kernel module of the KNI,
|
2014-12-18 10:51:19 +00:00
|
|
|
located in the kmod sub-directory of the DPDK target directory,
|
2014-11-11 12:27:01 +00:00
|
|
|
can be loaded with parameters as follows:
|
|
|
|
|
|
|
|
* #insmod rte_kni.ko lo_mode=lo_mode_fifo
|
|
|
|
|
|
|
|
This loopback mode will involve ring enqueue/dequeue operations in kernel space.
|
|
|
|
|
|
|
|
* #insmod rte_kni.ko lo_mode=lo_mode_fifo_skb
|
|
|
|
|
|
|
|
This loopback mode will involve ring enqueue/dequeue operations and sk buffer copies in kernel space.
|
|
|
|
|
|
|
|
Running the Application
|
|
|
|
-----------------------
|
|
|
|
|
|
|
|
The application requires a number of command line options:
|
|
|
|
|
|
|
|
.. code-block:: console
|
|
|
|
|
|
|
|
kni [EAL options] -- -P -p PORTMASK --config="(port,lcore_rx,lcore_tx[,lcore_kthread,...])[,port,lcore_rx,lcore_tx[,lcore_kthread,...]]"
|
|
|
|
|
|
|
|
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.
|
|
|
|
|
|
|
|
* -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 of RX, TX, kernel thread are mapped to which ports.
|
|
|
|
|
2014-12-18 10:51:19 +00:00
|
|
|
Refer to *DPDK Getting Started Guide* for general information on running applications and the Environment Abstraction Layer (EAL) options.
|
2014-11-11 12:27:01 +00:00
|
|
|
|
2017-02-27 19:13:40 +00:00
|
|
|
The -c coremask or -l corelist parameter of the EAL options should include the lcores indicated by the lcore_rx and lcore_tx,
|
2014-11-11 12:27:01 +00:00
|
|
|
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.
|
|
|
|
|
|
|
|
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.
|
|
|
|
|
|
|
|
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:
|
|
|
|
|
|
|
|
.. code-block:: console
|
|
|
|
|
2017-02-27 19:13:40 +00:00
|
|
|
./build/kni -l 4-7 -n 4 -- -P -p 0x3 -config="(0,4,6,8),(1,5,7,9)"
|
2014-11-11 12:27:01 +00:00
|
|
|
|
|
|
|
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
|
|
|
|
|
|
|
|
Displaying the NIC registers:
|
|
|
|
|
|
|
|
.. code-block:: console
|
|
|
|
|
|
|
|
#ethtool -d vEth0_0
|
|
|
|
|
|
|
|
Dumping the network traffic:
|
|
|
|
|
|
|
|
.. code-block:: console
|
|
|
|
|
|
|
|
#tcpdump -i vEth0_0
|
|
|
|
|
2014-12-18 10:51:19 +00:00
|
|
|
When the DPDK userspace application is closed, all the KNI devices are deleted from Linux*.
|
2014-11-11 12:27:01 +00:00
|
|
|
|
|
|
|
Explanation
|
|
|
|
-----------
|
|
|
|
|
|
|
|
The following sections provide some explanation of code.
|
|
|
|
|
|
|
|
Initialization
|
|
|
|
~~~~~~~~~~~~~~
|
|
|
|
|
2016-02-25 17:02:27 +00:00
|
|
|
Setup of mbuf pool, driver and queues is similar to the setup done in the :doc:`l2_forward_real_virtual`..
|
2014-11-11 12:27:01 +00:00
|
|
|
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 as follows:
|
|
|
|
|
|
|
|
.. code-block:: c
|
|
|
|
|
|
|
|
static int
|
2017-10-13 13:17:01 +00:00
|
|
|
kni_alloc(uint16_t port_id)
|
2014-11-11 12:27:01 +00:00
|
|
|
{
|
|
|
|
uint8_t i;
|
|
|
|
struct rte_kni *kni;
|
|
|
|
struct rte_kni_conf conf;
|
|
|
|
struct kni_port_params **params = kni_port_params_array;
|
|
|
|
|
|
|
|
if (port_id >= RTE_MAX_ETHPORTS || !params[port_id])
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
params[port_id]->nb_kni = params[port_id]->nb_lcore_k ? params[port_id]->nb_lcore_k : 1;
|
|
|
|
|
|
|
|
for (i = 0; i < params[port_id]->nb_kni; i++) {
|
|
|
|
|
|
|
|
/* Clear conf at first */
|
|
|
|
|
|
|
|
memset(&conf, 0, sizeof(conf));
|
|
|
|
if (params[port_id]->nb_lcore_k) {
|
2015-12-15 09:26:20 +00:00
|
|
|
snprintf(conf.name, RTE_KNI_NAMESIZE, "vEth%u_%u", port_id, i);
|
2014-11-11 12:27:01 +00:00
|
|
|
conf.core_id = params[port_id]->lcore_k[i];
|
|
|
|
conf.force_bind = 1;
|
|
|
|
} else
|
2015-12-15 09:26:20 +00:00
|
|
|
snprintf(conf.name, RTE_KNI_NAMESIZE, "vEth%u", port_id);
|
2014-11-11 12:27:01 +00:00
|
|
|
conf.group_id = (uint16_t)port_id;
|
|
|
|
conf.mbuf_size = MAX_PACKET_SZ;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* The first KNI device associated to a port
|
|
|
|
* is the master, for multiple kernel thread
|
|
|
|
* environment.
|
|
|
|
*/
|
|
|
|
|
|
|
|
if (i == 0) {
|
|
|
|
struct rte_kni_ops ops;
|
|
|
|
struct rte_eth_dev_info dev_info;
|
|
|
|
|
|
|
|
memset(&dev_info, 0, sizeof(dev_info)); rte_eth_dev_info_get(port_id, &dev_info);
|
|
|
|
|
|
|
|
conf.addr = dev_info.pci_dev->addr;
|
|
|
|
conf.id = dev_info.pci_dev->id;
|
|
|
|
|
|
|
|
memset(&ops, 0, sizeof(ops));
|
|
|
|
|
|
|
|
ops.port_id = port_id;
|
|
|
|
ops.change_mtu = kni_change_mtu;
|
|
|
|
ops.config_network_if = kni_config_network_interface;
|
|
|
|
|
|
|
|
kni = rte_kni_alloc(pktmbuf_pool, &conf, &ops);
|
|
|
|
} else
|
|
|
|
kni = rte_kni_alloc(pktmbuf_pool, &conf, NULL);
|
|
|
|
|
|
|
|
if (!kni)
|
|
|
|
rte_exit(EXIT_FAILURE, "Fail to create kni for "
|
|
|
|
"port: %d\n", port_id);
|
|
|
|
|
|
|
|
params[port_id]->kni[i] = kni;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
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 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];
|
2017-10-13 13:17:01 +00:00
|
|
|
uint16_t port_id, nb_kni_port_params = 0;
|
2014-11-11 12:27:01 +00:00
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2015-12-15 09:26:20 +00:00
|
|
|
snprintf(s, sizeof(s), "%.*s", size, p);
|
2014-11-11 12:27:01 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2014-11-19 12:26:06 +00:00
|
|
|
kni_port_params_array[port_id] = (struct kni_port_params*)rte_zmalloc("KNI_port_params", sizeof(struct kni_port_params), RTE_CACHE_LINE_SIZE);
|
2014-11-11 12:27:01 +00:00
|
|
|
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;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
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,
|
|
|
|
the packet reception is the same as in L2 Forwarding sample application
|
2016-02-25 17:02:27 +00:00
|
|
|
(see :ref:`l2_fwd_app_rx_tx_packets`).
|
2014-11-11 12:27:01 +00:00
|
|
|
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()`.
|
|
|
|
The packet transmission is the same as in the L2 Forwarding sample application
|
2016-02-25 17:02:27 +00:00
|
|
|
(see :ref:`l2_fwd_app_rx_tx_packets`).
|
2014-11-11 12:27:01 +00:00
|
|
|
|
|
|
|
.. 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 and configuring the network interface (up/ down) are supported.
|
|
|
|
|
|
|
|
.. code-block:: c
|
|
|
|
|
|
|
|
static struct rte_kni_ops kni_ops = {
|
|
|
|
.change_mtu = kni_change_mtu,
|
|
|
|
.config_network_if = kni_config_network_interface,
|
|
|
|
};
|
|
|
|
|
|
|
|
/* Callback for request of changing MTU */
|
|
|
|
|
|
|
|
static int
|
2017-10-13 13:17:01 +00:00
|
|
|
kni_change_mtu(uint16_t port_id, unsigned new_mtu)
|
2014-11-11 12:27:01 +00:00
|
|
|
{
|
|
|
|
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
|
2017-10-13 13:17:01 +00:00
|
|
|
kni_config_network_interface(uint16_t port_id, uint8_t if_up)
|
2014-11-11 12:27:01 +00:00
|
|
|
{
|
|
|
|
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;
|
|
|
|
}
|