xref: /dpdk/doc/guides/sample_app_ug/skeleton.rst (revision 945acb4a0d644d194f1823084a234f9c286dcf8c)
1..  BSD LICENSE
2    Copyright(c) 2015 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
31
32Basic Forwarding Sample Application
33===================================
34
35The Basic Forwarding sample application is a simple *skeleton* example of a
36forwarding application.
37
38It is intended as a demonstration of the basic components of a DPDK forwarding
39application. For more detailed implementations see the L2 and L3 forwarding
40sample applications.
41
42Compiling the Application
43-------------------------
44
45To compile the sample application see :doc:`compiling`.
46
47The application is located in the ``skeleton`` sub-directory.
48
49Running the Application
50-----------------------
51
52To run the example in a ``linuxapp`` environment:
53
54.. code-block:: console
55
56    ./build/basicfwd -l 1 -n 4
57
58Refer to *DPDK Getting Started Guide* for general information on running
59applications and the Environment Abstraction Layer (EAL) options.
60
61
62Explanation
63-----------
64
65The following sections provide an explanation of the main components of the
66code.
67
68All DPDK library functions used in the sample code are prefixed with ``rte_``
69and are explained in detail in the *DPDK API Documentation*.
70
71
72The Main Function
73~~~~~~~~~~~~~~~~~
74
75The ``main()`` function performs the initialization and calls the execution
76threads for each lcore.
77
78The first task is to initialize the Environment Abstraction Layer (EAL).  The
79``argc`` and ``argv`` arguments are provided to the ``rte_eal_init()``
80function. The value returned is the number of parsed arguments:
81
82.. code-block:: c
83
84    int ret = rte_eal_init(argc, argv);
85    if (ret < 0)
86        rte_exit(EXIT_FAILURE, "Error with EAL initialization\n");
87
88
89The ``main()`` also allocates a mempool to hold the mbufs (Message Buffers)
90used by the application:
91
92.. code-block:: c
93
94    mbuf_pool = rte_mempool_create("MBUF_POOL",
95                                   NUM_MBUFS * nb_ports,
96                                   MBUF_SIZE,
97                                   MBUF_CACHE_SIZE,
98                                   sizeof(struct rte_pktmbuf_pool_private),
99                                   rte_pktmbuf_pool_init, NULL,
100                                   rte_pktmbuf_init,      NULL,
101                                   rte_socket_id(),
102                                   0);
103
104Mbufs are the packet buffer structure used by DPDK. They are explained in
105detail in the "Mbuf Library" section of the *DPDK Programmer's Guide*.
106
107The ``main()`` function also initializes all the ports using the user defined
108``port_init()`` function which is explained in the next section:
109
110.. code-block:: c
111
112    for (portid = 0; portid < nb_ports; portid++) {
113        if (port_init(portid, mbuf_pool) != 0) {
114            rte_exit(EXIT_FAILURE,
115                     "Cannot init port %" PRIu8 "\n", portid);
116        }
117    }
118
119
120Once the initialization is complete, the application is ready to launch a
121function on an lcore. In this example ``lcore_main()`` is called on a single
122lcore.
123
124
125.. code-block:: c
126
127	lcore_main();
128
129The ``lcore_main()`` function is explained below.
130
131
132
133The Port Initialization  Function
134~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
135
136The main functional part of the port initialization used in the Basic
137Forwarding application is shown below:
138
139.. code-block:: c
140
141    static inline int
142    port_init(uint16_t port, struct rte_mempool *mbuf_pool)
143    {
144        struct rte_eth_conf port_conf = port_conf_default;
145        const uint16_t rx_rings = 1, tx_rings = 1;
146        struct ether_addr addr;
147        int retval;
148        uint16_t q;
149
150        if (port >= rte_eth_dev_count())
151            return -1;
152
153        /* Configure the Ethernet device. */
154        retval = rte_eth_dev_configure(port, rx_rings, tx_rings, &port_conf);
155        if (retval != 0)
156            return retval;
157
158        /* Allocate and set up 1 RX queue per Ethernet port. */
159        for (q = 0; q < rx_rings; q++) {
160            retval = rte_eth_rx_queue_setup(port, q, RX_RING_SIZE,
161                    rte_eth_dev_socket_id(port), NULL, mbuf_pool);
162            if (retval < 0)
163                return retval;
164        }
165
166        /* Allocate and set up 1 TX queue per Ethernet port. */
167        for (q = 0; q < tx_rings; q++) {
168            retval = rte_eth_tx_queue_setup(port, q, TX_RING_SIZE,
169                    rte_eth_dev_socket_id(port), NULL);
170            if (retval < 0)
171                return retval;
172        }
173
174        /* Start the Ethernet port. */
175        retval = rte_eth_dev_start(port);
176        if (retval < 0)
177            return retval;
178
179        /* Enable RX in promiscuous mode for the Ethernet device. */
180        rte_eth_promiscuous_enable(port);
181
182        return 0;
183    }
184
185The Ethernet ports are configured with default settings using the
186``rte_eth_dev_configure()`` function and the ``port_conf_default`` struct:
187
188.. code-block:: c
189
190    static const struct rte_eth_conf port_conf_default = {
191        .rxmode = { .max_rx_pkt_len = ETHER_MAX_LEN }
192    };
193
194For this example the ports are set up with 1 RX and 1 TX queue using the
195``rte_eth_rx_queue_setup()`` and ``rte_eth_tx_queue_setup()`` functions.
196
197The Ethernet port is then started:
198
199.. code-block:: c
200
201        retval  = rte_eth_dev_start(port);
202
203
204Finally the RX port is set in promiscuous mode:
205
206.. code-block:: c
207
208        rte_eth_promiscuous_enable(port);
209
210
211The Lcores Main
212~~~~~~~~~~~~~~~
213
214As we saw above the ``main()`` function calls an application function on the
215available lcores. For the Basic Forwarding application the lcore function
216looks like the following:
217
218.. code-block:: c
219
220    static __attribute__((noreturn)) void
221    lcore_main(void)
222    {
223        const uint16_t nb_ports = rte_eth_dev_count();
224        uint16_t port;
225
226        /*
227         * Check that the port is on the same NUMA node as the polling thread
228         * for best performance.
229         */
230        for (port = 0; port < nb_ports; port++)
231            if (rte_eth_dev_socket_id(port) > 0 &&
232                    rte_eth_dev_socket_id(port) !=
233                            (int)rte_socket_id())
234                printf("WARNING, port %u is on remote NUMA node to "
235                        "polling thread.\n\tPerformance will "
236                        "not be optimal.\n", port);
237
238        printf("\nCore %u forwarding packets. [Ctrl+C to quit]\n",
239                rte_lcore_id());
240
241        /* Run until the application is quit or killed. */
242        for (;;) {
243            /*
244             * Receive packets on a port and forward them on the paired
245             * port. The mapping is 0 -> 1, 1 -> 0, 2 -> 3, 3 -> 2, etc.
246             */
247            for (port = 0; port < nb_ports; port++) {
248
249                /* Get burst of RX packets, from first port of pair. */
250                struct rte_mbuf *bufs[BURST_SIZE];
251                const uint16_t nb_rx = rte_eth_rx_burst(port, 0,
252                        bufs, BURST_SIZE);
253
254                if (unlikely(nb_rx == 0))
255                    continue;
256
257                /* Send burst of TX packets, to second port of pair. */
258                const uint16_t nb_tx = rte_eth_tx_burst(port ^ 1, 0,
259                        bufs, nb_rx);
260
261                /* Free any unsent packets. */
262                if (unlikely(nb_tx < nb_rx)) {
263                    uint16_t buf;
264                    for (buf = nb_tx; buf < nb_rx; buf++)
265                        rte_pktmbuf_free(bufs[buf]);
266                }
267            }
268        }
269    }
270
271
272The main work of the application is done within the loop:
273
274.. code-block:: c
275
276        for (;;) {
277            for (port = 0; port < nb_ports; port++) {
278
279                /* Get burst of RX packets, from first port of pair. */
280                struct rte_mbuf *bufs[BURST_SIZE];
281                const uint16_t nb_rx = rte_eth_rx_burst(port, 0,
282                        bufs, BURST_SIZE);
283
284                if (unlikely(nb_rx == 0))
285                    continue;
286
287                /* Send burst of TX packets, to second port of pair. */
288                const uint16_t nb_tx = rte_eth_tx_burst(port ^ 1, 0,
289                        bufs, nb_rx);
290
291                /* Free any unsent packets. */
292                if (unlikely(nb_tx < nb_rx)) {
293                    uint16_t buf;
294                    for (buf = nb_tx; buf < nb_rx; buf++)
295                        rte_pktmbuf_free(bufs[buf]);
296                }
297            }
298        }
299
300Packets are received in bursts on the RX ports and transmitted in bursts on
301the TX ports. The ports are grouped in pairs with a simple mapping scheme
302using the an XOR on the port number::
303
304    0 -> 1
305    1 -> 0
306
307    2 -> 3
308    3 -> 2
309
310    etc.
311
312The ``rte_eth_tx_burst()`` function frees the memory buffers of packets that
313are transmitted. If packets fail to transmit, ``(nb_tx < nb_rx)``, then they
314must be freed explicitly using ``rte_pktmbuf_free()``.
315
316The forwarding loop can be interrupted and the application closed using
317``Ctrl-C``.
318