From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from mx1.redhat.com (mx1.redhat.com [209.132.183.28]) by dpdk.org (Postfix) with ESMTP id B51E72935 for ; Fri, 10 Jun 2016 20:48:09 +0200 (CEST) Received: from int-mx10.intmail.prod.int.phx2.redhat.com (int-mx10.intmail.prod.int.phx2.redhat.com [10.5.11.23]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mx1.redhat.com (Postfix) with ESMTPS id C9EA18F501; Fri, 10 Jun 2016 18:48:08 +0000 (UTC) Received: from aconole-fed23 (vpn-48-136.rdu2.redhat.com [10.10.48.136]) by int-mx10.intmail.prod.int.phx2.redhat.com (8.14.4/8.14.4) with ESMTP id u5AIm3cD030927 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-GCM-SHA384 bits=256 verify=NO); Fri, 10 Jun 2016 14:48:06 -0400 From: Aaron Conole To: Reshma Pattan Cc: dev@dpdk.org References: <1465487895-5870-1-git-send-email-reshma.pattan@intel.com> <1465575534-23605-1-git-send-email-reshma.pattan@intel.com> <1465575534-23605-6-git-send-email-reshma.pattan@intel.com> Date: Fri, 10 Jun 2016 14:48:02 -0400 In-Reply-To: <1465575534-23605-6-git-send-email-reshma.pattan@intel.com> (Reshma Pattan's message of "Fri, 10 Jun 2016 17:18:51 +0100") Message-ID: User-Agent: Gnus/5.13 (Gnus v5.13) Emacs/25.1.50 (gnu/linux) MIME-Version: 1.0 Content-Type: text/plain X-Scanned-By: MIMEDefang 2.68 on 10.5.11.23 X-Greylist: Sender IP whitelisted, not delayed by milter-greylist-4.5.16 (mx1.redhat.com [10.5.110.28]); Fri, 10 Jun 2016 18:48:08 +0000 (UTC) Subject: Re: [dpdk-dev] [PATCH v8 5/8] lib/librte_pdump: add new library for packet capturing support X-BeenThere: dev@dpdk.org X-Mailman-Version: 2.1.15 Precedence: list List-Id: patches and discussions about DPDK List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Fri, 10 Jun 2016 18:48:10 -0000 Hi Reshma, Reshma Pattan writes: > Added new library for packet capturing support. > > Added public api rte_pdump_init, applications should call > this as part of their application setup to have packet > capturing framework ready. > > Added public api rte_pdump_uninit to uninitialize the packet > capturing framework. > > Added public apis rte_pdump_enable and rte_pdump_disable to > enable and disable packet capturing on specific port and queue. > > Added public apis rte_pdump_enable_by_deviceid and > rte_pdump_disable_by_deviceid to enable and disable packet > capturing on a specific device (pci address or name) and queue. > > Added public api rte_pdump_set_socket_dir to set the > server socket path. Thanks for this, it is quite useful! I am wondering, should the same API work for a client socket as well? The code becomes a bit easier to maintain, and the API behaves whether executed from client or server. Thoughts? Thanks, Aaron > Signed-off-by: Reshma Pattan > --- > MAINTAINERS | 4 + > config/common_base | 5 + > lib/Makefile | 1 + > lib/librte_pdump/Makefile | 55 ++ > lib/librte_pdump/rte_pdump.c | 904 +++++++++++++++++++++++++++++++++ > lib/librte_pdump/rte_pdump.h | 208 ++++++++ > lib/librte_pdump/rte_pdump_version.map | 13 + > mk/rte.app.mk | 1 + > 8 files changed, 1191 insertions(+) > create mode 100644 lib/librte_pdump/Makefile > create mode 100644 lib/librte_pdump/rte_pdump.c > create mode 100644 lib/librte_pdump/rte_pdump.h > create mode 100644 lib/librte_pdump/rte_pdump_version.map > > diff --git a/MAINTAINERS b/MAINTAINERS > index 3e8558f..cc3ffdb 100644 > --- a/MAINTAINERS > +++ b/MAINTAINERS > @@ -432,6 +432,10 @@ F: app/test/test_reorder* > F: examples/packet_ordering/ > F: doc/guides/sample_app_ug/packet_ordering.rst > > +Pdump > +M: Reshma Pattan > +F: lib/librte_pdump/ > + > Hierarchical scheduler > M: Cristian Dumitrescu > F: lib/librte_sched/ > diff --git a/config/common_base b/config/common_base > index 47c26f6..a2d5d72 100644 > --- a/config/common_base > +++ b/config/common_base > @@ -484,6 +484,11 @@ CONFIG_RTE_LIBRTE_DISTRIBUTOR=y > CONFIG_RTE_LIBRTE_REORDER=y > > # > +# Compile the pdump library > +# > +CONFIG_RTE_LIBRTE_PDUMP=y > + > +# > # Compile librte_port > # > CONFIG_RTE_LIBRTE_PORT=y > diff --git a/lib/Makefile b/lib/Makefile > index f254dba..ca7c02f 100644 > --- a/lib/Makefile > +++ b/lib/Makefile > @@ -57,6 +57,7 @@ DIRS-$(CONFIG_RTE_LIBRTE_PORT) += librte_port > DIRS-$(CONFIG_RTE_LIBRTE_TABLE) += librte_table > DIRS-$(CONFIG_RTE_LIBRTE_PIPELINE) += librte_pipeline > DIRS-$(CONFIG_RTE_LIBRTE_REORDER) += librte_reorder > +DIRS-$(CONFIG_RTE_LIBRTE_PDUMP) += librte_pdump > > ifeq ($(CONFIG_RTE_EXEC_ENV_LINUXAPP),y) > DIRS-$(CONFIG_RTE_LIBRTE_KNI) += librte_kni > diff --git a/lib/librte_pdump/Makefile b/lib/librte_pdump/Makefile > new file mode 100644 > index 0000000..af81a28 > --- /dev/null > +++ b/lib/librte_pdump/Makefile > @@ -0,0 +1,55 @@ > +# BSD LICENSE > +# > +# Copyright(c) 2016 Intel Corporation. All rights reserved. > +# All rights reserved. > +# > +# Redistribution and use in source and binary forms, with or without > +# modification, are permitted provided that the following conditions > +# are met: > +# > +# * Redistributions of source code must retain the above copyright > +# notice, this list of conditions and the following disclaimer. > +# * Redistributions in binary form must reproduce the above copyright > +# notice, this list of conditions and the following disclaimer in > +# the documentation and/or other materials provided with the > +# distribution. > +# * Neither the name of Intel Corporation nor the names of its > +# contributors may be used to endorse or promote products derived > +# from this software without specific prior written permission. > +# > +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS > +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT > +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR > +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT > +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, > +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT > +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, > +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY > +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT > +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE > +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. > + > +include $(RTE_SDK)/mk/rte.vars.mk > + > +# library name > +LIB = librte_pdump.a > + > +CFLAGS += $(WERROR_FLAGS) -I$(SRCDIR) -O3 > +CFLAGS += -D_GNU_SOURCE > + > +EXPORT_MAP := rte_pdump_version.map > + > +LIBABIVER := 1 > + > +# all source are stored in SRCS-y > +SRCS-$(CONFIG_RTE_LIBRTE_PDUMP) := rte_pdump.c > + > +# install this header file > +SYMLINK-$(CONFIG_RTE_LIBRTE_PDUMP)-include := rte_pdump.h > + > +# this lib depends upon: > +DEPDIRS-$(CONFIG_RTE_LIBRTE_PDUMP) += lib/librte_mbuf > +DEPDIRS-$(CONFIG_RTE_LIBRTE_PDUMP) += lib/librte_eal > +DEPDIRS-$(CONFIG_RTE_LIBRTE_PDUMP) += lib/librte_ether > + > +include $(RTE_SDK)/mk/rte.lib.mk > diff --git a/lib/librte_pdump/rte_pdump.c b/lib/librte_pdump/rte_pdump.c > new file mode 100644 > index 0000000..c4233cb > --- /dev/null > +++ b/lib/librte_pdump/rte_pdump.c > @@ -0,0 +1,904 @@ > +/*- > + * BSD LICENSE > + * > + * Copyright(c) 2016 Intel Corporation. All rights reserved. > + * All rights reserved. > + * > + * Redistribution and use in source and binary forms, with or without > + * modification, are permitted provided that the following conditions > + * are met: > + * > + * * Redistributions of source code must retain the above copyright > + * notice, this list of conditions and the following disclaimer. > + * * Redistributions in binary form must reproduce the above copyright > + * notice, this list of conditions and the following disclaimer in > + * the documentation and/or other materials provided with the > + * distribution. > + * * Neither the name of Intel Corporation nor the names of its > + * contributors may be used to endorse or promote products derived > + * from this software without specific prior written permission. > + * > + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS > + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT > + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR > + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT > + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, > + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT > + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, > + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY > + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT > + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE > + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. > + */ > + > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > + > +#include > +#include > +#include > +#include > +#include > +#include > +#include > + > +#include "rte_pdump.h" > + > +#define SOCKET_PATH_VAR_RUN "/var/run/pdump_sockets" > +#define SOCKET_PATH_HOME "HOME/pdump_sockets" > +#define SERVER_SOCKET "%s/pdump_server_socket" > +#define CLIENT_SOCKET "%s/pdump_client_socket_%d_%u" > +#define DEVICE_ID_SIZE 64 > +/* Macros for printing using RTE_LOG */ > +#define RTE_LOGTYPE_PDUMP RTE_LOGTYPE_USER1 > + > +enum pdump_operation { > + DISABLE = 1, > + ENABLE = 2 > +}; > + > +enum pdump_socktype { > + SERVER = 1, > + CLIENT = 2 > +}; > + > +enum pdump_version { > + V1 = 1 > +}; > + > +static pthread_t pdump_thread; > +static int pdump_socket_fd; > +static char socket_dir[PATH_MAX]; > + > +struct pdump_request { > + uint16_t ver; > + uint16_t op; > + uint32_t flags; > + union pdump_data { > + struct enable_v1 { > + char device[DEVICE_ID_SIZE]; > + uint16_t queue; > + struct rte_ring *ring; > + struct rte_mempool *mp; > + void *filter; > + } en_v1; > + struct disable_v1 { > + char device[DEVICE_ID_SIZE]; > + uint16_t queue; > + struct rte_ring *ring; > + struct rte_mempool *mp; > + void *filter; > + } dis_v1; > + } data; > +}; > + > +struct pdump_response { > + uint16_t ver; > + uint16_t res_op; > + int32_t err_value; > +}; > + > +static struct pdump_rxtx_cbs { > + struct rte_ring *ring; > + struct rte_mempool *mp; > + struct rte_eth_rxtx_callback *cb; > + void *filter; > +} rx_cbs[RTE_MAX_ETHPORTS][RTE_MAX_QUEUES_PER_PORT], > +tx_cbs[RTE_MAX_ETHPORTS][RTE_MAX_QUEUES_PER_PORT]; > + > +static inline int > +pdump_pktmbuf_copy_data(struct rte_mbuf *seg, const struct rte_mbuf *m) > +{ > + if (rte_pktmbuf_tailroom(seg) < m->data_len) { > + RTE_LOG(ERR, PDUMP, > + "User mempool: insufficient data_len of mbuf\n"); > + return -EINVAL; > + } > + > + seg->port = m->port; > + seg->vlan_tci = m->vlan_tci; > + seg->hash = m->hash; > + seg->tx_offload = m->tx_offload; > + seg->ol_flags = m->ol_flags; > + seg->packet_type = m->packet_type; > + seg->vlan_tci_outer = m->vlan_tci_outer; > + seg->data_len = m->data_len; > + seg->pkt_len = seg->data_len; > + rte_memcpy(rte_pktmbuf_mtod(seg, void *), > + rte_pktmbuf_mtod(m, void *), > + rte_pktmbuf_data_len(seg)); > + > + return 0; > +} > + > +static inline struct rte_mbuf * > +pdump_pktmbuf_copy(struct rte_mbuf *m, struct rte_mempool *mp) > +{ > + struct rte_mbuf *m_dup, *seg, **prev; > + uint32_t pktlen; > + uint8_t nseg; > + > + m_dup = rte_pktmbuf_alloc(mp); > + if (unlikely(m_dup == NULL)) > + return NULL; > + > + seg = m_dup; > + prev = &seg->next; > + pktlen = m->pkt_len; > + nseg = 0; > + > + do { > + nseg++; > + if (pdump_pktmbuf_copy_data(seg, m) < 0) { > + rte_pktmbuf_free(m_dup); > + return NULL; > + } > + *prev = seg; > + prev = &seg->next; > + } while ((m = m->next) != NULL && > + (seg = rte_pktmbuf_alloc(mp)) != NULL); > + > + *prev = NULL; > + m_dup->nb_segs = nseg; > + m_dup->pkt_len = pktlen; > + > + /* Allocation of new indirect segment failed */ > + if (unlikely(seg == NULL)) { > + rte_pktmbuf_free(m_dup); > + return NULL; > + } > + > + __rte_mbuf_sanity_check(m_dup, 1); > + return m_dup; > +} > + > +static inline void > +pdump_copy(struct rte_mbuf **pkts, uint16_t nb_pkts, void *user_params) > +{ > + unsigned i; > + int ring_enq; > + uint16_t d_pkts = 0; > + struct rte_mbuf *dup_bufs[nb_pkts]; > + struct pdump_rxtx_cbs *cbs; > + struct rte_ring *ring; > + struct rte_mempool *mp; > + struct rte_mbuf *p; > + > + cbs = user_params; > + ring = cbs->ring; > + mp = cbs->mp; > + for (i = 0; i < nb_pkts; i++) { > + p = pdump_pktmbuf_copy(pkts[i], mp); > + if (p) > + dup_bufs[d_pkts++] = p; > + } > + > + ring_enq = rte_ring_enqueue_burst(ring, (void *)dup_bufs, d_pkts); > + if (unlikely(ring_enq < d_pkts)) { > + RTE_LOG(DEBUG, PDUMP, > + "only %d of packets enqueued to ring\n", ring_enq); > + do { > + rte_pktmbuf_free(dup_bufs[ring_enq]); > + } while (++ring_enq < d_pkts); > + } > +} > + > +static uint16_t > +pdump_rx(uint8_t port __rte_unused, uint16_t qidx __rte_unused, > + struct rte_mbuf **pkts, uint16_t nb_pkts, > + uint16_t max_pkts __rte_unused, > + void *user_params) > +{ > + pdump_copy(pkts, nb_pkts, user_params); > + return nb_pkts; > +} > + > +static uint16_t > +pdump_tx(uint8_t port __rte_unused, uint16_t qidx __rte_unused, > + struct rte_mbuf **pkts, uint16_t nb_pkts, void *user_params) > +{ > + pdump_copy(pkts, nb_pkts, user_params); > + return nb_pkts; > +} > + > +static int > +pdump_get_dombdf(char *device_id, char *domBDF, size_t len) > +{ > + int ret; > + struct rte_pci_addr dev_addr = {0}; > + > + /* identify if device_id is pci address or name */ > + ret = eal_parse_pci_DomBDF(device_id, &dev_addr); > + if (ret < 0) > + return -1; > + > + if (dev_addr.domain) > + ret = snprintf(domBDF, len, "%u:%u:%u.%u", dev_addr.domain, > + dev_addr.bus, dev_addr.devid, > + dev_addr.function); > + else > + ret = snprintf(domBDF, len, "%u:%u.%u", dev_addr.bus, > + dev_addr.devid, > + dev_addr.function); > + > + return ret; > +} > + > +static int > +pdump_regitser_rx_callbacks(uint16_t end_q, uint8_t port, uint16_t queue, > + struct rte_ring *ring, struct rte_mempool *mp, > + uint16_t operation) > +{ > + uint16_t qid; > + struct pdump_rxtx_cbs *cbs = NULL; > + > + qid = (queue == RTE_PDUMP_ALL_QUEUES) ? 0 : queue; > + for (; qid < end_q; qid++) { > + cbs = &rx_cbs[port][qid]; > + if (cbs && operation == ENABLE) { > + if (cbs->cb) { > + RTE_LOG(ERR, PDUMP, > + "failed to add rx callback for port=%d " > + "and queue=%d, callback already exists\n", > + port, qid); > + return -EEXIST; > + } > + cbs->ring = ring; > + cbs->mp = mp; > + cbs->cb = rte_eth_add_first_rx_callback(port, qid, > + pdump_rx, cbs); > + if (cbs->cb == NULL) { > + RTE_LOG(ERR, PDUMP, > + "failed to add rx callback, errno=%d\n", > + rte_errno); > + return rte_errno; > + } > + } > + if (cbs && operation == DISABLE) { > + int ret; > + > + if (cbs->cb == NULL) { > + RTE_LOG(ERR, PDUMP, > + "failed to delete non existing rx " > + "callback for port=%d and queue=%d\n", > + port, qid); > + return -EINVAL; > + } > + ret = rte_eth_remove_rx_callback(port, qid, cbs->cb); > + if (ret < 0) { > + RTE_LOG(ERR, PDUMP, > + "failed to remove rx callback, errno=%d\n", > + rte_errno); > + return ret; > + } > + cbs->cb = NULL; > + } > + } > + > + return 0; > +} > + > +static int > +pdump_regitser_tx_callbacks(uint16_t end_q, uint8_t port, uint16_t queue, > + struct rte_ring *ring, struct rte_mempool *mp, > + uint16_t operation) > +{ > + > + uint16_t qid; > + struct pdump_rxtx_cbs *cbs = NULL; > + > + qid = (queue == RTE_PDUMP_ALL_QUEUES) ? 0 : queue; > + for (; qid < end_q; qid++) { > + cbs = &tx_cbs[port][qid]; > + if (cbs && operation == ENABLE) { > + if (cbs->cb) { > + RTE_LOG(ERR, PDUMP, > + "failed to add tx callback for port=%d " > + "and queue=%d, callback already exists\n", > + port, qid); > + return -EEXIST; > + } > + cbs->ring = ring; > + cbs->mp = mp; > + cbs->cb = rte_eth_add_tx_callback(port, qid, pdump_tx, > + cbs); > + if (cbs->cb == NULL) { > + RTE_LOG(ERR, PDUMP, > + "failed to add tx callback, errno=%d\n", > + rte_errno); > + return rte_errno; > + } > + } > + if (cbs && operation == DISABLE) { > + int ret; > + > + if (cbs->cb == NULL) { > + RTE_LOG(ERR, PDUMP, > + "failed to delete non existing tx " > + "callback for port=%d and queue=%d\n", > + port, qid); > + return -EINVAL; > + } > + ret = rte_eth_remove_tx_callback(port, qid, cbs->cb); > + if (ret < 0) { > + RTE_LOG(ERR, PDUMP, > + "failed to remove tx callback, errno=%d\n", > + rte_errno); > + return ret; > + } > + cbs->cb = NULL; > + } > + } > + > + return 0; > +} > + > +static int > +set_pdump_rxtx_cbs(struct pdump_request *p) > +{ > + uint16_t nb_rx_q, nb_tx_q = 0, end_q, queue; > + uint8_t port; > + int ret = 0; > + uint32_t flags; > + uint16_t operation; > + struct rte_ring *ring; > + struct rte_mempool *mp; > + > + flags = p->flags; > + operation = p->op; > + if (operation == ENABLE) { > + ret = rte_eth_dev_get_port_by_name(p->data.en_v1.device, > + &port); > + if (ret < 0) { > + RTE_LOG(ERR, PDUMP, > + "failed to get potid for device id=%s\n", > + p->data.en_v1.device); > + return -EINVAL; > + } > + queue = p->data.en_v1.queue; > + ring = p->data.en_v1.ring; > + mp = p->data.en_v1.mp; > + } else { > + ret = rte_eth_dev_get_port_by_name(p->data.dis_v1.device, > + &port); > + if (ret < 0) { > + RTE_LOG(ERR, PDUMP, > + "failed to get potid for device id=%s\n", > + p->data.dis_v1.device); > + return -EINVAL; > + } > + queue = p->data.dis_v1.queue; > + ring = p->data.dis_v1.ring; > + mp = p->data.dis_v1.mp; > + } > + > + /* validation if packet capture is for all queues */ > + if (queue == RTE_PDUMP_ALL_QUEUES) { > + struct rte_eth_dev_info dev_info; > + > + rte_eth_dev_info_get(port, &dev_info); > + nb_rx_q = dev_info.nb_rx_queues; > + nb_tx_q = dev_info.nb_tx_queues; > + if (nb_rx_q == 0 && flags & RTE_PDUMP_FLAG_RX) { > + RTE_LOG(ERR, PDUMP, > + "number of rx queues cannot be 0\n"); > + return -EINVAL; > + } > + if (nb_tx_q == 0 && flags & RTE_PDUMP_FLAG_TX) { > + RTE_LOG(ERR, PDUMP, > + "number of tx queues cannot be 0\n"); > + return -EINVAL; > + } > + if ((nb_tx_q == 0 || nb_rx_q == 0) && > + flags == RTE_PDUMP_FLAG_RXTX) { > + RTE_LOG(ERR, PDUMP, > + "both tx&rx queues must be non zero\n"); > + return -EINVAL; > + } > + } > + > + /* register RX callback */ > + if (flags & RTE_PDUMP_FLAG_RX) { > + end_q = (queue == RTE_PDUMP_ALL_QUEUES) ? nb_rx_q : queue + 1; > + ret = pdump_regitser_rx_callbacks(end_q, port, queue, ring, mp, > + operation); > + if (ret < 0) > + return ret; > + } > + > + /* register TX callback */ > + if (flags & RTE_PDUMP_FLAG_TX) { > + end_q = (queue == RTE_PDUMP_ALL_QUEUES) ? nb_tx_q : queue + 1; > + ret = pdump_regitser_tx_callbacks(end_q, port, queue, ring, mp, > + operation); > + if (ret < 0) > + return ret; > + } > + > + return ret; > +} > + > +/* get socket path (/var/run if root, $HOME otherwise) */ > +static void > +pdump_get_socket_path(char *buffer, int bufsz, enum pdump_socktype type) > +{ > + const char *dir = NULL; > + > + if (type == SERVER && socket_dir[0] != 0) > + dir = socket_dir; > + else { > + > + if (getuid() != 0) > + dir = getenv(SOCKET_PATH_HOME); > + else > + dir = SOCKET_PATH_VAR_RUN; > + } > + > + mkdir(dir, 700); > + if (type == SERVER) > + snprintf(buffer, bufsz, SERVER_SOCKET, dir); > + else > + snprintf(buffer, bufsz, CLIENT_SOCKET, dir, getpid(), > + rte_sys_gettid()); > +} > + > +static int > +pdump_create_server_socket(void) > +{ > + int ret, socket_fd; > + struct sockaddr_un addr; > + socklen_t addr_len; > + > + pdump_get_socket_path(addr.sun_path, sizeof(addr.sun_path), SERVER); > + addr.sun_family = AF_UNIX; > + > + /* remove if file already exists */ > + unlink(addr.sun_path); > + > + /* set up a server socket */ > + socket_fd = socket(AF_UNIX, SOCK_DGRAM, 0); > + if (socket_fd < 0) { > + RTE_LOG(ERR, PDUMP, > + "Failed to create server socket: %s, %s:%d\n", > + strerror(errno), __func__, __LINE__); > + return -1; > + } > + > + addr_len = sizeof(struct sockaddr_un); > + ret = bind(socket_fd, (struct sockaddr *) &addr, addr_len); > + if (ret) { > + RTE_LOG(ERR, PDUMP, > + "Failed to bind to server socket: %s, %s:%d\n", > + strerror(errno), __func__, __LINE__); > + close(socket_fd); > + return -1; > + } > + > + /* save the socket in local configuration */ > + pdump_socket_fd = socket_fd; > + > + return 0; > +} > + > +static __attribute__((noreturn)) void * > +pdump_thread_main(__rte_unused void *arg) > +{ > + struct sockaddr_un cli_addr; > + socklen_t cli_len; > + struct pdump_request cli_req; > + struct pdump_response resp; > + int n; > + int ret = 0; > + > + /* host thread, never break out */ > + for (;;) { > + /* recv client requests */ > + cli_len = sizeof(cli_addr); > + n = recvfrom(pdump_socket_fd, &cli_req, > + sizeof(struct pdump_request), 0, > + (struct sockaddr *)&cli_addr, &cli_len); > + if (n < 0) { > + RTE_LOG(ERR, PDUMP, > + "failed to recv from client:%s, %s:%d\n", > + strerror(errno), __func__, __LINE__); > + continue; > + } > + > + ret = set_pdump_rxtx_cbs(&cli_req); > + > + resp.ver = cli_req.ver; > + resp.res_op = cli_req.op; > + resp.err_value = ret; > + n = sendto(pdump_socket_fd, &resp, > + sizeof(struct pdump_response), > + 0, (struct sockaddr *)&cli_addr, cli_len); > + if (n < 0) { > + RTE_LOG(ERR, PDUMP, > + "failed to send to client:%s, %s:%d\n", > + strerror(errno), __func__, __LINE__); > + } > + } > +} > + > +int > +rte_pdump_init(const char *path) > +{ > + int ret = 0; > + char thread_name[RTE_MAX_THREAD_NAME_LEN]; > + > + ret = rte_pdump_set_socket_dir(path); > + if (ret != 0) > + return -1; > + > + ret = pdump_create_server_socket(); > + if (ret != 0) { > + RTE_LOG(ERR, PDUMP, > + "Failed to create server socket:%s:%d\n", > + __func__, __LINE__); > + return -1; > + } > + > + /* create the host thread to wait/handle pdump requests */ > + ret = pthread_create(&pdump_thread, NULL, pdump_thread_main, NULL); > + if (ret != 0) { > + RTE_LOG(ERR, PDUMP, > + "Failed to create the pdump thread:%s, %s:%d\n", > + strerror(errno), __func__, __LINE__); > + return -1; > + } > + /* Set thread_name for aid in debugging. */ > + snprintf(thread_name, RTE_MAX_THREAD_NAME_LEN, "pdump-thread"); > + ret = rte_thread_setname(pdump_thread, thread_name); > + if (ret != 0) { > + RTE_LOG(DEBUG, PDUMP, > + "Failed to set thread name for pdump handling\n"); > + } > + > + return 0; > +} > + > +int > +rte_pdump_uninit(void) > +{ > + int ret; > + > + ret = pthread_cancel(pdump_thread); > + if (ret != 0) { > + RTE_LOG(ERR, PDUMP, > + "Failed to cancel the pdump thread:%s, %s:%d\n", > + strerror(errno), __func__, __LINE__); > + return -1; > + } > + > + ret = close(pdump_socket_fd); > + if (ret != 0) { > + RTE_LOG(ERR, PDUMP, > + "Failed to close server socket: %s, %s:%d\n", > + strerror(errno), __func__, __LINE__); > + return -1; > + } > + > + struct sockaddr_un addr; > + > + pdump_get_socket_path(addr.sun_path, sizeof(addr.sun_path), SERVER); > + ret = unlink(addr.sun_path); > + if (ret != 0) { > + RTE_LOG(ERR, PDUMP, > + "Failed to remove server socket addr: %s, %s:%d\n", > + strerror(errno), __func__, __LINE__); > + return -1; > + } > + > + return 0; > +} > + > +static int > +pdump_create_client_socket(struct pdump_request *p) > +{ > + int ret, socket_fd; > + int pid; > + int n; > + struct pdump_response server_resp; > + struct sockaddr_un addr, serv_addr, from; > + socklen_t addr_len, serv_len; > + > + pid = getpid(); > + > + socket_fd = socket(AF_UNIX, SOCK_DGRAM, 0); > + if (socket_fd < 0) { > + RTE_LOG(ERR, PDUMP, > + "client socket(): %s:pid(%d):tid(%u), %s:%d\n", > + strerror(errno), pid, rte_sys_gettid(), > + __func__, __LINE__); > + ret = errno; > + return ret; > + } > + > + pdump_get_socket_path(addr.sun_path, sizeof(addr.sun_path), CLIENT); > + addr.sun_family = AF_UNIX; > + addr_len = sizeof(struct sockaddr_un); > + > + do { > + ret = bind(socket_fd, (struct sockaddr *) &addr, addr_len); > + if (ret) { > + RTE_LOG(ERR, PDUMP, > + "client bind(): %s, %s:%d\n", > + strerror(errno), __func__, __LINE__); > + ret = errno; > + break; > + } > + > + serv_len = sizeof(struct sockaddr_un); > + memset(&serv_addr, 0, sizeof(serv_addr)); > + pdump_get_socket_path(serv_addr.sun_path, > + sizeof(serv_addr.sun_path), > + SERVER); > + serv_addr.sun_family = AF_UNIX; > + > + n = sendto(socket_fd, p, sizeof(struct pdump_request), 0, > + (struct sockaddr *)&serv_addr, serv_len); > + if (n < 0) { > + RTE_LOG(ERR, PDUMP, > + "failed to send to server:%s, %s:%d\n", > + strerror(errno), __func__, __LINE__); > + ret = errno; > + break; > + } > + > + n = recvfrom(socket_fd, &server_resp, > + sizeof(struct pdump_response), 0, > + (struct sockaddr *)&from, &serv_len); > + if (n < 0) { > + RTE_LOG(ERR, PDUMP, > + "failed to recv from server:%s, %s:%d\n", > + strerror(errno), __func__, __LINE__); > + ret = errno; > + break; > + } > + ret = server_resp.err_value; > + } while (0); > + > + close(socket_fd); > + unlink(addr.sun_path); > + return ret; > +} > + > +static int > +pdump_validate_ring_mp(struct rte_ring *ring, struct rte_mempool *mp) > +{ > + if (ring == NULL || mp == NULL) { > + RTE_LOG(ERR, PDUMP, "NULL ring or mempool are passed %s:%d\n", > + __func__, __LINE__); > + rte_errno = EINVAL; > + return -1; > + } > + if (mp->flags & MEMPOOL_F_SP_PUT || mp->flags & MEMPOOL_F_SC_GET) { > + RTE_LOG(ERR, PDUMP, "mempool with either SP or SC settings" > + " is not valid for pdump, should have MP and MC settings\n"); > + rte_errno = EINVAL; > + return -1; > + } > + if (ring->prod.sp_enqueue || ring->cons.sc_dequeue) { > + RTE_LOG(ERR, PDUMP, "ring with either SP or SC settings" > + " is not valid for pdump, should have MP and MC settings\n"); > + rte_errno = EINVAL; > + return -1; > + } > + > + return 0; > +} > + > +static int > +pdump_validate_flags(uint32_t flags) > +{ > + if (flags != RTE_PDUMP_FLAG_RX && flags != RTE_PDUMP_FLAG_TX && > + flags != RTE_PDUMP_FLAG_RXTX) { > + RTE_LOG(ERR, PDUMP, > + "invalid flags, should be either rx/tx/rxtx\n"); > + rte_errno = EINVAL; > + return -1; > + } > + > + return 0; > +} > + > +static int > +pdump_validate_port(uint8_t port, char *name) > +{ > + int ret = 0; > + > + if (port >= RTE_MAX_ETHPORTS) { > + RTE_LOG(ERR, PDUMP, "Invalid port id %u, %s:%d\n", port, > + __func__, __LINE__); > + rte_errno = EINVAL; > + return -1; > + } > + > + ret = rte_eth_dev_get_name_by_port(port, name); > + if (ret < 0) { > + RTE_LOG(ERR, PDUMP, > + "port id to name mapping failed for port id=%u, %s:%d\n", > + port, __func__, __LINE__); > + rte_errno = EINVAL; > + return -1; > + } > + > + return 0; > +} > + > +static int > +pdump_prepare_client_request(char *device, uint16_t queue, > + uint32_t flags, > + uint16_t operation, > + struct rte_ring *ring, > + struct rte_mempool *mp, > + void *filter) > +{ > + int ret; > + struct pdump_request req = {.ver = 1,}; > + > + req.flags = flags; > + req.op = operation; > + if ((operation & ENABLE) != 0) { > + strncpy(req.data.en_v1.device, device, strlen(device)); > + req.data.en_v1.queue = queue; > + req.data.en_v1.ring = ring; > + req.data.en_v1.mp = mp; > + req.data.en_v1.filter = filter; > + } else { > + strncpy(req.data.dis_v1.device, device, strlen(device)); > + req.data.dis_v1.queue = queue; > + req.data.dis_v1.ring = NULL; > + req.data.dis_v1.mp = NULL; > + req.data.dis_v1.filter = NULL; > + } > + > + ret = pdump_create_client_socket(&req); > + if (ret < 0) { > + RTE_LOG(ERR, PDUMP, > + "client request for pdump enable/disable failed\n"); > + rte_errno = ret; > + return -1; > + } > + > + return 0; > +} > + > +int > +rte_pdump_enable(uint8_t port, uint16_t queue, uint32_t flags, > + struct rte_ring *ring, > + struct rte_mempool *mp, > + void *filter) > +{ > + > + int ret = 0; > + char name[DEVICE_ID_SIZE]; > + > + ret = pdump_validate_port(port, name); > + if (ret < 0) > + return ret; > + ret = pdump_validate_ring_mp(ring, mp); > + if (ret < 0) > + return ret; > + ret = pdump_validate_flags(flags); > + if (ret < 0) > + return ret; > + > + ret = pdump_prepare_client_request(name, queue, flags, > + ENABLE, ring, mp, filter); > + > + return ret; > +} > + > +int > +rte_pdump_enable_by_deviceid(char *device_id, uint16_t queue, > + uint32_t flags, > + struct rte_ring *ring, > + struct rte_mempool *mp, > + void *filter) > +{ > + int ret = 0; > + char domBDF[DEVICE_ID_SIZE]; > + > + ret = pdump_validate_ring_mp(ring, mp); > + if (ret < 0) > + return ret; > + ret = pdump_validate_flags(flags); > + if (ret < 0) > + return ret; > + > + if (pdump_get_dombdf(device_id, domBDF, sizeof(domBDF)) > 0) > + ret = pdump_prepare_client_request(domBDF, queue, flags, > + ENABLE, ring, mp, filter); > + else > + ret = pdump_prepare_client_request(device_id, queue, flags, > + ENABLE, ring, mp, filter); > + > + return ret; > +} > + > +int > +rte_pdump_disable(uint8_t port, uint16_t queue, uint32_t flags) > +{ > + int ret = 0; > + char name[DEVICE_ID_SIZE]; > + > + ret = pdump_validate_port(port, name); > + if (ret < 0) > + return ret; > + ret = pdump_validate_flags(flags); > + if (ret < 0) > + return ret; > + > + ret = pdump_prepare_client_request(name, queue, flags, > + DISABLE, NULL, NULL, NULL); > + > + return ret; > +} > + > +int > +rte_pdump_disable_by_deviceid(char *device_id, uint16_t queue, > + uint32_t flags) > +{ > + int ret = 0; > + char domBDF[DEVICE_ID_SIZE]; > + > + ret = pdump_validate_flags(flags); > + if (ret < 0) > + return ret; > + > + if (pdump_get_dombdf(device_id, domBDF, sizeof(domBDF)) > 0) > + ret = pdump_prepare_client_request(domBDF, queue, flags, > + DISABLE, NULL, NULL, NULL); > + else > + ret = pdump_prepare_client_request(device_id, queue, flags, > + DISABLE, NULL, NULL, NULL); > + > + return ret; > +} > + > +int > +rte_pdump_set_socket_dir(const char *path) > +{ > + int ret, count; > + > + if (path != NULL) { > + count = sizeof(socket_dir); > + ret = snprintf(socket_dir, count, "%s", path); > + if (ret < 0 || ret >= count) { > + RTE_LOG(ERR, PDUMP, > + "Invalid server socket path:%s:%d\n", > + __func__, __LINE__); > + socket_dir[0] = 0; > + return -EINVAL; > + } > + } > + > + return 0; > +} > diff --git a/lib/librte_pdump/rte_pdump.h b/lib/librte_pdump/rte_pdump.h > new file mode 100644 > index 0000000..63e8ac3 > --- /dev/null > +++ b/lib/librte_pdump/rte_pdump.h > @@ -0,0 +1,208 @@ > +/*- > + * BSD LICENSE > + * > + * Copyright(c) 2016 Intel Corporation. All rights reserved. > + * All rights reserved. > + * > + * Redistribution and use in source and binary forms, with or without > + * modification, are permitted provided that the following conditions > + * are met: > + * > + * * Redistributions of source code must retain the above copyright > + * notice, this list of conditions and the following disclaimer. > + * * Redistributions in binary form must reproduce the above copyright > + * notice, this list of conditions and the following disclaimer in > + * the documentation and/or other materials provided with the > + * distribution. > + * * Neither the name of Intel Corporation nor the names of its > + * contributors may be used to endorse or promote products derived > + * from this software without specific prior written permission. > + * > + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS > + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT > + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR > + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT > + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, > + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT > + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, > + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY > + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT > + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE > + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. > + */ > + > +#ifndef _RTE_PDUMP_H_ > +#define _RTE_PDUMP_H_ > + > +/** > + * @file > + * RTE pdump > + * > + * packet dump library to provide packet capturing support on dpdk. > + */ > + > +#ifdef __cplusplus > +extern "C" { > +#endif > + > +#define RTE_PDUMP_ALL_QUEUES UINT16_MAX > + > +enum { > + RTE_PDUMP_FLAG_RX = 1, /* receive direction */ > + RTE_PDUMP_FLAG_TX = 2, /* transmit direction */ > + /* both receive and transmit directions */ > + RTE_PDUMP_FLAG_RXTX = (RTE_PDUMP_FLAG_RX|RTE_PDUMP_FLAG_TX) > +}; > + > +/** > + * Initialize packet capturing handling > + * > + * Creates pthread and server socket for handling clients > + * requests to enable/disable rxtx callbacks. > + * > + * @param path > + * directory path for server socket. > + * > + * @return > + * 0 on success, -1 on error > + */ > +int > +rte_pdump_init(const char *path); > + > +/** > + * Un initialize packet capturing handling > + * > + * Cancels pthread, close server socket, removes server socket address. > + * > + * @return > + * 0 on success, -1 on error > + */ > +int > +rte_pdump_uninit(void); > + > +/** > + * Enables packet capturing on given port and queue. > + * > + * @param port > + * port on which packet capturing should be enabled. > + * @param queue > + * queue of a given port on which packet capturing should be enabled. > + * users should pass on value UINT16_MAX to enable packet capturing on all > + * queues of a given port. > + * @param flags > + * flags specifies RTE_PDUMP_FLAG_RX/RTE_PDUMP_FLAG_TX/RTE_PDUMP_FLAG_RXTX > + * on which packet capturing should be enabled for a given port and queue. > + * @param ring > + * ring on which captured packets will be enqueued for user. > + * @param mp > + * mempool on to which original packets will be mirrored or duplicated. > + * @param filter > + * place holder for packet filtering. > + * > + * @return > + * 0 on success, -1 on error, rte_errno is set accordingly. > + */ > + > +int > +rte_pdump_enable(uint8_t port, uint16_t queue, uint32_t flags, > + struct rte_ring *ring, > + struct rte_mempool *mp, > + void *filter); > + > +/** > + * Disables packet capturing on given port and queue. > + * > + * @param port > + * port on which packet capturing should be disabled. > + * @param queue > + * queue of a given port on which packet capturing should be disabled. > + * users should pass on value UINT16_MAX to disable packet capturing on all > + * queues of a given port. > + * @param flags > + * flags specifies RTE_PDUMP_FLAG_RX/RTE_PDUMP_FLAG_TX/RTE_PDUMP_FLAG_RXTX > + * on which packet capturing should be enabled for a given port and queue. > + * > + * @return > + * 0 on success, -1 on error, rte_errno is set accordingly. > + */ > + > +int > +rte_pdump_disable(uint8_t port, uint16_t queue, uint32_t flags); > + > +/** > + * Enables packet capturing on given device id and queue. > + * device_id can be name or pci address of device. > + * > + * @param device_id > + * device id on which packet capturing should be enabled. > + * @param queue > + * queue of a given device id on which packet capturing should be enabled. > + * users should pass on value UINT16_MAX to enable packet capturing on all > + * queues of a given device id. > + * @param flags > + * flags specifies RTE_PDUMP_FLAG_RX/RTE_PDUMP_FLAG_TX/RTE_PDUMP_FLAG_RXTX > + * on which packet capturing should be enabled for a given port and queue. > + * @param ring > + * ring on which captured packets will be enqueued for user. > + * @param mp > + * mempool on to which original packets will be mirrored or duplicated. > + * @param filter > + * place holder for packet filtering. > + * > + * @return > + * 0 on success, -1 on error, rte_errno is set accordingly. > + */ > + > +int > +rte_pdump_enable_by_deviceid(char *device_id, uint16_t queue, > + uint32_t flags, > + struct rte_ring *ring, > + struct rte_mempool *mp, > + void *filter); > + > +/** > + * Disables packet capturing on given device_id and queue. > + * device_id can be name or pci address of device. > + * > + * @param device_id > + * pci address or name of the device on which packet capturing > + * should be disabled. > + * @param queue > + * queue of a given device on which packet capturing should be disabled. > + * users should pass on value UINT16_MAX to disable packet capturing on all > + * queues of a given device id. > + * @param flags > + * flags specifies RTE_PDUMP_FLAG_RX/RTE_PDUMP_FLAG_TX/RTE_PDUMP_FLAG_RXTX > + * on which packet capturing should be enabled for a given port and queue. > + * > + * @return > + * 0 on success, -1 on error, rte_errno is set accordingly. > + */ > +int > +rte_pdump_disable_by_deviceid(char *device_id, uint16_t queue, > + uint32_t flags); > + > +/** > + * Allows applications to set server socket path. > + * If specified path is null default path will be selected, i.e. > + *"/var/run/" for root user and "$HOME" for non root user. > + * Clients need to call this API only when their server path is non default > + * path. This path will be used internally to send pdump enable or disable > + * requests to the server. > + * This API is not thread-safe. > + * > + * @param path > + * directory path for server socket. > + * > + * @return > + * 0 on success, -EINVAL on error > + * > + */ > +int > +rte_pdump_set_socket_dir(const char *path); > + > +#ifdef __cplusplus > +} > +#endif > + > +#endif /* _RTE_PDUMP_H_ */ > diff --git a/lib/librte_pdump/rte_pdump_version.map > b/lib/librte_pdump/rte_pdump_version.map > new file mode 100644 > index 0000000..edec99a > --- /dev/null > +++ b/lib/librte_pdump/rte_pdump_version.map > @@ -0,0 +1,13 @@ > +DPDK_16.07 { > + global: > + > + rte_pdump_disable; > + rte_pdump_disable_by_deviceid; > + rte_pdump_enable; > + rte_pdump_enable_by_deviceid; > + rte_pdump_init; > + rte_pdump_set_socket_dir; > + rte_pdump_uninit; > + > + local: *; > +}; > diff --git a/mk/rte.app.mk b/mk/rte.app.mk > index b84b56d..f792f2a 100644 > --- a/mk/rte.app.mk > +++ b/mk/rte.app.mk > @@ -61,6 +61,7 @@ _LDLIBS-y += --whole-archive > > _LDLIBS-$(CONFIG_RTE_LIBRTE_DISTRIBUTOR) += -lrte_distributor > _LDLIBS-$(CONFIG_RTE_LIBRTE_REORDER) += -lrte_reorder > +_LDLIBS-$(CONFIG_RTE_LIBRTE_PDUMP) += -lrte_pdump > > ifeq ($(CONFIG_RTE_EXEC_ENV_LINUXAPP),y) > _LDLIBS-$(CONFIG_RTE_LIBRTE_KNI) += -lrte_kni