From patchwork Tue Oct 11 00:10:16 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Stephen Hemminger X-Patchwork-Id: 117827 X-Patchwork-Delegate: thomas@monjalon.net Return-Path: X-Original-To: patchwork@inbox.dpdk.org Delivered-To: patchwork@inbox.dpdk.org Received: from mails.dpdk.org (mails.dpdk.org [217.70.189.124]) by inbox.dpdk.org (Postfix) with ESMTP id EE804A0544; Tue, 11 Oct 2022 02:10:23 +0200 (CEST) Received: from [217.70.189.124] (localhost [127.0.0.1]) by mails.dpdk.org (Postfix) with ESMTP id 9A1AD40DFD; Tue, 11 Oct 2022 02:10:22 +0200 (CEST) Received: from mail-pj1-f51.google.com (mail-pj1-f51.google.com [209.85.216.51]) by mails.dpdk.org (Postfix) with ESMTP id BC6A140146 for ; Tue, 11 Oct 2022 02:10:20 +0200 (CEST) Received: by mail-pj1-f51.google.com with SMTP id l1-20020a17090a72c100b0020a6949a66aso11689683pjk.1 for ; Mon, 10 Oct 2022 17:10:20 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=networkplumber-org.20210112.gappssmtp.com; s=20210112; h=content-transfer-encoding:mime-version:message-id:date:subject:cc :to:from:from:to:cc:subject:date:message-id:reply-to; bh=M+iMwCGDEaVRN1gj+4mnrDRxuSZVSg2r8gmYJKQH+DY=; b=5mNG0+vdjzxuUiWNvgHK1PpkZak4T1/jENOBv2poytyxLEnDsRb7lNoIHWGFchGunT C2teglL/7pBFE8ZiA6VKnGq/MqN24kVB5ysosw/7TJEKH8rzhlmXejMTzS0XGvg44o3g PqgHA4B+v3oVyWl2DkoLDbPJuMxp0f1FDjekQhgTQbe4IPLmS6z8oAik2yt1/ERKVh3z mgZeJmah7wLI9xyDmEY64b5tOVPh516ISo7IJ6VROJ+ojYiyTsEKmDbiHWSf9gXRO66q RQg7y/uA1KQTWmwpwxFo+nvjTY4KJ/QV1gQYyl7nMRz6CobDM7q9Rh/pkDq/KKQqwSo+ NAtw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=content-transfer-encoding:mime-version:message-id:date:subject:cc :to:from:x-gm-message-state:from:to:cc:subject:date:message-id :reply-to; bh=M+iMwCGDEaVRN1gj+4mnrDRxuSZVSg2r8gmYJKQH+DY=; b=1gvV5GiwmpUXOXg32joK0g4wmQiQSmRtETxtQ4BQ8vbZSQVfcgbStyjb52zDXI/TBk thbeSnMO2T+bSPwPxZ36pqWJSUduyLBToSoUumz6IBEcYkpqwEK/jCgm0bAf3bdXxwUR jHwBq6ZeD0zVolZvXKVlAK1tyECeXOJs/lKOOdthvLhNttzxNFdAThZoVFe7c9AAcZLh y1sJlyZDYJcrdADIkTl4oLFGFUzZpNA4vOtyBx/69mnAVY8WRzQvRAE7uIWvF4c7f4aS P2+k1o+e4xVlKQGpNfz+eUqkI4TGEFiDlKIymSDR1kg8EX1E5KyjQOaqw1kfs66SayIF G3Cg== X-Gm-Message-State: ACrzQf0lnOxOrdAWiRfLghdG+Me8EnZOuRkZLfxy5DlTR9a5NyGp2EVW jyradeLX+meh/W18waR7Ge1oQ1wG3aXUXA== X-Google-Smtp-Source: AMsMyM7wBO528t7JO1ymQdoqUyXB1axUWNrvjLkgK9L4zDODszQvFNxKjl/tfe8mAqQFg4qccu0htw== X-Received: by 2002:a17:903:1112:b0:178:a030:5f72 with SMTP id n18-20020a170903111200b00178a0305f72mr21691407plh.12.1665447019100; Mon, 10 Oct 2022 17:10:19 -0700 (PDT) Received: from hermes.local (204-195-120-218.wavecable.com. [204.195.120.218]) by smtp.gmail.com with ESMTPSA id x15-20020a170902a38f00b0017f73dc1549sm7123693pla.263.2022.10.10.17.10.18 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 10 Oct 2022 17:10:18 -0700 (PDT) From: Stephen Hemminger To: dev@dpdk.org Cc: Stephen Hemminger Subject: [RFC] net: add experimental UDP encapsulation PMD Date: Mon, 10 Oct 2022 17:10:16 -0700 Message-Id: <20221011001016.173447-1-stephen@networkplumber.org> X-Mailer: git-send-email 2.35.1 MIME-Version: 1.0 X-BeenThere: dev@dpdk.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: DPDK patches and discussions List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: dev-bounces@dpdk.org This is a new PMD which can be useful to test a DPDK application from another test program. The PMD binds to a connected UDP socket and expects to receive and send raw Ethernet packets over that socket. This is especially useful for testing envirionments where you can't/don't want to give the test driver program route permission. Signed-off-by: Stephen Hemminger --- This is 1st draft port of some test infrastructure to get feedback and comments from community. Later version will include an example and unit tests. doc/guides/nics/features/udp.ini | 10 + doc/guides/nics/udp.rst | 30 ++ drivers/net/meson.build | 1 + drivers/net/udp/meson.build | 11 + drivers/net/udp/rte_eth_udp.c | 728 +++++++++++++++++++++++++++++++ drivers/net/udp/version.map | 3 + 6 files changed, 783 insertions(+) create mode 100644 doc/guides/nics/features/udp.ini create mode 100644 doc/guides/nics/udp.rst create mode 100644 drivers/net/udp/meson.build create mode 100644 drivers/net/udp/rte_eth_udp.c create mode 100644 drivers/net/udp/version.map diff --git a/doc/guides/nics/features/udp.ini b/doc/guides/nics/features/udp.ini new file mode 100644 index 000000000000..dfc39204dacf --- /dev/null +++ b/doc/guides/nics/features/udp.ini @@ -0,0 +1,10 @@ +; +; Supported features of the 'udp' network poll mode driver. +; +; Refer to default.ini for the full list of available PMD features. +; +[Features] +Basic stats = Y +Stats per queue = Y +Multiprocess aware = Y +Scattered Rx = P diff --git a/doc/guides/nics/udp.rst b/doc/guides/nics/udp.rst new file mode 100644 index 000000000000..7b86f5e273e9 --- /dev/null +++ b/doc/guides/nics/udp.rst @@ -0,0 +1,30 @@ +.. SPDX-License-Identifier: BSD-3-Clause + Copyright(c) 2018 Intel Corporation. + +UDP Poll Mode Driver +==================== + +UDP Poll Mode Driver (PMD) is a basic virtual driver useful for testing. +It provides a simple bare encapsulation of Ethernet frames in a UDP +socket. This is useful since the test driver application can +easily open a local UDP socket and interact with a DPDK application. +This can even be done inside a VM or container in automated test setup. + + +Driver Configuration +-------------------- + +The driver is a virtual device configured with the --vdev option. +The device name must start with the net_udp prefix follwed by numbers +or letters The name is unique for each device. Each device can have +multiple stream options and multiple devices can be used. +Multiple device definitions can be arranged using multiple --vdev. + +Both local and remote address must be specified. Both IPv4 and IPv6 +are supported examples: + +.. code-block:: console + + .//app/dpdk-testpmd -l 0-3 -n 4 \ + --vdev 'net_udp0,local=127.0.0.1:9000,remote=192.0.2.1:9000', + --vdev 'net_udp1,local=[:0],9000,remote=[2001:DB8::1]:9000' diff --git a/drivers/net/meson.build b/drivers/net/meson.build index 35bfa78dee66..36f2d9ed9b96 100644 --- a/drivers/net/meson.build +++ b/drivers/net/meson.build @@ -56,6 +56,7 @@ drivers = [ 'tap', 'thunderx', 'txgbe', + 'udp', 'vdev_netvsc', 'vhost', 'virtio', diff --git a/drivers/net/udp/meson.build b/drivers/net/udp/meson.build new file mode 100644 index 000000000000..e7bfd843f4b2 --- /dev/null +++ b/drivers/net/udp/meson.build @@ -0,0 +1,11 @@ +# SPDX-License-Identifier: BSD-3-Clause + +if is_windows + build = false + reason = 'not supported on Windows' + subdir_done() +endif + +sources = files('rte_eth_udp.c') + +pmd_supports_disable_iova_as_pa = true diff --git a/drivers/net/udp/rte_eth_udp.c b/drivers/net/udp/rte_eth_udp.c new file mode 100644 index 000000000000..8ce65721b3ec --- /dev/null +++ b/drivers/net/udp/rte_eth_udp.c @@ -0,0 +1,728 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * + * Copyright (c) 2022 Microsoft Corp. + * All rights reserved. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Which strings are valid kvargs for this driver */ +#define ETH_UDP_LOCAL_ARG "local" +#define ETH_UDP_REMOTE_ARG "remote" + +static int eth_udp_logtype; +#define PMD_LOG(level, fmt, args...) \ + rte_log(RTE_LOG_ ## level, eth_udp_logtype, \ + "%s(): " fmt "\n", __func__, ##args) + +struct pmd_internals; + +struct udp_queue { + struct rte_mempool *mb_pool; + struct pmd_internals *internals; + uint16_t queue_id; + int sock_fd; + + uint64_t pkts; + uint64_t bytes; + uint64_t nobufs; +}; + +struct pmd_internals { + uint16_t port_id; + int sock_fd; + + struct sockaddr_storage remote; + struct sockaddr_storage local; + struct rte_ether_addr eth_addr; + + struct udp_queue rx_queues[RTE_MAX_QUEUES_PER_PORT]; + struct udp_queue tx_queues[RTE_MAX_QUEUES_PER_PORT]; +}; + +static struct rte_eth_link pmd_link = { + .link_speed = RTE_ETH_SPEED_NUM_10G, + .link_duplex = RTE_ETH_LINK_FULL_DUPLEX, + .link_status = RTE_ETH_LINK_DOWN, + .link_autoneg = RTE_ETH_LINK_FIXED, +}; + +static int +parse_ipv6_address(const char *value, struct sockaddr_in6 *sin6) +{ + char *str = strdupa(value); + char *endp; + + ++str; /* skip leading '[' */ + endp = strchr(str, ']'); + if (endp == NULL) { + PMD_LOG(ERR, "missing closing ]"); + return -EINVAL; + } + + *endp++ = '\0'; + sin6->sin6_family = AF_INET6; + + if (inet_pton(AF_INET6, ++str, &sin6->sin6_addr) != 1) { + PMD_LOG(ERR, "invalid ipv6 address '%s'", str); + return -EINVAL; + } + + /* Handle [ff80::1]:999 as address and port */ + if (*endp == ':') { + sin6->sin6_port = htons(strtoul(endp + 1, NULL, 0)); + } else if (*endp != '\0') { + PMD_LOG(ERR, "incorrect ipv6 port syntax"); + return -EINVAL; + } + return 0; +} + +static int +parse_ipv4_address(const char *value, struct sockaddr_in *sin) +{ + char *str = strdupa(value); + char *endp; + + endp = strchr(str, ':'); + if (endp) + *endp++ = '\0'; + + memset(sin, 0, sizeof(*sin)); + sin->sin_family = AF_INET; + + if (inet_pton(AF_INET, str, &sin->sin_addr) != 1) { + PMD_LOG(ERR, "invalid ipv4 address '%s'", str); + return -EINVAL; + } + + if (endp != NULL) + sin->sin_port = htons(strtoul(endp, NULL, 0)); + + return 0; +} + +/* Addresses are given on Kvargs as: + * 127.0.0.1:9000 + * [::1]:9000 + */ +static int +get_address_arg(const char *key, const char *value, void *sarg) +{ + if (value == NULL) + return -EINVAL; + + PMD_LOG(DEBUG, "%s='%s'", key, value); + + if (*value == '[') + return parse_ipv6_address(value, sarg); + else + return parse_ipv4_address(value, sarg); +} + +/* Helper function to determine how many mbufs are needed per packet */ +static uint16_t +eth_mbuf_per_pkt(uint16_t port_id, + struct rte_mempool *mb_pool) +{ + const struct rte_eth_dev *dev = &rte_eth_devices[port_id]; + uint16_t buf_size = rte_pktmbuf_data_room_size(mb_pool); + + return (dev->data->mtu + buf_size - 1) / buf_size; +} + + +/* + * Receive packets from socket into mbufs. + * + * In order to handle multiple packets at a time and scattered receive + * this allocates the worst case number of buffers. + * + * If out of memory, or socket gives error returns 0. + */ +static uint16_t +eth_udp_rx(void *queue, struct rte_mbuf **pkts, uint16_t nb_pkts) +{ + struct udp_queue *udp_q = queue; + uint16_t port_id = udp_q->internals->port_id; + struct rte_mempool *mpool = udp_q->mb_pool; + unsigned int segs_per_pkt = eth_mbuf_per_pkt(port_id, mpool); + unsigned int num_segs = nb_pkts * segs_per_pkt; + struct rte_mbuf *bufs[num_segs]; + struct iovec iovecs[num_segs]; + struct mmsghdr msgs[nb_pkts]; + unsigned int seg_idx = 0, nb_iovs = 0; + uint64_t num_rx_bytes = 0; + int ret; + + /* Allocate worst case number of buffers to be used. */ + if (rte_pktmbuf_alloc_bulk(mpool, bufs, num_segs) != 0) { + PMD_LOG(ERR, "alloc mbuf failed"); + ++udp_q->nobufs; + return 0; + } + + /* Initialize the multi-packet headers and link the mbufs per packet */ + memset(msgs, 0, sizeof(msgs)); + for (uint16_t i = 0; i < nb_pkts; i++) { + msgs[i].msg_hdr.msg_iov = &iovecs[nb_iovs]; + msgs[i].msg_hdr.msg_iovlen = segs_per_pkt; + + for (unsigned int n = 0; n < segs_per_pkt; n++, nb_iovs++) { + struct rte_mbuf *mb = bufs[nb_iovs]; + + iovecs[nb_iovs].iov_base = rte_pktmbuf_mtod(mb, void *); + iovecs[nb_iovs].iov_len = rte_pktmbuf_tailroom(mb); + } + } + assert(nb_iovs == num_segs); + + ret = recvmmsg(udp_q->sock_fd, msgs, nb_pkts, 0, NULL); + if (ret < 0) { + if (!(errno == EWOULDBLOCK || errno == EINTR)) + PMD_LOG(ERR, "recv failed: %s", strerror(errno)); + + rte_pktmbuf_free_bulk(bufs, num_segs); + return 0; + } + PMD_LOG(DEBUG, "recvmmsg returned %d", ret); + + /* Adjust mbuf length and segments based on result. */ + for (int i = 0; i < ret; i++) { + struct rte_mbuf **top = &pkts[i]; + struct rte_mbuf *m0, *mb; + unsigned int unfilled; + size_t len; + + /* Number of bytes in this packet */ + len = msgs[i].msg_len; + num_rx_bytes += len; + + m0 = mb = bufs[seg_idx]; + m0->pkt_len = len; + m0->port = port_id; + m0->nb_segs = 0; + + while (len > 0) { + mb->data_len = RTE_MIN(len, + rte_pktmbuf_tailroom(mb)); + len -= mb->data_len; + *top = mb; + top = &mb->next; + + ++m0->nb_segs; + mb = bufs[++seg_idx]; + + } + *top = NULL; + + /* Drop rest of chain */ + unfilled = segs_per_pkt - m0->nb_segs; + if (unfilled > 0) { + rte_pktmbuf_free_bulk(bufs + seg_idx, unfilled); + seg_idx += unfilled; + } + } + + udp_q->pkts += ret; + udp_q->bytes += num_rx_bytes; + + /* Free any unused buffers */ + if (seg_idx < num_segs) + rte_pktmbuf_free_bulk(bufs + seg_idx, num_segs - seg_idx); + + return ret; +} + +/* + * Send mbufs over UDP socket. + */ +static uint16_t +eth_udp_tx(void *queue, struct rte_mbuf **bufs, uint16_t nb_bufs) +{ + struct udp_queue *udp_q = queue; + struct iovec iovecs[nb_bufs * RTE_MBUF_MAX_NB_SEGS]; + struct mmsghdr msgs[nb_bufs]; + unsigned int iov_iter; + int ret; + + memset(msgs, 0, sizeof(msgs)); + iov_iter = 0; + for (uint16_t i = 0; i < nb_bufs; i++) { + struct rte_mbuf *mb = bufs[i]; + unsigned int nsegs = mb->nb_segs; + + msgs[i].msg_hdr.msg_iov = &iovecs[iov_iter]; + msgs[i].msg_hdr.msg_iovlen = nsegs; + + for (unsigned int n = 0; n < nsegs; n++) { + iovecs[iov_iter].iov_base = rte_pktmbuf_mtod(mb, void *); + iovecs[iov_iter].iov_len = rte_pktmbuf_tailroom(mb); + iov_iter++; + mb = mb->next; + } + assert(mb == NULL); + } + + ret = sendmmsg(udp_q->sock_fd, msgs, nb_bufs, 0); + if (ret < 0) { + if (!(errno == EWOULDBLOCK || errno == EINTR)) + PMD_LOG(ERR, "sendmmsg failed: %s", strerror(errno)); + ret = 0; + } else { + uint64_t num_tx_bytes = 0; + + for (int i = 0; i < ret; i++) + num_tx_bytes += msgs[i].msg_len; + + udp_q->pkts += ret; + udp_q->bytes += num_tx_bytes; + } + + if (ret < nb_bufs) + rte_pktmbuf_free_bulk(bufs + ret, nb_bufs - ret); + + return ret; +} + +static int +eth_dev_configure(struct rte_eth_dev *dev __rte_unused) +{ + return 0; +} + +static int +eth_dev_start(struct rte_eth_dev *dev) +{ + dev->data->dev_link.link_status = RTE_ETH_LINK_UP; + return 0; +} + +static int +eth_dev_stop(struct rte_eth_dev *dev) +{ + struct pmd_internals *internal = dev->data->dev_private; + unsigned int i; + + + for (i = 0; i < dev->data->nb_tx_queues; i++) + internal->tx_queues[i].sock_fd = -1; + + for (i = 0; i < dev->data->nb_rx_queues; i++) { + struct udp_queue *rxq = &internal->rx_queues[i]; + + close(rxq->sock_fd); + rxq->sock_fd = -1; + } + + dev->data->dev_link.link_status = RTE_ETH_LINK_DOWN; + return 0; +} + +static int create_socket(struct pmd_internals *internals) +{ + socklen_t addrlen; + int family, sock_fd, on = 1; + + family = internals->local.ss_family; + sock_fd = socket(family, SOCK_DGRAM | SOCK_NONBLOCK | SOCK_CLOEXEC, 0); + if (sock_fd < 0) { + PMD_LOG(ERR, "socket(): failed %s", strerror(errno)); + return -1; + } + + if (setsockopt(sock_fd, SOL_SOCKET, SO_REUSEPORT, &on, sizeof(on)) < 0) { + PMD_LOG(ERR, "setsockopt(SO_REUSEPORT): failed %s", strerror(errno)); + goto fail; + } + + if (family == AF_INET6) + addrlen = sizeof(struct sockaddr_in6); + else + addrlen = sizeof(struct sockaddr_in); + + + /* if address family is not set, then local address not specified */ + if (bind(sock_fd, (struct sockaddr *)&internals->local, addrlen) < 0) { + PMD_LOG(ERR, "bind: failed %s", strerror(errno)); + goto fail; + } + + if (connect(sock_fd, (struct sockaddr *)&internals->remote, addrlen) < 0) { + PMD_LOG(ERR, "connect: failed %s", strerror(errno)); + goto fail; + } + + /* Get actual local family to reuse same address */ + addrlen = sizeof(internals->local); + if (getsockname(sock_fd, (struct sockaddr *)&internals->local, &addrlen) < 0) { + PMD_LOG(ERR, "getsockname failed %s", strerror(errno)); + goto fail; + } + + addrlen = sizeof(internals->remote); + if (getpeername(sock_fd, (struct sockaddr *)&internals->remote, &addrlen) < 0) { + PMD_LOG(ERR, "getsockname failed %s", strerror(errno)); + goto fail; + } + + return sock_fd; + +fail: + close(sock_fd); + return -1; +} + +static int +eth_rx_queue_setup(struct rte_eth_dev *dev, uint16_t rx_queue_id, + uint16_t nb_rx_desc __rte_unused, + unsigned int socket_id __rte_unused, + const struct rte_eth_rxconf *rx_conf __rte_unused, + struct rte_mempool *mb_pool) +{ + struct pmd_internals *internals = dev->data->dev_private; + struct udp_queue *rx_q = &internals->rx_queues[rx_queue_id]; + + dev->data->rx_queues[rx_queue_id] = rx_q; + rx_q->internals = internals; + rx_q->queue_id = rx_queue_id; + rx_q->mb_pool = mb_pool; + + if (rx_queue_id == 0) + rx_q->sock_fd = internals->sock_fd; + else + rx_q->sock_fd = create_socket(internals); + + return (rx_q->sock_fd < 0) ? -1 : 0; +} + +static void +eth_rx_queue_release(struct rte_eth_dev *dev, uint16_t rx_queue_id) +{ + struct pmd_internals *internals = dev->data->dev_private; + struct udp_queue *rx_q = &internals->rx_queues[rx_queue_id]; + + if (rx_q->queue_id > 0) + close(rx_q->sock_fd); + + rx_q->sock_fd = -1; +} + +static int +eth_tx_queue_setup(struct rte_eth_dev *dev, uint16_t tx_queue_id, + uint16_t nb_tx_desc __rte_unused, + unsigned int socket_id __rte_unused, + const struct rte_eth_txconf *tx_conf __rte_unused) +{ + struct pmd_internals *internals = dev->data->dev_private; + struct udp_queue *tx_q = &internals->tx_queues[tx_queue_id]; + + dev->data->tx_queues[tx_queue_id] = tx_q; + tx_q->queue_id = tx_queue_id; + tx_q->internals = internals; + tx_q->sock_fd = internals->sock_fd; + + return 0; +} + +static void +eth_tx_queue_release(struct rte_eth_dev *dev, uint16_t tx_queue_id) +{ + struct pmd_internals *internals = dev->data->dev_private; + struct udp_queue *tx_q = &internals->tx_queues[tx_queue_id]; + + tx_q->sock_fd = -1; +} + +static int +eth_mtu_set(struct rte_eth_dev *dev __rte_unused, uint16_t mtu __rte_unused) +{ + return 0; +} + +static int +eth_dev_info(struct rte_eth_dev *dev __rte_unused, + struct rte_eth_dev_info *dev_info) +{ + dev_info->max_mac_addrs = 1; + dev_info->max_rx_pktlen = UINT16_MAX; + dev_info->max_rx_queues = RTE_MAX_QUEUES_PER_PORT; + dev_info->max_tx_queues = RTE_MAX_QUEUES_PER_PORT; + dev_info->min_rx_bufsize = 0; + dev_info->tx_offload_capa = RTE_ETH_TX_OFFLOAD_MULTI_SEGS; + dev_info->rx_offload_capa = RTE_ETH_RX_OFFLOAD_SCATTER; + + return 0; +} + +static int +eth_link_update(struct rte_eth_dev *dev __rte_unused, + int wait_to_complete __rte_unused) +{ + return 0; +} + +static int +eth_mac_address_set(__rte_unused struct rte_eth_dev *dev, + __rte_unused struct rte_ether_addr *addr) +{ + return 0; +} + +static int +eth_stats_get(struct rte_eth_dev *dev, struct rte_eth_stats *stats) +{ + const struct pmd_internals *internal = dev->data->dev_private; + unsigned int i, num_stats; + + num_stats = RTE_MIN((unsigned)RTE_ETHDEV_QUEUE_STAT_CNTRS, + dev->data->nb_rx_queues); + for (i = 0; i < num_stats; i++) { + const struct udp_queue *q = &internal->rx_queues[i]; + + stats->q_ipackets[i] = q->pkts; + stats->ipackets += q->pkts; + stats->q_ibytes[i] += q->bytes; + stats->ibytes += q->bytes; + stats->rx_nombuf += q->nobufs; + } + + num_stats = RTE_MIN((unsigned)RTE_ETHDEV_QUEUE_STAT_CNTRS, + dev->data->nb_tx_queues); + for (i = 0; i < num_stats; i++) { + const struct udp_queue *q = &internal->tx_queues[i]; + + stats->q_opackets[i] = q->pkts; + stats->opackets += q->pkts; + stats->q_obytes[i] += q->bytes; + stats->obytes += q->bytes; + } + + return 0; +} + +static int +eth_stats_reset(struct rte_eth_dev *dev) +{ + struct pmd_internals *internal = dev->data->dev_private; + unsigned int i; + + for (i = 0; i < RTE_DIM(internal->rx_queues); i++) { + struct udp_queue *q = &internal->rx_queues[i]; + + q->pkts = 0; + q->bytes = 0; + q->nobufs = 0; + } + + + for (i = 0; i < RTE_DIM(internal->tx_queues); i++) { + struct udp_queue *q = &internal->tx_queues[i]; + + q->pkts = 0; + q->bytes = 0; + q->nobufs = 0; + } + + return 0; +} + +static const struct eth_dev_ops ops = { + .dev_start = eth_dev_start, + .dev_stop = eth_dev_stop, + .dev_configure = eth_dev_configure, + .dev_infos_get = eth_dev_info, + .rx_queue_setup = eth_rx_queue_setup, + .tx_queue_setup = eth_tx_queue_setup, + .rx_queue_release = eth_rx_queue_release, + .tx_queue_release = eth_tx_queue_release, + .mtu_set = eth_mtu_set, + .link_update = eth_link_update, + .mac_addr_set = eth_mac_address_set, + .stats_get = eth_stats_get, + .stats_reset = eth_stats_reset, +}; + +static int +parse_parameters(struct pmd_internals *internals, const char *params) +{ + static const char * const valid_args[] = { + "local", "remote", NULL + }; + struct rte_kvargs *kvlist; + int ret; + + if (params == NULL && params[0] == '\0') + return 0; + + PMD_LOG(INFO, "parameters \"%s\"", params); + kvlist = rte_kvargs_parse(params, valid_args); + if (kvlist == NULL) + return -1; + + ret = rte_kvargs_process(kvlist, ETH_UDP_LOCAL_ARG, + &get_address_arg, &internals->local); + if (ret < 0) + goto out; + + ret = rte_kvargs_process(kvlist, ETH_UDP_REMOTE_ARG, + &get_address_arg, &internals->remote); + +out: + rte_kvargs_free(kvlist); + return ret; +} + +static int +validate_parameters(struct pmd_internals *internals) +{ + int family = internals->remote.ss_family; + + if (family == AF_UNSPEC) { + PMD_LOG(ERR, "remote address required"); + return -EINVAL; + } + + /* + * if no local address is specified, + * then use same port and and let kernel choose. + */ + if (internals->local.ss_family == AF_UNSPEC) { + internals->local.ss_family = family; + } else if (internals->local.ss_family != family) { + PMD_LOG(ERR, "Local and remote address family differ"); + return -EINVAL; + } + + return 0; +} + +static int +rte_pmd_udp_probe(struct rte_vdev_device *dev) +{ + struct pmd_internals *internals; + struct rte_eth_dev *eth_dev; + struct rte_eth_dev_data *data; + const char *name; + int ret; + + name = rte_vdev_device_name(dev); + + PMD_LOG(INFO, "Initializing pmd_udp for %s", name); + + if (rte_eal_process_type() == RTE_PROC_SECONDARY) { + PMD_LOG(ERR, "Secondary not supported"); + return -ENOTSUP; + } + + eth_dev = rte_eth_vdev_allocate(dev, sizeof(*internals)); + if (!eth_dev) + return -ENOMEM; + + internals = eth_dev->data->dev_private; + internals->port_id = eth_dev->data->port_id; + + ret = parse_parameters(internals, rte_vdev_device_args(dev)); + if (ret < 0) + goto fail; + + ret = validate_parameters(internals); + if (ret < 0) + goto fail; + + /* + * Note: first socket is used for transmit and for + * receive queue 0. + */ + internals->sock_fd = create_socket(internals); + if (internals->sock_fd < 0) { + ret = errno ? -errno : -EINVAL; + goto fail; + } + + rte_eth_random_addr(internals->eth_addr.addr_bytes); + + data = eth_dev->data; + data->dev_link = pmd_link; + data->mac_addrs = &internals->eth_addr; + data->promiscuous = 1; + data->all_multicast = 1; + + eth_dev->dev_ops = &ops; + eth_dev->rx_pkt_burst = eth_udp_rx; + eth_dev->tx_pkt_burst = eth_udp_tx; + + rte_eth_dev_probing_finish(eth_dev); + +fail: + if (ret != 0) { + eth_dev->data->mac_addrs = NULL; + rte_eth_dev_release_port(eth_dev); + } + + return ret; +} + +static int +rte_pmd_udp_remove(struct rte_vdev_device *dev) +{ + struct rte_eth_dev *eth_dev = NULL; + const char *name; + + name = rte_vdev_device_name(dev); + + PMD_LOG(INFO, "Closing udp ethdev %s", name); + + /* find the ethdev entry */ + eth_dev = rte_eth_dev_allocated(name); + if (eth_dev == NULL) + return -1; + + /* mac_addrs must not be freed alone because part of dev_private */ + if (rte_eal_process_type() == RTE_PROC_PRIMARY) + eth_dev->data->mac_addrs = NULL; + + rte_eth_dev_release_port(eth_dev); + + return 0; +} + +static struct rte_vdev_driver pmd_udp_drv = { + .probe = rte_pmd_udp_probe, + .remove = rte_pmd_udp_remove, +}; + +RTE_PMD_REGISTER_VDEV(net_udp, pmd_udp_drv); +RTE_PMD_REGISTER_ALIAS(net_udp, eth_udp); +RTE_PMD_REGISTER_PARAM_STRING(net_udp, + "local=" + "remote="); + +RTE_INIT(eth_udp_init_log) +{ + eth_udp_logtype = rte_log_register("pmd.net.udp"); + if (eth_udp_logtype >= 0) + rte_log_set_level(eth_udp_logtype, RTE_LOG_NOTICE); +} diff --git a/drivers/net/udp/version.map b/drivers/net/udp/version.map new file mode 100644 index 000000000000..78c3585d7c6b --- /dev/null +++ b/drivers/net/udp/version.map @@ -0,0 +1,3 @@ +DPDK_23 { + local: *; +};