1 /* SPDX-License-Identifier: BSD-3-Clause
2 * Copyright(c) 2016 Intel Corporation
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>
24 #include <rte_kvargs.h>
25 #include <rte_mempool.h>
27 #include <rte_string_fns.h>
28 #include <rte_pdump.h>
30 #define CMD_LINE_OPT_PDUMP "pdump"
31 #define PDUMP_PORT_ARG "port"
32 #define PDUMP_PCI_ARG "device_id"
33 #define PDUMP_QUEUE_ARG "queue"
34 #define PDUMP_DIR_ARG "dir"
35 #define PDUMP_RX_DEV_ARG "rx-dev"
36 #define PDUMP_TX_DEV_ARG "tx-dev"
37 #define PDUMP_RING_SIZE_ARG "ring-size"
38 #define PDUMP_MSIZE_ARG "mbuf-size"
39 #define PDUMP_NUM_MBUFS_ARG "total-num-mbufs"
41 #define VDEV_PCAP "net_pcap_%s_%d,tx_pcap=%s"
42 #define VDEV_IFACE "net_pcap_%s_%d,tx_iface=%s"
43 #define TX_STREAM_SIZE 64
45 #define MP_NAME "pdump_pool_%d"
47 #define RX_RING "rx_ring_%d"
48 #define TX_RING "tx_ring_%d"
53 /* Maximum long option length for option parsing. */
54 #define APP_ARG_TCPDUMP_MAX_TUPLES 54
55 #define MBUF_POOL_CACHE_SIZE 250
56 #define TX_DESC_PER_QUEUE 512
57 #define RX_DESC_PER_QUEUE 128
58 #define MBUFS_PER_POOL 65535
59 #define MAX_LONG_OPT_SZ 64
60 #define RING_SIZE 16384
65 /* true if x is a power of 2 */
66 #define POWEROF2(x) ((((x)-1) & (x)) == 0)
83 const char *valid_pdump_arguments[] = {
97 uint64_t dequeue_pkts;
102 struct pdump_tuples {
107 char rx_dev[TX_STREAM_SIZE];
108 char tx_dev[TX_STREAM_SIZE];
110 uint16_t mbuf_data_size;
111 uint32_t total_num_mbufs;
113 /* params for library API call */
115 struct rte_mempool *mp;
116 struct rte_ring *rx_ring;
117 struct rte_ring *tx_ring;
119 /* params for packet dumping */
120 enum pdump_by dump_by_type;
123 enum pcap_stream rx_vdev_stream_type;
124 enum pcap_stream tx_vdev_stream_type;
125 bool single_pdump_dev;
128 struct pdump_stats stats;
129 } __rte_cache_aligned;
130 static struct pdump_tuples pdump_t[APP_ARG_TCPDUMP_MAX_TUPLES];
139 static struct rte_eth_conf port_conf_default;
140 volatile uint8_t quit_signal;
142 /**< display usage */
144 pdump_usage(const char *prgname)
146 printf("usage: %s [EAL options] -- --pdump "
147 "'(port=<port id> | device_id=<pci id or vdev name>),"
148 "(queue=<queue_id>),"
149 "(rx-dev=<iface or pcap file> |"
150 " tx-dev=<iface or pcap file>,"
151 "[ring-size=<ring size>default:16384],"
152 "[mbuf-size=<mbuf data size>default:2176],"
153 "[total-num-mbufs=<number of mbufs>default:65535]'\n",
158 parse_device_id(const char *key __rte_unused, const char *value,
161 struct pdump_tuples *pt = extra_args;
163 pt->device_id = strdup(value);
164 pt->dump_by_type = DEVICE_ID;
170 parse_queue(const char *key __rte_unused, const char *value, void *extra_args)
173 struct pdump_tuples *pt = extra_args;
175 if (!strcmp(value, "*"))
176 pt->queue = RTE_PDUMP_ALL_QUEUES;
178 n = strtoul(value, NULL, 10);
179 pt->queue = (uint16_t) n;
185 parse_rxtxdev(const char *key, const char *value, void *extra_args)
188 struct pdump_tuples *pt = extra_args;
190 if (!strcmp(key, PDUMP_RX_DEV_ARG)) {
191 snprintf(pt->rx_dev, sizeof(pt->rx_dev), "%s", value);
192 /* identify the tx stream type for pcap vdev */
193 if (if_nametoindex(pt->rx_dev))
194 pt->rx_vdev_stream_type = IFACE;
195 } else if (!strcmp(key, PDUMP_TX_DEV_ARG)) {
196 snprintf(pt->tx_dev, sizeof(pt->tx_dev), "%s", value);
197 /* identify the tx stream type for pcap vdev */
198 if (if_nametoindex(pt->tx_dev))
199 pt->tx_vdev_stream_type = IFACE;
206 parse_uint_value(const char *key, const char *value, void *extra_args)
215 t = strtoul(value, &end, 10);
217 if (errno != 0 || end[0] != 0 || t < v->min || t > v->max) {
218 printf("invalid value:\"%s\" for key:\"%s\", "
219 "value must be >= %"PRIu64" and <= %"PRIu64"\n",
220 value, key, v->min, v->max);
223 if (!strcmp(key, PDUMP_RING_SIZE_ARG) && !POWEROF2(t)) {
224 printf("invalid value:\"%s\" for key:\"%s\", "
225 "value must be power of 2\n", value, key);
237 parse_pdump(const char *optarg)
239 struct rte_kvargs *kvlist;
240 int ret = 0, cnt1, cnt2;
241 struct pdump_tuples *pt;
242 struct parse_val v = {0};
244 pt = &pdump_t[num_tuples];
246 /* initial check for invalid arguments */
247 kvlist = rte_kvargs_parse(optarg, valid_pdump_arguments);
248 if (kvlist == NULL) {
249 printf("--pdump=\"%s\": invalid argument passed\n", optarg);
253 /* port/device_id parsing and validation */
254 cnt1 = rte_kvargs_count(kvlist, PDUMP_PORT_ARG);
255 cnt2 = rte_kvargs_count(kvlist, PDUMP_PCI_ARG);
256 if (!((cnt1 == 1 && cnt2 == 0) || (cnt1 == 0 && cnt2 == 1))) {
257 printf("--pdump=\"%s\": must have either port or "
258 "device_id argument\n", optarg);
261 } else if (cnt1 == 1) {
263 v.max = RTE_MAX_ETHPORTS-1;
264 ret = rte_kvargs_process(kvlist, PDUMP_PORT_ARG,
265 &parse_uint_value, &v);
268 pt->port = (uint8_t) v.val;
269 pt->dump_by_type = PORT_ID;
270 } else if (cnt2 == 1) {
271 ret = rte_kvargs_process(kvlist, PDUMP_PCI_ARG,
272 &parse_device_id, pt);
277 /* queue parsing and validation */
278 cnt1 = rte_kvargs_count(kvlist, PDUMP_QUEUE_ARG);
280 printf("--pdump=\"%s\": must have queue argument\n", optarg);
284 ret = rte_kvargs_process(kvlist, PDUMP_QUEUE_ARG, &parse_queue, pt);
288 /* rx-dev and tx-dev parsing and validation */
289 cnt1 = rte_kvargs_count(kvlist, PDUMP_RX_DEV_ARG);
290 cnt2 = rte_kvargs_count(kvlist, PDUMP_TX_DEV_ARG);
291 if (cnt1 == 0 && cnt2 == 0) {
292 printf("--pdump=\"%s\": must have either rx-dev or "
293 "tx-dev argument\n", optarg);
296 } else if (cnt1 == 1 && cnt2 == 1) {
297 ret = rte_kvargs_process(kvlist, PDUMP_RX_DEV_ARG,
301 ret = rte_kvargs_process(kvlist, PDUMP_TX_DEV_ARG,
305 /* if captured packets has to send to the same vdev */
306 if (!strcmp(pt->rx_dev, pt->tx_dev))
307 pt->single_pdump_dev = true;
308 pt->dir = RTE_PDUMP_FLAG_RXTX;
309 } else if (cnt1 == 1) {
310 ret = rte_kvargs_process(kvlist, PDUMP_RX_DEV_ARG,
314 pt->dir = RTE_PDUMP_FLAG_RX;
315 } else if (cnt2 == 1) {
316 ret = rte_kvargs_process(kvlist, PDUMP_TX_DEV_ARG,
320 pt->dir = RTE_PDUMP_FLAG_TX;
324 /* ring_size parsing and validation */
325 cnt1 = rte_kvargs_count(kvlist, PDUMP_RING_SIZE_ARG);
328 v.max = RTE_RING_SZ_MASK-1;
329 ret = rte_kvargs_process(kvlist, PDUMP_RING_SIZE_ARG,
330 &parse_uint_value, &v);
333 pt->ring_size = (uint32_t) v.val;
335 pt->ring_size = RING_SIZE;
337 /* mbuf_data_size parsing and validation */
338 cnt1 = rte_kvargs_count(kvlist, PDUMP_MSIZE_ARG);
342 ret = rte_kvargs_process(kvlist, PDUMP_MSIZE_ARG,
343 &parse_uint_value, &v);
346 pt->mbuf_data_size = (uint16_t) v.val;
348 pt->mbuf_data_size = RTE_MBUF_DEFAULT_BUF_SIZE;
350 /* total_num_mbufs parsing and validation */
351 cnt1 = rte_kvargs_count(kvlist, PDUMP_NUM_MBUFS_ARG);
355 ret = rte_kvargs_process(kvlist, PDUMP_NUM_MBUFS_ARG,
356 &parse_uint_value, &v);
359 pt->total_num_mbufs = (uint16_t) v.val;
361 pt->total_num_mbufs = MBUFS_PER_POOL;
366 rte_kvargs_free(kvlist);
370 /* Parse the argument given in the command line of the application */
372 launch_args_parse(int argc, char **argv, char *prgname)
376 static struct option long_option[] = {
382 pdump_usage(prgname);
384 /* Parse command line */
385 while ((opt = getopt_long(argc, argv, " ",
386 long_option, &option_index)) != EOF) {
389 if (!strncmp(long_option[option_index].name,
391 sizeof(CMD_LINE_OPT_PDUMP))) {
392 ret = parse_pdump(optarg);
394 pdump_usage(prgname);
400 pdump_usage(prgname);
409 print_pdump_stats(void)
412 struct pdump_tuples *pt;
414 for (i = 0; i < num_tuples; i++) {
415 printf("##### PDUMP DEBUG STATS #####\n");
417 printf(" -packets dequeued: %"PRIu64"\n",
418 pt->stats.dequeue_pkts);
419 printf(" -packets transmitted to vdev: %"PRIu64"\n",
421 printf(" -packets freed: %"PRIu64"\n",
422 pt->stats.freed_pkts);
427 disable_pdump(struct pdump_tuples *pt)
429 if (pt->dump_by_type == DEVICE_ID)
430 rte_pdump_disable_by_deviceid(pt->device_id, pt->queue,
432 else if (pt->dump_by_type == PORT_ID)
433 rte_pdump_disable(pt->port, pt->queue, pt->dir);
437 pdump_rxtx(struct rte_ring *ring, uint8_t vdev_id, struct pdump_stats *stats)
439 /* write input packets of port to vdev for pdump */
440 struct rte_mbuf *rxtx_bufs[BURST_SIZE];
442 /* first dequeue packets from ring of primary process */
443 const uint16_t nb_in_deq = rte_ring_dequeue_burst(ring,
444 (void *)rxtx_bufs, BURST_SIZE, NULL);
445 stats->dequeue_pkts += nb_in_deq;
448 /* then sent on vdev */
449 uint16_t nb_in_txd = rte_eth_tx_burst(
451 0, rxtx_bufs, nb_in_deq);
452 stats->tx_pkts += nb_in_txd;
454 if (unlikely(nb_in_txd < nb_in_deq)) {
456 rte_pktmbuf_free(rxtx_bufs[nb_in_txd]);
458 } while (++nb_in_txd < nb_in_deq);
464 free_ring_data(struct rte_ring *ring, uint8_t vdev_id,
465 struct pdump_stats *stats)
467 while (rte_ring_count(ring))
468 pdump_rxtx(ring, vdev_id, stats);
475 struct pdump_tuples *pt;
477 for (i = 0; i < num_tuples; i++) {
485 rte_ring_free(pt->rx_ring);
487 rte_ring_free(pt->tx_ring);
492 cleanup_pdump_resources(void)
495 struct pdump_tuples *pt;
497 /* disable pdump and free the pdump_tuple resources */
498 for (i = 0; i < num_tuples; i++) {
501 /* remove callbacks */
505 * transmit rest of the enqueued packets of the rings on to
506 * the vdev, in order to release mbufs to the mepool.
508 if (pt->dir & RTE_PDUMP_FLAG_RX)
509 free_ring_data(pt->rx_ring, pt->rx_vdev_id, &pt->stats);
510 if (pt->dir & RTE_PDUMP_FLAG_TX)
511 free_ring_data(pt->tx_ring, pt->tx_vdev_id, &pt->stats);
517 signal_handler(int sig_num)
519 if (sig_num == SIGINT) {
520 printf("\n\nSignal %d received, preparing to exit...\n",
527 configure_vdev(uint16_t port_id)
529 struct ether_addr addr;
530 const uint16_t rxRings = 0, txRings = 1;
534 if (!rte_eth_dev_is_valid_port(port_id))
537 ret = rte_eth_dev_configure(port_id, rxRings, txRings,
540 rte_exit(EXIT_FAILURE, "dev config failed\n");
542 for (q = 0; q < txRings; q++) {
543 ret = rte_eth_tx_queue_setup(port_id, q, TX_DESC_PER_QUEUE,
544 rte_eth_dev_socket_id(port_id), NULL);
546 rte_exit(EXIT_FAILURE, "queue setup failed\n");
549 ret = rte_eth_dev_start(port_id);
551 rte_exit(EXIT_FAILURE, "dev start failed\n");
553 rte_eth_macaddr_get(port_id, &addr);
554 printf("Port %u MAC: %02"PRIx8" %02"PRIx8" %02"PRIx8
555 " %02"PRIx8" %02"PRIx8" %02"PRIx8"\n",
557 addr.addr_bytes[0], addr.addr_bytes[1],
558 addr.addr_bytes[2], addr.addr_bytes[3],
559 addr.addr_bytes[4], addr.addr_bytes[5]);
561 rte_eth_promiscuous_enable(port_id);
567 create_mp_ring_vdev(void)
571 struct pdump_tuples *pt = NULL;
572 struct rte_mempool *mbuf_pool = NULL;
573 char vdev_args[SIZE];
574 char ring_name[SIZE];
575 char mempool_name[SIZE];
577 for (i = 0; i < num_tuples; i++) {
579 snprintf(mempool_name, SIZE, MP_NAME, i);
580 mbuf_pool = rte_mempool_lookup(mempool_name);
581 if (mbuf_pool == NULL) {
583 mbuf_pool = rte_pktmbuf_pool_create(mempool_name,
585 MBUF_POOL_CACHE_SIZE, 0,
588 if (mbuf_pool == NULL) {
590 rte_exit(EXIT_FAILURE,
591 "Mempool creation failed: %s\n",
592 rte_strerror(rte_errno));
597 if (pt->dir == RTE_PDUMP_FLAG_RXTX) {
598 /* if captured packets has to send to the same vdev */
600 snprintf(ring_name, SIZE, RX_RING, i);
601 pt->rx_ring = rte_ring_create(ring_name, pt->ring_size,
603 if (pt->rx_ring == NULL) {
605 rte_exit(EXIT_FAILURE, "%s:%s:%d\n",
606 rte_strerror(rte_errno),
611 snprintf(ring_name, SIZE, TX_RING, i);
612 pt->tx_ring = rte_ring_create(ring_name, pt->ring_size,
614 if (pt->tx_ring == NULL) {
616 rte_exit(EXIT_FAILURE, "%s:%s:%d\n",
617 rte_strerror(rte_errno),
622 (pt->rx_vdev_stream_type == IFACE) ?
623 snprintf(vdev_args, SIZE, VDEV_IFACE, RX_STR, i,
625 snprintf(vdev_args, SIZE, VDEV_PCAP, RX_STR, i,
627 if (rte_eth_dev_attach(vdev_args, &portid) < 0) {
629 rte_exit(EXIT_FAILURE,
630 "vdev creation failed:%s:%d\n",
633 pt->rx_vdev_id = portid;
636 configure_vdev(pt->rx_vdev_id);
638 if (pt->single_pdump_dev)
639 pt->tx_vdev_id = portid;
641 (pt->tx_vdev_stream_type == IFACE) ?
642 snprintf(vdev_args, SIZE, VDEV_IFACE, TX_STR, i,
644 snprintf(vdev_args, SIZE, VDEV_PCAP, TX_STR, i,
646 if (rte_eth_dev_attach(vdev_args,
649 rte_exit(EXIT_FAILURE,
650 "vdev creation failed:"
651 "%s:%d\n", __func__, __LINE__);
653 pt->tx_vdev_id = portid;
656 configure_vdev(pt->tx_vdev_id);
658 } else if (pt->dir == RTE_PDUMP_FLAG_RX) {
661 snprintf(ring_name, SIZE, RX_RING, i);
662 pt->rx_ring = rte_ring_create(ring_name, pt->ring_size,
664 if (pt->rx_ring == NULL) {
666 rte_exit(EXIT_FAILURE, "%s\n",
667 rte_strerror(rte_errno));
670 (pt->rx_vdev_stream_type == IFACE) ?
671 snprintf(vdev_args, SIZE, VDEV_IFACE, RX_STR, i,
673 snprintf(vdev_args, SIZE, VDEV_PCAP, RX_STR, i,
675 if (rte_eth_dev_attach(vdev_args, &portid) < 0) {
677 rte_exit(EXIT_FAILURE,
678 "vdev creation failed:%s:%d\n",
681 pt->rx_vdev_id = portid;
683 configure_vdev(pt->rx_vdev_id);
684 } else if (pt->dir == RTE_PDUMP_FLAG_TX) {
687 snprintf(ring_name, SIZE, TX_RING, i);
688 pt->tx_ring = rte_ring_create(ring_name, pt->ring_size,
690 if (pt->tx_ring == NULL) {
692 rte_exit(EXIT_FAILURE, "%s\n",
693 rte_strerror(rte_errno));
696 (pt->tx_vdev_stream_type == IFACE) ?
697 snprintf(vdev_args, SIZE, VDEV_IFACE, TX_STR, i,
699 snprintf(vdev_args, SIZE, VDEV_PCAP, TX_STR, i,
701 if (rte_eth_dev_attach(vdev_args, &portid) < 0) {
703 rte_exit(EXIT_FAILURE,
704 "vdev creation failed\n");
706 pt->tx_vdev_id = portid;
709 configure_vdev(pt->tx_vdev_id);
718 struct pdump_tuples *pt;
719 int ret = 0, ret1 = 0;
721 for (i = 0; i < num_tuples; i++) {
723 if (pt->dir == RTE_PDUMP_FLAG_RXTX) {
724 if (pt->dump_by_type == DEVICE_ID) {
725 ret = rte_pdump_enable_by_deviceid(
731 ret1 = rte_pdump_enable_by_deviceid(
737 } else if (pt->dump_by_type == PORT_ID) {
738 ret = rte_pdump_enable(pt->port, pt->queue,
740 pt->rx_ring, pt->mp, NULL);
741 ret1 = rte_pdump_enable(pt->port, pt->queue,
743 pt->tx_ring, pt->mp, NULL);
745 } else if (pt->dir == RTE_PDUMP_FLAG_RX) {
746 if (pt->dump_by_type == DEVICE_ID)
747 ret = rte_pdump_enable_by_deviceid(
750 pt->dir, pt->rx_ring,
752 else if (pt->dump_by_type == PORT_ID)
753 ret = rte_pdump_enable(pt->port, pt->queue,
755 pt->rx_ring, pt->mp, NULL);
756 } else if (pt->dir == RTE_PDUMP_FLAG_TX) {
757 if (pt->dump_by_type == DEVICE_ID)
758 ret = rte_pdump_enable_by_deviceid(
762 pt->tx_ring, pt->mp, NULL);
763 else if (pt->dump_by_type == PORT_ID)
764 ret = rte_pdump_enable(pt->port, pt->queue,
766 pt->tx_ring, pt->mp, NULL);
768 if (ret < 0 || ret1 < 0) {
769 cleanup_pdump_resources();
770 rte_exit(EXIT_FAILURE, "%s\n", rte_strerror(rte_errno));
779 struct pdump_tuples *pt;
781 while (!quit_signal) {
782 for (i = 0; i < num_tuples; i++) {
784 if (pt->dir & RTE_PDUMP_FLAG_RX)
785 pdump_rxtx(pt->rx_ring, pt->rx_vdev_id,
787 if (pt->dir & RTE_PDUMP_FLAG_TX)
788 pdump_rxtx(pt->tx_ring, pt->tx_vdev_id,
795 main(int argc, char **argv)
801 char c_flag[] = "-c1";
802 char n_flag[] = "-n4";
803 char mp_flag[] = "--proc-type=secondary";
804 char *argp[argc + 3];
806 /* catch ctrl-c so we can print on exit */
807 signal(SIGINT, signal_handler);
814 for (i = 1; i < argc; i++)
815 argp[i + 3] = argv[i];
819 diag = rte_eal_init(argc, argp);
821 rte_panic("Cannot init EAL\n");
823 if (rte_eth_dev_count_avail() == 0)
824 rte_exit(EXIT_FAILURE, "No Ethernet ports - bye\n");
829 /* parse app arguments */
831 ret = launch_args_parse(argc, argv, argp[0]);
833 rte_exit(EXIT_FAILURE, "Invalid argument\n");
836 /* create mempool, ring and vdevs info */
837 create_mp_ring_vdev();
841 cleanup_pdump_resources();
842 /* dump debug stats */
845 ret = rte_eal_cleanup();
847 printf("Error from rte_eal_cleanup(), %d\n", ret);