xref: /dpdk/doc/guides/sample_app_ug/skeleton.rst (revision 8809f78c7dd9f33a44a4f89c58fc91ded34296ed)
1..  SPDX-License-Identifier: BSD-3-Clause
2    Copyright(c) 2015 Intel Corporation.
3
4Basic Forwarding Sample Application
5===================================
6
7The Basic Forwarding sample application is a simple *skeleton* example of a
8forwarding application.
9
10It is intended as a demonstration of the basic components of a DPDK forwarding
11application. For more detailed implementations see the L2 and L3 forwarding
12sample applications.
13
14Compiling the Application
15-------------------------
16
17To compile the sample application see :doc:`compiling`.
18
19The application is located in the ``skeleton`` sub-directory.
20
21Running the Application
22-----------------------
23
24To run the example in a ``linux`` environment:
25
26.. code-block:: console
27
28    ./<build_dir>/examples/dpdk-skeleton -l 1 -n 4
29
30Refer to *DPDK Getting Started Guide* for general information on running
31applications and the Environment Abstraction Layer (EAL) options.
32
33
34Explanation
35-----------
36
37The following sections provide an explanation of the main components of the
38code.
39
40All DPDK library functions used in the sample code are prefixed with ``rte_``
41and are explained in detail in the *DPDK API Documentation*.
42
43
44The Main Function
45~~~~~~~~~~~~~~~~~
46
47The ``main()`` function performs the initialization and calls the execution
48threads for each lcore.
49
50The first task is to initialize the Environment Abstraction Layer (EAL).  The
51``argc`` and ``argv`` arguments are provided to the ``rte_eal_init()``
52function. 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
61The ``main()`` also allocates a mempool to hold the mbufs (Message Buffers)
62used 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
76Mbufs are the packet buffer structure used by DPDK. They are explained in
77detail in the "Mbuf Library" section of the *DPDK Programmer's Guide*.
78
79The ``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    RTE_ETH_FOREACH_DEV(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
92Once the initialization is complete, the application is ready to launch a
93function on an lcore. In this example ``lcore_main()`` is called on a single
94lcore.
95
96
97.. code-block:: c
98
99	lcore_main();
100
101The ``lcore_main()`` function is explained below.
102
103
104
105The Port Initialization  Function
106~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
107
108The main functional part of the port initialization used in the Basic
109Forwarding 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 rte_ether_addr addr;
119        int retval;
120        uint16_t q;
121
122        if (!rte_eth_dev_is_valid_port(port))
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        retval = rte_eth_promiscuous_enable(port);
153        if (retval != 0)
154            return retval;
155
156        return 0;
157    }
158
159The Ethernet ports are configured with default settings using the
160``rte_eth_dev_configure()`` function and the ``port_conf_default`` struct:
161
162.. code-block:: c
163
164    static const struct rte_eth_conf port_conf_default = {
165        .rxmode = { .max_rx_pkt_len = RTE_ETHER_MAX_LEN }
166    };
167
168For this example the ports are set up with 1 RX and 1 TX queue using the
169``rte_eth_rx_queue_setup()`` and ``rte_eth_tx_queue_setup()`` functions.
170
171The Ethernet port is then started:
172
173.. code-block:: c
174
175        retval  = rte_eth_dev_start(port);
176
177
178Finally the RX port is set in promiscuous mode:
179
180.. code-block:: c
181
182        retval = rte_eth_promiscuous_enable(port);
183
184
185The Lcores Main
186~~~~~~~~~~~~~~~
187
188As we saw above the ``main()`` function calls an application function on the
189available lcores. For the Basic Forwarding application the lcore function
190looks like the following:
191
192.. code-block:: c
193
194    static __rte_noreturn void
195    lcore_main(void)
196    {
197        uint16_t port;
198
199        /*
200         * Check that the port is on the same NUMA node as the polling thread
201         * for best performance.
202         */
203        RTE_ETH_FOREACH_DEV(port)
204            if (rte_eth_dev_socket_id(port) > 0 &&
205                    rte_eth_dev_socket_id(port) !=
206                            (int)rte_socket_id())
207                printf("WARNING, port %u is on remote NUMA node to "
208                        "polling thread.\n\tPerformance will "
209                        "not be optimal.\n", port);
210
211        printf("\nCore %u forwarding packets. [Ctrl+C to quit]\n",
212                rte_lcore_id());
213
214        /* Run until the application is quit or killed. */
215        for (;;) {
216            /*
217             * Receive packets on a port and forward them on the paired
218             * port. The mapping is 0 -> 1, 1 -> 0, 2 -> 3, 3 -> 2, etc.
219             */
220            RTE_ETH_FOREACH_DEV(port) {
221
222                /* Get burst of RX packets, from first port of pair. */
223                struct rte_mbuf *bufs[BURST_SIZE];
224                const uint16_t nb_rx = rte_eth_rx_burst(port, 0,
225                        bufs, BURST_SIZE);
226
227                if (unlikely(nb_rx == 0))
228                    continue;
229
230                /* Send burst of TX packets, to second port of pair. */
231                const uint16_t nb_tx = rte_eth_tx_burst(port ^ 1, 0,
232                        bufs, nb_rx);
233
234                /* Free any unsent packets. */
235                if (unlikely(nb_tx < nb_rx)) {
236                    uint16_t buf;
237                    for (buf = nb_tx; buf < nb_rx; buf++)
238                        rte_pktmbuf_free(bufs[buf]);
239                }
240            }
241        }
242    }
243
244
245The main work of the application is done within the loop:
246
247.. code-block:: c
248
249        for (;;) {
250            RTE_ETH_FOREACH_DEV(port) {
251
252                /* Get burst of RX packets, from first port of pair. */
253                struct rte_mbuf *bufs[BURST_SIZE];
254                const uint16_t nb_rx = rte_eth_rx_burst(port, 0,
255                        bufs, BURST_SIZE);
256
257                if (unlikely(nb_rx == 0))
258                    continue;
259
260                /* Send burst of TX packets, to second port of pair. */
261                const uint16_t nb_tx = rte_eth_tx_burst(port ^ 1, 0,
262                        bufs, nb_rx);
263
264                /* Free any unsent packets. */
265                if (unlikely(nb_tx < nb_rx)) {
266                    uint16_t buf;
267                    for (buf = nb_tx; buf < nb_rx; buf++)
268                        rte_pktmbuf_free(bufs[buf]);
269                }
270            }
271        }
272
273Packets are received in bursts on the RX ports and transmitted in bursts on
274the TX ports. The ports are grouped in pairs with a simple mapping scheme
275using the an XOR on the port number::
276
277    0 -> 1
278    1 -> 0
279
280    2 -> 3
281    3 -> 2
282
283    etc.
284
285The ``rte_eth_tx_burst()`` function frees the memory buffers of packets that
286are transmitted. If packets fail to transmit, ``(nb_tx < nb_rx)``, then they
287must be freed explicitly using ``rte_pktmbuf_free()``.
288
289The forwarding loop can be interrupted and the application closed using
290``Ctrl-C``.
291