]>
Commit | Line | Data |
---|---|---|
11fdf7f2 TL |
1 | /* SPDX-License-Identifier: BSD-3-Clause |
2 | * Copyright(c) 2016 Intel Corporation | |
7c673cae FG |
3 | */ |
4 | ||
5 | #include <stdio.h> | |
6 | #include <string.h> | |
7 | #include <stdint.h> | |
8 | #include <inttypes.h> | |
9 | #include <stdlib.h> | |
10 | #include <getopt.h> | |
11 | #include <signal.h> | |
12 | #include <stdbool.h> | |
13 | #include <net/if.h> | |
14 | ||
15 | #include <rte_eal.h> | |
f67539c2 | 16 | #include <rte_alarm.h> |
7c673cae FG |
17 | #include <rte_common.h> |
18 | #include <rte_debug.h> | |
19 | #include <rte_ethdev.h> | |
20 | #include <rte_memory.h> | |
21 | #include <rte_lcore.h> | |
22 | #include <rte_branch_prediction.h> | |
23 | #include <rte_errno.h> | |
24 | #include <rte_dev.h> | |
25 | #include <rte_kvargs.h> | |
26 | #include <rte_mempool.h> | |
27 | #include <rte_ring.h> | |
11fdf7f2 | 28 | #include <rte_string_fns.h> |
7c673cae FG |
29 | #include <rte_pdump.h> |
30 | ||
31 | #define CMD_LINE_OPT_PDUMP "pdump" | |
9f95a23c TL |
32 | #define CMD_LINE_OPT_PDUMP_NUM 256 |
33 | #define CMD_LINE_OPT_MULTI "multi" | |
34 | #define CMD_LINE_OPT_MULTI_NUM 257 | |
7c673cae FG |
35 | #define PDUMP_PORT_ARG "port" |
36 | #define PDUMP_PCI_ARG "device_id" | |
37 | #define PDUMP_QUEUE_ARG "queue" | |
38 | #define PDUMP_DIR_ARG "dir" | |
39 | #define PDUMP_RX_DEV_ARG "rx-dev" | |
40 | #define PDUMP_TX_DEV_ARG "tx-dev" | |
41 | #define PDUMP_RING_SIZE_ARG "ring-size" | |
42 | #define PDUMP_MSIZE_ARG "mbuf-size" | |
43 | #define PDUMP_NUM_MBUFS_ARG "total-num-mbufs" | |
7c673cae | 44 | |
11fdf7f2 TL |
45 | #define VDEV_NAME_FMT "net_pcap_%s_%d" |
46 | #define VDEV_PCAP_ARGS_FMT "tx_pcap=%s" | |
47 | #define VDEV_IFACE_ARGS_FMT "tx_iface=%s" | |
7c673cae FG |
48 | #define TX_STREAM_SIZE 64 |
49 | ||
50 | #define MP_NAME "pdump_pool_%d" | |
51 | ||
52 | #define RX_RING "rx_ring_%d" | |
53 | #define TX_RING "tx_ring_%d" | |
54 | ||
55 | #define RX_STR "rx" | |
56 | #define TX_STR "tx" | |
57 | ||
58 | /* Maximum long option length for option parsing. */ | |
59 | #define APP_ARG_TCPDUMP_MAX_TUPLES 54 | |
60 | #define MBUF_POOL_CACHE_SIZE 250 | |
61 | #define TX_DESC_PER_QUEUE 512 | |
62 | #define RX_DESC_PER_QUEUE 128 | |
63 | #define MBUFS_PER_POOL 65535 | |
64 | #define MAX_LONG_OPT_SZ 64 | |
65 | #define RING_SIZE 16384 | |
66 | #define SIZE 256 | |
67 | #define BURST_SIZE 32 | |
68 | #define NUM_VDEVS 2 | |
f67539c2 TL |
69 | /* Maximum delay for exiting after primary process. */ |
70 | #define MONITOR_INTERVAL (500 * 1000) | |
7c673cae | 71 | |
7c673cae FG |
72 | /* true if x is a power of 2 */ |
73 | #define POWEROF2(x) ((((x)-1) & (x)) == 0) | |
74 | ||
75 | enum pdump_en_dis { | |
76 | DISABLE = 1, | |
77 | ENABLE = 2 | |
78 | }; | |
79 | ||
80 | enum pcap_stream { | |
81 | IFACE = 1, | |
82 | PCAP = 2 | |
83 | }; | |
84 | ||
85 | enum pdump_by { | |
86 | PORT_ID = 1, | |
87 | DEVICE_ID = 2 | |
88 | }; | |
89 | ||
9f95a23c | 90 | static const char * const valid_pdump_arguments[] = { |
7c673cae FG |
91 | PDUMP_PORT_ARG, |
92 | PDUMP_PCI_ARG, | |
93 | PDUMP_QUEUE_ARG, | |
94 | PDUMP_DIR_ARG, | |
95 | PDUMP_RX_DEV_ARG, | |
96 | PDUMP_TX_DEV_ARG, | |
97 | PDUMP_RING_SIZE_ARG, | |
98 | PDUMP_MSIZE_ARG, | |
99 | PDUMP_NUM_MBUFS_ARG, | |
100 | NULL | |
101 | }; | |
102 | ||
103 | struct pdump_stats { | |
104 | uint64_t dequeue_pkts; | |
105 | uint64_t tx_pkts; | |
106 | uint64_t freed_pkts; | |
107 | }; | |
108 | ||
109 | struct pdump_tuples { | |
110 | /* cli params */ | |
11fdf7f2 | 111 | uint16_t port; |
7c673cae FG |
112 | char *device_id; |
113 | uint16_t queue; | |
114 | char rx_dev[TX_STREAM_SIZE]; | |
115 | char tx_dev[TX_STREAM_SIZE]; | |
116 | uint32_t ring_size; | |
117 | uint16_t mbuf_data_size; | |
118 | uint32_t total_num_mbufs; | |
119 | ||
120 | /* params for library API call */ | |
121 | uint32_t dir; | |
122 | struct rte_mempool *mp; | |
123 | struct rte_ring *rx_ring; | |
124 | struct rte_ring *tx_ring; | |
125 | ||
126 | /* params for packet dumping */ | |
127 | enum pdump_by dump_by_type; | |
9f95a23c TL |
128 | uint16_t rx_vdev_id; |
129 | uint16_t tx_vdev_id; | |
7c673cae FG |
130 | enum pcap_stream rx_vdev_stream_type; |
131 | enum pcap_stream tx_vdev_stream_type; | |
132 | bool single_pdump_dev; | |
133 | ||
134 | /* stats */ | |
135 | struct pdump_stats stats; | |
136 | } __rte_cache_aligned; | |
137 | static struct pdump_tuples pdump_t[APP_ARG_TCPDUMP_MAX_TUPLES]; | |
138 | ||
139 | struct parse_val { | |
140 | uint64_t min; | |
141 | uint64_t max; | |
142 | uint64_t val; | |
143 | }; | |
144 | ||
9f95a23c | 145 | static int num_tuples; |
7c673cae | 146 | static struct rte_eth_conf port_conf_default; |
9f95a23c TL |
147 | static volatile uint8_t quit_signal; |
148 | static uint8_t multiple_core_capture; | |
7c673cae FG |
149 | |
150 | /**< display usage */ | |
151 | static void | |
152 | pdump_usage(const char *prgname) | |
153 | { | |
f67539c2 | 154 | printf("usage: %s [EAL options] --" |
9f95a23c TL |
155 | " --["CMD_LINE_OPT_MULTI"]\n" |
156 | " --"CMD_LINE_OPT_PDUMP" " | |
7c673cae FG |
157 | "'(port=<port id> | device_id=<pci id or vdev name>)," |
158 | "(queue=<queue_id>)," | |
159 | "(rx-dev=<iface or pcap file> |" | |
160 | " tx-dev=<iface or pcap file>," | |
161 | "[ring-size=<ring size>default:16384]," | |
162 | "[mbuf-size=<mbuf data size>default:2176]," | |
11fdf7f2 | 163 | "[total-num-mbufs=<number of mbufs>default:65535]'\n", |
7c673cae FG |
164 | prgname); |
165 | } | |
166 | ||
167 | static int | |
168 | parse_device_id(const char *key __rte_unused, const char *value, | |
169 | void *extra_args) | |
170 | { | |
171 | struct pdump_tuples *pt = extra_args; | |
172 | ||
173 | pt->device_id = strdup(value); | |
174 | pt->dump_by_type = DEVICE_ID; | |
175 | ||
176 | return 0; | |
177 | } | |
178 | ||
179 | static int | |
180 | parse_queue(const char *key __rte_unused, const char *value, void *extra_args) | |
181 | { | |
182 | unsigned long n; | |
183 | struct pdump_tuples *pt = extra_args; | |
184 | ||
185 | if (!strcmp(value, "*")) | |
186 | pt->queue = RTE_PDUMP_ALL_QUEUES; | |
187 | else { | |
188 | n = strtoul(value, NULL, 10); | |
189 | pt->queue = (uint16_t) n; | |
190 | } | |
191 | return 0; | |
192 | } | |
193 | ||
194 | static int | |
195 | parse_rxtxdev(const char *key, const char *value, void *extra_args) | |
196 | { | |
197 | ||
198 | struct pdump_tuples *pt = extra_args; | |
199 | ||
200 | if (!strcmp(key, PDUMP_RX_DEV_ARG)) { | |
9f95a23c | 201 | strlcpy(pt->rx_dev, value, sizeof(pt->rx_dev)); |
7c673cae FG |
202 | /* identify the tx stream type for pcap vdev */ |
203 | if (if_nametoindex(pt->rx_dev)) | |
204 | pt->rx_vdev_stream_type = IFACE; | |
205 | } else if (!strcmp(key, PDUMP_TX_DEV_ARG)) { | |
9f95a23c | 206 | strlcpy(pt->tx_dev, value, sizeof(pt->tx_dev)); |
7c673cae FG |
207 | /* identify the tx stream type for pcap vdev */ |
208 | if (if_nametoindex(pt->tx_dev)) | |
209 | pt->tx_vdev_stream_type = IFACE; | |
210 | } | |
211 | ||
212 | return 0; | |
213 | } | |
214 | ||
215 | static int | |
216 | parse_uint_value(const char *key, const char *value, void *extra_args) | |
217 | { | |
218 | struct parse_val *v; | |
219 | unsigned long t; | |
220 | char *end; | |
221 | int ret = 0; | |
222 | ||
223 | errno = 0; | |
224 | v = extra_args; | |
225 | t = strtoul(value, &end, 10); | |
226 | ||
227 | if (errno != 0 || end[0] != 0 || t < v->min || t > v->max) { | |
228 | printf("invalid value:\"%s\" for key:\"%s\", " | |
229 | "value must be >= %"PRIu64" and <= %"PRIu64"\n", | |
230 | value, key, v->min, v->max); | |
231 | ret = -EINVAL; | |
232 | } | |
233 | if (!strcmp(key, PDUMP_RING_SIZE_ARG) && !POWEROF2(t)) { | |
234 | printf("invalid value:\"%s\" for key:\"%s\", " | |
235 | "value must be power of 2\n", value, key); | |
236 | ret = -EINVAL; | |
237 | } | |
238 | ||
239 | if (ret != 0) | |
240 | return ret; | |
241 | ||
242 | v->val = t; | |
243 | return 0; | |
244 | } | |
245 | ||
246 | static int | |
247 | parse_pdump(const char *optarg) | |
248 | { | |
249 | struct rte_kvargs *kvlist; | |
250 | int ret = 0, cnt1, cnt2; | |
251 | struct pdump_tuples *pt; | |
252 | struct parse_val v = {0}; | |
253 | ||
254 | pt = &pdump_t[num_tuples]; | |
255 | ||
256 | /* initial check for invalid arguments */ | |
257 | kvlist = rte_kvargs_parse(optarg, valid_pdump_arguments); | |
258 | if (kvlist == NULL) { | |
259 | printf("--pdump=\"%s\": invalid argument passed\n", optarg); | |
260 | return -1; | |
261 | } | |
262 | ||
263 | /* port/device_id parsing and validation */ | |
264 | cnt1 = rte_kvargs_count(kvlist, PDUMP_PORT_ARG); | |
265 | cnt2 = rte_kvargs_count(kvlist, PDUMP_PCI_ARG); | |
266 | if (!((cnt1 == 1 && cnt2 == 0) || (cnt1 == 0 && cnt2 == 1))) { | |
267 | printf("--pdump=\"%s\": must have either port or " | |
268 | "device_id argument\n", optarg); | |
269 | ret = -1; | |
270 | goto free_kvlist; | |
271 | } else if (cnt1 == 1) { | |
272 | v.min = 0; | |
273 | v.max = RTE_MAX_ETHPORTS-1; | |
274 | ret = rte_kvargs_process(kvlist, PDUMP_PORT_ARG, | |
275 | &parse_uint_value, &v); | |
276 | if (ret < 0) | |
277 | goto free_kvlist; | |
9f95a23c | 278 | pt->port = (uint16_t) v.val; |
7c673cae FG |
279 | pt->dump_by_type = PORT_ID; |
280 | } else if (cnt2 == 1) { | |
281 | ret = rte_kvargs_process(kvlist, PDUMP_PCI_ARG, | |
282 | &parse_device_id, pt); | |
283 | if (ret < 0) | |
284 | goto free_kvlist; | |
285 | } | |
286 | ||
287 | /* queue parsing and validation */ | |
288 | cnt1 = rte_kvargs_count(kvlist, PDUMP_QUEUE_ARG); | |
289 | if (cnt1 != 1) { | |
290 | printf("--pdump=\"%s\": must have queue argument\n", optarg); | |
291 | ret = -1; | |
292 | goto free_kvlist; | |
293 | } | |
294 | ret = rte_kvargs_process(kvlist, PDUMP_QUEUE_ARG, &parse_queue, pt); | |
295 | if (ret < 0) | |
296 | goto free_kvlist; | |
297 | ||
298 | /* rx-dev and tx-dev parsing and validation */ | |
299 | cnt1 = rte_kvargs_count(kvlist, PDUMP_RX_DEV_ARG); | |
300 | cnt2 = rte_kvargs_count(kvlist, PDUMP_TX_DEV_ARG); | |
301 | if (cnt1 == 0 && cnt2 == 0) { | |
302 | printf("--pdump=\"%s\": must have either rx-dev or " | |
303 | "tx-dev argument\n", optarg); | |
304 | ret = -1; | |
305 | goto free_kvlist; | |
306 | } else if (cnt1 == 1 && cnt2 == 1) { | |
307 | ret = rte_kvargs_process(kvlist, PDUMP_RX_DEV_ARG, | |
308 | &parse_rxtxdev, pt); | |
309 | if (ret < 0) | |
310 | goto free_kvlist; | |
311 | ret = rte_kvargs_process(kvlist, PDUMP_TX_DEV_ARG, | |
312 | &parse_rxtxdev, pt); | |
313 | if (ret < 0) | |
314 | goto free_kvlist; | |
315 | /* if captured packets has to send to the same vdev */ | |
316 | if (!strcmp(pt->rx_dev, pt->tx_dev)) | |
317 | pt->single_pdump_dev = true; | |
318 | pt->dir = RTE_PDUMP_FLAG_RXTX; | |
319 | } else if (cnt1 == 1) { | |
320 | ret = rte_kvargs_process(kvlist, PDUMP_RX_DEV_ARG, | |
321 | &parse_rxtxdev, pt); | |
322 | if (ret < 0) | |
323 | goto free_kvlist; | |
324 | pt->dir = RTE_PDUMP_FLAG_RX; | |
325 | } else if (cnt2 == 1) { | |
326 | ret = rte_kvargs_process(kvlist, PDUMP_TX_DEV_ARG, | |
327 | &parse_rxtxdev, pt); | |
328 | if (ret < 0) | |
329 | goto free_kvlist; | |
330 | pt->dir = RTE_PDUMP_FLAG_TX; | |
331 | } | |
332 | ||
333 | /* optional */ | |
334 | /* ring_size parsing and validation */ | |
335 | cnt1 = rte_kvargs_count(kvlist, PDUMP_RING_SIZE_ARG); | |
336 | if (cnt1 == 1) { | |
337 | v.min = 2; | |
338 | v.max = RTE_RING_SZ_MASK-1; | |
339 | ret = rte_kvargs_process(kvlist, PDUMP_RING_SIZE_ARG, | |
340 | &parse_uint_value, &v); | |
341 | if (ret < 0) | |
342 | goto free_kvlist; | |
343 | pt->ring_size = (uint32_t) v.val; | |
344 | } else | |
345 | pt->ring_size = RING_SIZE; | |
346 | ||
347 | /* mbuf_data_size parsing and validation */ | |
348 | cnt1 = rte_kvargs_count(kvlist, PDUMP_MSIZE_ARG); | |
349 | if (cnt1 == 1) { | |
350 | v.min = 1; | |
351 | v.max = UINT16_MAX; | |
352 | ret = rte_kvargs_process(kvlist, PDUMP_MSIZE_ARG, | |
353 | &parse_uint_value, &v); | |
354 | if (ret < 0) | |
355 | goto free_kvlist; | |
356 | pt->mbuf_data_size = (uint16_t) v.val; | |
357 | } else | |
358 | pt->mbuf_data_size = RTE_MBUF_DEFAULT_BUF_SIZE; | |
359 | ||
360 | /* total_num_mbufs parsing and validation */ | |
361 | cnt1 = rte_kvargs_count(kvlist, PDUMP_NUM_MBUFS_ARG); | |
362 | if (cnt1 == 1) { | |
363 | v.min = 1025; | |
364 | v.max = UINT16_MAX; | |
365 | ret = rte_kvargs_process(kvlist, PDUMP_NUM_MBUFS_ARG, | |
366 | &parse_uint_value, &v); | |
367 | if (ret < 0) | |
368 | goto free_kvlist; | |
369 | pt->total_num_mbufs = (uint16_t) v.val; | |
370 | } else | |
371 | pt->total_num_mbufs = MBUFS_PER_POOL; | |
372 | ||
373 | num_tuples++; | |
374 | ||
375 | free_kvlist: | |
376 | rte_kvargs_free(kvlist); | |
377 | return ret; | |
378 | } | |
379 | ||
380 | /* Parse the argument given in the command line of the application */ | |
381 | static int | |
382 | launch_args_parse(int argc, char **argv, char *prgname) | |
383 | { | |
384 | int opt, ret; | |
385 | int option_index; | |
386 | static struct option long_option[] = { | |
9f95a23c TL |
387 | {CMD_LINE_OPT_PDUMP, 1, 0, CMD_LINE_OPT_PDUMP_NUM}, |
388 | {CMD_LINE_OPT_MULTI, 0, 0, CMD_LINE_OPT_MULTI_NUM}, | |
7c673cae FG |
389 | {NULL, 0, 0, 0} |
390 | }; | |
391 | ||
392 | if (argc == 1) | |
393 | pdump_usage(prgname); | |
394 | ||
395 | /* Parse command line */ | |
396 | while ((opt = getopt_long(argc, argv, " ", | |
397 | long_option, &option_index)) != EOF) { | |
398 | switch (opt) { | |
9f95a23c TL |
399 | case CMD_LINE_OPT_PDUMP_NUM: |
400 | ret = parse_pdump(optarg); | |
401 | if (ret) { | |
402 | pdump_usage(prgname); | |
403 | return -1; | |
7c673cae | 404 | } |
7c673cae | 405 | break; |
9f95a23c TL |
406 | case CMD_LINE_OPT_MULTI_NUM: |
407 | multiple_core_capture = 1; | |
408 | break; | |
7c673cae FG |
409 | default: |
410 | pdump_usage(prgname); | |
411 | return -1; | |
412 | } | |
413 | } | |
414 | ||
415 | return 0; | |
416 | } | |
417 | ||
f67539c2 TL |
418 | static void |
419 | monitor_primary(void *arg __rte_unused) | |
420 | { | |
421 | if (quit_signal) | |
422 | return; | |
423 | ||
424 | if (rte_eal_primary_proc_alive(NULL)) { | |
425 | rte_eal_alarm_set(MONITOR_INTERVAL, monitor_primary, NULL); | |
426 | return; | |
427 | } | |
428 | ||
429 | printf("Primary process is no longer active, exiting...\n"); | |
430 | quit_signal = 1; | |
431 | } | |
432 | ||
7c673cae FG |
433 | static void |
434 | print_pdump_stats(void) | |
435 | { | |
436 | int i; | |
437 | struct pdump_tuples *pt; | |
438 | ||
439 | for (i = 0; i < num_tuples; i++) { | |
440 | printf("##### PDUMP DEBUG STATS #####\n"); | |
441 | pt = &pdump_t[i]; | |
442 | printf(" -packets dequeued: %"PRIu64"\n", | |
443 | pt->stats.dequeue_pkts); | |
444 | printf(" -packets transmitted to vdev: %"PRIu64"\n", | |
445 | pt->stats.tx_pkts); | |
446 | printf(" -packets freed: %"PRIu64"\n", | |
447 | pt->stats.freed_pkts); | |
448 | } | |
449 | } | |
450 | ||
451 | static inline void | |
452 | disable_pdump(struct pdump_tuples *pt) | |
453 | { | |
454 | if (pt->dump_by_type == DEVICE_ID) | |
455 | rte_pdump_disable_by_deviceid(pt->device_id, pt->queue, | |
456 | pt->dir); | |
457 | else if (pt->dump_by_type == PORT_ID) | |
458 | rte_pdump_disable(pt->port, pt->queue, pt->dir); | |
459 | } | |
460 | ||
461 | static inline void | |
9f95a23c | 462 | pdump_rxtx(struct rte_ring *ring, uint16_t vdev_id, struct pdump_stats *stats) |
7c673cae FG |
463 | { |
464 | /* write input packets of port to vdev for pdump */ | |
465 | struct rte_mbuf *rxtx_bufs[BURST_SIZE]; | |
466 | ||
467 | /* first dequeue packets from ring of primary process */ | |
468 | const uint16_t nb_in_deq = rte_ring_dequeue_burst(ring, | |
11fdf7f2 | 469 | (void *)rxtx_bufs, BURST_SIZE, NULL); |
7c673cae FG |
470 | stats->dequeue_pkts += nb_in_deq; |
471 | ||
472 | if (nb_in_deq) { | |
473 | /* then sent on vdev */ | |
474 | uint16_t nb_in_txd = rte_eth_tx_burst( | |
475 | vdev_id, | |
476 | 0, rxtx_bufs, nb_in_deq); | |
477 | stats->tx_pkts += nb_in_txd; | |
478 | ||
479 | if (unlikely(nb_in_txd < nb_in_deq)) { | |
480 | do { | |
481 | rte_pktmbuf_free(rxtx_bufs[nb_in_txd]); | |
482 | stats->freed_pkts++; | |
483 | } while (++nb_in_txd < nb_in_deq); | |
484 | } | |
485 | } | |
486 | } | |
487 | ||
488 | static void | |
9f95a23c | 489 | free_ring_data(struct rte_ring *ring, uint16_t vdev_id, |
7c673cae FG |
490 | struct pdump_stats *stats) |
491 | { | |
492 | while (rte_ring_count(ring)) | |
493 | pdump_rxtx(ring, vdev_id, stats); | |
494 | } | |
495 | ||
496 | static void | |
497 | cleanup_rings(void) | |
498 | { | |
499 | int i; | |
500 | struct pdump_tuples *pt; | |
501 | ||
502 | for (i = 0; i < num_tuples; i++) { | |
503 | pt = &pdump_t[i]; | |
504 | ||
505 | if (pt->device_id) | |
506 | free(pt->device_id); | |
507 | ||
508 | /* free the rings */ | |
509 | if (pt->rx_ring) | |
510 | rte_ring_free(pt->rx_ring); | |
511 | if (pt->tx_ring) | |
512 | rte_ring_free(pt->tx_ring); | |
513 | } | |
514 | } | |
515 | ||
516 | static void | |
517 | cleanup_pdump_resources(void) | |
518 | { | |
519 | int i; | |
520 | struct pdump_tuples *pt; | |
9f95a23c | 521 | char name[RTE_ETH_NAME_MAX_LEN]; |
7c673cae FG |
522 | |
523 | /* disable pdump and free the pdump_tuple resources */ | |
524 | for (i = 0; i < num_tuples; i++) { | |
525 | pt = &pdump_t[i]; | |
526 | ||
527 | /* remove callbacks */ | |
528 | disable_pdump(pt); | |
529 | ||
530 | /* | |
531 | * transmit rest of the enqueued packets of the rings on to | |
532 | * the vdev, in order to release mbufs to the mepool. | |
533 | **/ | |
534 | if (pt->dir & RTE_PDUMP_FLAG_RX) | |
535 | free_ring_data(pt->rx_ring, pt->rx_vdev_id, &pt->stats); | |
536 | if (pt->dir & RTE_PDUMP_FLAG_TX) | |
537 | free_ring_data(pt->tx_ring, pt->tx_vdev_id, &pt->stats); | |
9f95a23c TL |
538 | |
539 | /* Remove the vdev(s) created */ | |
540 | if (pt->dir & RTE_PDUMP_FLAG_RX) { | |
541 | rte_eth_dev_get_name_by_port(pt->rx_vdev_id, name); | |
542 | rte_eal_hotplug_remove("vdev", name); | |
543 | } | |
544 | ||
545 | if (pt->single_pdump_dev) | |
546 | continue; | |
547 | ||
548 | if (pt->dir & RTE_PDUMP_FLAG_TX) { | |
549 | rte_eth_dev_get_name_by_port(pt->tx_vdev_id, name); | |
550 | rte_eal_hotplug_remove("vdev", name); | |
551 | } | |
552 | ||
7c673cae FG |
553 | } |
554 | cleanup_rings(); | |
555 | } | |
556 | ||
f67539c2 TL |
557 | static void |
558 | disable_primary_monitor(void) | |
559 | { | |
560 | int ret; | |
561 | ||
562 | /* | |
563 | * Cancel monitoring of primary process. | |
564 | * There will be no error if no alarm is set | |
565 | * (in case primary process kill was detected earlier). | |
566 | */ | |
567 | ret = rte_eal_alarm_cancel(monitor_primary, NULL); | |
568 | if (ret < 0) | |
569 | printf("Fail to disable monitor:%d\n", ret); | |
570 | } | |
571 | ||
7c673cae FG |
572 | static void |
573 | signal_handler(int sig_num) | |
574 | { | |
575 | if (sig_num == SIGINT) { | |
576 | printf("\n\nSignal %d received, preparing to exit...\n", | |
577 | sig_num); | |
578 | quit_signal = 1; | |
579 | } | |
580 | } | |
581 | ||
582 | static inline int | |
11fdf7f2 | 583 | configure_vdev(uint16_t port_id) |
7c673cae | 584 | { |
f67539c2 | 585 | struct rte_ether_addr addr; |
7c673cae | 586 | const uint16_t rxRings = 0, txRings = 1; |
7c673cae FG |
587 | int ret; |
588 | uint16_t q; | |
589 | ||
11fdf7f2 | 590 | if (!rte_eth_dev_is_valid_port(port_id)) |
7c673cae FG |
591 | return -1; |
592 | ||
593 | ret = rte_eth_dev_configure(port_id, rxRings, txRings, | |
594 | &port_conf_default); | |
595 | if (ret != 0) | |
596 | rte_exit(EXIT_FAILURE, "dev config failed\n"); | |
597 | ||
f67539c2 | 598 | for (q = 0; q < txRings; q++) { |
7c673cae FG |
599 | ret = rte_eth_tx_queue_setup(port_id, q, TX_DESC_PER_QUEUE, |
600 | rte_eth_dev_socket_id(port_id), NULL); | |
601 | if (ret < 0) | |
602 | rte_exit(EXIT_FAILURE, "queue setup failed\n"); | |
603 | } | |
604 | ||
605 | ret = rte_eth_dev_start(port_id); | |
606 | if (ret < 0) | |
607 | rte_exit(EXIT_FAILURE, "dev start failed\n"); | |
608 | ||
f67539c2 TL |
609 | ret = rte_eth_macaddr_get(port_id, &addr); |
610 | if (ret != 0) | |
611 | rte_exit(EXIT_FAILURE, "macaddr get failed\n"); | |
612 | ||
7c673cae FG |
613 | printf("Port %u MAC: %02"PRIx8" %02"PRIx8" %02"PRIx8 |
614 | " %02"PRIx8" %02"PRIx8" %02"PRIx8"\n", | |
11fdf7f2 | 615 | port_id, |
7c673cae FG |
616 | addr.addr_bytes[0], addr.addr_bytes[1], |
617 | addr.addr_bytes[2], addr.addr_bytes[3], | |
618 | addr.addr_bytes[4], addr.addr_bytes[5]); | |
619 | ||
f67539c2 TL |
620 | ret = rte_eth_promiscuous_enable(port_id); |
621 | if (ret != 0) { | |
622 | rte_exit(EXIT_FAILURE, | |
623 | "promiscuous mode enable failed: %s\n", | |
624 | rte_strerror(-ret)); | |
625 | return ret; | |
626 | } | |
7c673cae FG |
627 | |
628 | return 0; | |
629 | } | |
630 | ||
631 | static void | |
632 | create_mp_ring_vdev(void) | |
633 | { | |
634 | int i; | |
11fdf7f2 | 635 | uint16_t portid; |
7c673cae FG |
636 | struct pdump_tuples *pt = NULL; |
637 | struct rte_mempool *mbuf_pool = NULL; | |
11fdf7f2 | 638 | char vdev_name[SIZE]; |
7c673cae FG |
639 | char vdev_args[SIZE]; |
640 | char ring_name[SIZE]; | |
641 | char mempool_name[SIZE]; | |
642 | ||
643 | for (i = 0; i < num_tuples; i++) { | |
644 | pt = &pdump_t[i]; | |
645 | snprintf(mempool_name, SIZE, MP_NAME, i); | |
646 | mbuf_pool = rte_mempool_lookup(mempool_name); | |
647 | if (mbuf_pool == NULL) { | |
648 | /* create mempool */ | |
f67539c2 | 649 | mbuf_pool = rte_pktmbuf_pool_create_by_ops(mempool_name, |
7c673cae FG |
650 | pt->total_num_mbufs, |
651 | MBUF_POOL_CACHE_SIZE, 0, | |
652 | pt->mbuf_data_size, | |
f67539c2 | 653 | rte_socket_id(), "ring_mp_mc"); |
7c673cae FG |
654 | if (mbuf_pool == NULL) { |
655 | cleanup_rings(); | |
656 | rte_exit(EXIT_FAILURE, | |
657 | "Mempool creation failed: %s\n", | |
658 | rte_strerror(rte_errno)); | |
659 | } | |
660 | } | |
661 | pt->mp = mbuf_pool; | |
662 | ||
663 | if (pt->dir == RTE_PDUMP_FLAG_RXTX) { | |
664 | /* if captured packets has to send to the same vdev */ | |
665 | /* create rx_ring */ | |
666 | snprintf(ring_name, SIZE, RX_RING, i); | |
667 | pt->rx_ring = rte_ring_create(ring_name, pt->ring_size, | |
668 | rte_socket_id(), 0); | |
669 | if (pt->rx_ring == NULL) { | |
670 | cleanup_rings(); | |
671 | rte_exit(EXIT_FAILURE, "%s:%s:%d\n", | |
672 | rte_strerror(rte_errno), | |
673 | __func__, __LINE__); | |
674 | } | |
675 | ||
676 | /* create tx_ring */ | |
677 | snprintf(ring_name, SIZE, TX_RING, i); | |
678 | pt->tx_ring = rte_ring_create(ring_name, pt->ring_size, | |
679 | rte_socket_id(), 0); | |
680 | if (pt->tx_ring == NULL) { | |
681 | cleanup_rings(); | |
682 | rte_exit(EXIT_FAILURE, "%s:%s:%d\n", | |
683 | rte_strerror(rte_errno), | |
684 | __func__, __LINE__); | |
685 | } | |
686 | ||
687 | /* create vdevs */ | |
11fdf7f2 TL |
688 | snprintf(vdev_name, sizeof(vdev_name), |
689 | VDEV_NAME_FMT, RX_STR, i); | |
7c673cae | 690 | (pt->rx_vdev_stream_type == IFACE) ? |
11fdf7f2 TL |
691 | snprintf(vdev_args, sizeof(vdev_args), |
692 | VDEV_IFACE_ARGS_FMT, pt->rx_dev) : | |
693 | snprintf(vdev_args, sizeof(vdev_args), | |
694 | VDEV_PCAP_ARGS_FMT, pt->rx_dev); | |
695 | if (rte_eal_hotplug_add("vdev", vdev_name, | |
696 | vdev_args) < 0) { | |
7c673cae FG |
697 | cleanup_rings(); |
698 | rte_exit(EXIT_FAILURE, | |
699 | "vdev creation failed:%s:%d\n", | |
700 | __func__, __LINE__); | |
701 | } | |
11fdf7f2 TL |
702 | if (rte_eth_dev_get_port_by_name(vdev_name, |
703 | &portid) != 0) { | |
704 | rte_eal_hotplug_remove("vdev", vdev_name); | |
705 | cleanup_rings(); | |
706 | rte_exit(EXIT_FAILURE, | |
707 | "cannot find added vdev %s:%s:%d\n", | |
708 | vdev_name, __func__, __LINE__); | |
709 | } | |
7c673cae FG |
710 | pt->rx_vdev_id = portid; |
711 | ||
712 | /* configure vdev */ | |
713 | configure_vdev(pt->rx_vdev_id); | |
714 | ||
715 | if (pt->single_pdump_dev) | |
716 | pt->tx_vdev_id = portid; | |
717 | else { | |
11fdf7f2 TL |
718 | snprintf(vdev_name, sizeof(vdev_name), |
719 | VDEV_NAME_FMT, TX_STR, i); | |
720 | (pt->rx_vdev_stream_type == IFACE) ? | |
721 | snprintf(vdev_args, sizeof(vdev_args), | |
722 | VDEV_IFACE_ARGS_FMT, pt->tx_dev) : | |
723 | snprintf(vdev_args, sizeof(vdev_args), | |
724 | VDEV_PCAP_ARGS_FMT, pt->tx_dev); | |
725 | if (rte_eal_hotplug_add("vdev", vdev_name, | |
726 | vdev_args) < 0) { | |
7c673cae FG |
727 | cleanup_rings(); |
728 | rte_exit(EXIT_FAILURE, | |
729 | "vdev creation failed:" | |
730 | "%s:%d\n", __func__, __LINE__); | |
731 | } | |
11fdf7f2 TL |
732 | if (rte_eth_dev_get_port_by_name(vdev_name, |
733 | &portid) != 0) { | |
734 | rte_eal_hotplug_remove("vdev", | |
735 | vdev_name); | |
736 | cleanup_rings(); | |
737 | rte_exit(EXIT_FAILURE, | |
738 | "cannot find added vdev %s:%s:%d\n", | |
739 | vdev_name, __func__, __LINE__); | |
740 | } | |
7c673cae FG |
741 | pt->tx_vdev_id = portid; |
742 | ||
743 | /* configure vdev */ | |
744 | configure_vdev(pt->tx_vdev_id); | |
745 | } | |
746 | } else if (pt->dir == RTE_PDUMP_FLAG_RX) { | |
747 | ||
748 | /* create rx_ring */ | |
749 | snprintf(ring_name, SIZE, RX_RING, i); | |
750 | pt->rx_ring = rte_ring_create(ring_name, pt->ring_size, | |
751 | rte_socket_id(), 0); | |
752 | if (pt->rx_ring == NULL) { | |
753 | cleanup_rings(); | |
754 | rte_exit(EXIT_FAILURE, "%s\n", | |
755 | rte_strerror(rte_errno)); | |
756 | } | |
757 | ||
11fdf7f2 TL |
758 | snprintf(vdev_name, sizeof(vdev_name), |
759 | VDEV_NAME_FMT, RX_STR, i); | |
7c673cae | 760 | (pt->rx_vdev_stream_type == IFACE) ? |
11fdf7f2 TL |
761 | snprintf(vdev_args, sizeof(vdev_args), |
762 | VDEV_IFACE_ARGS_FMT, pt->rx_dev) : | |
763 | snprintf(vdev_args, sizeof(vdev_args), | |
764 | VDEV_PCAP_ARGS_FMT, pt->rx_dev); | |
765 | if (rte_eal_hotplug_add("vdev", vdev_name, | |
766 | vdev_args) < 0) { | |
7c673cae FG |
767 | cleanup_rings(); |
768 | rte_exit(EXIT_FAILURE, | |
769 | "vdev creation failed:%s:%d\n", | |
770 | __func__, __LINE__); | |
771 | } | |
11fdf7f2 TL |
772 | if (rte_eth_dev_get_port_by_name(vdev_name, |
773 | &portid) != 0) { | |
774 | rte_eal_hotplug_remove("vdev", vdev_name); | |
775 | cleanup_rings(); | |
776 | rte_exit(EXIT_FAILURE, | |
777 | "cannot find added vdev %s:%s:%d\n", | |
778 | vdev_name, __func__, __LINE__); | |
779 | } | |
7c673cae FG |
780 | pt->rx_vdev_id = portid; |
781 | /* configure vdev */ | |
782 | configure_vdev(pt->rx_vdev_id); | |
783 | } else if (pt->dir == RTE_PDUMP_FLAG_TX) { | |
784 | ||
785 | /* create tx_ring */ | |
786 | snprintf(ring_name, SIZE, TX_RING, i); | |
787 | pt->tx_ring = rte_ring_create(ring_name, pt->ring_size, | |
788 | rte_socket_id(), 0); | |
789 | if (pt->tx_ring == NULL) { | |
790 | cleanup_rings(); | |
791 | rte_exit(EXIT_FAILURE, "%s\n", | |
792 | rte_strerror(rte_errno)); | |
793 | } | |
794 | ||
11fdf7f2 TL |
795 | snprintf(vdev_name, sizeof(vdev_name), |
796 | VDEV_NAME_FMT, TX_STR, i); | |
7c673cae | 797 | (pt->tx_vdev_stream_type == IFACE) ? |
11fdf7f2 TL |
798 | snprintf(vdev_args, sizeof(vdev_args), |
799 | VDEV_IFACE_ARGS_FMT, pt->tx_dev) : | |
800 | snprintf(vdev_args, sizeof(vdev_args), | |
801 | VDEV_PCAP_ARGS_FMT, pt->tx_dev); | |
802 | if (rte_eal_hotplug_add("vdev", vdev_name, | |
803 | vdev_args) < 0) { | |
7c673cae FG |
804 | cleanup_rings(); |
805 | rte_exit(EXIT_FAILURE, | |
806 | "vdev creation failed\n"); | |
807 | } | |
11fdf7f2 TL |
808 | if (rte_eth_dev_get_port_by_name(vdev_name, |
809 | &portid) != 0) { | |
810 | rte_eal_hotplug_remove("vdev", vdev_name); | |
811 | cleanup_rings(); | |
812 | rte_exit(EXIT_FAILURE, | |
813 | "cannot find added vdev %s:%s:%d\n", | |
814 | vdev_name, __func__, __LINE__); | |
815 | } | |
7c673cae FG |
816 | pt->tx_vdev_id = portid; |
817 | ||
818 | /* configure vdev */ | |
819 | configure_vdev(pt->tx_vdev_id); | |
820 | } | |
821 | } | |
822 | } | |
823 | ||
824 | static void | |
825 | enable_pdump(void) | |
826 | { | |
827 | int i; | |
828 | struct pdump_tuples *pt; | |
829 | int ret = 0, ret1 = 0; | |
830 | ||
7c673cae FG |
831 | for (i = 0; i < num_tuples; i++) { |
832 | pt = &pdump_t[i]; | |
833 | if (pt->dir == RTE_PDUMP_FLAG_RXTX) { | |
834 | if (pt->dump_by_type == DEVICE_ID) { | |
835 | ret = rte_pdump_enable_by_deviceid( | |
836 | pt->device_id, | |
837 | pt->queue, | |
838 | RTE_PDUMP_FLAG_RX, | |
839 | pt->rx_ring, | |
840 | pt->mp, NULL); | |
841 | ret1 = rte_pdump_enable_by_deviceid( | |
842 | pt->device_id, | |
843 | pt->queue, | |
844 | RTE_PDUMP_FLAG_TX, | |
845 | pt->tx_ring, | |
846 | pt->mp, NULL); | |
847 | } else if (pt->dump_by_type == PORT_ID) { | |
848 | ret = rte_pdump_enable(pt->port, pt->queue, | |
849 | RTE_PDUMP_FLAG_RX, | |
850 | pt->rx_ring, pt->mp, NULL); | |
851 | ret1 = rte_pdump_enable(pt->port, pt->queue, | |
852 | RTE_PDUMP_FLAG_TX, | |
853 | pt->tx_ring, pt->mp, NULL); | |
854 | } | |
855 | } else if (pt->dir == RTE_PDUMP_FLAG_RX) { | |
856 | if (pt->dump_by_type == DEVICE_ID) | |
857 | ret = rte_pdump_enable_by_deviceid( | |
858 | pt->device_id, | |
859 | pt->queue, | |
860 | pt->dir, pt->rx_ring, | |
861 | pt->mp, NULL); | |
862 | else if (pt->dump_by_type == PORT_ID) | |
863 | ret = rte_pdump_enable(pt->port, pt->queue, | |
864 | pt->dir, | |
865 | pt->rx_ring, pt->mp, NULL); | |
866 | } else if (pt->dir == RTE_PDUMP_FLAG_TX) { | |
867 | if (pt->dump_by_type == DEVICE_ID) | |
868 | ret = rte_pdump_enable_by_deviceid( | |
869 | pt->device_id, | |
870 | pt->queue, | |
871 | pt->dir, | |
872 | pt->tx_ring, pt->mp, NULL); | |
873 | else if (pt->dump_by_type == PORT_ID) | |
874 | ret = rte_pdump_enable(pt->port, pt->queue, | |
875 | pt->dir, | |
876 | pt->tx_ring, pt->mp, NULL); | |
877 | } | |
878 | if (ret < 0 || ret1 < 0) { | |
879 | cleanup_pdump_resources(); | |
880 | rte_exit(EXIT_FAILURE, "%s\n", rte_strerror(rte_errno)); | |
881 | } | |
882 | } | |
883 | } | |
884 | ||
9f95a23c TL |
885 | static inline void |
886 | pdump_packets(struct pdump_tuples *pt) | |
887 | { | |
888 | if (pt->dir & RTE_PDUMP_FLAG_RX) | |
889 | pdump_rxtx(pt->rx_ring, pt->rx_vdev_id, &pt->stats); | |
890 | if (pt->dir & RTE_PDUMP_FLAG_TX) | |
891 | pdump_rxtx(pt->tx_ring, pt->tx_vdev_id, &pt->stats); | |
892 | } | |
893 | ||
894 | static int | |
895 | dump_packets_core(void *arg) | |
896 | { | |
897 | struct pdump_tuples *pt = (struct pdump_tuples *) arg; | |
898 | ||
899 | printf(" core (%u); port %u device (%s) queue %u\n", | |
900 | rte_lcore_id(), pt->port, pt->device_id, pt->queue); | |
901 | fflush(stdout); | |
902 | ||
903 | while (!quit_signal) | |
904 | pdump_packets(pt); | |
905 | ||
906 | return 0; | |
907 | } | |
908 | ||
7c673cae FG |
909 | static inline void |
910 | dump_packets(void) | |
911 | { | |
912 | int i; | |
9f95a23c TL |
913 | uint32_t lcore_id = 0; |
914 | ||
915 | if (!multiple_core_capture) { | |
916 | printf(" core (%u), capture for (%d) tuples\n", | |
917 | rte_lcore_id(), num_tuples); | |
7c673cae | 918 | |
9f95a23c TL |
919 | for (i = 0; i < num_tuples; i++) |
920 | printf(" - port %u device (%s) queue %u\n", | |
921 | pdump_t[i].port, | |
922 | pdump_t[i].device_id, | |
923 | pdump_t[i].queue); | |
924 | ||
925 | while (!quit_signal) { | |
926 | for (i = 0; i < num_tuples; i++) | |
927 | pdump_packets(&pdump_t[i]); | |
7c673cae | 928 | } |
9f95a23c TL |
929 | |
930 | return; | |
7c673cae | 931 | } |
9f95a23c TL |
932 | |
933 | /* check if there enough core */ | |
934 | if ((uint32_t)num_tuples >= rte_lcore_count()) { | |
935 | printf("Insufficient cores to run parallel!\n"); | |
936 | return; | |
937 | } | |
938 | ||
939 | lcore_id = rte_get_next_lcore(lcore_id, 1, 0); | |
940 | ||
941 | for (i = 0; i < num_tuples; i++) { | |
942 | rte_eal_remote_launch(dump_packets_core, | |
943 | &pdump_t[i], lcore_id); | |
944 | lcore_id = rte_get_next_lcore(lcore_id, 1, 0); | |
945 | ||
946 | if (rte_eal_wait_lcore(lcore_id) < 0) | |
947 | rte_exit(EXIT_FAILURE, "failed to wait\n"); | |
948 | } | |
949 | ||
950 | /* master core */ | |
951 | while (!quit_signal) | |
952 | ; | |
7c673cae FG |
953 | } |
954 | ||
f67539c2 TL |
955 | static void |
956 | enable_primary_monitor(void) | |
957 | { | |
958 | int ret; | |
959 | ||
960 | /* Once primary exits, so will pdump. */ | |
961 | ret = rte_eal_alarm_set(MONITOR_INTERVAL, monitor_primary, NULL); | |
962 | if (ret < 0) | |
963 | printf("Fail to enable monitor:%d\n", ret); | |
964 | } | |
965 | ||
7c673cae FG |
966 | int |
967 | main(int argc, char **argv) | |
968 | { | |
969 | int diag; | |
970 | int ret; | |
971 | int i; | |
972 | ||
7c673cae FG |
973 | char n_flag[] = "-n4"; |
974 | char mp_flag[] = "--proc-type=secondary"; | |
9f95a23c | 975 | char *argp[argc + 2]; |
7c673cae FG |
976 | |
977 | /* catch ctrl-c so we can print on exit */ | |
978 | signal(SIGINT, signal_handler); | |
979 | ||
980 | argp[0] = argv[0]; | |
9f95a23c TL |
981 | argp[1] = n_flag; |
982 | argp[2] = mp_flag; | |
7c673cae FG |
983 | |
984 | for (i = 1; i < argc; i++) | |
9f95a23c | 985 | argp[i + 2] = argv[i]; |
7c673cae | 986 | |
9f95a23c | 987 | argc += 2; |
7c673cae FG |
988 | |
989 | diag = rte_eal_init(argc, argp); | |
990 | if (diag < 0) | |
991 | rte_panic("Cannot init EAL\n"); | |
992 | ||
11fdf7f2 TL |
993 | if (rte_eth_dev_count_avail() == 0) |
994 | rte_exit(EXIT_FAILURE, "No Ethernet ports - bye\n"); | |
995 | ||
7c673cae | 996 | argc -= diag; |
9f95a23c | 997 | argv += (diag - 2); |
7c673cae FG |
998 | |
999 | /* parse app arguments */ | |
1000 | if (argc > 1) { | |
1001 | ret = launch_args_parse(argc, argv, argp[0]); | |
1002 | if (ret < 0) | |
1003 | rte_exit(EXIT_FAILURE, "Invalid argument\n"); | |
1004 | } | |
1005 | ||
1006 | /* create mempool, ring and vdevs info */ | |
1007 | create_mp_ring_vdev(); | |
1008 | enable_pdump(); | |
f67539c2 | 1009 | enable_primary_monitor(); |
7c673cae FG |
1010 | dump_packets(); |
1011 | ||
f67539c2 | 1012 | disable_primary_monitor(); |
7c673cae FG |
1013 | cleanup_pdump_resources(); |
1014 | /* dump debug stats */ | |
1015 | print_pdump_stats(); | |
1016 | ||
11fdf7f2 TL |
1017 | ret = rte_eal_cleanup(); |
1018 | if (ret) | |
1019 | printf("Error from rte_eal_cleanup(), %d\n", ret); | |
1020 | ||
7c673cae FG |
1021 | return 0; |
1022 | } |