New upstream version 17.11-rc3
[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 Compiling the Application
43 -------------------------
44
45 To compile the sample application see :doc:`compiling`.
46
47 The application is located in the ``skeleton`` sub-directory.
48
49 Running the Application
50 -----------------------
51
52 To run the example in a ``linuxapp`` environment:
53
54 .. code-block:: console
55
56     ./build/basicfwd -l 1 -n 4
57
58 Refer to *DPDK Getting Started Guide* for general information on running
59 applications and the Environment Abstraction Layer (EAL) options.
60
61
62 Explanation
63 -----------
64
65 The following sections provide an explanation of the main components of the
66 code.
67
68 All DPDK library functions used in the sample code are prefixed with ``rte_``
69 and are explained in detail in the *DPDK API Documentation*.
70
71
72 The Main Function
73 ~~~~~~~~~~~~~~~~~
74
75 The ``main()`` function performs the initialization and calls the execution
76 threads for each lcore.
77
78 The first task is to initialize the Environment Abstraction Layer (EAL).  The
79 ``argc`` and ``argv`` arguments are provided to the ``rte_eal_init()``
80 function. 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
89 The ``main()`` also allocates a mempool to hold the mbufs (Message Buffers)
90 used 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
104 Mbufs are the packet buffer structure used by DPDK. They are explained in
105 detail in the "Mbuf Library" section of the *DPDK Programmer's Guide*.
106
107 The ``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
120 Once the initialization is complete, the application is ready to launch a
121 function on an lcore. In this example ``lcore_main()`` is called on a single
122 lcore.
123
124
125 .. code-block:: c
126
127         lcore_main();
128
129 The ``lcore_main()`` function is explained below.
130
131
132
133 The Port Initialization  Function
134 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
135
136 The main functional part of the port initialization used in the Basic
137 Forwarding 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
185 The 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
194 For 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
197 The Ethernet port is then started:
198
199 .. code-block:: c
200
201         retval  = rte_eth_dev_start(port);
202
203
204 Finally the RX port is set in promiscuous mode:
205
206 .. code-block:: c
207
208         rte_eth_promiscuous_enable(port);
209
210
211 The Lcores Main
212 ~~~~~~~~~~~~~~~
213
214 As we saw above the ``main()`` function calls an application function on the
215 available lcores. For the Basic Forwarding application the lcore function
216 looks 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
272 The 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
300 Packets are received in bursts on the RX ports and transmitted in bursts on
301 the TX ports. The ports are grouped in pairs with a simple mapping scheme
302 using 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
312 The ``rte_eth_tx_burst()`` function frees the memory buffers of packets that
313 are transmitted. If packets fail to transmit, ``(nb_tx < nb_rx)``, then they
314 must be freed explicitly using ``rte_pktmbuf_free()``.
315
316 The forwarding loop can be interrupted and the application closed using
317 ``Ctrl-C``.