From: Ferruh Yigit <ferruh.yigit@amd.com>
To: Stephen Hemminger <stephen@networkplumber.org>, dev@dpdk.org
Subject: Re: [RFC] net: add experimental UDP encapsulation PMD
Date: Tue, 11 Oct 2022 17:48:20 +0100 [thread overview]
Message-ID: <669dfda0-383a-7515-dd2d-47692da86e1f@amd.com> (raw)
In-Reply-To: <20221011001016.173447-1-stephen@networkplumber.org>
On 10/11/2022 1:10 AM, Stephen Hemminger wrote:
> 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
s/envirionments/environments/
> can't/don't want to give the test driver program route permission.
>
root permission?
> Signed-off-by: Stephen Hemminger <stephen@networkplumber.org>
> ---
> 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 ++
need to update 'doc/guides/nics/index.rst' and add new file.
> 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.
> +
Is copyright owner Intel?
> +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
followed
> +or letters The name is unique for each device. Each device can have
or letters. The ...
> +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
> +
> + ./<build_dir>/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'
maybe good to document ipv6 should start with '['. (this is according to
the below code)
> 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 <assert.h>
> +#include <errno.h>
> +#include <stdbool.h>
> +#include <stdint.h>
> +#include <stdlib.h>
> +#include <sys/socket.h>
> +#include <netinet/in.h>
> +#include <arpa/inet.h>
> +#include <sys/uio.h>
> +#include <unistd.h>
> +#include <netdb.h>
> +#include <string.h>
> +
> +#include <rte_bus_vdev.h>
> +#include <rte_common.h>
> +#include <rte_ethdev.h>
> +#include <ethdev_vdev.h>
> +#include <ethdev_driver.h>
> +#include <bus_vdev_driver.h>
> +#include <rte_ether.h>
> +#include <rte_kvargs.h>
> +#include <rte_log.h>
> +#include <rte_mbuf.h>
> +
> +/* 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:
kvargs
> + * 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];
Not good to access to global device array, 'rte_eth_devices[]'.
Instead, what do you think to have 'eth_dev->data' in "struct udp_queue"
and pass it to this function?
It is an option to have 'eth_dev' reference in "struct udp_queue" but
that reference differs for primary and secondary process, that is why
'eth_dev->data' is safer.
> + 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);
> +
It can be better to use 'RTE_LOG_DP' in datapath.
> + /* 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);
User can do stop/start. If socket closed here, will it work with next start?
Should the socket be closed in 'close()' dev_ops?
> + 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);
ah, I guess 'SO_REUSEPORT' requirement is because connecting to same
port per Rx queue.
> +
> + 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);
queue stats shouldn't be part of basic stats anymore, in that case no
need to take RTE_ETHDEV_QUEUE_STAT_CNTRS into account.
> + 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);
alias is for old drivers, new ones shouldn't have it
> +RTE_PMD_REGISTER_PARAM_STRING(net_udp,
> + "local=<string>"
> + "remote=<string>");
> +
> +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);
> +}
There is 'RTE_LOG_REGISTER_DEFAULT' macro to help above
> 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: *;
> +};
prev parent reply other threads:[~2022-10-11 16:48 UTC|newest]
Thread overview: 6+ messages / expand[flat|nested] mbox.gz Atom feed top
2022-10-11 0:10 Stephen Hemminger
2022-10-11 6:47 ` Morten Brørup
2022-10-11 14:06 ` Stephen Hemminger
2022-10-11 16:18 ` Ferruh Yigit
2022-10-11 17:54 ` Stephen Hemminger
2022-10-11 16:48 ` Ferruh Yigit [this message]
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=669dfda0-383a-7515-dd2d-47692da86e1f@amd.com \
--to=ferruh.yigit@amd.com \
--cc=dev@dpdk.org \
--cc=stephen@networkplumber.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).