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