]>
Commit | Line | Data |
---|---|---|
11fdf7f2 TL |
1 | .. SPDX-License-Identifier: BSD-3-Clause |
2 | Copyright(c) 2015 Intel Corporation. | |
7c673cae FG |
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 | ||
7c673cae FG |
14 | Compiling the Application |
15 | ------------------------- | |
16 | ||
11fdf7f2 | 17 | To compile the sample application see :doc:`compiling`. |
7c673cae | 18 | |
11fdf7f2 | 19 | The application is located in the ``skeleton`` sub-directory. |
7c673cae FG |
20 | |
21 | Running the Application | |
22 | ----------------------- | |
23 | ||
9f95a23c | 24 | To run the example in a ``linux`` environment: |
7c673cae FG |
25 | |
26 | .. code-block:: console | |
27 | ||
11fdf7f2 | 28 | ./build/basicfwd -l 1 -n 4 |
7c673cae FG |
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 | ||
11fdf7f2 | 84 | RTE_ETH_FOREACH_DEV(portid) { |
7c673cae FG |
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 | |
11fdf7f2 | 114 | port_init(uint16_t port, struct rte_mempool *mbuf_pool) |
7c673cae FG |
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 | ||
11fdf7f2 | 122 | if (!rte_eth_dev_is_valid_port(port)) |
7c673cae FG |
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 | { | |
11fdf7f2 | 195 | uint16_t port; |
7c673cae FG |
196 | |
197 | /* | |
198 | * Check that the port is on the same NUMA node as the polling thread | |
199 | * for best performance. | |
200 | */ | |
11fdf7f2 | 201 | RTE_ETH_FOREACH_DEV(port) |
7c673cae FG |
202 | if (rte_eth_dev_socket_id(port) > 0 && |
203 | rte_eth_dev_socket_id(port) != | |
204 | (int)rte_socket_id()) | |
205 | printf("WARNING, port %u is on remote NUMA node to " | |
206 | "polling thread.\n\tPerformance will " | |
207 | "not be optimal.\n", port); | |
208 | ||
209 | printf("\nCore %u forwarding packets. [Ctrl+C to quit]\n", | |
210 | rte_lcore_id()); | |
211 | ||
212 | /* Run until the application is quit or killed. */ | |
213 | for (;;) { | |
214 | /* | |
215 | * Receive packets on a port and forward them on the paired | |
216 | * port. The mapping is 0 -> 1, 1 -> 0, 2 -> 3, 3 -> 2, etc. | |
217 | */ | |
11fdf7f2 | 218 | RTE_ETH_FOREACH_DEV(port) { |
7c673cae FG |
219 | |
220 | /* Get burst of RX packets, from first port of pair. */ | |
221 | struct rte_mbuf *bufs[BURST_SIZE]; | |
222 | const uint16_t nb_rx = rte_eth_rx_burst(port, 0, | |
223 | bufs, BURST_SIZE); | |
224 | ||
225 | if (unlikely(nb_rx == 0)) | |
226 | continue; | |
227 | ||
228 | /* Send burst of TX packets, to second port of pair. */ | |
229 | const uint16_t nb_tx = rte_eth_tx_burst(port ^ 1, 0, | |
230 | bufs, nb_rx); | |
231 | ||
232 | /* Free any unsent packets. */ | |
233 | if (unlikely(nb_tx < nb_rx)) { | |
234 | uint16_t buf; | |
235 | for (buf = nb_tx; buf < nb_rx; buf++) | |
236 | rte_pktmbuf_free(bufs[buf]); | |
237 | } | |
238 | } | |
239 | } | |
240 | } | |
241 | ||
242 | ||
243 | The main work of the application is done within the loop: | |
244 | ||
245 | .. code-block:: c | |
246 | ||
247 | for (;;) { | |
11fdf7f2 | 248 | RTE_ETH_FOREACH_DEV(port) { |
7c673cae FG |
249 | |
250 | /* Get burst of RX packets, from first port of pair. */ | |
251 | struct rte_mbuf *bufs[BURST_SIZE]; | |
252 | const uint16_t nb_rx = rte_eth_rx_burst(port, 0, | |
253 | bufs, BURST_SIZE); | |
254 | ||
255 | if (unlikely(nb_rx == 0)) | |
256 | continue; | |
257 | ||
258 | /* Send burst of TX packets, to second port of pair. */ | |
259 | const uint16_t nb_tx = rte_eth_tx_burst(port ^ 1, 0, | |
260 | bufs, nb_rx); | |
261 | ||
262 | /* Free any unsent packets. */ | |
263 | if (unlikely(nb_tx < nb_rx)) { | |
264 | uint16_t buf; | |
265 | for (buf = nb_tx; buf < nb_rx; buf++) | |
266 | rte_pktmbuf_free(bufs[buf]); | |
267 | } | |
268 | } | |
269 | } | |
270 | ||
271 | Packets are received in bursts on the RX ports and transmitted in bursts on | |
272 | the TX ports. The ports are grouped in pairs with a simple mapping scheme | |
273 | using the an XOR on the port number:: | |
274 | ||
275 | 0 -> 1 | |
276 | 1 -> 0 | |
277 | ||
278 | 2 -> 3 | |
279 | 3 -> 2 | |
280 | ||
281 | etc. | |
282 | ||
283 | The ``rte_eth_tx_burst()`` function frees the memory buffers of packets that | |
284 | are transmitted. If packets fail to transmit, ``(nb_tx < nb_rx)``, then they | |
285 | must be freed explicitly using ``rte_pktmbuf_free()``. | |
286 | ||
287 | The forwarding loop can be interrupted and the application closed using | |
288 | ``Ctrl-C``. |