]>
Commit | Line | Data |
---|---|---|
11fdf7f2 TL |
1 | /* SPDX-License-Identifier: BSD-3-Clause |
2 | * Copyright(c) 2010-2016 Intel Corporation | |
7c673cae FG |
3 | */ |
4 | ||
5 | #include <stdio.h> | |
6 | #include <stdlib.h> | |
7 | #include <stdint.h> | |
8 | #include <inttypes.h> | |
9 | #include <sys/types.h> | |
10 | #include <string.h> | |
11 | #include <sys/queue.h> | |
12 | #include <stdarg.h> | |
13 | #include <errno.h> | |
14 | #include <getopt.h> | |
15 | #include <signal.h> | |
16 | #include <stdbool.h> | |
17 | ||
18 | #include <rte_common.h> | |
19 | #include <rte_vect.h> | |
20 | #include <rte_byteorder.h> | |
21 | #include <rte_log.h> | |
22 | #include <rte_memory.h> | |
23 | #include <rte_memcpy.h> | |
7c673cae | 24 | #include <rte_eal.h> |
7c673cae FG |
25 | #include <rte_launch.h> |
26 | #include <rte_atomic.h> | |
27 | #include <rte_cycles.h> | |
28 | #include <rte_prefetch.h> | |
29 | #include <rte_lcore.h> | |
30 | #include <rte_per_lcore.h> | |
31 | #include <rte_branch_prediction.h> | |
32 | #include <rte_interrupts.h> | |
7c673cae FG |
33 | #include <rte_random.h> |
34 | #include <rte_debug.h> | |
35 | #include <rte_ether.h> | |
36 | #include <rte_ethdev.h> | |
37 | #include <rte_mempool.h> | |
38 | #include <rte_mbuf.h> | |
39 | #include <rte_ip.h> | |
40 | #include <rte_tcp.h> | |
41 | #include <rte_udp.h> | |
42 | #include <rte_string_fns.h> | |
43 | #include <rte_cpuflags.h> | |
44 | ||
45 | #include <cmdline_parse.h> | |
46 | #include <cmdline_parse_etheraddr.h> | |
47 | ||
48 | #include "l3fwd.h" | |
49 | ||
50 | /* | |
51 | * Configurable number of RX/TX ring descriptors | |
52 | */ | |
11fdf7f2 TL |
53 | #define RTE_TEST_RX_DESC_DEFAULT 1024 |
54 | #define RTE_TEST_TX_DESC_DEFAULT 1024 | |
7c673cae FG |
55 | |
56 | #define MAX_TX_QUEUE_PER_PORT RTE_MAX_ETHPORTS | |
57 | #define MAX_RX_QUEUE_PER_PORT 128 | |
58 | ||
59 | #define MAX_LCORE_PARAMS 1024 | |
60 | ||
61 | /* Static global variables used within this file. */ | |
62 | static uint16_t nb_rxd = RTE_TEST_RX_DESC_DEFAULT; | |
63 | static uint16_t nb_txd = RTE_TEST_TX_DESC_DEFAULT; | |
64 | ||
65 | /**< Ports set in promiscuous mode off by default. */ | |
66 | static int promiscuous_on; | |
67 | ||
68 | /* Select Longest-Prefix or Exact match. */ | |
69 | static int l3fwd_lpm_on; | |
70 | static int l3fwd_em_on; | |
71 | ||
9f95a23c TL |
72 | /* Global variables. */ |
73 | ||
7c673cae FG |
74 | static int numa_on = 1; /**< NUMA is enabled by default. */ |
75 | static int parse_ptype; /**< Parse packet type using rx callback, and */ | |
76 | /**< disabled by default */ | |
9f95a23c TL |
77 | static int per_port_pool; /**< Use separate buffer pools per port; disabled */ |
78 | /**< by default */ | |
7c673cae FG |
79 | |
80 | volatile bool force_quit; | |
81 | ||
82 | /* ethernet addresses of ports */ | |
83 | uint64_t dest_eth_addr[RTE_MAX_ETHPORTS]; | |
84 | struct ether_addr ports_eth_addr[RTE_MAX_ETHPORTS]; | |
85 | ||
86 | xmm_t val_eth[RTE_MAX_ETHPORTS]; | |
87 | ||
88 | /* mask of enabled ports */ | |
89 | uint32_t enabled_port_mask; | |
90 | ||
91 | /* Used only in exact match mode. */ | |
92 | int ipv6; /**< ipv6 is false by default. */ | |
93 | uint32_t hash_entry_number = HASH_ENTRY_NUMBER_DEFAULT; | |
94 | ||
95 | struct lcore_conf lcore_conf[RTE_MAX_LCORE]; | |
96 | ||
97 | struct lcore_params { | |
11fdf7f2 | 98 | uint16_t port_id; |
7c673cae FG |
99 | uint8_t queue_id; |
100 | uint8_t lcore_id; | |
101 | } __rte_cache_aligned; | |
102 | ||
103 | static struct lcore_params lcore_params_array[MAX_LCORE_PARAMS]; | |
104 | static struct lcore_params lcore_params_array_default[] = { | |
105 | {0, 0, 2}, | |
106 | {0, 1, 2}, | |
107 | {0, 2, 2}, | |
108 | {1, 0, 2}, | |
109 | {1, 1, 2}, | |
110 | {1, 2, 2}, | |
111 | {2, 0, 2}, | |
112 | {3, 0, 3}, | |
113 | {3, 1, 3}, | |
114 | }; | |
115 | ||
116 | static struct lcore_params * lcore_params = lcore_params_array_default; | |
117 | static uint16_t nb_lcore_params = sizeof(lcore_params_array_default) / | |
118 | sizeof(lcore_params_array_default[0]); | |
119 | ||
120 | static struct rte_eth_conf port_conf = { | |
121 | .rxmode = { | |
122 | .mq_mode = ETH_MQ_RX_RSS, | |
123 | .max_rx_pkt_len = ETHER_MAX_LEN, | |
124 | .split_hdr_size = 0, | |
9f95a23c | 125 | .offloads = DEV_RX_OFFLOAD_CHECKSUM, |
7c673cae FG |
126 | }, |
127 | .rx_adv_conf = { | |
128 | .rss_conf = { | |
129 | .rss_key = NULL, | |
130 | .rss_hf = ETH_RSS_IP, | |
131 | }, | |
132 | }, | |
133 | .txmode = { | |
134 | .mq_mode = ETH_MQ_TX_NONE, | |
135 | }, | |
136 | }; | |
137 | ||
9f95a23c TL |
138 | static struct rte_mempool *pktmbuf_pool[RTE_MAX_ETHPORTS][NB_SOCKETS]; |
139 | static uint8_t lkp_per_socket[NB_SOCKETS]; | |
7c673cae FG |
140 | |
141 | struct l3fwd_lkp_mode { | |
142 | void (*setup)(int); | |
143 | int (*check_ptype)(int); | |
144 | rte_rx_callback_fn cb_parse_ptype; | |
145 | int (*main_loop)(void *); | |
146 | void* (*get_ipv4_lookup_struct)(int); | |
147 | void* (*get_ipv6_lookup_struct)(int); | |
148 | }; | |
149 | ||
150 | static struct l3fwd_lkp_mode l3fwd_lkp; | |
151 | ||
152 | static struct l3fwd_lkp_mode l3fwd_em_lkp = { | |
153 | .setup = setup_hash, | |
154 | .check_ptype = em_check_ptype, | |
155 | .cb_parse_ptype = em_cb_parse_ptype, | |
156 | .main_loop = em_main_loop, | |
157 | .get_ipv4_lookup_struct = em_get_ipv4_l3fwd_lookup_struct, | |
158 | .get_ipv6_lookup_struct = em_get_ipv6_l3fwd_lookup_struct, | |
159 | }; | |
160 | ||
161 | static struct l3fwd_lkp_mode l3fwd_lpm_lkp = { | |
162 | .setup = setup_lpm, | |
163 | .check_ptype = lpm_check_ptype, | |
164 | .cb_parse_ptype = lpm_cb_parse_ptype, | |
165 | .main_loop = lpm_main_loop, | |
166 | .get_ipv4_lookup_struct = lpm_get_ipv4_l3fwd_lookup_struct, | |
167 | .get_ipv6_lookup_struct = lpm_get_ipv6_l3fwd_lookup_struct, | |
168 | }; | |
169 | ||
170 | /* | |
171 | * Setup lookup methods for forwarding. | |
172 | * Currently exact-match and longest-prefix-match | |
173 | * are supported ones. | |
174 | */ | |
175 | static void | |
176 | setup_l3fwd_lookup_tables(void) | |
177 | { | |
178 | /* Setup HASH lookup functions. */ | |
179 | if (l3fwd_em_on) | |
180 | l3fwd_lkp = l3fwd_em_lkp; | |
181 | /* Setup LPM lookup functions. */ | |
182 | else | |
183 | l3fwd_lkp = l3fwd_lpm_lkp; | |
184 | } | |
185 | ||
186 | static int | |
187 | check_lcore_params(void) | |
188 | { | |
189 | uint8_t queue, lcore; | |
190 | uint16_t i; | |
191 | int socketid; | |
192 | ||
193 | for (i = 0; i < nb_lcore_params; ++i) { | |
194 | queue = lcore_params[i].queue_id; | |
195 | if (queue >= MAX_RX_QUEUE_PER_PORT) { | |
196 | printf("invalid queue number: %hhu\n", queue); | |
197 | return -1; | |
198 | } | |
199 | lcore = lcore_params[i].lcore_id; | |
200 | if (!rte_lcore_is_enabled(lcore)) { | |
201 | printf("error: lcore %hhu is not enabled in lcore mask\n", lcore); | |
202 | return -1; | |
203 | } | |
204 | if ((socketid = rte_lcore_to_socket_id(lcore) != 0) && | |
205 | (numa_on == 0)) { | |
206 | printf("warning: lcore %hhu is on socket %d with numa off \n", | |
207 | lcore, socketid); | |
208 | } | |
209 | } | |
210 | return 0; | |
211 | } | |
212 | ||
213 | static int | |
11fdf7f2 | 214 | check_port_config(void) |
7c673cae | 215 | { |
11fdf7f2 | 216 | uint16_t portid; |
7c673cae FG |
217 | uint16_t i; |
218 | ||
219 | for (i = 0; i < nb_lcore_params; ++i) { | |
220 | portid = lcore_params[i].port_id; | |
221 | if ((enabled_port_mask & (1 << portid)) == 0) { | |
222 | printf("port %u is not enabled in port mask\n", portid); | |
223 | return -1; | |
224 | } | |
11fdf7f2 | 225 | if (!rte_eth_dev_is_valid_port(portid)) { |
7c673cae FG |
226 | printf("port %u is not present on the board\n", portid); |
227 | return -1; | |
228 | } | |
229 | } | |
230 | return 0; | |
231 | } | |
232 | ||
233 | static uint8_t | |
11fdf7f2 | 234 | get_port_n_rx_queues(const uint16_t port) |
7c673cae FG |
235 | { |
236 | int queue = -1; | |
237 | uint16_t i; | |
238 | ||
239 | for (i = 0; i < nb_lcore_params; ++i) { | |
240 | if (lcore_params[i].port_id == port) { | |
241 | if (lcore_params[i].queue_id == queue+1) | |
242 | queue = lcore_params[i].queue_id; | |
243 | else | |
244 | rte_exit(EXIT_FAILURE, "queue ids of the port %d must be" | |
245 | " in sequence and must start with 0\n", | |
246 | lcore_params[i].port_id); | |
247 | } | |
248 | } | |
249 | return (uint8_t)(++queue); | |
250 | } | |
251 | ||
252 | static int | |
253 | init_lcore_rx_queues(void) | |
254 | { | |
255 | uint16_t i, nb_rx_queue; | |
256 | uint8_t lcore; | |
257 | ||
258 | for (i = 0; i < nb_lcore_params; ++i) { | |
259 | lcore = lcore_params[i].lcore_id; | |
260 | nb_rx_queue = lcore_conf[lcore].n_rx_queue; | |
261 | if (nb_rx_queue >= MAX_RX_QUEUE_PER_LCORE) { | |
262 | printf("error: too many queues (%u) for lcore: %u\n", | |
263 | (unsigned)nb_rx_queue + 1, (unsigned)lcore); | |
264 | return -1; | |
265 | } else { | |
266 | lcore_conf[lcore].rx_queue_list[nb_rx_queue].port_id = | |
267 | lcore_params[i].port_id; | |
268 | lcore_conf[lcore].rx_queue_list[nb_rx_queue].queue_id = | |
269 | lcore_params[i].queue_id; | |
270 | lcore_conf[lcore].n_rx_queue++; | |
271 | } | |
272 | } | |
273 | return 0; | |
274 | } | |
275 | ||
276 | /* display usage */ | |
277 | static void | |
278 | print_usage(const char *prgname) | |
279 | { | |
11fdf7f2 | 280 | fprintf(stderr, "%s [EAL options] --" |
7c673cae FG |
281 | " -p PORTMASK" |
282 | " [-P]" | |
283 | " [-E]" | |
284 | " [-L]" | |
285 | " --config (port,queue,lcore)[,(port,queue,lcore)]" | |
286 | " [--eth-dest=X,MM:MM:MM:MM:MM:MM]" | |
287 | " [--enable-jumbo [--max-pkt-len PKTLEN]]" | |
288 | " [--no-numa]" | |
289 | " [--hash-entry-num]" | |
290 | " [--ipv6]" | |
9f95a23c TL |
291 | " [--parse-ptype]" |
292 | " [--per-port-pool]\n\n" | |
7c673cae FG |
293 | |
294 | " -p PORTMASK: Hexadecimal bitmask of ports to configure\n" | |
295 | " -P : Enable promiscuous mode\n" | |
296 | " -E : Enable exact match\n" | |
297 | " -L : Enable longest prefix match (default)\n" | |
298 | " --config (port,queue,lcore): Rx queue configuration\n" | |
299 | " --eth-dest=X,MM:MM:MM:MM:MM:MM: Ethernet destination for port X\n" | |
300 | " --enable-jumbo: Enable jumbo frames\n" | |
301 | " --max-pkt-len: Under the premise of enabling jumbo,\n" | |
302 | " maximum packet length in decimal (64-9600)\n" | |
303 | " --no-numa: Disable numa awareness\n" | |
304 | " --hash-entry-num: Specify the hash entry number in hexadecimal to be setup\n" | |
305 | " --ipv6: Set if running ipv6 packets\n" | |
9f95a23c TL |
306 | " --parse-ptype: Set to use software to analyze packet type\n" |
307 | " --per-port-pool: Use separate buffer pool per port\n\n", | |
7c673cae FG |
308 | prgname); |
309 | } | |
310 | ||
311 | static int | |
312 | parse_max_pkt_len(const char *pktlen) | |
313 | { | |
314 | char *end = NULL; | |
315 | unsigned long len; | |
316 | ||
317 | /* parse decimal string */ | |
318 | len = strtoul(pktlen, &end, 10); | |
319 | if ((pktlen[0] == '\0') || (end == NULL) || (*end != '\0')) | |
320 | return -1; | |
321 | ||
322 | if (len == 0) | |
323 | return -1; | |
324 | ||
325 | return len; | |
326 | } | |
327 | ||
328 | static int | |
329 | parse_portmask(const char *portmask) | |
330 | { | |
331 | char *end = NULL; | |
332 | unsigned long pm; | |
333 | ||
334 | /* parse hexadecimal string */ | |
335 | pm = strtoul(portmask, &end, 16); | |
336 | if ((portmask[0] == '\0') || (end == NULL) || (*end != '\0')) | |
337 | return -1; | |
338 | ||
339 | if (pm == 0) | |
340 | return -1; | |
341 | ||
342 | return pm; | |
343 | } | |
344 | ||
345 | static int | |
346 | parse_hash_entry_number(const char *hash_entry_num) | |
347 | { | |
348 | char *end = NULL; | |
349 | unsigned long hash_en; | |
350 | /* parse hexadecimal string */ | |
351 | hash_en = strtoul(hash_entry_num, &end, 16); | |
352 | if ((hash_entry_num[0] == '\0') || (end == NULL) || (*end != '\0')) | |
353 | return -1; | |
354 | ||
355 | if (hash_en == 0) | |
356 | return -1; | |
357 | ||
358 | return hash_en; | |
359 | } | |
360 | ||
361 | static int | |
362 | parse_config(const char *q_arg) | |
363 | { | |
364 | char s[256]; | |
365 | const char *p, *p0 = q_arg; | |
366 | char *end; | |
367 | enum fieldnames { | |
368 | FLD_PORT = 0, | |
369 | FLD_QUEUE, | |
370 | FLD_LCORE, | |
371 | _NUM_FLD | |
372 | }; | |
373 | unsigned long int_fld[_NUM_FLD]; | |
374 | char *str_fld[_NUM_FLD]; | |
375 | int i; | |
376 | unsigned size; | |
377 | ||
378 | nb_lcore_params = 0; | |
379 | ||
380 | while ((p = strchr(p0,'(')) != NULL) { | |
381 | ++p; | |
382 | if((p0 = strchr(p,')')) == NULL) | |
383 | return -1; | |
384 | ||
385 | size = p0 - p; | |
386 | if(size >= sizeof(s)) | |
387 | return -1; | |
388 | ||
389 | snprintf(s, sizeof(s), "%.*s", size, p); | |
390 | if (rte_strsplit(s, sizeof(s), str_fld, _NUM_FLD, ',') != _NUM_FLD) | |
391 | return -1; | |
392 | for (i = 0; i < _NUM_FLD; i++){ | |
393 | errno = 0; | |
394 | int_fld[i] = strtoul(str_fld[i], &end, 0); | |
395 | if (errno != 0 || end == str_fld[i] || int_fld[i] > 255) | |
396 | return -1; | |
397 | } | |
398 | if (nb_lcore_params >= MAX_LCORE_PARAMS) { | |
399 | printf("exceeded max number of lcore params: %hu\n", | |
400 | nb_lcore_params); | |
401 | return -1; | |
402 | } | |
403 | lcore_params_array[nb_lcore_params].port_id = | |
404 | (uint8_t)int_fld[FLD_PORT]; | |
405 | lcore_params_array[nb_lcore_params].queue_id = | |
406 | (uint8_t)int_fld[FLD_QUEUE]; | |
407 | lcore_params_array[nb_lcore_params].lcore_id = | |
408 | (uint8_t)int_fld[FLD_LCORE]; | |
409 | ++nb_lcore_params; | |
410 | } | |
411 | lcore_params = lcore_params_array; | |
412 | return 0; | |
413 | } | |
414 | ||
415 | static void | |
416 | parse_eth_dest(const char *optarg) | |
417 | { | |
11fdf7f2 | 418 | uint16_t portid; |
7c673cae FG |
419 | char *port_end; |
420 | uint8_t c, *dest, peer_addr[6]; | |
421 | ||
422 | errno = 0; | |
423 | portid = strtoul(optarg, &port_end, 10); | |
424 | if (errno != 0 || port_end == optarg || *port_end++ != ',') | |
425 | rte_exit(EXIT_FAILURE, | |
426 | "Invalid eth-dest: %s", optarg); | |
427 | if (portid >= RTE_MAX_ETHPORTS) | |
428 | rte_exit(EXIT_FAILURE, | |
429 | "eth-dest: port %d >= RTE_MAX_ETHPORTS(%d)\n", | |
430 | portid, RTE_MAX_ETHPORTS); | |
431 | ||
432 | if (cmdline_parse_etheraddr(NULL, port_end, | |
433 | &peer_addr, sizeof(peer_addr)) < 0) | |
434 | rte_exit(EXIT_FAILURE, | |
435 | "Invalid ethernet address: %s\n", | |
436 | port_end); | |
437 | dest = (uint8_t *)&dest_eth_addr[portid]; | |
438 | for (c = 0; c < 6; c++) | |
439 | dest[c] = peer_addr[c]; | |
440 | *(uint64_t *)(val_eth + portid) = dest_eth_addr[portid]; | |
441 | } | |
442 | ||
443 | #define MAX_JUMBO_PKT_LEN 9600 | |
444 | #define MEMPOOL_CACHE_SIZE 256 | |
445 | ||
11fdf7f2 TL |
446 | static const char short_options[] = |
447 | "p:" /* portmask */ | |
448 | "P" /* promiscuous */ | |
449 | "L" /* enable long prefix match */ | |
450 | "E" /* enable exact match */ | |
451 | ; | |
452 | ||
7c673cae FG |
453 | #define CMD_LINE_OPT_CONFIG "config" |
454 | #define CMD_LINE_OPT_ETH_DEST "eth-dest" | |
455 | #define CMD_LINE_OPT_NO_NUMA "no-numa" | |
456 | #define CMD_LINE_OPT_IPV6 "ipv6" | |
457 | #define CMD_LINE_OPT_ENABLE_JUMBO "enable-jumbo" | |
458 | #define CMD_LINE_OPT_HASH_ENTRY_NUM "hash-entry-num" | |
459 | #define CMD_LINE_OPT_PARSE_PTYPE "parse-ptype" | |
9f95a23c | 460 | #define CMD_LINE_OPT_PER_PORT_POOL "per-port-pool" |
11fdf7f2 TL |
461 | enum { |
462 | /* long options mapped to a short option */ | |
463 | ||
464 | /* first long only option value must be >= 256, so that we won't | |
465 | * conflict with short options */ | |
466 | CMD_LINE_OPT_MIN_NUM = 256, | |
467 | CMD_LINE_OPT_CONFIG_NUM, | |
468 | CMD_LINE_OPT_ETH_DEST_NUM, | |
469 | CMD_LINE_OPT_NO_NUMA_NUM, | |
470 | CMD_LINE_OPT_IPV6_NUM, | |
471 | CMD_LINE_OPT_ENABLE_JUMBO_NUM, | |
472 | CMD_LINE_OPT_HASH_ENTRY_NUM_NUM, | |
473 | CMD_LINE_OPT_PARSE_PTYPE_NUM, | |
9f95a23c | 474 | CMD_LINE_OPT_PARSE_PER_PORT_POOL, |
11fdf7f2 TL |
475 | }; |
476 | ||
477 | static const struct option lgopts[] = { | |
478 | {CMD_LINE_OPT_CONFIG, 1, 0, CMD_LINE_OPT_CONFIG_NUM}, | |
479 | {CMD_LINE_OPT_ETH_DEST, 1, 0, CMD_LINE_OPT_ETH_DEST_NUM}, | |
480 | {CMD_LINE_OPT_NO_NUMA, 0, 0, CMD_LINE_OPT_NO_NUMA_NUM}, | |
481 | {CMD_LINE_OPT_IPV6, 0, 0, CMD_LINE_OPT_IPV6_NUM}, | |
482 | {CMD_LINE_OPT_ENABLE_JUMBO, 0, 0, CMD_LINE_OPT_ENABLE_JUMBO_NUM}, | |
483 | {CMD_LINE_OPT_HASH_ENTRY_NUM, 1, 0, CMD_LINE_OPT_HASH_ENTRY_NUM_NUM}, | |
484 | {CMD_LINE_OPT_PARSE_PTYPE, 0, 0, CMD_LINE_OPT_PARSE_PTYPE_NUM}, | |
9f95a23c | 485 | {CMD_LINE_OPT_PER_PORT_POOL, 0, 0, CMD_LINE_OPT_PARSE_PER_PORT_POOL}, |
11fdf7f2 TL |
486 | {NULL, 0, 0, 0} |
487 | }; | |
7c673cae FG |
488 | |
489 | /* | |
490 | * This expression is used to calculate the number of mbufs needed | |
491 | * depending on user input, taking into account memory for rx and | |
492 | * tx hardware rings, cache per lcore and mtable per port per lcore. | |
493 | * RTE_MAX is used to ensure that NB_MBUF never goes below a minimum | |
494 | * value of 8192 | |
495 | */ | |
9f95a23c TL |
496 | #define NB_MBUF(nports) RTE_MAX( \ |
497 | (nports*nb_rx_queue*nb_rxd + \ | |
498 | nports*nb_lcores*MAX_PKT_BURST + \ | |
499 | nports*n_tx_queue*nb_txd + \ | |
11fdf7f2 | 500 | nb_lcores*MEMPOOL_CACHE_SIZE), \ |
7c673cae FG |
501 | (unsigned)8192) |
502 | ||
503 | /* Parse the argument given in the command line of the application */ | |
504 | static int | |
505 | parse_args(int argc, char **argv) | |
506 | { | |
507 | int opt, ret; | |
508 | char **argvopt; | |
509 | int option_index; | |
510 | char *prgname = argv[0]; | |
7c673cae FG |
511 | |
512 | argvopt = argv; | |
513 | ||
514 | /* Error or normal output strings. */ | |
11fdf7f2 | 515 | while ((opt = getopt_long(argc, argvopt, short_options, |
7c673cae FG |
516 | lgopts, &option_index)) != EOF) { |
517 | ||
518 | switch (opt) { | |
519 | /* portmask */ | |
520 | case 'p': | |
521 | enabled_port_mask = parse_portmask(optarg); | |
522 | if (enabled_port_mask == 0) { | |
11fdf7f2 | 523 | fprintf(stderr, "Invalid portmask\n"); |
7c673cae FG |
524 | print_usage(prgname); |
525 | return -1; | |
526 | } | |
527 | break; | |
11fdf7f2 | 528 | |
7c673cae | 529 | case 'P': |
7c673cae FG |
530 | promiscuous_on = 1; |
531 | break; | |
532 | ||
533 | case 'E': | |
7c673cae FG |
534 | l3fwd_em_on = 1; |
535 | break; | |
536 | ||
537 | case 'L': | |
7c673cae FG |
538 | l3fwd_lpm_on = 1; |
539 | break; | |
540 | ||
541 | /* long options */ | |
11fdf7f2 TL |
542 | case CMD_LINE_OPT_CONFIG_NUM: |
543 | ret = parse_config(optarg); | |
544 | if (ret) { | |
545 | fprintf(stderr, "Invalid config\n"); | |
546 | print_usage(prgname); | |
547 | return -1; | |
7c673cae | 548 | } |
11fdf7f2 | 549 | break; |
7c673cae | 550 | |
11fdf7f2 TL |
551 | case CMD_LINE_OPT_ETH_DEST_NUM: |
552 | parse_eth_dest(optarg); | |
553 | break; | |
7c673cae | 554 | |
11fdf7f2 TL |
555 | case CMD_LINE_OPT_NO_NUMA_NUM: |
556 | numa_on = 0; | |
557 | break; | |
7c673cae | 558 | |
11fdf7f2 TL |
559 | case CMD_LINE_OPT_IPV6_NUM: |
560 | ipv6 = 1; | |
561 | break; | |
7c673cae | 562 | |
11fdf7f2 TL |
563 | case CMD_LINE_OPT_ENABLE_JUMBO_NUM: { |
564 | const struct option lenopts = { | |
565 | "max-pkt-len", required_argument, 0, 0 | |
566 | }; | |
567 | ||
568 | port_conf.rxmode.offloads |= DEV_RX_OFFLOAD_JUMBO_FRAME; | |
569 | port_conf.txmode.offloads |= DEV_TX_OFFLOAD_MULTI_SEGS; | |
570 | ||
571 | /* | |
572 | * if no max-pkt-len set, use the default | |
573 | * value ETHER_MAX_LEN. | |
574 | */ | |
575 | if (getopt_long(argc, argvopt, "", | |
576 | &lenopts, &option_index) == 0) { | |
577 | ret = parse_max_pkt_len(optarg); | |
578 | if (ret < 64 || ret > MAX_JUMBO_PKT_LEN) { | |
579 | fprintf(stderr, | |
580 | "invalid maximum packet length\n"); | |
7c673cae FG |
581 | print_usage(prgname); |
582 | return -1; | |
583 | } | |
11fdf7f2 | 584 | port_conf.rxmode.max_rx_pkt_len = ret; |
7c673cae | 585 | } |
11fdf7f2 TL |
586 | break; |
587 | } | |
7c673cae | 588 | |
11fdf7f2 TL |
589 | case CMD_LINE_OPT_HASH_ENTRY_NUM_NUM: |
590 | ret = parse_hash_entry_number(optarg); | |
591 | if ((ret > 0) && (ret <= L3FWD_HASH_ENTRIES)) { | |
592 | hash_entry_number = ret; | |
593 | } else { | |
594 | fprintf(stderr, "invalid hash entry number\n"); | |
595 | print_usage(prgname); | |
596 | return -1; | |
7c673cae | 597 | } |
11fdf7f2 | 598 | break; |
7c673cae | 599 | |
11fdf7f2 TL |
600 | case CMD_LINE_OPT_PARSE_PTYPE_NUM: |
601 | printf("soft parse-ptype is enabled\n"); | |
602 | parse_ptype = 1; | |
7c673cae FG |
603 | break; |
604 | ||
9f95a23c TL |
605 | case CMD_LINE_OPT_PARSE_PER_PORT_POOL: |
606 | printf("per port buffer pool is enabled\n"); | |
607 | per_port_pool = 1; | |
608 | break; | |
609 | ||
7c673cae FG |
610 | default: |
611 | print_usage(prgname); | |
612 | return -1; | |
613 | } | |
614 | } | |
615 | ||
616 | /* If both LPM and EM are selected, return error. */ | |
617 | if (l3fwd_lpm_on && l3fwd_em_on) { | |
11fdf7f2 | 618 | fprintf(stderr, "LPM and EM are mutually exclusive, select only one\n"); |
7c673cae FG |
619 | return -1; |
620 | } | |
621 | ||
622 | /* | |
623 | * Nothing is selected, pick longest-prefix match | |
624 | * as default match. | |
625 | */ | |
626 | if (!l3fwd_lpm_on && !l3fwd_em_on) { | |
11fdf7f2 | 627 | fprintf(stderr, "LPM or EM none selected, default LPM on\n"); |
7c673cae | 628 | l3fwd_lpm_on = 1; |
7c673cae FG |
629 | } |
630 | ||
631 | /* | |
632 | * ipv6 and hash flags are valid only for | |
633 | * exact macth, reset them to default for | |
634 | * longest-prefix match. | |
635 | */ | |
636 | if (l3fwd_lpm_on) { | |
637 | ipv6 = 0; | |
638 | hash_entry_number = HASH_ENTRY_NUMBER_DEFAULT; | |
639 | } | |
640 | ||
641 | if (optind >= 0) | |
642 | argv[optind-1] = prgname; | |
643 | ||
644 | ret = optind-1; | |
11fdf7f2 | 645 | optind = 1; /* reset getopt lib */ |
7c673cae FG |
646 | return ret; |
647 | } | |
648 | ||
649 | static void | |
650 | print_ethaddr(const char *name, const struct ether_addr *eth_addr) | |
651 | { | |
652 | char buf[ETHER_ADDR_FMT_SIZE]; | |
653 | ether_format_addr(buf, ETHER_ADDR_FMT_SIZE, eth_addr); | |
654 | printf("%s%s", name, buf); | |
655 | } | |
656 | ||
657 | static int | |
9f95a23c | 658 | init_mem(uint16_t portid, unsigned int nb_mbuf) |
7c673cae FG |
659 | { |
660 | struct lcore_conf *qconf; | |
661 | int socketid; | |
662 | unsigned lcore_id; | |
663 | char s[64]; | |
664 | ||
665 | for (lcore_id = 0; lcore_id < RTE_MAX_LCORE; lcore_id++) { | |
666 | if (rte_lcore_is_enabled(lcore_id) == 0) | |
667 | continue; | |
668 | ||
669 | if (numa_on) | |
670 | socketid = rte_lcore_to_socket_id(lcore_id); | |
671 | else | |
672 | socketid = 0; | |
673 | ||
674 | if (socketid >= NB_SOCKETS) { | |
675 | rte_exit(EXIT_FAILURE, | |
676 | "Socket %d of lcore %u is out of range %d\n", | |
677 | socketid, lcore_id, NB_SOCKETS); | |
678 | } | |
679 | ||
9f95a23c TL |
680 | if (pktmbuf_pool[portid][socketid] == NULL) { |
681 | snprintf(s, sizeof(s), "mbuf_pool_%d:%d", | |
682 | portid, socketid); | |
683 | pktmbuf_pool[portid][socketid] = | |
7c673cae FG |
684 | rte_pktmbuf_pool_create(s, nb_mbuf, |
685 | MEMPOOL_CACHE_SIZE, 0, | |
686 | RTE_MBUF_DEFAULT_BUF_SIZE, socketid); | |
9f95a23c | 687 | if (pktmbuf_pool[portid][socketid] == NULL) |
7c673cae FG |
688 | rte_exit(EXIT_FAILURE, |
689 | "Cannot init mbuf pool on socket %d\n", | |
690 | socketid); | |
691 | else | |
692 | printf("Allocated mbuf pool on socket %d\n", | |
693 | socketid); | |
694 | ||
9f95a23c TL |
695 | /* Setup either LPM or EM(f.e Hash). But, only once per |
696 | * available socket. | |
697 | */ | |
698 | if (!lkp_per_socket[socketid]) { | |
699 | l3fwd_lkp.setup(socketid); | |
700 | lkp_per_socket[socketid] = 1; | |
701 | } | |
7c673cae FG |
702 | } |
703 | qconf = &lcore_conf[lcore_id]; | |
704 | qconf->ipv4_lookup_struct = | |
705 | l3fwd_lkp.get_ipv4_lookup_struct(socketid); | |
706 | qconf->ipv6_lookup_struct = | |
707 | l3fwd_lkp.get_ipv6_lookup_struct(socketid); | |
708 | } | |
709 | return 0; | |
710 | } | |
711 | ||
712 | /* Check the link status of all ports in up to 9s, and print them finally */ | |
713 | static void | |
11fdf7f2 | 714 | check_all_ports_link_status(uint32_t port_mask) |
7c673cae FG |
715 | { |
716 | #define CHECK_INTERVAL 100 /* 100ms */ | |
717 | #define MAX_CHECK_TIME 90 /* 9s (90 * 100ms) in total */ | |
11fdf7f2 TL |
718 | uint16_t portid; |
719 | uint8_t count, all_ports_up, print_flag = 0; | |
7c673cae FG |
720 | struct rte_eth_link link; |
721 | ||
722 | printf("\nChecking link status"); | |
723 | fflush(stdout); | |
724 | for (count = 0; count <= MAX_CHECK_TIME; count++) { | |
725 | if (force_quit) | |
726 | return; | |
727 | all_ports_up = 1; | |
11fdf7f2 | 728 | RTE_ETH_FOREACH_DEV(portid) { |
7c673cae FG |
729 | if (force_quit) |
730 | return; | |
731 | if ((port_mask & (1 << portid)) == 0) | |
732 | continue; | |
733 | memset(&link, 0, sizeof(link)); | |
734 | rte_eth_link_get_nowait(portid, &link); | |
735 | /* print link status if flag set */ | |
736 | if (print_flag == 1) { | |
737 | if (link.link_status) | |
11fdf7f2 TL |
738 | printf( |
739 | "Port%d Link Up. Speed %u Mbps -%s\n", | |
740 | portid, link.link_speed, | |
7c673cae FG |
741 | (link.link_duplex == ETH_LINK_FULL_DUPLEX) ? |
742 | ("full-duplex") : ("half-duplex\n")); | |
743 | else | |
11fdf7f2 | 744 | printf("Port %d Link Down\n", portid); |
7c673cae FG |
745 | continue; |
746 | } | |
747 | /* clear all_ports_up flag if any link down */ | |
748 | if (link.link_status == ETH_LINK_DOWN) { | |
749 | all_ports_up = 0; | |
750 | break; | |
751 | } | |
752 | } | |
753 | /* after finally printing all link status, get out */ | |
754 | if (print_flag == 1) | |
755 | break; | |
756 | ||
757 | if (all_ports_up == 0) { | |
758 | printf("."); | |
759 | fflush(stdout); | |
760 | rte_delay_ms(CHECK_INTERVAL); | |
761 | } | |
762 | ||
763 | /* set the print_flag if all ports up or timeout */ | |
764 | if (all_ports_up == 1 || count == (MAX_CHECK_TIME - 1)) { | |
765 | print_flag = 1; | |
766 | printf("done\n"); | |
767 | } | |
768 | } | |
769 | } | |
770 | ||
771 | static void | |
772 | signal_handler(int signum) | |
773 | { | |
774 | if (signum == SIGINT || signum == SIGTERM) { | |
775 | printf("\n\nSignal %d received, preparing to exit...\n", | |
776 | signum); | |
777 | force_quit = true; | |
778 | } | |
779 | } | |
780 | ||
781 | static int | |
11fdf7f2 | 782 | prepare_ptype_parser(uint16_t portid, uint16_t queueid) |
7c673cae FG |
783 | { |
784 | if (parse_ptype) { | |
785 | printf("Port %d: softly parse packet type info\n", portid); | |
786 | if (rte_eth_add_rx_callback(portid, queueid, | |
787 | l3fwd_lkp.cb_parse_ptype, | |
788 | NULL)) | |
789 | return 1; | |
790 | ||
791 | printf("Failed to add rx callback: port=%d\n", portid); | |
792 | return 0; | |
793 | } | |
794 | ||
795 | if (l3fwd_lkp.check_ptype(portid)) | |
796 | return 1; | |
797 | ||
798 | printf("port %d cannot parse packet type, please add --%s\n", | |
799 | portid, CMD_LINE_OPT_PARSE_PTYPE); | |
800 | return 0; | |
801 | } | |
802 | ||
803 | int | |
804 | main(int argc, char **argv) | |
805 | { | |
806 | struct lcore_conf *qconf; | |
807 | struct rte_eth_dev_info dev_info; | |
808 | struct rte_eth_txconf *txconf; | |
809 | int ret; | |
810 | unsigned nb_ports; | |
11fdf7f2 | 811 | uint16_t queueid, portid; |
7c673cae FG |
812 | unsigned lcore_id; |
813 | uint32_t n_tx_queue, nb_lcores; | |
11fdf7f2 | 814 | uint8_t nb_rx_queue, queue, socketid; |
7c673cae FG |
815 | |
816 | /* init EAL */ | |
817 | ret = rte_eal_init(argc, argv); | |
818 | if (ret < 0) | |
819 | rte_exit(EXIT_FAILURE, "Invalid EAL parameters\n"); | |
820 | argc -= ret; | |
821 | argv += ret; | |
822 | ||
823 | force_quit = false; | |
824 | signal(SIGINT, signal_handler); | |
825 | signal(SIGTERM, signal_handler); | |
826 | ||
827 | /* pre-init dst MACs for all ports to 02:00:00:00:00:xx */ | |
828 | for (portid = 0; portid < RTE_MAX_ETHPORTS; portid++) { | |
829 | dest_eth_addr[portid] = | |
830 | ETHER_LOCAL_ADMIN_ADDR + ((uint64_t)portid << 40); | |
831 | *(uint64_t *)(val_eth + portid) = dest_eth_addr[portid]; | |
832 | } | |
833 | ||
834 | /* parse application arguments (after the EAL ones) */ | |
835 | ret = parse_args(argc, argv); | |
836 | if (ret < 0) | |
837 | rte_exit(EXIT_FAILURE, "Invalid L3FWD parameters\n"); | |
838 | ||
839 | if (check_lcore_params() < 0) | |
840 | rte_exit(EXIT_FAILURE, "check_lcore_params failed\n"); | |
841 | ||
842 | ret = init_lcore_rx_queues(); | |
843 | if (ret < 0) | |
844 | rte_exit(EXIT_FAILURE, "init_lcore_rx_queues failed\n"); | |
845 | ||
11fdf7f2 | 846 | nb_ports = rte_eth_dev_count_avail(); |
7c673cae | 847 | |
11fdf7f2 | 848 | if (check_port_config() < 0) |
7c673cae FG |
849 | rte_exit(EXIT_FAILURE, "check_port_config failed\n"); |
850 | ||
851 | nb_lcores = rte_lcore_count(); | |
852 | ||
853 | /* Setup function pointers for lookup method. */ | |
854 | setup_l3fwd_lookup_tables(); | |
855 | ||
856 | /* initialize all ports */ | |
11fdf7f2 TL |
857 | RTE_ETH_FOREACH_DEV(portid) { |
858 | struct rte_eth_conf local_port_conf = port_conf; | |
859 | ||
7c673cae FG |
860 | /* skip ports that are not enabled */ |
861 | if ((enabled_port_mask & (1 << portid)) == 0) { | |
862 | printf("\nSkipping disabled port %d\n", portid); | |
863 | continue; | |
864 | } | |
865 | ||
866 | /* init port */ | |
867 | printf("Initializing port %d ... ", portid ); | |
868 | fflush(stdout); | |
869 | ||
870 | nb_rx_queue = get_port_n_rx_queues(portid); | |
871 | n_tx_queue = nb_lcores; | |
872 | if (n_tx_queue > MAX_TX_QUEUE_PER_PORT) | |
873 | n_tx_queue = MAX_TX_QUEUE_PER_PORT; | |
874 | printf("Creating queues: nb_rxq=%d nb_txq=%u... ", | |
875 | nb_rx_queue, (unsigned)n_tx_queue ); | |
11fdf7f2 TL |
876 | |
877 | rte_eth_dev_info_get(portid, &dev_info); | |
878 | if (dev_info.tx_offload_capa & DEV_TX_OFFLOAD_MBUF_FAST_FREE) | |
879 | local_port_conf.txmode.offloads |= | |
880 | DEV_TX_OFFLOAD_MBUF_FAST_FREE; | |
881 | ||
882 | local_port_conf.rx_adv_conf.rss_conf.rss_hf &= | |
883 | dev_info.flow_type_rss_offloads; | |
884 | if (local_port_conf.rx_adv_conf.rss_conf.rss_hf != | |
885 | port_conf.rx_adv_conf.rss_conf.rss_hf) { | |
886 | printf("Port %u modified RSS hash function based on hardware support," | |
887 | "requested:%#"PRIx64" configured:%#"PRIx64"\n", | |
888 | portid, | |
889 | port_conf.rx_adv_conf.rss_conf.rss_hf, | |
890 | local_port_conf.rx_adv_conf.rss_conf.rss_hf); | |
891 | } | |
892 | ||
7c673cae | 893 | ret = rte_eth_dev_configure(portid, nb_rx_queue, |
11fdf7f2 | 894 | (uint16_t)n_tx_queue, &local_port_conf); |
7c673cae FG |
895 | if (ret < 0) |
896 | rte_exit(EXIT_FAILURE, | |
897 | "Cannot configure device: err=%d, port=%d\n", | |
898 | ret, portid); | |
899 | ||
11fdf7f2 TL |
900 | ret = rte_eth_dev_adjust_nb_rx_tx_desc(portid, &nb_rxd, |
901 | &nb_txd); | |
902 | if (ret < 0) | |
903 | rte_exit(EXIT_FAILURE, | |
904 | "Cannot adjust number of descriptors: err=%d, " | |
905 | "port=%d\n", ret, portid); | |
906 | ||
7c673cae FG |
907 | rte_eth_macaddr_get(portid, &ports_eth_addr[portid]); |
908 | print_ethaddr(" Address:", &ports_eth_addr[portid]); | |
909 | printf(", "); | |
910 | print_ethaddr("Destination:", | |
911 | (const struct ether_addr *)&dest_eth_addr[portid]); | |
912 | printf(", "); | |
913 | ||
914 | /* | |
915 | * prepare src MACs for each port. | |
916 | */ | |
917 | ether_addr_copy(&ports_eth_addr[portid], | |
918 | (struct ether_addr *)(val_eth + portid) + 1); | |
919 | ||
920 | /* init memory */ | |
9f95a23c TL |
921 | if (!per_port_pool) { |
922 | /* portid = 0; this is *not* signifying the first port, | |
923 | * rather, it signifies that portid is ignored. | |
924 | */ | |
925 | ret = init_mem(0, NB_MBUF(nb_ports)); | |
926 | } else { | |
927 | ret = init_mem(portid, NB_MBUF(1)); | |
928 | } | |
7c673cae FG |
929 | if (ret < 0) |
930 | rte_exit(EXIT_FAILURE, "init_mem failed\n"); | |
931 | ||
932 | /* init one TX queue per couple (lcore,port) */ | |
933 | queueid = 0; | |
934 | for (lcore_id = 0; lcore_id < RTE_MAX_LCORE; lcore_id++) { | |
935 | if (rte_lcore_is_enabled(lcore_id) == 0) | |
936 | continue; | |
937 | ||
938 | if (numa_on) | |
939 | socketid = | |
940 | (uint8_t)rte_lcore_to_socket_id(lcore_id); | |
941 | else | |
942 | socketid = 0; | |
943 | ||
944 | printf("txq=%u,%d,%d ", lcore_id, queueid, socketid); | |
945 | fflush(stdout); | |
946 | ||
7c673cae | 947 | txconf = &dev_info.default_txconf; |
11fdf7f2 | 948 | txconf->offloads = local_port_conf.txmode.offloads; |
7c673cae FG |
949 | ret = rte_eth_tx_queue_setup(portid, queueid, nb_txd, |
950 | socketid, txconf); | |
951 | if (ret < 0) | |
952 | rte_exit(EXIT_FAILURE, | |
953 | "rte_eth_tx_queue_setup: err=%d, " | |
954 | "port=%d\n", ret, portid); | |
955 | ||
956 | qconf = &lcore_conf[lcore_id]; | |
957 | qconf->tx_queue_id[portid] = queueid; | |
958 | queueid++; | |
959 | ||
960 | qconf->tx_port_id[qconf->n_tx_port] = portid; | |
961 | qconf->n_tx_port++; | |
962 | } | |
963 | printf("\n"); | |
964 | } | |
965 | ||
966 | for (lcore_id = 0; lcore_id < RTE_MAX_LCORE; lcore_id++) { | |
967 | if (rte_lcore_is_enabled(lcore_id) == 0) | |
968 | continue; | |
969 | qconf = &lcore_conf[lcore_id]; | |
970 | printf("\nInitializing rx queues on lcore %u ... ", lcore_id ); | |
971 | fflush(stdout); | |
972 | /* init RX queues */ | |
973 | for(queue = 0; queue < qconf->n_rx_queue; ++queue) { | |
11fdf7f2 TL |
974 | struct rte_eth_dev *dev; |
975 | struct rte_eth_conf *conf; | |
976 | struct rte_eth_rxconf rxq_conf; | |
977 | ||
7c673cae FG |
978 | portid = qconf->rx_queue_list[queue].port_id; |
979 | queueid = qconf->rx_queue_list[queue].queue_id; | |
11fdf7f2 TL |
980 | dev = &rte_eth_devices[portid]; |
981 | conf = &dev->data->dev_conf; | |
7c673cae FG |
982 | |
983 | if (numa_on) | |
984 | socketid = | |
985 | (uint8_t)rte_lcore_to_socket_id(lcore_id); | |
986 | else | |
987 | socketid = 0; | |
988 | ||
989 | printf("rxq=%d,%d,%d ", portid, queueid, socketid); | |
990 | fflush(stdout); | |
991 | ||
11fdf7f2 TL |
992 | rte_eth_dev_info_get(portid, &dev_info); |
993 | rxq_conf = dev_info.default_rxconf; | |
994 | rxq_conf.offloads = conf->rxmode.offloads; | |
9f95a23c TL |
995 | if (!per_port_pool) |
996 | ret = rte_eth_rx_queue_setup(portid, queueid, | |
997 | nb_rxd, socketid, | |
998 | &rxq_conf, | |
999 | pktmbuf_pool[0][socketid]); | |
1000 | else | |
1001 | ret = rte_eth_rx_queue_setup(portid, queueid, | |
1002 | nb_rxd, socketid, | |
1003 | &rxq_conf, | |
1004 | pktmbuf_pool[portid][socketid]); | |
7c673cae FG |
1005 | if (ret < 0) |
1006 | rte_exit(EXIT_FAILURE, | |
1007 | "rte_eth_rx_queue_setup: err=%d, port=%d\n", | |
1008 | ret, portid); | |
1009 | } | |
1010 | } | |
1011 | ||
1012 | printf("\n"); | |
1013 | ||
1014 | /* start ports */ | |
11fdf7f2 | 1015 | RTE_ETH_FOREACH_DEV(portid) { |
7c673cae FG |
1016 | if ((enabled_port_mask & (1 << portid)) == 0) { |
1017 | continue; | |
1018 | } | |
1019 | /* Start device */ | |
1020 | ret = rte_eth_dev_start(portid); | |
1021 | if (ret < 0) | |
1022 | rte_exit(EXIT_FAILURE, | |
1023 | "rte_eth_dev_start: err=%d, port=%d\n", | |
1024 | ret, portid); | |
1025 | ||
1026 | /* | |
1027 | * If enabled, put device in promiscuous mode. | |
1028 | * This allows IO forwarding mode to forward packets | |
1029 | * to itself through 2 cross-connected ports of the | |
1030 | * target machine. | |
1031 | */ | |
1032 | if (promiscuous_on) | |
1033 | rte_eth_promiscuous_enable(portid); | |
1034 | } | |
1035 | ||
1036 | printf("\n"); | |
1037 | ||
1038 | for (lcore_id = 0; lcore_id < RTE_MAX_LCORE; lcore_id++) { | |
1039 | if (rte_lcore_is_enabled(lcore_id) == 0) | |
1040 | continue; | |
1041 | qconf = &lcore_conf[lcore_id]; | |
1042 | for (queue = 0; queue < qconf->n_rx_queue; ++queue) { | |
1043 | portid = qconf->rx_queue_list[queue].port_id; | |
1044 | queueid = qconf->rx_queue_list[queue].queue_id; | |
1045 | if (prepare_ptype_parser(portid, queueid) == 0) | |
1046 | rte_exit(EXIT_FAILURE, "ptype check fails\n"); | |
1047 | } | |
1048 | } | |
1049 | ||
1050 | ||
11fdf7f2 | 1051 | check_all_ports_link_status(enabled_port_mask); |
7c673cae FG |
1052 | |
1053 | ret = 0; | |
1054 | /* launch per-lcore init on every lcore */ | |
1055 | rte_eal_mp_remote_launch(l3fwd_lkp.main_loop, NULL, CALL_MASTER); | |
1056 | RTE_LCORE_FOREACH_SLAVE(lcore_id) { | |
1057 | if (rte_eal_wait_lcore(lcore_id) < 0) { | |
1058 | ret = -1; | |
1059 | break; | |
1060 | } | |
1061 | } | |
1062 | ||
1063 | /* stop ports */ | |
11fdf7f2 | 1064 | RTE_ETH_FOREACH_DEV(portid) { |
7c673cae FG |
1065 | if ((enabled_port_mask & (1 << portid)) == 0) |
1066 | continue; | |
1067 | printf("Closing port %d...", portid); | |
1068 | rte_eth_dev_stop(portid); | |
1069 | rte_eth_dev_close(portid); | |
1070 | printf(" Done\n"); | |
1071 | } | |
1072 | printf("Bye...\n"); | |
1073 | ||
1074 | return ret; | |
1075 | } |