xref: /dpdk/doc/guides/sample_app_ug/link_status_intr.rst (revision d0dff9ba445e47199a420dd4b5451ec2860d3d3b)
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 = &eth->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], &eth->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