skeleton.rst revision 97f17497
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
42
43Compiling the Application
44-------------------------
45
46To compile the application export the path to the DPDK source tree and go to
47the example directory:
48
49.. code-block:: console
50
51    export RTE_SDK=/path/to/rte_sdk
52
53    cd ${RTE_SDK}/examples/skeleton
54
55Set the target, for example:
56
57.. code-block:: console
58
59    export RTE_TARGET=x86_64-native-linuxapp-gcc
60
61See the *DPDK Getting Started* Guide for possible ``RTE_TARGET`` values.
62
63Build the application as follows:
64
65.. code-block:: console
66
67    make
68
69
70Running the Application
71-----------------------
72
73To run the example in a ``linuxapp`` environment:
74
75.. code-block:: console
76
77    ./build/basicfwd -c 2 -n 4
78
79Refer to *DPDK Getting Started Guide* for general information on running
80applications and the Environment Abstraction Layer (EAL) options.
81
82
83Explanation
84-----------
85
86The following sections provide an explanation of the main components of the
87code.
88
89All DPDK library functions used in the sample code are prefixed with ``rte_``
90and are explained in detail in the *DPDK API Documentation*.
91
92
93The Main Function
94~~~~~~~~~~~~~~~~~
95
96The ``main()`` function performs the initialization and calls the execution
97threads for each lcore.
98
99The first task is to initialize the Environment Abstraction Layer (EAL).  The
100``argc`` and ``argv`` arguments are provided to the ``rte_eal_init()``
101function. 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
110The ``main()`` also allocates a mempool to hold the mbufs (Message Buffers)
111used 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
125Mbufs are the packet buffer structure used by DPDK. They are explained in
126detail in the "Mbuf Library" section of the *DPDK Programmer's Guide*.
127
128The ``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
141Once the initialization is complete, the application is ready to launch a
142function on an lcore. In this example ``lcore_main()`` is called on a single
143lcore.
144
145
146.. code-block:: c
147
148	lcore_main();
149
150The ``lcore_main()`` function is explained below.
151
152
153
154The Port Initialization  Function
155~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
156
157The main functional part of the port initialization used in the Basic
158Forwarding 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
206The 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
215For 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
218The Ethernet port is then started:
219
220.. code-block:: c
221
222        retval  = rte_eth_dev_start(port);
223
224
225Finally the RX port is set in promiscuous mode:
226
227.. code-block:: c
228
229        rte_eth_promiscuous_enable(port);
230
231
232The Lcores Main
233~~~~~~~~~~~~~~~
234
235As we saw above the ``main()`` function calls an application function on the
236available lcores. For the Basic Forwarding application the lcore function
237looks 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
293The 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
321Packets are received in bursts on the RX ports and transmitted in bursts on
322the TX ports. The ports are grouped in pairs with a simple mapping scheme
323using 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
333The ``rte_eth_tx_burst()`` function frees the memory buffers of packets that
334are transmitted. If packets fail to transmit, ``(nb_tx < nb_rx)``, then they
335must be freed explicitly using ``rte_pktmbuf_free()``.
336
337The forwarding loop can be interrupted and the application closed using
338``Ctrl-C``.
339