]>
Commit | Line | Data |
---|---|---|
9f95a23c TL |
1 | /* SPDX-License-Identifier: BSD-3-Clause |
2 | * Copyright(c) 2010-2014 Intel Corporation | |
7c673cae FG |
3 | */ |
4 | ||
5 | #include <stdint.h> | |
6 | #include <sys/queue.h> | |
7 | #include <stdlib.h> | |
8 | #include <string.h> | |
9 | #include <stdio.h> | |
10 | #include <assert.h> | |
11 | #include <errno.h> | |
12 | #include <signal.h> | |
13 | #include <stdarg.h> | |
14 | #include <inttypes.h> | |
15 | #include <getopt.h> | |
16 | ||
17 | #include <rte_common.h> | |
18 | #include <rte_log.h> | |
19 | #include <rte_memory.h> | |
20 | #include <rte_memcpy.h> | |
7c673cae | 21 | #include <rte_eal.h> |
7c673cae FG |
22 | #include <rte_launch.h> |
23 | #include <rte_atomic.h> | |
24 | #include <rte_cycles.h> | |
25 | #include <rte_prefetch.h> | |
26 | #include <rte_lcore.h> | |
27 | #include <rte_per_lcore.h> | |
28 | #include <rte_branch_prediction.h> | |
29 | #include <rte_interrupts.h> | |
7c673cae FG |
30 | #include <rte_random.h> |
31 | #include <rte_debug.h> | |
32 | #include <rte_ether.h> | |
33 | #include <rte_ethdev.h> | |
7c673cae FG |
34 | #include <rte_mempool.h> |
35 | #include <rte_mbuf.h> | |
7c673cae FG |
36 | |
37 | /* basic constants used in application */ | |
38 | #define MAX_QUEUES 1024 | |
39 | /* | |
40 | * 1024 queues require to meet the needs of a large number of vmdq_pools. | |
41 | * (RX/TX_queue_nb * RX/TX_ring_descriptors_nb) per port. | |
42 | */ | |
43 | #define NUM_MBUFS_PER_PORT (MAX_QUEUES * RTE_MAX(RTE_TEST_RX_DESC_DEFAULT, \ | |
44 | RTE_TEST_TX_DESC_DEFAULT)) | |
45 | #define MBUF_CACHE_SIZE 64 | |
46 | ||
47 | #define MAX_PKT_BURST 32 | |
48 | ||
49 | /* | |
50 | * Configurable number of RX/TX ring descriptors | |
51 | */ | |
9f95a23c TL |
52 | #define RTE_TEST_RX_DESC_DEFAULT 1024 |
53 | #define RTE_TEST_TX_DESC_DEFAULT 1024 | |
7c673cae FG |
54 | |
55 | #define INVALID_PORT_ID 0xFF | |
56 | ||
57 | /* mask of enabled ports */ | |
58 | static uint32_t enabled_port_mask; | |
9f95a23c | 59 | static uint16_t ports[RTE_MAX_ETHPORTS]; |
7c673cae FG |
60 | static unsigned num_ports; |
61 | ||
62 | /* number of pools (if user does not specify any, 32 by default */ | |
63 | static enum rte_eth_nb_pools num_pools = ETH_32_POOLS; | |
64 | static enum rte_eth_nb_tcs num_tcs = ETH_4_TCS; | |
65 | static uint16_t num_queues, num_vmdq_queues; | |
66 | static uint16_t vmdq_pool_base, vmdq_queue_base; | |
67 | static uint8_t rss_enable; | |
68 | ||
69 | /* empty vmdq+dcb configuration structure. Filled in programatically */ | |
70 | static const struct rte_eth_conf vmdq_dcb_conf_default = { | |
71 | .rxmode = { | |
72 | .mq_mode = ETH_MQ_RX_VMDQ_DCB, | |
73 | .split_hdr_size = 0, | |
7c673cae FG |
74 | }, |
75 | .txmode = { | |
76 | .mq_mode = ETH_MQ_TX_VMDQ_DCB, | |
77 | }, | |
78 | /* | |
79 | * should be overridden separately in code with | |
80 | * appropriate values | |
81 | */ | |
82 | .rx_adv_conf = { | |
83 | .vmdq_dcb_conf = { | |
84 | .nb_queue_pools = ETH_32_POOLS, | |
85 | .enable_default_pool = 0, | |
86 | .default_pool = 0, | |
87 | .nb_pool_maps = 0, | |
88 | .pool_map = {{0, 0},}, | |
89 | .dcb_tc = {0}, | |
90 | }, | |
91 | .dcb_rx_conf = { | |
92 | .nb_tcs = ETH_4_TCS, | |
93 | /** Traffic class each UP mapped to. */ | |
94 | .dcb_tc = {0}, | |
95 | }, | |
96 | .vmdq_rx_conf = { | |
97 | .nb_queue_pools = ETH_32_POOLS, | |
98 | .enable_default_pool = 0, | |
99 | .default_pool = 0, | |
100 | .nb_pool_maps = 0, | |
101 | .pool_map = {{0, 0},}, | |
102 | }, | |
103 | }, | |
104 | .tx_adv_conf = { | |
105 | .vmdq_dcb_tx_conf = { | |
106 | .nb_queue_pools = ETH_32_POOLS, | |
107 | .dcb_tc = {0}, | |
108 | }, | |
109 | }, | |
110 | }; | |
111 | ||
112 | /* array used for printing out statistics */ | |
113 | volatile unsigned long rxPackets[MAX_QUEUES] = {0}; | |
114 | ||
115 | const uint16_t vlan_tags[] = { | |
116 | 0, 1, 2, 3, 4, 5, 6, 7, | |
117 | 8, 9, 10, 11, 12, 13, 14, 15, | |
118 | 16, 17, 18, 19, 20, 21, 22, 23, | |
119 | 24, 25, 26, 27, 28, 29, 30, 31 | |
120 | }; | |
121 | ||
122 | const uint16_t num_vlans = RTE_DIM(vlan_tags); | |
123 | /* pool mac addr template, pool mac addr is like: 52 54 00 12 port# pool# */ | |
124 | static struct ether_addr pool_addr_template = { | |
125 | .addr_bytes = {0x52, 0x54, 0x00, 0x12, 0x00, 0x00} | |
126 | }; | |
127 | ||
128 | /* ethernet addresses of ports */ | |
129 | static struct ether_addr vmdq_ports_eth_addr[RTE_MAX_ETHPORTS]; | |
130 | ||
131 | /* Builds up the correct configuration for vmdq+dcb based on the vlan tags array | |
132 | * given above, and the number of traffic classes available for use. */ | |
133 | static inline int | |
134 | get_eth_conf(struct rte_eth_conf *eth_conf) | |
135 | { | |
136 | struct rte_eth_vmdq_dcb_conf conf; | |
137 | struct rte_eth_vmdq_rx_conf vmdq_conf; | |
138 | struct rte_eth_dcb_rx_conf dcb_conf; | |
139 | struct rte_eth_vmdq_dcb_tx_conf tx_conf; | |
140 | uint8_t i; | |
141 | ||
142 | conf.nb_queue_pools = (enum rte_eth_nb_pools)num_pools; | |
143 | vmdq_conf.nb_queue_pools = (enum rte_eth_nb_pools)num_pools; | |
144 | tx_conf.nb_queue_pools = (enum rte_eth_nb_pools)num_pools; | |
145 | conf.nb_pool_maps = num_pools; | |
146 | vmdq_conf.nb_pool_maps = num_pools; | |
147 | conf.enable_default_pool = 0; | |
148 | vmdq_conf.enable_default_pool = 0; | |
149 | conf.default_pool = 0; /* set explicit value, even if not used */ | |
150 | vmdq_conf.default_pool = 0; | |
151 | ||
152 | for (i = 0; i < conf.nb_pool_maps; i++) { | |
153 | conf.pool_map[i].vlan_id = vlan_tags[i]; | |
154 | vmdq_conf.pool_map[i].vlan_id = vlan_tags[i]; | |
155 | conf.pool_map[i].pools = 1UL << i; | |
156 | vmdq_conf.pool_map[i].pools = 1UL << i; | |
157 | } | |
158 | for (i = 0; i < ETH_DCB_NUM_USER_PRIORITIES; i++){ | |
159 | conf.dcb_tc[i] = i % num_tcs; | |
160 | dcb_conf.dcb_tc[i] = i % num_tcs; | |
161 | tx_conf.dcb_tc[i] = i % num_tcs; | |
162 | } | |
163 | dcb_conf.nb_tcs = (enum rte_eth_nb_tcs)num_tcs; | |
164 | (void)(rte_memcpy(eth_conf, &vmdq_dcb_conf_default, sizeof(*eth_conf))); | |
165 | (void)(rte_memcpy(ð_conf->rx_adv_conf.vmdq_dcb_conf, &conf, | |
166 | sizeof(conf))); | |
167 | (void)(rte_memcpy(ð_conf->rx_adv_conf.dcb_rx_conf, &dcb_conf, | |
168 | sizeof(dcb_conf))); | |
169 | (void)(rte_memcpy(ð_conf->rx_adv_conf.vmdq_rx_conf, &vmdq_conf, | |
170 | sizeof(vmdq_conf))); | |
171 | (void)(rte_memcpy(ð_conf->tx_adv_conf.vmdq_dcb_tx_conf, &tx_conf, | |
172 | sizeof(tx_conf))); | |
173 | if (rss_enable) { | |
174 | eth_conf->rxmode.mq_mode = ETH_MQ_RX_VMDQ_DCB_RSS; | |
175 | eth_conf->rx_adv_conf.rss_conf.rss_hf = ETH_RSS_IP | | |
176 | ETH_RSS_UDP | | |
177 | ETH_RSS_TCP | | |
178 | ETH_RSS_SCTP; | |
179 | } | |
180 | return 0; | |
181 | } | |
182 | ||
183 | /* | |
184 | * Initialises a given port using global settings and with the rx buffers | |
185 | * coming from the mbuf_pool passed as parameter | |
186 | */ | |
187 | static inline int | |
9f95a23c | 188 | port_init(uint16_t port, struct rte_mempool *mbuf_pool) |
7c673cae FG |
189 | { |
190 | struct rte_eth_dev_info dev_info; | |
191 | struct rte_eth_conf port_conf = {0}; | |
9f95a23c TL |
192 | uint16_t rxRingSize = RTE_TEST_RX_DESC_DEFAULT; |
193 | uint16_t txRingSize = RTE_TEST_TX_DESC_DEFAULT; | |
7c673cae FG |
194 | int retval; |
195 | uint16_t q; | |
196 | uint16_t queues_per_pool; | |
197 | uint32_t max_nb_pools; | |
9f95a23c TL |
198 | struct rte_eth_txconf txq_conf; |
199 | uint64_t rss_hf_tmp; | |
7c673cae FG |
200 | |
201 | /* | |
202 | * The max pool number from dev_info will be used to validate the pool | |
203 | * number specified in cmd line | |
204 | */ | |
205 | rte_eth_dev_info_get(port, &dev_info); | |
206 | max_nb_pools = (uint32_t)dev_info.max_vmdq_pools; | |
207 | /* | |
208 | * We allow to process part of VMDQ pools specified by num_pools in | |
209 | * command line. | |
210 | */ | |
211 | if (num_pools > max_nb_pools) { | |
212 | printf("num_pools %d >max_nb_pools %d\n", | |
213 | num_pools, max_nb_pools); | |
214 | return -1; | |
215 | } | |
216 | ||
217 | /* | |
218 | * NIC queues are divided into pf queues and vmdq queues. | |
219 | * There is assumption here all ports have the same configuration! | |
220 | */ | |
221 | vmdq_queue_base = dev_info.vmdq_queue_base; | |
222 | vmdq_pool_base = dev_info.vmdq_pool_base; | |
223 | printf("vmdq queue base: %d pool base %d\n", | |
224 | vmdq_queue_base, vmdq_pool_base); | |
225 | if (vmdq_pool_base == 0) { | |
226 | num_vmdq_queues = dev_info.max_rx_queues; | |
227 | num_queues = dev_info.max_rx_queues; | |
228 | if (num_tcs != num_vmdq_queues / num_pools) { | |
229 | printf("nb_tcs %d is invalid considering with" | |
230 | " nb_pools %d, nb_tcs * nb_pools should = %d\n", | |
231 | num_tcs, num_pools, num_vmdq_queues); | |
232 | return -1; | |
233 | } | |
234 | } else { | |
235 | queues_per_pool = dev_info.vmdq_queue_num / | |
236 | dev_info.max_vmdq_pools; | |
237 | if (num_tcs > queues_per_pool) { | |
238 | printf("num_tcs %d > num of queues per pool %d\n", | |
239 | num_tcs, queues_per_pool); | |
240 | return -1; | |
241 | } | |
242 | num_vmdq_queues = num_pools * queues_per_pool; | |
243 | num_queues = vmdq_queue_base + num_vmdq_queues; | |
244 | printf("Configured vmdq pool num: %u," | |
245 | " each vmdq pool has %u queues\n", | |
246 | num_pools, queues_per_pool); | |
247 | } | |
248 | ||
9f95a23c | 249 | if (!rte_eth_dev_is_valid_port(port)) |
7c673cae FG |
250 | return -1; |
251 | ||
252 | retval = get_eth_conf(&port_conf); | |
253 | if (retval < 0) | |
254 | return retval; | |
255 | ||
9f95a23c TL |
256 | rte_eth_dev_info_get(port, &dev_info); |
257 | if (dev_info.tx_offload_capa & DEV_TX_OFFLOAD_MBUF_FAST_FREE) | |
258 | port_conf.txmode.offloads |= | |
259 | DEV_TX_OFFLOAD_MBUF_FAST_FREE; | |
260 | ||
261 | rss_hf_tmp = port_conf.rx_adv_conf.rss_conf.rss_hf; | |
262 | port_conf.rx_adv_conf.rss_conf.rss_hf &= | |
263 | dev_info.flow_type_rss_offloads; | |
264 | if (port_conf.rx_adv_conf.rss_conf.rss_hf != rss_hf_tmp) { | |
265 | printf("Port %u modified RSS hash function based on hardware support," | |
266 | "requested:%#"PRIx64" configured:%#"PRIx64"\n", | |
267 | port, | |
268 | rss_hf_tmp, | |
269 | port_conf.rx_adv_conf.rss_conf.rss_hf); | |
270 | } | |
271 | ||
7c673cae FG |
272 | /* |
273 | * Though in this example, all queues including pf queues are setup. | |
274 | * This is because VMDQ queues doesn't always start from zero, and the | |
275 | * PMD layer doesn't support selectively initialising part of rx/tx | |
276 | * queues. | |
277 | */ | |
278 | retval = rte_eth_dev_configure(port, num_queues, num_queues, &port_conf); | |
279 | if (retval != 0) | |
280 | return retval; | |
281 | ||
9f95a23c TL |
282 | retval = rte_eth_dev_adjust_nb_rx_tx_desc(port, &rxRingSize, |
283 | &txRingSize); | |
284 | if (retval != 0) | |
285 | return retval; | |
286 | if (RTE_MAX(rxRingSize, txRingSize) > | |
287 | RTE_MAX(RTE_TEST_RX_DESC_DEFAULT, RTE_TEST_TX_DESC_DEFAULT)) { | |
288 | printf("Mbuf pool has an insufficient size for port %u.\n", | |
289 | port); | |
290 | return -1; | |
291 | } | |
292 | ||
7c673cae FG |
293 | for (q = 0; q < num_queues; q++) { |
294 | retval = rte_eth_rx_queue_setup(port, q, rxRingSize, | |
295 | rte_eth_dev_socket_id(port), | |
296 | NULL, | |
297 | mbuf_pool); | |
298 | if (retval < 0) { | |
299 | printf("initialize rx queue %d failed\n", q); | |
300 | return retval; | |
301 | } | |
302 | } | |
303 | ||
9f95a23c TL |
304 | txq_conf = dev_info.default_txconf; |
305 | txq_conf.offloads = port_conf.txmode.offloads; | |
7c673cae FG |
306 | for (q = 0; q < num_queues; q++) { |
307 | retval = rte_eth_tx_queue_setup(port, q, txRingSize, | |
308 | rte_eth_dev_socket_id(port), | |
9f95a23c | 309 | &txq_conf); |
7c673cae FG |
310 | if (retval < 0) { |
311 | printf("initialize tx queue %d failed\n", q); | |
312 | return retval; | |
313 | } | |
314 | } | |
315 | ||
316 | retval = rte_eth_dev_start(port); | |
317 | if (retval < 0) { | |
318 | printf("port %d start failed\n", port); | |
319 | return retval; | |
320 | } | |
321 | ||
322 | rte_eth_macaddr_get(port, &vmdq_ports_eth_addr[port]); | |
323 | printf("Port %u MAC: %02"PRIx8" %02"PRIx8" %02"PRIx8 | |
324 | " %02"PRIx8" %02"PRIx8" %02"PRIx8"\n", | |
325 | (unsigned)port, | |
326 | vmdq_ports_eth_addr[port].addr_bytes[0], | |
327 | vmdq_ports_eth_addr[port].addr_bytes[1], | |
328 | vmdq_ports_eth_addr[port].addr_bytes[2], | |
329 | vmdq_ports_eth_addr[port].addr_bytes[3], | |
330 | vmdq_ports_eth_addr[port].addr_bytes[4], | |
331 | vmdq_ports_eth_addr[port].addr_bytes[5]); | |
332 | ||
333 | /* Set mac for each pool.*/ | |
334 | for (q = 0; q < num_pools; q++) { | |
335 | struct ether_addr mac; | |
336 | ||
337 | mac = pool_addr_template; | |
338 | mac.addr_bytes[4] = port; | |
339 | mac.addr_bytes[5] = q; | |
340 | printf("Port %u vmdq pool %u set mac %02x:%02x:%02x:%02x:%02x:%02x\n", | |
341 | port, q, | |
342 | mac.addr_bytes[0], mac.addr_bytes[1], | |
343 | mac.addr_bytes[2], mac.addr_bytes[3], | |
344 | mac.addr_bytes[4], mac.addr_bytes[5]); | |
345 | retval = rte_eth_dev_mac_addr_add(port, &mac, | |
346 | q + vmdq_pool_base); | |
347 | if (retval) { | |
348 | printf("mac addr add failed at pool %d\n", q); | |
349 | return retval; | |
350 | } | |
351 | } | |
352 | ||
353 | return 0; | |
354 | } | |
355 | ||
356 | /* Check num_pools parameter and set it if OK*/ | |
357 | static int | |
358 | vmdq_parse_num_pools(const char *q_arg) | |
359 | { | |
360 | char *end = NULL; | |
361 | int n; | |
362 | ||
363 | /* parse number string */ | |
364 | n = strtol(q_arg, &end, 10); | |
365 | if ((q_arg[0] == '\0') || (end == NULL) || (*end != '\0')) | |
366 | return -1; | |
367 | if (n != 16 && n != 32) | |
368 | return -1; | |
369 | if (n == 16) | |
370 | num_pools = ETH_16_POOLS; | |
371 | else | |
372 | num_pools = ETH_32_POOLS; | |
373 | ||
374 | return 0; | |
375 | } | |
376 | ||
377 | /* Check num_tcs parameter and set it if OK*/ | |
378 | static int | |
379 | vmdq_parse_num_tcs(const char *q_arg) | |
380 | { | |
381 | char *end = NULL; | |
382 | int n; | |
383 | ||
384 | /* parse number string */ | |
385 | n = strtol(q_arg, &end, 10); | |
386 | if ((q_arg[0] == '\0') || (end == NULL) || (*end != '\0')) | |
387 | return -1; | |
388 | ||
389 | if (n != 4 && n != 8) | |
390 | return -1; | |
391 | if (n == 4) | |
392 | num_tcs = ETH_4_TCS; | |
393 | else | |
394 | num_tcs = ETH_8_TCS; | |
395 | ||
396 | return 0; | |
397 | } | |
398 | ||
399 | static int | |
400 | parse_portmask(const char *portmask) | |
401 | { | |
402 | char *end = NULL; | |
403 | unsigned long pm; | |
404 | ||
405 | /* parse hexadecimal string */ | |
406 | pm = strtoul(portmask, &end, 16); | |
407 | if ((portmask[0] == '\0') || (end == NULL) || (*end != '\0')) | |
408 | return -1; | |
409 | ||
410 | if (pm == 0) | |
411 | return -1; | |
412 | ||
413 | return pm; | |
414 | } | |
415 | ||
416 | /* Display usage */ | |
417 | static void | |
418 | vmdq_usage(const char *prgname) | |
419 | { | |
420 | printf("%s [EAL options] -- -p PORTMASK]\n" | |
421 | " --nb-pools NP: number of pools (32 default, 16)\n" | |
422 | " --nb-tcs NP: number of TCs (4 default, 8)\n" | |
423 | " --enable-rss: enable RSS (disabled by default)\n", | |
424 | prgname); | |
425 | } | |
426 | ||
427 | /* Parse the argument (num_pools) given in the command line of the application */ | |
428 | static int | |
429 | vmdq_parse_args(int argc, char **argv) | |
430 | { | |
431 | int opt; | |
432 | int option_index; | |
433 | unsigned i; | |
434 | const char *prgname = argv[0]; | |
435 | static struct option long_option[] = { | |
436 | {"nb-pools", required_argument, NULL, 0}, | |
437 | {"nb-tcs", required_argument, NULL, 0}, | |
438 | {"enable-rss", 0, NULL, 0}, | |
439 | {NULL, 0, 0, 0} | |
440 | }; | |
441 | ||
442 | /* Parse command line */ | |
443 | while ((opt = getopt_long(argc, argv, "p:", long_option, | |
444 | &option_index)) != EOF) { | |
445 | switch (opt) { | |
446 | /* portmask */ | |
447 | case 'p': | |
448 | enabled_port_mask = parse_portmask(optarg); | |
449 | if (enabled_port_mask == 0) { | |
450 | printf("invalid portmask\n"); | |
451 | vmdq_usage(prgname); | |
452 | return -1; | |
453 | } | |
454 | break; | |
455 | case 0: | |
456 | if (!strcmp(long_option[option_index].name, "nb-pools")) { | |
457 | if (vmdq_parse_num_pools(optarg) == -1) { | |
458 | printf("invalid number of pools\n"); | |
459 | return -1; | |
460 | } | |
461 | } | |
462 | ||
463 | if (!strcmp(long_option[option_index].name, "nb-tcs")) { | |
464 | if (vmdq_parse_num_tcs(optarg) == -1) { | |
465 | printf("invalid number of tcs\n"); | |
466 | return -1; | |
467 | } | |
468 | } | |
469 | ||
470 | if (!strcmp(long_option[option_index].name, "enable-rss")) | |
471 | rss_enable = 1; | |
472 | break; | |
473 | ||
474 | default: | |
475 | vmdq_usage(prgname); | |
476 | return -1; | |
477 | } | |
478 | } | |
479 | ||
480 | for (i = 0; i < RTE_MAX_ETHPORTS; i++) { | |
481 | if (enabled_port_mask & (1 << i)) | |
482 | ports[num_ports++] = (uint8_t)i; | |
483 | } | |
484 | ||
485 | if (num_ports < 2 || num_ports % 2) { | |
486 | printf("Current enabled port number is %u," | |
487 | " but it should be even and at least 2\n", num_ports); | |
488 | return -1; | |
489 | } | |
490 | ||
491 | return 0; | |
492 | } | |
493 | ||
494 | static void | |
495 | update_mac_address(struct rte_mbuf *m, unsigned dst_port) | |
496 | { | |
497 | struct ether_hdr *eth; | |
498 | void *tmp; | |
499 | ||
500 | eth = rte_pktmbuf_mtod(m, struct ether_hdr *); | |
501 | ||
502 | /* 02:00:00:00:00:xx */ | |
503 | tmp = ð->d_addr.addr_bytes[0]; | |
504 | *((uint64_t *)tmp) = 0x000000000002 + ((uint64_t)dst_port << 40); | |
505 | ||
506 | /* src addr */ | |
507 | ether_addr_copy(&vmdq_ports_eth_addr[dst_port], ð->s_addr); | |
508 | } | |
509 | ||
510 | /* When we receive a HUP signal, print out our stats */ | |
511 | static void | |
512 | sighup_handler(int signum) | |
513 | { | |
514 | unsigned q = vmdq_queue_base; | |
515 | ||
516 | for (; q < num_queues; q++) { | |
517 | if (q % (num_vmdq_queues / num_pools) == 0) | |
518 | printf("\nPool %u: ", (q - vmdq_queue_base) / | |
519 | (num_vmdq_queues / num_pools)); | |
520 | printf("%lu ", rxPackets[q]); | |
521 | } | |
522 | printf("\nFinished handling signal %d\n", signum); | |
523 | } | |
524 | ||
525 | /* | |
526 | * Main thread that does the work, reading from INPUT_PORT | |
527 | * and writing to OUTPUT_PORT | |
528 | */ | |
529 | static int | |
530 | lcore_main(void *arg) | |
531 | { | |
532 | const uintptr_t core_num = (uintptr_t)arg; | |
533 | const unsigned num_cores = rte_lcore_count(); | |
534 | uint16_t startQueue, endQueue; | |
535 | uint16_t q, i, p; | |
536 | const uint16_t quot = (uint16_t)(num_vmdq_queues / num_cores); | |
537 | const uint16_t remainder = (uint16_t)(num_vmdq_queues % num_cores); | |
538 | ||
539 | ||
540 | if (remainder) { | |
541 | if (core_num < remainder) { | |
542 | startQueue = (uint16_t)(core_num * (quot + 1)); | |
543 | endQueue = (uint16_t)(startQueue + quot + 1); | |
544 | } else { | |
545 | startQueue = (uint16_t)(core_num * quot + remainder); | |
546 | endQueue = (uint16_t)(startQueue + quot); | |
547 | } | |
548 | } else { | |
549 | startQueue = (uint16_t)(core_num * quot); | |
550 | endQueue = (uint16_t)(startQueue + quot); | |
551 | } | |
552 | ||
553 | /* vmdq queue idx doesn't always start from zero.*/ | |
554 | startQueue += vmdq_queue_base; | |
555 | endQueue += vmdq_queue_base; | |
556 | printf("Core %u(lcore %u) reading queues %i-%i\n", (unsigned)core_num, | |
557 | rte_lcore_id(), startQueue, endQueue - 1); | |
558 | ||
559 | if (startQueue == endQueue) { | |
560 | printf("lcore %u has nothing to do\n", (unsigned)core_num); | |
561 | return 0; | |
562 | } | |
563 | ||
564 | for (;;) { | |
565 | struct rte_mbuf *buf[MAX_PKT_BURST]; | |
566 | const uint16_t buf_size = sizeof(buf) / sizeof(buf[0]); | |
567 | for (p = 0; p < num_ports; p++) { | |
568 | const uint8_t src = ports[p]; | |
569 | const uint8_t dst = ports[p ^ 1]; /* 0 <-> 1, 2 <-> 3 etc */ | |
570 | ||
571 | if ((src == INVALID_PORT_ID) || (dst == INVALID_PORT_ID)) | |
572 | continue; | |
573 | ||
574 | for (q = startQueue; q < endQueue; q++) { | |
575 | const uint16_t rxCount = rte_eth_rx_burst(src, | |
576 | q, buf, buf_size); | |
577 | ||
578 | if (unlikely(rxCount == 0)) | |
579 | continue; | |
580 | ||
581 | rxPackets[q] += rxCount; | |
582 | ||
583 | for (i = 0; i < rxCount; i++) | |
584 | update_mac_address(buf[i], dst); | |
585 | ||
586 | const uint16_t txCount = rte_eth_tx_burst(dst, | |
587 | q, buf, rxCount); | |
588 | if (txCount != rxCount) { | |
589 | for (i = txCount; i < rxCount; i++) | |
590 | rte_pktmbuf_free(buf[i]); | |
591 | } | |
592 | } | |
593 | } | |
594 | } | |
595 | } | |
596 | ||
597 | /* | |
598 | * Update the global var NUM_PORTS and array PORTS according to system ports number | |
599 | * and return valid ports number | |
600 | */ | |
601 | static unsigned check_ports_num(unsigned nb_ports) | |
602 | { | |
603 | unsigned valid_num_ports = num_ports; | |
604 | unsigned portid; | |
605 | ||
606 | if (num_ports > nb_ports) { | |
607 | printf("\nSpecified port number(%u) exceeds total system port number(%u)\n", | |
608 | num_ports, nb_ports); | |
609 | num_ports = nb_ports; | |
610 | } | |
611 | ||
612 | for (portid = 0; portid < num_ports; portid++) { | |
9f95a23c TL |
613 | if (!rte_eth_dev_is_valid_port(ports[portid])) { |
614 | printf("\nSpecified port ID(%u) is not valid\n", | |
615 | ports[portid]); | |
7c673cae FG |
616 | ports[portid] = INVALID_PORT_ID; |
617 | valid_num_ports--; | |
618 | } | |
619 | } | |
620 | return valid_num_ports; | |
621 | } | |
622 | ||
623 | ||
624 | /* Main function, does initialisation and calls the per-lcore functions */ | |
625 | int | |
626 | main(int argc, char *argv[]) | |
627 | { | |
628 | unsigned cores; | |
629 | struct rte_mempool *mbuf_pool; | |
630 | unsigned lcore_id; | |
631 | uintptr_t i; | |
632 | int ret; | |
633 | unsigned nb_ports, valid_num_ports; | |
9f95a23c | 634 | uint16_t portid; |
7c673cae FG |
635 | |
636 | signal(SIGHUP, sighup_handler); | |
637 | ||
638 | /* init EAL */ | |
639 | ret = rte_eal_init(argc, argv); | |
640 | if (ret < 0) | |
641 | rte_exit(EXIT_FAILURE, "Error with EAL initialization\n"); | |
642 | argc -= ret; | |
643 | argv += ret; | |
644 | ||
645 | /* parse app arguments */ | |
646 | ret = vmdq_parse_args(argc, argv); | |
647 | if (ret < 0) | |
648 | rte_exit(EXIT_FAILURE, "Invalid VMDQ argument\n"); | |
649 | ||
650 | cores = rte_lcore_count(); | |
651 | if ((cores & (cores - 1)) != 0 || cores > RTE_MAX_LCORE) { | |
652 | rte_exit(EXIT_FAILURE,"This program can only run on an even" | |
653 | " number of cores(1-%d)\n\n", RTE_MAX_LCORE); | |
654 | } | |
655 | ||
9f95a23c | 656 | nb_ports = rte_eth_dev_count_avail(); |
7c673cae FG |
657 | |
658 | /* | |
659 | * Update the global var NUM_PORTS and global array PORTS | |
660 | * and get value of var VALID_NUM_PORTS according to system ports number | |
661 | */ | |
662 | valid_num_ports = check_ports_num(nb_ports); | |
663 | ||
664 | if (valid_num_ports < 2 || valid_num_ports % 2) { | |
665 | printf("Current valid ports number is %u\n", valid_num_ports); | |
666 | rte_exit(EXIT_FAILURE, "Error with valid ports number is not even or less than 2\n"); | |
667 | } | |
668 | ||
669 | mbuf_pool = rte_pktmbuf_pool_create("MBUF_POOL", | |
670 | NUM_MBUFS_PER_PORT * nb_ports, MBUF_CACHE_SIZE, | |
671 | 0, RTE_MBUF_DEFAULT_BUF_SIZE, rte_socket_id()); | |
672 | if (mbuf_pool == NULL) | |
673 | rte_exit(EXIT_FAILURE, "Cannot create mbuf pool\n"); | |
674 | ||
675 | /* initialize all ports */ | |
9f95a23c | 676 | RTE_ETH_FOREACH_DEV(portid) { |
7c673cae FG |
677 | /* skip ports that are not enabled */ |
678 | if ((enabled_port_mask & (1 << portid)) == 0) { | |
679 | printf("\nSkipping disabled port %d\n", portid); | |
680 | continue; | |
681 | } | |
682 | if (port_init(portid, mbuf_pool) != 0) | |
683 | rte_exit(EXIT_FAILURE, "Cannot initialize network ports\n"); | |
684 | } | |
685 | ||
686 | /* call lcore_main() on every slave lcore */ | |
687 | i = 0; | |
688 | RTE_LCORE_FOREACH_SLAVE(lcore_id) { | |
689 | rte_eal_remote_launch(lcore_main, (void*)i++, lcore_id); | |
690 | } | |
691 | /* call on master too */ | |
692 | (void) lcore_main((void*)i); | |
693 | ||
694 | return 0; | |
695 | } |