1*d0dff9baSBernard Iremonger.. BSD LICENSE 2*d0dff9baSBernard Iremonger Copyright(c) 2010-2014 Intel Corporation. All rights reserved. 3*d0dff9baSBernard Iremonger All rights reserved. 4*d0dff9baSBernard Iremonger 5*d0dff9baSBernard Iremonger Redistribution and use in source and binary forms, with or without 6*d0dff9baSBernard Iremonger modification, are permitted provided that the following conditions 7*d0dff9baSBernard Iremonger are met: 8*d0dff9baSBernard Iremonger 9*d0dff9baSBernard Iremonger * Redistributions of source code must retain the above copyright 10*d0dff9baSBernard Iremonger notice, this list of conditions and the following disclaimer. 11*d0dff9baSBernard Iremonger * Redistributions in binary form must reproduce the above copyright 12*d0dff9baSBernard Iremonger notice, this list of conditions and the following disclaimer in 13*d0dff9baSBernard Iremonger the documentation and/or other materials provided with the 14*d0dff9baSBernard Iremonger distribution. 15*d0dff9baSBernard Iremonger * Neither the name of Intel Corporation nor the names of its 16*d0dff9baSBernard Iremonger contributors may be used to endorse or promote products derived 17*d0dff9baSBernard Iremonger from this software without specific prior written permission. 18*d0dff9baSBernard Iremonger 19*d0dff9baSBernard Iremonger THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 20*d0dff9baSBernard Iremonger "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 21*d0dff9baSBernard Iremonger LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 22*d0dff9baSBernard Iremonger A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 23*d0dff9baSBernard Iremonger OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 24*d0dff9baSBernard Iremonger SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 25*d0dff9baSBernard Iremonger LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 26*d0dff9baSBernard Iremonger DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 27*d0dff9baSBernard Iremonger THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 28*d0dff9baSBernard Iremonger (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29*d0dff9baSBernard Iremonger OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30*d0dff9baSBernard Iremonger 31*d0dff9baSBernard IremongerLink Status Interrupt Sample Application 32*d0dff9baSBernard Iremonger======================================== 33*d0dff9baSBernard Iremonger 34*d0dff9baSBernard IremongerThe Link Status Interrupt sample application is a simple example of packet processing using 35*d0dff9baSBernard Iremongerthe Intel® Data Plane Development Kit (Intel® DPDK) that 36*d0dff9baSBernard Iremongerdemonstrates how network link status changes for a network port can be captured and 37*d0dff9baSBernard Iremongerused by an Intel® DPDK application. 38*d0dff9baSBernard Iremonger 39*d0dff9baSBernard IremongerOverview 40*d0dff9baSBernard Iremonger-------- 41*d0dff9baSBernard Iremonger 42*d0dff9baSBernard IremongerThe Link Status Interrupt sample application registers a user space callback for the link status interrupt of each port 43*d0dff9baSBernard Iremongerand performs L2 forwarding for each packet that is received on an RX_PORT. 44*d0dff9baSBernard IremongerThe following operations are performed: 45*d0dff9baSBernard Iremonger 46*d0dff9baSBernard Iremonger* RX_PORT and TX_PORT are paired with available ports one-by-one according to the core mask 47*d0dff9baSBernard Iremonger 48*d0dff9baSBernard Iremonger* The source MAC address is replaced by the TX_PORT MAC address 49*d0dff9baSBernard Iremonger 50*d0dff9baSBernard Iremonger* The destination MAC address is replaced by 02:00:00:00:00:TX_PORT_ID 51*d0dff9baSBernard Iremonger 52*d0dff9baSBernard IremongerThis application can be used to demonstrate the usage of link status interrupt and its user space callbacks 53*d0dff9baSBernard Iremongerand the behavior of L2 forwarding each time the link status changes. 54*d0dff9baSBernard Iremonger 55*d0dff9baSBernard IremongerCompiling the Application 56*d0dff9baSBernard Iremonger------------------------- 57*d0dff9baSBernard Iremonger 58*d0dff9baSBernard Iremonger#. Go to the example directory: 59*d0dff9baSBernard Iremonger 60*d0dff9baSBernard Iremonger .. code-block:: console 61*d0dff9baSBernard Iremonger 62*d0dff9baSBernard Iremonger export RTE_SDK=/path/to/rte_sdk 63*d0dff9baSBernard Iremonger cd ${RTE_SDK}/examples/link_status_interrupt 64*d0dff9baSBernard Iremonger 65*d0dff9baSBernard Iremonger#. Set the target (a default target is used if not specified). For example: 66*d0dff9baSBernard Iremonger 67*d0dff9baSBernard Iremonger .. code-block:: console 68*d0dff9baSBernard Iremonger 69*d0dff9baSBernard Iremonger export RTE_TARGET=x86_64-native-linuxapp-gcc 70*d0dff9baSBernard Iremonger 71*d0dff9baSBernard Iremonger See the *Intel® DPDK Getting Started Guide* for possible RTE_TARGET values. 72*d0dff9baSBernard Iremonger 73*d0dff9baSBernard Iremonger#. Build the application: 74*d0dff9baSBernard Iremonger 75*d0dff9baSBernard Iremonger .. code-block:: console 76*d0dff9baSBernard Iremonger 77*d0dff9baSBernard Iremonger make 78*d0dff9baSBernard Iremonger 79*d0dff9baSBernard Iremonger.. note:: 80*d0dff9baSBernard Iremonger 81*d0dff9baSBernard Iremonger The compiled application is written to the build subdirectory. 82*d0dff9baSBernard Iremonger To have the application written to a different location, 83*d0dff9baSBernard Iremonger the O=/path/to/build/directory option may be specified on the make command line. 84*d0dff9baSBernard Iremonger 85*d0dff9baSBernard IremongerRunning the Application 86*d0dff9baSBernard Iremonger----------------------- 87*d0dff9baSBernard Iremonger 88*d0dff9baSBernard IremongerThe application requires a number of command line options: 89*d0dff9baSBernard Iremonger 90*d0dff9baSBernard Iremonger.. code-block:: console 91*d0dff9baSBernard Iremonger 92*d0dff9baSBernard Iremonger ./build/link_status_interrupt [EAL options] -- -p PORTMASK [-q NQ][-T PERIOD] 93*d0dff9baSBernard Iremonger 94*d0dff9baSBernard Iremongerwhere, 95*d0dff9baSBernard Iremonger 96*d0dff9baSBernard Iremonger* -p PORTMASK: A hexadecimal bitmask of the ports to configure 97*d0dff9baSBernard Iremonger 98*d0dff9baSBernard Iremonger* -q NQ: A number of queues (=ports) per lcore (default is 1) 99*d0dff9baSBernard Iremonger 100*d0dff9baSBernard Iremonger* -T PERIOD: statistics will be refreshed each PERIOD seconds (0 to disable, 10 default) 101*d0dff9baSBernard Iremonger 102*d0dff9baSBernard IremongerTo run the application in a linuxapp environment with 4 lcores, 4 memory channels, 16 ports and 8 RX queues per lcore, 103*d0dff9baSBernard Iremongerissue the command: 104*d0dff9baSBernard Iremonger 105*d0dff9baSBernard Iremonger.. code-block:: console 106*d0dff9baSBernard Iremonger 107*d0dff9baSBernard Iremonger $ ./build/link_status_interrupt -c f -n 4-- -q 8 -p ffff 108*d0dff9baSBernard Iremonger 109*d0dff9baSBernard IremongerRefer to the *Intel® DPDK Getting Started Guide* for general information on running applications 110*d0dff9baSBernard Iremongerand the Environment Abstraction Layer (EAL) options. 111*d0dff9baSBernard Iremonger 112*d0dff9baSBernard IremongerExplanation 113*d0dff9baSBernard Iremonger----------- 114*d0dff9baSBernard Iremonger 115*d0dff9baSBernard IremongerThe following sections provide some explanation of the code. 116*d0dff9baSBernard Iremonger 117*d0dff9baSBernard IremongerCommand Line Arguments 118*d0dff9baSBernard Iremonger~~~~~~~~~~~~~~~~~~~~~~ 119*d0dff9baSBernard Iremonger 120*d0dff9baSBernard IremongerThe Link Status Interrupt sample application takes specific parameters, 121*d0dff9baSBernard Iremongerin addition to Environment Abstraction Layer (EAL) arguments (see Section 13.3). 122*d0dff9baSBernard Iremonger 123*d0dff9baSBernard IremongerCommand line parsing is done in the same way as it is done in the L2 Forwarding Sample Application. 124*d0dff9baSBernard IremongerSee Section 9.4.1, "Command Line Arguments" for more information. 125*d0dff9baSBernard Iremonger 126*d0dff9baSBernard IremongerMbuf Pool Initialization 127*d0dff9baSBernard Iremonger~~~~~~~~~~~~~~~~~~~~~~~~ 128*d0dff9baSBernard Iremonger 129*d0dff9baSBernard IremongerMbuf pool initialization is done in the same way as it is done in the L2 Forwarding Sample Application. 130*d0dff9baSBernard IremongerSee Section 9.4.2, "Mbuf Pool Initialization" for more information. 131*d0dff9baSBernard Iremonger 132*d0dff9baSBernard IremongerDriver Initialization 133*d0dff9baSBernard Iremonger~~~~~~~~~~~~~~~~~~~~~ 134*d0dff9baSBernard Iremonger 135*d0dff9baSBernard IremongerThe main part of the code in the main() function relates to the initialization of the driver. 136*d0dff9baSBernard IremongerTo fully understand this code, it is recommended to study the chapters that related to the Poll Mode Driver in the 137*d0dff9baSBernard Iremonger*Intel® DPDK Programmer's Guide and the Intel® DPDK API Reference*. 138*d0dff9baSBernard Iremonger 139*d0dff9baSBernard Iremonger.. code-block:: c 140*d0dff9baSBernard Iremonger 141*d0dff9baSBernard Iremonger if (rte_eal_pci_probe() < 0) 142*d0dff9baSBernard Iremonger rte_exit(EXIT_FAILURE, "Cannot probe PCI\n"); 143*d0dff9baSBernard Iremonger 144*d0dff9baSBernard Iremonger nb_ports = rte_eth_dev_count(); 145*d0dff9baSBernard Iremonger if (nb_ports == 0) 146*d0dff9baSBernard Iremonger rte_exit(EXIT_FAILURE, "No Ethernet ports - bye\n"); 147*d0dff9baSBernard Iremonger 148*d0dff9baSBernard Iremonger if (nb_ports > RTE_MAX_ETHPORTS) 149*d0dff9baSBernard Iremonger nb_ports = RTE_MAX_ETHPORTS; 150*d0dff9baSBernard Iremonger 151*d0dff9baSBernard Iremonger /* 152*d0dff9baSBernard Iremonger * Each logical core is assigned a dedicated TX queue on each port. 153*d0dff9baSBernard Iremonger */ 154*d0dff9baSBernard Iremonger 155*d0dff9baSBernard Iremonger for (portid = 0; portid < nb_ports; portid++) { 156*d0dff9baSBernard Iremonger /* skip ports that are not enabled */ 157*d0dff9baSBernard Iremonger 158*d0dff9baSBernard Iremonger if ((lsi_enabled_port_mask & (1 << portid)) == 0) 159*d0dff9baSBernard Iremonger continue; 160*d0dff9baSBernard Iremonger 161*d0dff9baSBernard Iremonger /* save the destination port id */ 162*d0dff9baSBernard Iremonger 163*d0dff9baSBernard Iremonger if (nb_ports_in_mask % 2) { 164*d0dff9baSBernard Iremonger lsi_dst_ports[portid] = portid_last; 165*d0dff9baSBernard Iremonger lsi_dst_ports[portid_last] = portid; 166*d0dff9baSBernard Iremonger } 167*d0dff9baSBernard Iremonger else 168*d0dff9baSBernard Iremonger portid_last = portid; 169*d0dff9baSBernard Iremonger 170*d0dff9baSBernard Iremonger nb_ports_in_mask++; 171*d0dff9baSBernard Iremonger 172*d0dff9baSBernard Iremonger rte_eth_dev_info_get((uint8_t) portid, &dev_info); 173*d0dff9baSBernard Iremonger } 174*d0dff9baSBernard Iremonger 175*d0dff9baSBernard IremongerObserve that: 176*d0dff9baSBernard Iremonger 177*d0dff9baSBernard Iremonger* rte_eal_pci_probe() parses the devices on the PCI bus and initializes recognized devices. 178*d0dff9baSBernard Iremonger 179*d0dff9baSBernard IremongerThe next step is to configure the RX and TX queues. 180*d0dff9baSBernard IremongerFor each port, there is only one RX queue (only one lcore is able to poll a given port). 181*d0dff9baSBernard IremongerThe number of TX queues depends on the number of available lcores. 182*d0dff9baSBernard IremongerThe rte_eth_dev_configure() function is used to configure the number of queues for a port: 183*d0dff9baSBernard Iremonger 184*d0dff9baSBernard Iremonger.. code-block:: c 185*d0dff9baSBernard Iremonger 186*d0dff9baSBernard Iremonger ret = rte_eth_dev_configure((uint8_t) portid, 1, 1, &port_conf); 187*d0dff9baSBernard Iremonger if (ret < 0) 188*d0dff9baSBernard Iremonger rte_exit(EXIT_FAILURE, "Cannot configure device: err=%d, port=%u\n", ret, portid); 189*d0dff9baSBernard Iremonger 190*d0dff9baSBernard IremongerThe global configuration is stored in a static structure: 191*d0dff9baSBernard Iremonger 192*d0dff9baSBernard Iremonger.. code-block:: c 193*d0dff9baSBernard Iremonger 194*d0dff9baSBernard Iremonger static const struct rte_eth_conf port_conf = { 195*d0dff9baSBernard Iremonger .rxmode = { 196*d0dff9baSBernard Iremonger .split_hdr_size = 0, 197*d0dff9baSBernard Iremonger .header_split = 0, /**< Header Split disabled */ 198*d0dff9baSBernard Iremonger .hw_ip_checksum = 0, /**< IP checksum offload disabled */ 199*d0dff9baSBernard Iremonger .hw_vlan_filter = 0, /**< VLAN filtering disabled */ 200*d0dff9baSBernard Iremonger .hw_strip_crc= 0, /**< CRC stripped by hardware */ 201*d0dff9baSBernard Iremonger }, 202*d0dff9baSBernard Iremonger .txmode = {}, 203*d0dff9baSBernard Iremonger .intr_conf = { 204*d0dff9baSBernard Iremonger .lsc = 1, /**< link status interrupt feature enabled */ 205*d0dff9baSBernard Iremonger }, 206*d0dff9baSBernard Iremonger }; 207*d0dff9baSBernard Iremonger 208*d0dff9baSBernard IremongerConfiguring lsc to 0 (the default) disables the generation of any link status change interrupts in kernel space 209*d0dff9baSBernard Iremongerand no user space interrupt event is received. 210*d0dff9baSBernard IremongerThe public interface rte_eth_link_get() accesses the NIC registers directly to update the link status. 211*d0dff9baSBernard IremongerConfiguring lsc to non-zero enables the generation of link status change interrupts in kernel space 212*d0dff9baSBernard Iremongerwhen a link status change is present and calls the user space callbacks registered by the application. 213*d0dff9baSBernard IremongerThe public interface rte_eth_link_get() just reads the link status in a global structure 214*d0dff9baSBernard Iremongerthat would be updated in the interrupt host thread only. 215*d0dff9baSBernard Iremonger 216*d0dff9baSBernard IremongerInterrupt Callback Registration 217*d0dff9baSBernard Iremonger~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 218*d0dff9baSBernard Iremonger 219*d0dff9baSBernard IremongerThe application can register one or more callbacks to a specific port and interrupt event. 220*d0dff9baSBernard IremongerAn example callback function that has been written as indicated below. 221*d0dff9baSBernard Iremonger 222*d0dff9baSBernard Iremonger.. code-block:: c 223*d0dff9baSBernard Iremonger 224*d0dff9baSBernard Iremonger static void 225*d0dff9baSBernard Iremonger lsi_event_callback(uint8_t port_id, enum rte_eth_event_type type, void *param) 226*d0dff9baSBernard Iremonger { 227*d0dff9baSBernard Iremonger struct rte_eth_link link; 228*d0dff9baSBernard Iremonger 229*d0dff9baSBernard Iremonger RTE_SET_USED(param); 230*d0dff9baSBernard Iremonger 231*d0dff9baSBernard Iremonger printf("\n\nIn registered callback...\n"); 232*d0dff9baSBernard Iremonger 233*d0dff9baSBernard Iremonger printf("Event type: %s\n", type == RTE_ETH_EVENT_INTR_LSC ? "LSC interrupt" : "unknown event"); 234*d0dff9baSBernard Iremonger 235*d0dff9baSBernard Iremonger rte_eth_link_get_nowait(port_id, &link); 236*d0dff9baSBernard Iremonger 237*d0dff9baSBernard Iremonger if (link.link_status) { 238*d0dff9baSBernard Iremonger printf("Port %d Link Up - speed %u Mbps - %s\n\n", port_id, (unsigned)link.link_speed, 239*d0dff9baSBernard Iremonger (link.link_duplex == ETH_LINK_FULL_DUPLEX) ? ("full-duplex") : ("half-duplex")); 240*d0dff9baSBernard Iremonger } else 241*d0dff9baSBernard Iremonger printf("Port %d Link Down\n\n", port_id); 242*d0dff9baSBernard Iremonger } 243*d0dff9baSBernard Iremonger 244*d0dff9baSBernard IremongerThis function is called when a link status interrupt is present for the right port. 245*d0dff9baSBernard IremongerThe port_id indicates which port the interrupt applies to. 246*d0dff9baSBernard IremongerThe type parameter identifies the interrupt event type, 247*d0dff9baSBernard Iremongerwhich currently can be RTE_ETH_EVENT_INTR_LSC only, but other types can be added in the future. 248*d0dff9baSBernard IremongerThe param parameter is the address of the parameter for the callback. 249*d0dff9baSBernard IremongerThis function should be implemented with care since it will be called in the interrupt host thread, 250*d0dff9baSBernard Iremongerwhich is different from the main thread of its caller. 251*d0dff9baSBernard Iremonger 252*d0dff9baSBernard IremongerThe application registers the lsi_event_callback and a NULL parameter to the link status interrupt event on each port: 253*d0dff9baSBernard Iremonger 254*d0dff9baSBernard Iremonger.. code-block:: c 255*d0dff9baSBernard Iremonger 256*d0dff9baSBernard Iremonger rte_eth_dev_callback_register((uint8_t)portid, RTE_ETH_EVENT_INTR_LSC, lsi_event_callback, NULL); 257*d0dff9baSBernard Iremonger 258*d0dff9baSBernard IremongerThis registration can be done only after calling the rte_eth_dev_configure() function and before calling any other function. 259*d0dff9baSBernard IremongerIf lsc is initialized with 0, the callback is never called since no interrupt event would ever be present. 260*d0dff9baSBernard Iremonger 261*d0dff9baSBernard IremongerRX Queue Initialization 262*d0dff9baSBernard Iremonger~~~~~~~~~~~~~~~~~~~~~~~ 263*d0dff9baSBernard Iremonger 264*d0dff9baSBernard IremongerThe application uses one lcore to poll one or several ports, depending on the -q option, 265*d0dff9baSBernard Iremongerwhich specifies the number of queues per lcore. 266*d0dff9baSBernard Iremonger 267*d0dff9baSBernard IremongerFor example, if the user specifies -q 4, the application is able to poll four ports with one lcore. 268*d0dff9baSBernard IremongerIf there are 16 ports on the target (and if the portmask argument is -p ffff), 269*d0dff9baSBernard Iremongerthe application will need four lcores to poll all the ports. 270*d0dff9baSBernard Iremonger 271*d0dff9baSBernard Iremonger.. code-block:: c 272*d0dff9baSBernard Iremonger 273*d0dff9baSBernard Iremonger ret = rte_eth_rx_queue_setup((uint8_t) portid, 0, nb_rxd, SOCKET0, &rx_conf, lsi_pktmbuf_pool); 274*d0dff9baSBernard Iremonger if (ret < 0) 275*d0dff9baSBernard Iremonger rte_exit(EXIT_FAILURE, "rte_eth_rx_queue_setup: err=%d, port=%u\n", ret, portid); 276*d0dff9baSBernard Iremonger 277*d0dff9baSBernard IremongerThe list of queues that must be polled for a given lcore is stored in a private structure called struct lcore_queue_conf. 278*d0dff9baSBernard Iremonger 279*d0dff9baSBernard Iremonger.. code-block:: c 280*d0dff9baSBernard Iremonger 281*d0dff9baSBernard Iremonger struct lcore_queue_conf { 282*d0dff9baSBernard Iremonger unsigned n_rx_port; 283*d0dff9baSBernard Iremonger unsigned rx_port_list[MAX_RX_QUEUE_PER_LCORE]; unsigned tx_queue_id; 284*d0dff9baSBernard Iremonger struct mbuf_table tx_mbufs[LSI_MAX_PORTS]; 285*d0dff9baSBernard Iremonger } rte_cache_aligned; 286*d0dff9baSBernard Iremonger 287*d0dff9baSBernard Iremonger struct lcore_queue_conf lcore_queue_conf[RTE_MAX_LCORE]; 288*d0dff9baSBernard Iremonger 289*d0dff9baSBernard IremongerThe n_rx_port and rx_port_list[] fields are used in the main packet processing loop 290*d0dff9baSBernard Iremonger(see Section 13.4.7, "Receive, Process and Transmit Packets" later in this chapter). 291*d0dff9baSBernard Iremonger 292*d0dff9baSBernard IremongerThe global configuration for the RX queues is stored in a static structure: 293*d0dff9baSBernard Iremonger 294*d0dff9baSBernard Iremonger.. code-block:: c 295*d0dff9baSBernard Iremonger 296*d0dff9baSBernard Iremonger static const struct rte_eth_rxconf rx_conf = { 297*d0dff9baSBernard Iremonger .rx_thresh = { 298*d0dff9baSBernard Iremonger .pthresh = RX_PTHRESH, 299*d0dff9baSBernard Iremonger .hthresh = RX_HTHRESH, 300*d0dff9baSBernard Iremonger .wthresh = RX_WTHRESH, 301*d0dff9baSBernard Iremonger }, 302*d0dff9baSBernard Iremonger }; 303*d0dff9baSBernard Iremonger 304*d0dff9baSBernard IremongerTX Queue Initialization 305*d0dff9baSBernard Iremonger~~~~~~~~~~~~~~~~~~~~~~~ 306*d0dff9baSBernard Iremonger 307*d0dff9baSBernard IremongerEach lcore should be able to transmit on any port. 308*d0dff9baSBernard IremongerFor every port, a single TX queue is initialized. 309*d0dff9baSBernard Iremonger 310*d0dff9baSBernard Iremonger.. code-block:: c 311*d0dff9baSBernard Iremonger 312*d0dff9baSBernard Iremonger /* init one TX queue logical core on each port */ 313*d0dff9baSBernard Iremonger 314*d0dff9baSBernard Iremonger fflush(stdout); 315*d0dff9baSBernard Iremonger 316*d0dff9baSBernard Iremonger ret = rte_eth_tx_queue_setup(portid, 0, nb_txd, rte_eth_dev_socket_id(portid), &tx_conf); 317*d0dff9baSBernard Iremonger if (ret < 0) 318*d0dff9baSBernard Iremonger rte_exit(EXIT_FAILURE, "rte_eth_tx_queue_setup: err=%d,port=%u\n", ret, (unsigned) portid); 319*d0dff9baSBernard Iremonger 320*d0dff9baSBernard IremongerThe global configuration for TX queues is stored in a static structure: 321*d0dff9baSBernard Iremonger 322*d0dff9baSBernard Iremonger.. code-block:: c 323*d0dff9baSBernard Iremonger 324*d0dff9baSBernard Iremonger static const struct rte_eth_txconf tx_conf = { 325*d0dff9baSBernard Iremonger .tx_thresh = { 326*d0dff9baSBernard Iremonger .pthresh = TX_PTHRESH, 327*d0dff9baSBernard Iremonger .hthresh = TX_HTHRESH, 328*d0dff9baSBernard Iremonger .wthresh = TX_WTHRESH, 329*d0dff9baSBernard Iremonger }, 330*d0dff9baSBernard Iremonger .tx_free_thresh = RTE_TEST_TX_DESC_DEFAULT + 1, /* disable feature */ 331*d0dff9baSBernard Iremonger }; 332*d0dff9baSBernard Iremonger 333*d0dff9baSBernard IremongerReceive, Process and Transmit Packets 334*d0dff9baSBernard Iremonger~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 335*d0dff9baSBernard Iremonger 336*d0dff9baSBernard IremongerIn the lsi_main_loop() function, the main task is to read ingress packets from the RX queues. 337*d0dff9baSBernard IremongerThis is done using the following code: 338*d0dff9baSBernard Iremonger 339*d0dff9baSBernard Iremonger.. code-block:: c 340*d0dff9baSBernard Iremonger 341*d0dff9baSBernard Iremonger /* 342*d0dff9baSBernard Iremonger * Read packet from RX queues 343*d0dff9baSBernard Iremonger */ 344*d0dff9baSBernard Iremonger 345*d0dff9baSBernard Iremonger for (i = 0; i < qconf->n_rx_port; i++) { 346*d0dff9baSBernard Iremonger portid = qconf->rx_port_list[i]; 347*d0dff9baSBernard Iremonger nb_rx = rte_eth_rx_burst((uint8_t) portid, 0, pkts_burst, MAX_PKT_BURST); 348*d0dff9baSBernard Iremonger port_statistics[portid].rx += nb_rx; 349*d0dff9baSBernard Iremonger 350*d0dff9baSBernard Iremonger for (j = 0; j < nb_rx; j++) { 351*d0dff9baSBernard Iremonger m = pkts_burst[j]; 352*d0dff9baSBernard Iremonger rte_prefetch0(rte_pktmbuf_mtod(m, void *)); 353*d0dff9baSBernard Iremonger lsi_simple_forward(m, portid); 354*d0dff9baSBernard Iremonger } 355*d0dff9baSBernard Iremonger } 356*d0dff9baSBernard Iremonger 357*d0dff9baSBernard IremongerPackets are read in a burst of size MAX_PKT_BURST. 358*d0dff9baSBernard IremongerThe rte_eth_rx_burst() function writes the mbuf pointers in a local table and returns the number of available mbufs in the table. 359*d0dff9baSBernard Iremonger 360*d0dff9baSBernard IremongerThen, each mbuf in the table is processed by the lsi_simple_forward() function. 361*d0dff9baSBernard IremongerThe processing is very simple: processes the TX port from the RX port and then replaces the source and destination MAC addresses. 362*d0dff9baSBernard Iremonger 363*d0dff9baSBernard Iremonger.. note:: 364*d0dff9baSBernard Iremonger 365*d0dff9baSBernard Iremonger In the following code, the two lines for calculating the output port require some explanation. 366*d0dff9baSBernard Iremonger If portId is even, the first line does nothing (as portid & 1 will be 0), and the second line adds 1. 367*d0dff9baSBernard Iremonger If portId is odd, the first line subtracts one and the second line does nothing. 368*d0dff9baSBernard Iremonger Therefore, 0 goes to 1, and 1 to 0, 2 goes to 3 and 3 to 2, and so on. 369*d0dff9baSBernard Iremonger 370*d0dff9baSBernard Iremonger.. code-block:: c 371*d0dff9baSBernard Iremonger 372*d0dff9baSBernard Iremonger static void 373*d0dff9baSBernard Iremonger lsi_simple_forward(struct rte_mbuf *m, unsigned portid) 374*d0dff9baSBernard Iremonger { 375*d0dff9baSBernard Iremonger struct ether_hdr *eth; 376*d0dff9baSBernard Iremonger void *tmp; 377*d0dff9baSBernard Iremonger unsigned dst_port = lsi_dst_ports[portid]; 378*d0dff9baSBernard Iremonger 379*d0dff9baSBernard Iremonger eth = rte_pktmbuf_mtod(m, struct ether_hdr *); 380*d0dff9baSBernard Iremonger 381*d0dff9baSBernard Iremonger /* 02:00:00:00:00:xx */ 382*d0dff9baSBernard Iremonger 383*d0dff9baSBernard Iremonger tmp = ð->d_addr.addr_bytes[0]; 384*d0dff9baSBernard Iremonger 385*d0dff9baSBernard Iremonger *((uint64_t *)tmp) = 0x000000000002 + (dst_port << 40); 386*d0dff9baSBernard Iremonger 387*d0dff9baSBernard Iremonger /* src addr */ 388*d0dff9baSBernard Iremonger ether_addr_copy(&lsi_ports_eth_addr[dst_port], ð->s_addr); 389*d0dff9baSBernard Iremonger 390*d0dff9baSBernard Iremonger lsi_send_packet(m, dst_port); 391*d0dff9baSBernard Iremonger } 392*d0dff9baSBernard Iremonger 393*d0dff9baSBernard IremongerThen, the packet is sent using the lsi_send_packet(m, dst_port) function. 394*d0dff9baSBernard IremongerFor this test application, the processing is exactly the same for all packets arriving on the same RX port. 395*d0dff9baSBernard IremongerTherefore, it would have been possible to call the lsi_send_burst() function directly from the main loop 396*d0dff9baSBernard Iremongerto send all the received packets on the same TX port using 397*d0dff9baSBernard Iremongerthe burst-oriented send function, which is more efficient. 398*d0dff9baSBernard Iremonger 399*d0dff9baSBernard IremongerHowever, in real-life applications (such as, L3 routing), 400*d0dff9baSBernard Iremongerpacket N is not necessarily forwarded on the same port as packet N-1. 401*d0dff9baSBernard IremongerThe application is implemented to illustrate that so the same approach can be reused in a more complex application. 402*d0dff9baSBernard Iremonger 403*d0dff9baSBernard IremongerThe lsi_send_packet() function stores the packet in a per-lcore and per-txport table. 404*d0dff9baSBernard IremongerIf the table is full, the whole packets table is transmitted using the lsi_send_burst() function: 405*d0dff9baSBernard Iremonger 406*d0dff9baSBernard Iremonger.. code-block:: c 407*d0dff9baSBernard Iremonger 408*d0dff9baSBernard Iremonger /* Send the packet on an output interface */ 409*d0dff9baSBernard Iremonger 410*d0dff9baSBernard Iremonger static int 411*d0dff9baSBernard Iremonger lsi_send_packet(struct rte_mbuf *m, uint8_t port) 412*d0dff9baSBernard Iremonger { 413*d0dff9baSBernard Iremonger unsigned lcore_id, len; 414*d0dff9baSBernard Iremonger struct lcore_queue_conf *qconf; 415*d0dff9baSBernard Iremonger 416*d0dff9baSBernard Iremonger lcore_id = rte_lcore_id(); 417*d0dff9baSBernard Iremonger qconf = &lcore_queue_conf[lcore_id]; 418*d0dff9baSBernard Iremonger len = qconf->tx_mbufs[port].len; 419*d0dff9baSBernard Iremonger qconf->tx_mbufs[port].m_table[len] = m; 420*d0dff9baSBernard Iremonger len++; 421*d0dff9baSBernard Iremonger 422*d0dff9baSBernard Iremonger /* enough pkts to be sent */ 423*d0dff9baSBernard Iremonger 424*d0dff9baSBernard Iremonger if (unlikely(len == MAX_PKT_BURST)) { 425*d0dff9baSBernard Iremonger lsi_send_burst(qconf, MAX_PKT_BURST, port); 426*d0dff9baSBernard Iremonger len = 0; 427*d0dff9baSBernard Iremonger } 428*d0dff9baSBernard Iremonger qconf->tx_mbufs[port].len = len; 429*d0dff9baSBernard Iremonger 430*d0dff9baSBernard Iremonger return 0; 431*d0dff9baSBernard Iremonger } 432*d0dff9baSBernard Iremonger 433*d0dff9baSBernard IremongerTo ensure that no packets remain in the tables, each lcore does a draining of the TX queue in its main loop. 434*d0dff9baSBernard IremongerThis technique introduces some latency when there are not many packets to send. 435*d0dff9baSBernard IremongerHowever, it improves performance: 436*d0dff9baSBernard Iremonger 437*d0dff9baSBernard Iremonger.. code-block:: c 438*d0dff9baSBernard Iremonger 439*d0dff9baSBernard Iremonger cur_tsc = rte_rdtsc(); 440*d0dff9baSBernard Iremonger 441*d0dff9baSBernard Iremonger /* 442*d0dff9baSBernard Iremonger * TX burst queue drain 443*d0dff9baSBernard Iremonger */ 444*d0dff9baSBernard Iremonger 445*d0dff9baSBernard Iremonger diff_tsc = cur_tsc - prev_tsc; 446*d0dff9baSBernard Iremonger 447*d0dff9baSBernard Iremonger if (unlikely(diff_tsc > drain_tsc)) { 448*d0dff9baSBernard Iremonger /* this could be optimized (use queueid instead of * portid), but it is not called so often */ 449*d0dff9baSBernard Iremonger 450*d0dff9baSBernard Iremonger for (portid = 0; portid < RTE_MAX_ETHPORTS; portid++) { 451*d0dff9baSBernard Iremonger if (qconf->tx_mbufs[portid].len == 0) 452*d0dff9baSBernard Iremonger continue; 453*d0dff9baSBernard Iremonger 454*d0dff9baSBernard Iremonger lsi_send_burst(&lcore_queue_conf[lcore_id], 455*d0dff9baSBernard Iremonger qconf->tx_mbufs[portid].len, (uint8_t) portid); 456*d0dff9baSBernard Iremonger qconf->tx_mbufs[portid].len = 0; 457*d0dff9baSBernard Iremonger } 458*d0dff9baSBernard Iremonger 459*d0dff9baSBernard Iremonger /* if timer is enabled */ 460*d0dff9baSBernard Iremonger 461*d0dff9baSBernard Iremonger if (timer_period > 0) { 462*d0dff9baSBernard Iremonger /* advance the timer */ 463*d0dff9baSBernard Iremonger 464*d0dff9baSBernard Iremonger timer_tsc += diff_tsc; 465*d0dff9baSBernard Iremonger 466*d0dff9baSBernard Iremonger /* if timer has reached its timeout */ 467*d0dff9baSBernard Iremonger 468*d0dff9baSBernard Iremonger if (unlikely(timer_tsc >= (uint64_t) timer_period)) { 469*d0dff9baSBernard Iremonger /* do this only on master core */ 470*d0dff9baSBernard Iremonger 471*d0dff9baSBernard Iremonger if (lcore_id == rte_get_master_lcore()) { 472*d0dff9baSBernard Iremonger print_stats(); 473*d0dff9baSBernard Iremonger 474*d0dff9baSBernard Iremonger /* reset the timer */ 475*d0dff9baSBernard Iremonger timer_tsc = 0; 476*d0dff9baSBernard Iremonger } 477*d0dff9baSBernard Iremonger } 478*d0dff9baSBernard Iremonger } 479*d0dff9baSBernard Iremonger prev_tsc = cur_tsc; 480*d0dff9baSBernard Iremonger } 481