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