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