]>
Commit | Line | Data |
---|---|---|
7c673cae FG |
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``. |