199a2dd95SBruce Richardson /* SPDX-License-Identifier: BSD-3-Clause 299a2dd95SBruce Richardson * Copyright(c) 2016-2018 Intel Corporation 399a2dd95SBruce Richardson */ 499a2dd95SBruce Richardson 599a2dd95SBruce Richardson #include <rte_memcpy.h> 699a2dd95SBruce Richardson #include <rte_mbuf.h> 799a2dd95SBruce Richardson #include <rte_ethdev.h> 899a2dd95SBruce Richardson #include <rte_lcore.h> 999a2dd95SBruce Richardson #include <rte_log.h> 1010f726efSStephen Hemminger #include <rte_memzone.h> 1199a2dd95SBruce Richardson #include <rte_errno.h> 1299a2dd95SBruce Richardson #include <rte_string_fns.h> 1310f726efSStephen Hemminger #include <rte_pcapng.h> 1499a2dd95SBruce Richardson 1599a2dd95SBruce Richardson #include "rte_pdump.h" 1699a2dd95SBruce Richardson 17eeded204SDavid Marchand RTE_LOG_REGISTER_DEFAULT(pdump_logtype, NOTICE); 1899a2dd95SBruce Richardson 1999a2dd95SBruce Richardson /* Macro for printing using RTE_LOG */ 2099a2dd95SBruce Richardson #define PDUMP_LOG(level, fmt, args...) \ 2199a2dd95SBruce Richardson rte_log(RTE_LOG_ ## level, pdump_logtype, "%s(): " fmt, \ 2299a2dd95SBruce Richardson __func__, ## args) 2399a2dd95SBruce Richardson 2499a2dd95SBruce Richardson /* Used for the multi-process communication */ 2599a2dd95SBruce Richardson #define PDUMP_MP "mp_pdump" 2699a2dd95SBruce Richardson 2799a2dd95SBruce Richardson enum pdump_operation { 2899a2dd95SBruce Richardson DISABLE = 1, 2999a2dd95SBruce Richardson ENABLE = 2 3099a2dd95SBruce Richardson }; 3199a2dd95SBruce Richardson 3210f726efSStephen Hemminger /* Internal version number in request */ 3399a2dd95SBruce Richardson enum pdump_version { 3410f726efSStephen Hemminger V1 = 1, /* no filtering or snap */ 3510f726efSStephen Hemminger V2 = 2, 3699a2dd95SBruce Richardson }; 3799a2dd95SBruce Richardson 3899a2dd95SBruce Richardson struct pdump_request { 3999a2dd95SBruce Richardson uint16_t ver; 4099a2dd95SBruce Richardson uint16_t op; 4199a2dd95SBruce Richardson uint32_t flags; 4299a2dd95SBruce Richardson char device[RTE_DEV_NAME_MAX_LEN]; 4399a2dd95SBruce Richardson uint16_t queue; 4499a2dd95SBruce Richardson struct rte_ring *ring; 4599a2dd95SBruce Richardson struct rte_mempool *mp; 4610f726efSStephen Hemminger 4710f726efSStephen Hemminger const struct rte_bpf_prm *prm; 4810f726efSStephen Hemminger uint32_t snaplen; 4999a2dd95SBruce Richardson }; 5099a2dd95SBruce Richardson 5199a2dd95SBruce Richardson struct pdump_response { 5299a2dd95SBruce Richardson uint16_t ver; 5399a2dd95SBruce Richardson uint16_t res_op; 5499a2dd95SBruce Richardson int32_t err_value; 5599a2dd95SBruce Richardson }; 5699a2dd95SBruce Richardson 5799a2dd95SBruce Richardson static struct pdump_rxtx_cbs { 5899a2dd95SBruce Richardson struct rte_ring *ring; 5999a2dd95SBruce Richardson struct rte_mempool *mp; 6099a2dd95SBruce Richardson const struct rte_eth_rxtx_callback *cb; 6110f726efSStephen Hemminger const struct rte_bpf *filter; 6210f726efSStephen Hemminger enum pdump_version ver; 6310f726efSStephen Hemminger uint32_t snaplen; 6499a2dd95SBruce Richardson } rx_cbs[RTE_MAX_ETHPORTS][RTE_MAX_QUEUES_PER_PORT], 6599a2dd95SBruce Richardson tx_cbs[RTE_MAX_ETHPORTS][RTE_MAX_QUEUES_PER_PORT]; 6699a2dd95SBruce Richardson 6799a2dd95SBruce Richardson 6810f726efSStephen Hemminger /* 6910f726efSStephen Hemminger * The packet capture statistics keep track of packets 7010f726efSStephen Hemminger * accepted, filtered and dropped. These are per-queue 7110f726efSStephen Hemminger * and in memory between primary and secondary processes. 7210f726efSStephen Hemminger */ 7310f726efSStephen Hemminger static const char MZ_RTE_PDUMP_STATS[] = "rte_pdump_stats"; 7410f726efSStephen Hemminger static struct { 7510f726efSStephen Hemminger struct rte_pdump_stats rx[RTE_MAX_ETHPORTS][RTE_MAX_QUEUES_PER_PORT]; 7610f726efSStephen Hemminger struct rte_pdump_stats tx[RTE_MAX_ETHPORTS][RTE_MAX_QUEUES_PER_PORT]; 77*53caecb8SKonstantin Ananyev const struct rte_memzone *mz; 7810f726efSStephen Hemminger } *pdump_stats; 7910f726efSStephen Hemminger 8010f726efSStephen Hemminger /* Create a clone of mbuf to be placed into ring. */ 8110f726efSStephen Hemminger static void 8210f726efSStephen Hemminger pdump_copy(uint16_t port_id, uint16_t queue, 8310f726efSStephen Hemminger enum rte_pcapng_direction direction, 8410f726efSStephen Hemminger struct rte_mbuf **pkts, uint16_t nb_pkts, 8510f726efSStephen Hemminger const struct pdump_rxtx_cbs *cbs, 8610f726efSStephen Hemminger struct rte_pdump_stats *stats) 8799a2dd95SBruce Richardson { 8899a2dd95SBruce Richardson unsigned int i; 8999a2dd95SBruce Richardson int ring_enq; 9099a2dd95SBruce Richardson uint16_t d_pkts = 0; 9199a2dd95SBruce Richardson struct rte_mbuf *dup_bufs[nb_pkts]; 9210f726efSStephen Hemminger uint64_t ts; 9399a2dd95SBruce Richardson struct rte_ring *ring; 9499a2dd95SBruce Richardson struct rte_mempool *mp; 9599a2dd95SBruce Richardson struct rte_mbuf *p; 9610f726efSStephen Hemminger uint64_t rcs[nb_pkts]; 9799a2dd95SBruce Richardson 9810f726efSStephen Hemminger if (cbs->filter) 9910f726efSStephen Hemminger rte_bpf_exec_burst(cbs->filter, (void **)pkts, rcs, nb_pkts); 10010f726efSStephen Hemminger 10110f726efSStephen Hemminger ts = rte_get_tsc_cycles(); 10299a2dd95SBruce Richardson ring = cbs->ring; 10399a2dd95SBruce Richardson mp = cbs->mp; 10499a2dd95SBruce Richardson for (i = 0; i < nb_pkts; i++) { 10510f726efSStephen Hemminger /* 10610f726efSStephen Hemminger * This uses same BPF return value convention as socket filter 10710f726efSStephen Hemminger * and pcap_offline_filter. 10810f726efSStephen Hemminger * if program returns zero 10910f726efSStephen Hemminger * then packet doesn't match the filter (will be ignored). 11010f726efSStephen Hemminger */ 11110f726efSStephen Hemminger if (cbs->filter && rcs[i] == 0) { 11210f726efSStephen Hemminger __atomic_fetch_add(&stats->filtered, 11310f726efSStephen Hemminger 1, __ATOMIC_RELAXED); 11410f726efSStephen Hemminger continue; 11510f726efSStephen Hemminger } 11610f726efSStephen Hemminger 11710f726efSStephen Hemminger /* 11810f726efSStephen Hemminger * If using pcapng then want to wrap packets 11910f726efSStephen Hemminger * otherwise a simple copy. 12010f726efSStephen Hemminger */ 12110f726efSStephen Hemminger if (cbs->ver == V2) 12210f726efSStephen Hemminger p = rte_pcapng_copy(port_id, queue, 12310f726efSStephen Hemminger pkts[i], mp, cbs->snaplen, 12410f726efSStephen Hemminger ts, direction); 12510f726efSStephen Hemminger else 12610f726efSStephen Hemminger p = rte_pktmbuf_copy(pkts[i], mp, 0, cbs->snaplen); 12710f726efSStephen Hemminger 12810f726efSStephen Hemminger if (unlikely(p == NULL)) 12910f726efSStephen Hemminger __atomic_fetch_add(&stats->nombuf, 1, __ATOMIC_RELAXED); 13010f726efSStephen Hemminger else 13199a2dd95SBruce Richardson dup_bufs[d_pkts++] = p; 13299a2dd95SBruce Richardson } 13399a2dd95SBruce Richardson 13410f726efSStephen Hemminger __atomic_fetch_add(&stats->accepted, d_pkts, __ATOMIC_RELAXED); 13510f726efSStephen Hemminger 13699a2dd95SBruce Richardson ring_enq = rte_ring_enqueue_burst(ring, (void *)dup_bufs, d_pkts, NULL); 13799a2dd95SBruce Richardson if (unlikely(ring_enq < d_pkts)) { 13899a2dd95SBruce Richardson unsigned int drops = d_pkts - ring_enq; 13999a2dd95SBruce Richardson 14010f726efSStephen Hemminger __atomic_fetch_add(&stats->ringfull, drops, __ATOMIC_RELAXED); 14199a2dd95SBruce Richardson rte_pktmbuf_free_bulk(&dup_bufs[ring_enq], drops); 14299a2dd95SBruce Richardson } 14399a2dd95SBruce Richardson } 14499a2dd95SBruce Richardson 14599a2dd95SBruce Richardson static uint16_t 14610f726efSStephen Hemminger pdump_rx(uint16_t port, uint16_t queue, 14799a2dd95SBruce Richardson struct rte_mbuf **pkts, uint16_t nb_pkts, 14810f726efSStephen Hemminger uint16_t max_pkts __rte_unused, void *user_params) 14999a2dd95SBruce Richardson { 15010f726efSStephen Hemminger const struct pdump_rxtx_cbs *cbs = user_params; 15110f726efSStephen Hemminger struct rte_pdump_stats *stats = &pdump_stats->rx[port][queue]; 15210f726efSStephen Hemminger 15310f726efSStephen Hemminger pdump_copy(port, queue, RTE_PCAPNG_DIRECTION_IN, 15410f726efSStephen Hemminger pkts, nb_pkts, cbs, stats); 15599a2dd95SBruce Richardson return nb_pkts; 15699a2dd95SBruce Richardson } 15799a2dd95SBruce Richardson 15899a2dd95SBruce Richardson static uint16_t 15910f726efSStephen Hemminger pdump_tx(uint16_t port, uint16_t queue, 16099a2dd95SBruce Richardson struct rte_mbuf **pkts, uint16_t nb_pkts, void *user_params) 16199a2dd95SBruce Richardson { 16210f726efSStephen Hemminger const struct pdump_rxtx_cbs *cbs = user_params; 16310f726efSStephen Hemminger struct rte_pdump_stats *stats = &pdump_stats->tx[port][queue]; 16410f726efSStephen Hemminger 16510f726efSStephen Hemminger pdump_copy(port, queue, RTE_PCAPNG_DIRECTION_OUT, 16610f726efSStephen Hemminger pkts, nb_pkts, cbs, stats); 16799a2dd95SBruce Richardson return nb_pkts; 16899a2dd95SBruce Richardson } 16999a2dd95SBruce Richardson 17099a2dd95SBruce Richardson static int 17110f726efSStephen Hemminger pdump_register_rx_callbacks(enum pdump_version ver, 17210f726efSStephen Hemminger uint16_t end_q, uint16_t port, uint16_t queue, 17399a2dd95SBruce Richardson struct rte_ring *ring, struct rte_mempool *mp, 17410f726efSStephen Hemminger struct rte_bpf *filter, 17510f726efSStephen Hemminger uint16_t operation, uint32_t snaplen) 17699a2dd95SBruce Richardson { 17799a2dd95SBruce Richardson uint16_t qid; 17899a2dd95SBruce Richardson 17999a2dd95SBruce Richardson qid = (queue == RTE_PDUMP_ALL_QUEUES) ? 0 : queue; 18099a2dd95SBruce Richardson for (; qid < end_q; qid++) { 18110f726efSStephen Hemminger struct pdump_rxtx_cbs *cbs = &rx_cbs[port][qid]; 18210f726efSStephen Hemminger 18310f726efSStephen Hemminger if (operation == ENABLE) { 18499a2dd95SBruce Richardson if (cbs->cb) { 18599a2dd95SBruce Richardson PDUMP_LOG(ERR, 18699a2dd95SBruce Richardson "rx callback for port=%d queue=%d, already exists\n", 18799a2dd95SBruce Richardson port, qid); 18899a2dd95SBruce Richardson return -EEXIST; 18999a2dd95SBruce Richardson } 19010f726efSStephen Hemminger cbs->ver = ver; 19199a2dd95SBruce Richardson cbs->ring = ring; 19299a2dd95SBruce Richardson cbs->mp = mp; 19310f726efSStephen Hemminger cbs->snaplen = snaplen; 19410f726efSStephen Hemminger cbs->filter = filter; 19510f726efSStephen Hemminger 19699a2dd95SBruce Richardson cbs->cb = rte_eth_add_first_rx_callback(port, qid, 19799a2dd95SBruce Richardson pdump_rx, cbs); 19899a2dd95SBruce Richardson if (cbs->cb == NULL) { 19999a2dd95SBruce Richardson PDUMP_LOG(ERR, 20099a2dd95SBruce Richardson "failed to add rx callback, errno=%d\n", 20199a2dd95SBruce Richardson rte_errno); 20299a2dd95SBruce Richardson return rte_errno; 20399a2dd95SBruce Richardson } 20410f726efSStephen Hemminger } else if (operation == DISABLE) { 20599a2dd95SBruce Richardson int ret; 20699a2dd95SBruce Richardson 20799a2dd95SBruce Richardson if (cbs->cb == NULL) { 20899a2dd95SBruce Richardson PDUMP_LOG(ERR, 20999a2dd95SBruce Richardson "no existing rx callback for port=%d queue=%d\n", 21099a2dd95SBruce Richardson port, qid); 21199a2dd95SBruce Richardson return -EINVAL; 21299a2dd95SBruce Richardson } 21399a2dd95SBruce Richardson ret = rte_eth_remove_rx_callback(port, qid, cbs->cb); 21499a2dd95SBruce Richardson if (ret < 0) { 21599a2dd95SBruce Richardson PDUMP_LOG(ERR, 21699a2dd95SBruce Richardson "failed to remove rx callback, errno=%d\n", 21799a2dd95SBruce Richardson -ret); 21899a2dd95SBruce Richardson return ret; 21999a2dd95SBruce Richardson } 22099a2dd95SBruce Richardson cbs->cb = NULL; 22199a2dd95SBruce Richardson } 22299a2dd95SBruce Richardson } 22399a2dd95SBruce Richardson 22499a2dd95SBruce Richardson return 0; 22599a2dd95SBruce Richardson } 22699a2dd95SBruce Richardson 22799a2dd95SBruce Richardson static int 22810f726efSStephen Hemminger pdump_register_tx_callbacks(enum pdump_version ver, 22910f726efSStephen Hemminger uint16_t end_q, uint16_t port, uint16_t queue, 23099a2dd95SBruce Richardson struct rte_ring *ring, struct rte_mempool *mp, 23110f726efSStephen Hemminger struct rte_bpf *filter, 23210f726efSStephen Hemminger uint16_t operation, uint32_t snaplen) 23399a2dd95SBruce Richardson { 23499a2dd95SBruce Richardson 23599a2dd95SBruce Richardson uint16_t qid; 23699a2dd95SBruce Richardson 23799a2dd95SBruce Richardson qid = (queue == RTE_PDUMP_ALL_QUEUES) ? 0 : queue; 23899a2dd95SBruce Richardson for (; qid < end_q; qid++) { 23910f726efSStephen Hemminger struct pdump_rxtx_cbs *cbs = &tx_cbs[port][qid]; 24010f726efSStephen Hemminger 24110f726efSStephen Hemminger if (operation == ENABLE) { 24299a2dd95SBruce Richardson if (cbs->cb) { 24399a2dd95SBruce Richardson PDUMP_LOG(ERR, 24499a2dd95SBruce Richardson "tx callback for port=%d queue=%d, already exists\n", 24599a2dd95SBruce Richardson port, qid); 24699a2dd95SBruce Richardson return -EEXIST; 24799a2dd95SBruce Richardson } 24810f726efSStephen Hemminger cbs->ver = ver; 24999a2dd95SBruce Richardson cbs->ring = ring; 25099a2dd95SBruce Richardson cbs->mp = mp; 25110f726efSStephen Hemminger cbs->snaplen = snaplen; 25210f726efSStephen Hemminger cbs->filter = filter; 25310f726efSStephen Hemminger 25499a2dd95SBruce Richardson cbs->cb = rte_eth_add_tx_callback(port, qid, pdump_tx, 25599a2dd95SBruce Richardson cbs); 25699a2dd95SBruce Richardson if (cbs->cb == NULL) { 25799a2dd95SBruce Richardson PDUMP_LOG(ERR, 25899a2dd95SBruce Richardson "failed to add tx callback, errno=%d\n", 25999a2dd95SBruce Richardson rte_errno); 26099a2dd95SBruce Richardson return rte_errno; 26199a2dd95SBruce Richardson } 26210f726efSStephen Hemminger } else if (operation == DISABLE) { 26399a2dd95SBruce Richardson int ret; 26499a2dd95SBruce Richardson 26599a2dd95SBruce Richardson if (cbs->cb == NULL) { 26699a2dd95SBruce Richardson PDUMP_LOG(ERR, 26799a2dd95SBruce Richardson "no existing tx callback for port=%d queue=%d\n", 26899a2dd95SBruce Richardson port, qid); 26999a2dd95SBruce Richardson return -EINVAL; 27099a2dd95SBruce Richardson } 27199a2dd95SBruce Richardson ret = rte_eth_remove_tx_callback(port, qid, cbs->cb); 27299a2dd95SBruce Richardson if (ret < 0) { 27399a2dd95SBruce Richardson PDUMP_LOG(ERR, 27499a2dd95SBruce Richardson "failed to remove tx callback, errno=%d\n", 27599a2dd95SBruce Richardson -ret); 27699a2dd95SBruce Richardson return ret; 27799a2dd95SBruce Richardson } 27899a2dd95SBruce Richardson cbs->cb = NULL; 27999a2dd95SBruce Richardson } 28099a2dd95SBruce Richardson } 28199a2dd95SBruce Richardson 28299a2dd95SBruce Richardson return 0; 28399a2dd95SBruce Richardson } 28499a2dd95SBruce Richardson 28599a2dd95SBruce Richardson static int 28699a2dd95SBruce Richardson set_pdump_rxtx_cbs(const struct pdump_request *p) 28799a2dd95SBruce Richardson { 28899a2dd95SBruce Richardson uint16_t nb_rx_q = 0, nb_tx_q = 0, end_q, queue; 28999a2dd95SBruce Richardson uint16_t port; 29099a2dd95SBruce Richardson int ret = 0; 29110f726efSStephen Hemminger struct rte_bpf *filter = NULL; 29299a2dd95SBruce Richardson uint32_t flags; 29399a2dd95SBruce Richardson uint16_t operation; 29499a2dd95SBruce Richardson struct rte_ring *ring; 29599a2dd95SBruce Richardson struct rte_mempool *mp; 29699a2dd95SBruce Richardson 29710f726efSStephen Hemminger /* Check for possible DPDK version mismatch */ 29810f726efSStephen Hemminger if (!(p->ver == V1 || p->ver == V2)) { 29910f726efSStephen Hemminger PDUMP_LOG(ERR, 30010f726efSStephen Hemminger "incorrect client version %u\n", p->ver); 30110f726efSStephen Hemminger return -EINVAL; 30210f726efSStephen Hemminger } 30310f726efSStephen Hemminger 30410f726efSStephen Hemminger if (p->prm) { 30510f726efSStephen Hemminger if (p->prm->prog_arg.type != RTE_BPF_ARG_PTR_MBUF) { 30610f726efSStephen Hemminger PDUMP_LOG(ERR, 30710f726efSStephen Hemminger "invalid BPF program type: %u\n", 30810f726efSStephen Hemminger p->prm->prog_arg.type); 30910f726efSStephen Hemminger return -EINVAL; 31010f726efSStephen Hemminger } 31110f726efSStephen Hemminger 31210f726efSStephen Hemminger filter = rte_bpf_load(p->prm); 31310f726efSStephen Hemminger if (filter == NULL) { 31410f726efSStephen Hemminger PDUMP_LOG(ERR, "cannot load BPF filter: %s\n", 31510f726efSStephen Hemminger rte_strerror(rte_errno)); 31610f726efSStephen Hemminger return -rte_errno; 31710f726efSStephen Hemminger } 31810f726efSStephen Hemminger } 31910f726efSStephen Hemminger 32099a2dd95SBruce Richardson flags = p->flags; 32199a2dd95SBruce Richardson operation = p->op; 32210f726efSStephen Hemminger queue = p->queue; 32310f726efSStephen Hemminger ring = p->ring; 32410f726efSStephen Hemminger mp = p->mp; 32510f726efSStephen Hemminger 32610f726efSStephen Hemminger ret = rte_eth_dev_get_port_by_name(p->device, &port); 32799a2dd95SBruce Richardson if (ret < 0) { 32899a2dd95SBruce Richardson PDUMP_LOG(ERR, 32999a2dd95SBruce Richardson "failed to get port id for device id=%s\n", 33010f726efSStephen Hemminger p->device); 33199a2dd95SBruce Richardson return -EINVAL; 33299a2dd95SBruce Richardson } 33399a2dd95SBruce Richardson 33499a2dd95SBruce Richardson /* validation if packet capture is for all queues */ 33599a2dd95SBruce Richardson if (queue == RTE_PDUMP_ALL_QUEUES) { 33699a2dd95SBruce Richardson struct rte_eth_dev_info dev_info; 33799a2dd95SBruce Richardson 33899a2dd95SBruce Richardson ret = rte_eth_dev_info_get(port, &dev_info); 33999a2dd95SBruce Richardson if (ret != 0) { 34099a2dd95SBruce Richardson PDUMP_LOG(ERR, 34199a2dd95SBruce Richardson "Error during getting device (port %u) info: %s\n", 34299a2dd95SBruce Richardson port, strerror(-ret)); 34399a2dd95SBruce Richardson return ret; 34499a2dd95SBruce Richardson } 34599a2dd95SBruce Richardson 34699a2dd95SBruce Richardson nb_rx_q = dev_info.nb_rx_queues; 34799a2dd95SBruce Richardson nb_tx_q = dev_info.nb_tx_queues; 34899a2dd95SBruce Richardson if (nb_rx_q == 0 && flags & RTE_PDUMP_FLAG_RX) { 34999a2dd95SBruce Richardson PDUMP_LOG(ERR, 35099a2dd95SBruce Richardson "number of rx queues cannot be 0\n"); 35199a2dd95SBruce Richardson return -EINVAL; 35299a2dd95SBruce Richardson } 35399a2dd95SBruce Richardson if (nb_tx_q == 0 && flags & RTE_PDUMP_FLAG_TX) { 35499a2dd95SBruce Richardson PDUMP_LOG(ERR, 35599a2dd95SBruce Richardson "number of tx queues cannot be 0\n"); 35699a2dd95SBruce Richardson return -EINVAL; 35799a2dd95SBruce Richardson } 35899a2dd95SBruce Richardson if ((nb_tx_q == 0 || nb_rx_q == 0) && 35999a2dd95SBruce Richardson flags == RTE_PDUMP_FLAG_RXTX) { 36099a2dd95SBruce Richardson PDUMP_LOG(ERR, 36199a2dd95SBruce Richardson "both tx&rx queues must be non zero\n"); 36299a2dd95SBruce Richardson return -EINVAL; 36399a2dd95SBruce Richardson } 36499a2dd95SBruce Richardson } 36599a2dd95SBruce Richardson 36699a2dd95SBruce Richardson /* register RX callback */ 36799a2dd95SBruce Richardson if (flags & RTE_PDUMP_FLAG_RX) { 36899a2dd95SBruce Richardson end_q = (queue == RTE_PDUMP_ALL_QUEUES) ? nb_rx_q : queue + 1; 36910f726efSStephen Hemminger ret = pdump_register_rx_callbacks(p->ver, end_q, port, queue, 37010f726efSStephen Hemminger ring, mp, filter, 37110f726efSStephen Hemminger operation, p->snaplen); 37299a2dd95SBruce Richardson if (ret < 0) 37399a2dd95SBruce Richardson return ret; 37499a2dd95SBruce Richardson } 37599a2dd95SBruce Richardson 37699a2dd95SBruce Richardson /* register TX callback */ 37799a2dd95SBruce Richardson if (flags & RTE_PDUMP_FLAG_TX) { 37899a2dd95SBruce Richardson end_q = (queue == RTE_PDUMP_ALL_QUEUES) ? nb_tx_q : queue + 1; 37910f726efSStephen Hemminger ret = pdump_register_tx_callbacks(p->ver, end_q, port, queue, 38010f726efSStephen Hemminger ring, mp, filter, 38110f726efSStephen Hemminger operation, p->snaplen); 38299a2dd95SBruce Richardson if (ret < 0) 38399a2dd95SBruce Richardson return ret; 38499a2dd95SBruce Richardson } 38599a2dd95SBruce Richardson 38699a2dd95SBruce Richardson return ret; 38799a2dd95SBruce Richardson } 38899a2dd95SBruce Richardson 38999a2dd95SBruce Richardson static int 39099a2dd95SBruce Richardson pdump_server(const struct rte_mp_msg *mp_msg, const void *peer) 39199a2dd95SBruce Richardson { 39299a2dd95SBruce Richardson struct rte_mp_msg mp_resp; 39399a2dd95SBruce Richardson const struct pdump_request *cli_req; 39499a2dd95SBruce Richardson struct pdump_response *resp = (struct pdump_response *)&mp_resp.param; 39599a2dd95SBruce Richardson 39699a2dd95SBruce Richardson /* recv client requests */ 39799a2dd95SBruce Richardson if (mp_msg->len_param != sizeof(*cli_req)) { 39899a2dd95SBruce Richardson PDUMP_LOG(ERR, "failed to recv from client\n"); 39999a2dd95SBruce Richardson resp->err_value = -EINVAL; 40099a2dd95SBruce Richardson } else { 40199a2dd95SBruce Richardson cli_req = (const struct pdump_request *)mp_msg->param; 40299a2dd95SBruce Richardson resp->ver = cli_req->ver; 40399a2dd95SBruce Richardson resp->res_op = cli_req->op; 40499a2dd95SBruce Richardson resp->err_value = set_pdump_rxtx_cbs(cli_req); 40599a2dd95SBruce Richardson } 40699a2dd95SBruce Richardson 40710f726efSStephen Hemminger rte_strscpy(mp_resp.name, PDUMP_MP, RTE_MP_MAX_NAME_LEN); 40899a2dd95SBruce Richardson mp_resp.len_param = sizeof(*resp); 40999a2dd95SBruce Richardson mp_resp.num_fds = 0; 41099a2dd95SBruce Richardson if (rte_mp_reply(&mp_resp, peer) < 0) { 41199a2dd95SBruce Richardson PDUMP_LOG(ERR, "failed to send to client:%s\n", 41299a2dd95SBruce Richardson strerror(rte_errno)); 41399a2dd95SBruce Richardson return -1; 41499a2dd95SBruce Richardson } 41599a2dd95SBruce Richardson 41699a2dd95SBruce Richardson return 0; 41799a2dd95SBruce Richardson } 41899a2dd95SBruce Richardson 41999a2dd95SBruce Richardson int 42099a2dd95SBruce Richardson rte_pdump_init(void) 42199a2dd95SBruce Richardson { 42210f726efSStephen Hemminger const struct rte_memzone *mz; 42399a2dd95SBruce Richardson int ret; 42499a2dd95SBruce Richardson 42510f726efSStephen Hemminger mz = rte_memzone_reserve(MZ_RTE_PDUMP_STATS, sizeof(*pdump_stats), 42610f726efSStephen Hemminger rte_socket_id(), 0); 42710f726efSStephen Hemminger if (mz == NULL) { 42810f726efSStephen Hemminger PDUMP_LOG(ERR, "cannot allocate pdump statistics\n"); 42910f726efSStephen Hemminger rte_errno = ENOMEM; 43010f726efSStephen Hemminger return -1; 43110f726efSStephen Hemminger } 43210f726efSStephen Hemminger pdump_stats = mz->addr; 433*53caecb8SKonstantin Ananyev pdump_stats->mz = mz; 43410f726efSStephen Hemminger 43599a2dd95SBruce Richardson ret = rte_mp_action_register(PDUMP_MP, pdump_server); 43699a2dd95SBruce Richardson if (ret && rte_errno != ENOTSUP) 43799a2dd95SBruce Richardson return -1; 43899a2dd95SBruce Richardson return 0; 43999a2dd95SBruce Richardson } 44099a2dd95SBruce Richardson 44199a2dd95SBruce Richardson int 44299a2dd95SBruce Richardson rte_pdump_uninit(void) 44399a2dd95SBruce Richardson { 44499a2dd95SBruce Richardson rte_mp_action_unregister(PDUMP_MP); 44599a2dd95SBruce Richardson 446*53caecb8SKonstantin Ananyev if (pdump_stats != NULL) { 447*53caecb8SKonstantin Ananyev rte_memzone_free(pdump_stats->mz); 448*53caecb8SKonstantin Ananyev pdump_stats = NULL; 449*53caecb8SKonstantin Ananyev } 450*53caecb8SKonstantin Ananyev 45199a2dd95SBruce Richardson return 0; 45299a2dd95SBruce Richardson } 45399a2dd95SBruce Richardson 45499a2dd95SBruce Richardson static int 45599a2dd95SBruce Richardson pdump_validate_ring_mp(struct rte_ring *ring, struct rte_mempool *mp) 45699a2dd95SBruce Richardson { 45799a2dd95SBruce Richardson if (ring == NULL || mp == NULL) { 45899a2dd95SBruce Richardson PDUMP_LOG(ERR, "NULL ring or mempool\n"); 45999a2dd95SBruce Richardson rte_errno = EINVAL; 46099a2dd95SBruce Richardson return -1; 46199a2dd95SBruce Richardson } 462c47d7b90SAndrew Rybchenko if (mp->flags & RTE_MEMPOOL_F_SP_PUT || 463c47d7b90SAndrew Rybchenko mp->flags & RTE_MEMPOOL_F_SC_GET) { 46499a2dd95SBruce Richardson PDUMP_LOG(ERR, 46599a2dd95SBruce Richardson "mempool with SP or SC set not valid for pdump," 46699a2dd95SBruce Richardson "must have MP and MC set\n"); 46799a2dd95SBruce Richardson rte_errno = EINVAL; 46899a2dd95SBruce Richardson return -1; 46999a2dd95SBruce Richardson } 47099a2dd95SBruce Richardson if (rte_ring_is_prod_single(ring) || rte_ring_is_cons_single(ring)) { 47199a2dd95SBruce Richardson PDUMP_LOG(ERR, 47299a2dd95SBruce Richardson "ring with SP or SC set is not valid for pdump," 47399a2dd95SBruce Richardson "must have MP and MC set\n"); 47499a2dd95SBruce Richardson rte_errno = EINVAL; 47599a2dd95SBruce Richardson return -1; 47699a2dd95SBruce Richardson } 47799a2dd95SBruce Richardson 47899a2dd95SBruce Richardson return 0; 47999a2dd95SBruce Richardson } 48099a2dd95SBruce Richardson 48199a2dd95SBruce Richardson static int 48299a2dd95SBruce Richardson pdump_validate_flags(uint32_t flags) 48399a2dd95SBruce Richardson { 48410f726efSStephen Hemminger if ((flags & RTE_PDUMP_FLAG_RXTX) == 0) { 48599a2dd95SBruce Richardson PDUMP_LOG(ERR, 48699a2dd95SBruce Richardson "invalid flags, should be either rx/tx/rxtx\n"); 48799a2dd95SBruce Richardson rte_errno = EINVAL; 48899a2dd95SBruce Richardson return -1; 48999a2dd95SBruce Richardson } 49099a2dd95SBruce Richardson 49110f726efSStephen Hemminger /* mask off the flags we know about */ 49210f726efSStephen Hemminger if (flags & ~(RTE_PDUMP_FLAG_RXTX | RTE_PDUMP_FLAG_PCAPNG)) { 49310f726efSStephen Hemminger PDUMP_LOG(ERR, 49410f726efSStephen Hemminger "unknown flags: %#x\n", flags); 49510f726efSStephen Hemminger rte_errno = ENOTSUP; 49610f726efSStephen Hemminger return -1; 49710f726efSStephen Hemminger } 49810f726efSStephen Hemminger 49999a2dd95SBruce Richardson return 0; 50099a2dd95SBruce Richardson } 50199a2dd95SBruce Richardson 50299a2dd95SBruce Richardson static int 50399a2dd95SBruce Richardson pdump_validate_port(uint16_t port, char *name) 50499a2dd95SBruce Richardson { 50599a2dd95SBruce Richardson int ret = 0; 50699a2dd95SBruce Richardson 50799a2dd95SBruce Richardson if (port >= RTE_MAX_ETHPORTS) { 50899a2dd95SBruce Richardson PDUMP_LOG(ERR, "Invalid port id %u\n", port); 50999a2dd95SBruce Richardson rte_errno = EINVAL; 51099a2dd95SBruce Richardson return -1; 51199a2dd95SBruce Richardson } 51299a2dd95SBruce Richardson 51399a2dd95SBruce Richardson ret = rte_eth_dev_get_name_by_port(port, name); 51499a2dd95SBruce Richardson if (ret < 0) { 51599a2dd95SBruce Richardson PDUMP_LOG(ERR, "port %u to name mapping failed\n", 51699a2dd95SBruce Richardson port); 51799a2dd95SBruce Richardson rte_errno = EINVAL; 51899a2dd95SBruce Richardson return -1; 51999a2dd95SBruce Richardson } 52099a2dd95SBruce Richardson 52199a2dd95SBruce Richardson return 0; 52299a2dd95SBruce Richardson } 52399a2dd95SBruce Richardson 52499a2dd95SBruce Richardson static int 52510f726efSStephen Hemminger pdump_prepare_client_request(const char *device, uint16_t queue, 52610f726efSStephen Hemminger uint32_t flags, uint32_t snaplen, 52799a2dd95SBruce Richardson uint16_t operation, 52899a2dd95SBruce Richardson struct rte_ring *ring, 52999a2dd95SBruce Richardson struct rte_mempool *mp, 53010f726efSStephen Hemminger const struct rte_bpf_prm *prm) 53199a2dd95SBruce Richardson { 53299a2dd95SBruce Richardson int ret = -1; 53399a2dd95SBruce Richardson struct rte_mp_msg mp_req, *mp_rep; 53499a2dd95SBruce Richardson struct rte_mp_reply mp_reply; 53599a2dd95SBruce Richardson struct timespec ts = {.tv_sec = 5, .tv_nsec = 0}; 53699a2dd95SBruce Richardson struct pdump_request *req = (struct pdump_request *)mp_req.param; 53799a2dd95SBruce Richardson struct pdump_response *resp; 53899a2dd95SBruce Richardson 53910f726efSStephen Hemminger memset(req, 0, sizeof(*req)); 54010f726efSStephen Hemminger 54110f726efSStephen Hemminger req->ver = (flags & RTE_PDUMP_FLAG_PCAPNG) ? V2 : V1; 54210f726efSStephen Hemminger req->flags = flags & RTE_PDUMP_FLAG_RXTX; 54399a2dd95SBruce Richardson req->op = operation; 54410f726efSStephen Hemminger req->queue = queue; 54510f726efSStephen Hemminger rte_strscpy(req->device, device, sizeof(req->device)); 54610f726efSStephen Hemminger 54799a2dd95SBruce Richardson if ((operation & ENABLE) != 0) { 54810f726efSStephen Hemminger req->ring = ring; 54910f726efSStephen Hemminger req->mp = mp; 55010f726efSStephen Hemminger req->prm = prm; 55110f726efSStephen Hemminger req->snaplen = snaplen; 55299a2dd95SBruce Richardson } 55399a2dd95SBruce Richardson 55410f726efSStephen Hemminger rte_strscpy(mp_req.name, PDUMP_MP, RTE_MP_MAX_NAME_LEN); 55599a2dd95SBruce Richardson mp_req.len_param = sizeof(*req); 55699a2dd95SBruce Richardson mp_req.num_fds = 0; 55799a2dd95SBruce Richardson if (rte_mp_request_sync(&mp_req, &mp_reply, &ts) == 0) { 55899a2dd95SBruce Richardson mp_rep = &mp_reply.msgs[0]; 55999a2dd95SBruce Richardson resp = (struct pdump_response *)mp_rep->param; 56099a2dd95SBruce Richardson rte_errno = resp->err_value; 56199a2dd95SBruce Richardson if (!resp->err_value) 56299a2dd95SBruce Richardson ret = 0; 56399a2dd95SBruce Richardson free(mp_reply.msgs); 56499a2dd95SBruce Richardson } 56599a2dd95SBruce Richardson 56699a2dd95SBruce Richardson if (ret < 0) 56799a2dd95SBruce Richardson PDUMP_LOG(ERR, 56899a2dd95SBruce Richardson "client request for pdump enable/disable failed\n"); 56999a2dd95SBruce Richardson return ret; 57099a2dd95SBruce Richardson } 57199a2dd95SBruce Richardson 57210f726efSStephen Hemminger /* 57310f726efSStephen Hemminger * There are two versions of this function, because although original API 57410f726efSStephen Hemminger * left place holder for future filter, it never checked the value. 57510f726efSStephen Hemminger * Therefore the API can't depend on application passing a non 57610f726efSStephen Hemminger * bogus value. 57710f726efSStephen Hemminger */ 57810f726efSStephen Hemminger static int 57910f726efSStephen Hemminger pdump_enable(uint16_t port, uint16_t queue, 58010f726efSStephen Hemminger uint32_t flags, uint32_t snaplen, 58110f726efSStephen Hemminger struct rte_ring *ring, struct rte_mempool *mp, 58210f726efSStephen Hemminger const struct rte_bpf_prm *prm) 58399a2dd95SBruce Richardson { 58499a2dd95SBruce Richardson int ret; 58599a2dd95SBruce Richardson char name[RTE_DEV_NAME_MAX_LEN]; 58699a2dd95SBruce Richardson 58799a2dd95SBruce Richardson ret = pdump_validate_port(port, name); 58899a2dd95SBruce Richardson if (ret < 0) 58999a2dd95SBruce Richardson return ret; 59099a2dd95SBruce Richardson ret = pdump_validate_ring_mp(ring, mp); 59199a2dd95SBruce Richardson if (ret < 0) 59299a2dd95SBruce Richardson return ret; 59399a2dd95SBruce Richardson ret = pdump_validate_flags(flags); 59499a2dd95SBruce Richardson if (ret < 0) 59599a2dd95SBruce Richardson return ret; 59699a2dd95SBruce Richardson 59710f726efSStephen Hemminger if (snaplen == 0) 59810f726efSStephen Hemminger snaplen = UINT32_MAX; 59999a2dd95SBruce Richardson 60010f726efSStephen Hemminger return pdump_prepare_client_request(name, queue, flags, snaplen, 60110f726efSStephen Hemminger ENABLE, ring, mp, prm); 60299a2dd95SBruce Richardson } 60399a2dd95SBruce Richardson 60499a2dd95SBruce Richardson int 60510f726efSStephen Hemminger rte_pdump_enable(uint16_t port, uint16_t queue, uint32_t flags, 60699a2dd95SBruce Richardson struct rte_ring *ring, 60799a2dd95SBruce Richardson struct rte_mempool *mp, 60810f726efSStephen Hemminger void *filter __rte_unused) 60999a2dd95SBruce Richardson { 61010f726efSStephen Hemminger return pdump_enable(port, queue, flags, 0, 61110f726efSStephen Hemminger ring, mp, NULL); 61210f726efSStephen Hemminger } 61310f726efSStephen Hemminger 61410f726efSStephen Hemminger int 61510f726efSStephen Hemminger rte_pdump_enable_bpf(uint16_t port, uint16_t queue, 61610f726efSStephen Hemminger uint32_t flags, uint32_t snaplen, 61710f726efSStephen Hemminger struct rte_ring *ring, 61810f726efSStephen Hemminger struct rte_mempool *mp, 61910f726efSStephen Hemminger const struct rte_bpf_prm *prm) 62010f726efSStephen Hemminger { 62110f726efSStephen Hemminger return pdump_enable(port, queue, flags, snaplen, 62210f726efSStephen Hemminger ring, mp, prm); 62310f726efSStephen Hemminger } 62410f726efSStephen Hemminger 62510f726efSStephen Hemminger static int 62610f726efSStephen Hemminger pdump_enable_by_deviceid(const char *device_id, uint16_t queue, 62710f726efSStephen Hemminger uint32_t flags, uint32_t snaplen, 62810f726efSStephen Hemminger struct rte_ring *ring, 62910f726efSStephen Hemminger struct rte_mempool *mp, 63010f726efSStephen Hemminger const struct rte_bpf_prm *prm) 63110f726efSStephen Hemminger { 63210f726efSStephen Hemminger int ret; 63399a2dd95SBruce Richardson 63499a2dd95SBruce Richardson ret = pdump_validate_ring_mp(ring, mp); 63599a2dd95SBruce Richardson if (ret < 0) 63699a2dd95SBruce Richardson return ret; 63799a2dd95SBruce Richardson ret = pdump_validate_flags(flags); 63899a2dd95SBruce Richardson if (ret < 0) 63999a2dd95SBruce Richardson return ret; 64099a2dd95SBruce Richardson 641b2be63b5SStephen Hemminger if (snaplen == 0) 642b2be63b5SStephen Hemminger snaplen = UINT32_MAX; 643b2be63b5SStephen Hemminger 64410f726efSStephen Hemminger return pdump_prepare_client_request(device_id, queue, flags, snaplen, 64510f726efSStephen Hemminger ENABLE, ring, mp, prm); 64610f726efSStephen Hemminger } 64799a2dd95SBruce Richardson 64810f726efSStephen Hemminger int 64910f726efSStephen Hemminger rte_pdump_enable_by_deviceid(char *device_id, uint16_t queue, 65010f726efSStephen Hemminger uint32_t flags, 65110f726efSStephen Hemminger struct rte_ring *ring, 65210f726efSStephen Hemminger struct rte_mempool *mp, 65310f726efSStephen Hemminger void *filter __rte_unused) 65410f726efSStephen Hemminger { 65510f726efSStephen Hemminger return pdump_enable_by_deviceid(device_id, queue, flags, 0, 65610f726efSStephen Hemminger ring, mp, NULL); 65710f726efSStephen Hemminger } 65810f726efSStephen Hemminger 65910f726efSStephen Hemminger int 66010f726efSStephen Hemminger rte_pdump_enable_bpf_by_deviceid(const char *device_id, uint16_t queue, 66110f726efSStephen Hemminger uint32_t flags, uint32_t snaplen, 66210f726efSStephen Hemminger struct rte_ring *ring, 66310f726efSStephen Hemminger struct rte_mempool *mp, 66410f726efSStephen Hemminger const struct rte_bpf_prm *prm) 66510f726efSStephen Hemminger { 66610f726efSStephen Hemminger return pdump_enable_by_deviceid(device_id, queue, flags, snaplen, 66710f726efSStephen Hemminger ring, mp, prm); 66899a2dd95SBruce Richardson } 66999a2dd95SBruce Richardson 67099a2dd95SBruce Richardson int 67199a2dd95SBruce Richardson rte_pdump_disable(uint16_t port, uint16_t queue, uint32_t flags) 67299a2dd95SBruce Richardson { 67399a2dd95SBruce Richardson int ret = 0; 67499a2dd95SBruce Richardson char name[RTE_DEV_NAME_MAX_LEN]; 67599a2dd95SBruce Richardson 67699a2dd95SBruce Richardson ret = pdump_validate_port(port, name); 67799a2dd95SBruce Richardson if (ret < 0) 67899a2dd95SBruce Richardson return ret; 67999a2dd95SBruce Richardson ret = pdump_validate_flags(flags); 68099a2dd95SBruce Richardson if (ret < 0) 68199a2dd95SBruce Richardson return ret; 68299a2dd95SBruce Richardson 68310f726efSStephen Hemminger ret = pdump_prepare_client_request(name, queue, flags, 0, 68499a2dd95SBruce Richardson DISABLE, NULL, NULL, NULL); 68599a2dd95SBruce Richardson 68699a2dd95SBruce Richardson return ret; 68799a2dd95SBruce Richardson } 68899a2dd95SBruce Richardson 68999a2dd95SBruce Richardson int 69099a2dd95SBruce Richardson rte_pdump_disable_by_deviceid(char *device_id, uint16_t queue, 69199a2dd95SBruce Richardson uint32_t flags) 69299a2dd95SBruce Richardson { 69399a2dd95SBruce Richardson int ret = 0; 69499a2dd95SBruce Richardson 69599a2dd95SBruce Richardson ret = pdump_validate_flags(flags); 69699a2dd95SBruce Richardson if (ret < 0) 69799a2dd95SBruce Richardson return ret; 69899a2dd95SBruce Richardson 69910f726efSStephen Hemminger ret = pdump_prepare_client_request(device_id, queue, flags, 0, 70099a2dd95SBruce Richardson DISABLE, NULL, NULL, NULL); 70199a2dd95SBruce Richardson 70299a2dd95SBruce Richardson return ret; 70399a2dd95SBruce Richardson } 70410f726efSStephen Hemminger 70510f726efSStephen Hemminger static void 70610f726efSStephen Hemminger pdump_sum_stats(uint16_t port, uint16_t nq, 70710f726efSStephen Hemminger struct rte_pdump_stats stats[RTE_MAX_ETHPORTS][RTE_MAX_QUEUES_PER_PORT], 70810f726efSStephen Hemminger struct rte_pdump_stats *total) 70910f726efSStephen Hemminger { 71010f726efSStephen Hemminger uint64_t *sum = (uint64_t *)total; 71110f726efSStephen Hemminger unsigned int i; 71210f726efSStephen Hemminger uint64_t val; 71310f726efSStephen Hemminger uint16_t qid; 71410f726efSStephen Hemminger 71510f726efSStephen Hemminger for (qid = 0; qid < nq; qid++) { 71610f726efSStephen Hemminger const uint64_t *perq = (const uint64_t *)&stats[port][qid]; 71710f726efSStephen Hemminger 71810f726efSStephen Hemminger for (i = 0; i < sizeof(*total) / sizeof(uint64_t); i++) { 71910f726efSStephen Hemminger val = __atomic_load_n(&perq[i], __ATOMIC_RELAXED); 72010f726efSStephen Hemminger sum[i] += val; 72110f726efSStephen Hemminger } 72210f726efSStephen Hemminger } 72310f726efSStephen Hemminger } 72410f726efSStephen Hemminger 72510f726efSStephen Hemminger int 72610f726efSStephen Hemminger rte_pdump_stats(uint16_t port, struct rte_pdump_stats *stats) 72710f726efSStephen Hemminger { 72810f726efSStephen Hemminger struct rte_eth_dev_info dev_info; 72910f726efSStephen Hemminger const struct rte_memzone *mz; 73010f726efSStephen Hemminger int ret; 73110f726efSStephen Hemminger 73210f726efSStephen Hemminger memset(stats, 0, sizeof(*stats)); 73310f726efSStephen Hemminger ret = rte_eth_dev_info_get(port, &dev_info); 73410f726efSStephen Hemminger if (ret != 0) { 73510f726efSStephen Hemminger PDUMP_LOG(ERR, 73610f726efSStephen Hemminger "Error during getting device (port %u) info: %s\n", 73710f726efSStephen Hemminger port, strerror(-ret)); 73810f726efSStephen Hemminger return ret; 73910f726efSStephen Hemminger } 74010f726efSStephen Hemminger 74110f726efSStephen Hemminger if (pdump_stats == NULL) { 74210f726efSStephen Hemminger if (rte_eal_process_type() == RTE_PROC_PRIMARY) { 74310f726efSStephen Hemminger /* rte_pdump_init was not called */ 74410f726efSStephen Hemminger PDUMP_LOG(ERR, "pdump stats not initialized\n"); 74510f726efSStephen Hemminger rte_errno = EINVAL; 74610f726efSStephen Hemminger return -1; 74710f726efSStephen Hemminger } 74810f726efSStephen Hemminger 74910f726efSStephen Hemminger /* secondary process looks up the memzone */ 75010f726efSStephen Hemminger mz = rte_memzone_lookup(MZ_RTE_PDUMP_STATS); 75110f726efSStephen Hemminger if (mz == NULL) { 75210f726efSStephen Hemminger /* rte_pdump_init was not called in primary process?? */ 75310f726efSStephen Hemminger PDUMP_LOG(ERR, "can not find pdump stats\n"); 75410f726efSStephen Hemminger rte_errno = EINVAL; 75510f726efSStephen Hemminger return -1; 75610f726efSStephen Hemminger } 75710f726efSStephen Hemminger pdump_stats = mz->addr; 75810f726efSStephen Hemminger } 75910f726efSStephen Hemminger 76010f726efSStephen Hemminger pdump_sum_stats(port, dev_info.nb_rx_queues, pdump_stats->rx, stats); 76110f726efSStephen Hemminger pdump_sum_stats(port, dev_info.nb_tx_queues, pdump_stats->tx, stats); 76210f726efSStephen Hemminger return 0; 76310f726efSStephen Hemminger } 764