examples/netmap-compat: remove example

Rather than providing a shim layer on top of netmap,
we should instead encourage users to create apps using
the DPDK APIs directly.

Signed-off-by: Ciara Power <ciara.power@intel.com>
Acked-by: Stephen Hemminger <stephen@networkplumber.org>
Acked-by: Hemant Agrawal <hemant.agrawal@nxp.com>
This commit is contained in:
Ciara Power 2019-10-25 10:56:07 +01:00 committed by David Marchand
parent 46971b273e
commit d82610b940
14 changed files with 2 additions and 1891 deletions

View File

@ -1501,9 +1501,6 @@ F: doc/guides/sample_app_ug/link_status_intr.rst
F: examples/load_balancer/
F: doc/guides/sample_app_ug/load_balancer.rst
F: examples/netmap_compat/
F: doc/guides/sample_app_ug/netmap_compatibility.rst
L-threads - EXPERIMENTAL
M: John McNamara <john.mcnamara@intel.com>
F: examples/performance-thread/

View File

@ -262,6 +262,7 @@ Removed Items
* Exception Path
* L3 Forwarding in a Virtualization Environment
* Netmap Compatibility
* Quota and Watermark
* vhost-scsi

View File

@ -42,7 +42,6 @@ Sample Applications User Guides
vhost
vhost_crypto
vdpa
netmap_compatibility
ip_pipeline
test_pipeline
eventdev_pipeline

View File

@ -1,130 +0,0 @@
.. SPDX-License-Identifier: BSD-3-Clause
Copyright(c) 2010-2014 Intel Corporation.
Netmap Compatibility Sample Application
=======================================
Introduction
------------
The Netmap compatibility library provides a minimal set of APIs to give programs written against the Netmap APIs
the ability to be run, with minimal changes to their source code, using the DPDK to perform the actual packet I/O.
Since Netmap applications use regular system calls, like ``open()``, ``ioctl()`` and
``mmap()`` to communicate with the Netmap kernel module performing the packet I/O,
the ``compat_netmap`` library provides a set of similar APIs to use in place of those system calls,
effectively turning a Netmap application into a DPDK application.
The provided library is currently minimal and doesn't support all the features that Netmap supports,
but is enough to run simple applications, such as the bridge example detailed below.
Knowledge of Netmap is required to understand the rest of this section.
Please refer to the Netmap distribution for details about Netmap.
Available APIs
--------------
The library provides the following drop-in replacements for system calls usually used in Netmap applications:
* ``rte_netmap_close()``
* ``rte_netmap_ioctl()``
* ``rte_netmap_open()``
* ``rte_netmap_mmap()``
* ``rte_netmap_poll()``
They use the same signature as their libc counterparts, and can be used as drop-in replacements in most cases.
Caveats
-------
Given the difference between the way Netmap and the DPDK approach packet I/O,
there are caveats and limitations to be aware of when trying to use the ``compat_netmap`` library, the most important of these are listed below.
These may change as the library is updated:
* Any system call that can potentially affect file descriptors cannot be used with a descriptor returned by the ``rte_netmap_open()`` function.
Note that:
* The ``rte_netmap_mmap()`` function merely returns the address of a DPDK memzone.
The address, length, flags, offset, and other arguments are ignored.
* The ``rte_netmap_poll()`` function only supports infinite (negative) or zero time outs.
It effectively turns calls to the ``poll()`` system call made in a Netmap application into polling of the DPDK ports,
changing the semantics of the usual POSIX defined poll.
* Not all of Netmap's features are supported: host rings,
slot flags and so on are not supported or are simply not relevant in the DPDK model.
* The Netmap manual page states that "*a device obtained through /dev/netmap also supports the ioctl supported by network devices*".
This is not the case with this compatibility layer.
* The Netmap kernel module exposes a sysfs interface to change some internal parameters, such as the size of the shared memory region.
This interface is not available when using this compatibility layer.
Porting Netmap Applications
---------------------------
Porting Netmap applications typically involves two major steps:
* Changing the system calls to use their ``compat_netmap`` library counterparts.
* Adding further DPDK initialization code.
Since the ``compat_netmap`` functions have the same signature as the usual libc calls, the change is trivial in most cases.
The usual DPDK initialization code involving ``rte_eal_init()`` and ``rte_pci_probe()``
has to be added to the Netmap application in the same way it is used in all other DPDK sample applications.
Please refer to the *DPDK Programmer's Guide* and example source code for details about initialization.
In addition of the regular DPDK initialization code,
the ported application needs to call initialization functions for the ``compat_netmap`` library,
namely ``rte_netmap_init()`` and ``rte_netmap_init_port()``.
These two initialization functions take ``compat_netmap`` specific data structures as parameters:
``struct rte_netmap_conf`` and ``struct rte_netmap_port_conf``.
The structures' fields are Netmap related and are self-explanatory for developers familiar with Netmap.
They are defined in ``$RTE_SDK/examples/netmap_compat/lib/compat_netmap.h``.
The bridge application is an example largely based on the bridge example shipped with the Netmap distribution.
It shows how a minimal Netmap application with minimal and straightforward source code changes can be run on top of the DPDK.
Please refer to ``$RTE_SDK/examples/netmap_compat/bridge/bridge.c`` for an example of a ported application.
Compiling the Application
-------------------------
To compile the sample application see :doc:`compiling`.
The application is located in the ``netmap_compat`` sub-directory.
Running the "bridge" Sample Application
---------------------------------------
The application requires a single command line option:
.. code-block:: console
./build/bridge [EAL options] -- -i INTERFACE_A [-i INTERFACE_B]
where,
* ``-i INTERFACE``: Interface (DPDK port number) to use.
If a single ``-i`` parameter is given, the interface will send back all the traffic it receives.
If two ``-i`` parameters are given, the two interfaces form a bridge,
where traffic received on one interface is replicated and sent to the other interface.
For example, to run the application in a linux environment using port 0 and 2:
.. code-block:: console
./build/bridge [EAL options] -- -i 0 -i 2
Refer to the *DPDK Getting Started Guide for Linux* for general information on running applications and
the Environment Abstraction Layer (EAL) options.
Note that unlike a traditional bridge or the ``l2fwd`` sample application, no MAC address changes are done on the frames.
Do not forget to take this into account when configuring a traffic generators and testing this sample application.

View File

@ -50,7 +50,6 @@ endif
DIRS-y += link_status_interrupt
DIRS-$(CONFIG_RTE_LIBRTE_LPM) += load_balancer
DIRS-y += multi_process
DIRS-y += netmap_compat/bridge
DIRS-y += ntb
DIRS-$(CONFIG_RTE_LIBRTE_REORDER) += packet_ordering
ifeq ($(CONFIG_RTE_ARCH_X86_64),y)

View File

@ -30,7 +30,7 @@ all_examples = [
'multi_process/hotplug_mp',
'multi_process/simple_mp',
'multi_process/symmetric_mp',
'netmap_compat', 'ntb', 'packet_ordering',
'ntb', 'packet_ordering',
'performance-thread', 'ptpclient',
'qos_meter', 'qos_sched',
'rxtx_callbacks',

View File

@ -1,22 +0,0 @@
# SPDX-License-Identifier: BSD-3-Clause
# Copyright(c) 2010-2014 Intel Corporation
ifeq ($(RTE_SDK),)
$(error "Please define RTE_SDK environment variable")
endif
# Default target, detect a build directory, by looking for a path with a .config
RTE_TARGET ?= $(notdir $(abspath $(dir $(firstword $(wildcard $(RTE_SDK)/*/.config)))))
include $(RTE_SDK)/mk/rte.vars.mk
unexport RTE_SRCDIR RTE_OUTPUT RTE_EXTMK
DIRS-y += bridge
.PHONY: all clean $(DIRS-y)
all: $(DIRS-y)
clean: $(DIRS-y)
$(DIRS-y):
$(MAKE) -C $@ $(MAKECMDGOALS) O=$(RTE_OUTPUT)

View File

@ -1,35 +0,0 @@
# SPDX-License-Identifier: BSD-3-Clause
# Copyright(c) 2010-2014 Intel Corporation
ifeq ($(RTE_SDK),)
$(error "Please define the RTE_SDK environment variable")
endif
# Default target, detect a build directory, by looking for a path with a .config
RTE_TARGET ?= $(notdir $(abspath $(dir $(firstword $(wildcard $(RTE_SDK)/*/.config)))))
include $(RTE_SDK)/mk/rte.vars.mk
ifneq ($(CONFIG_RTE_EXEC_ENV_LINUX),y)
$(info This application can only operate in a linux environment, \
please change the definition of the RTE_TARGET environment variable)
all:
clean:
else
# binary name
APP = bridge
# for compat_netmap.c
VPATH := $(SRCDIR)/../lib
# all source are stored in SRCS-y
SRCS-y := bridge.c
SRCS-y += compat_netmap.c
CFLAGS += -O3 -I$(SRCDIR)/../lib -I$(SRCDIR)/../netmap
CFLAGS += $(WERROR_FLAGS)
include $(RTE_SDK)/mk/rte.extapp.mk
endif

View File

@ -1,347 +0,0 @@
/* SPDX-License-Identifier: BSD-3-Clause
* Copyright(c) 2010-2014 Intel Corporation
*/
#include <fcntl.h>
#include <getopt.h>
#include <inttypes.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <rte_eal.h>
#include <rte_ethdev.h>
#include <rte_mbuf.h>
#include <rte_mempool.h>
#include <rte_string_fns.h>
#include "compat_netmap.h"
#define BUF_SIZE RTE_MBUF_DEFAULT_DATAROOM
#define MBUF_DATA_SIZE (BUF_SIZE + RTE_PKTMBUF_HEADROOM)
#define MBUF_PER_POOL 8192
struct rte_eth_conf eth_conf = {
.rxmode = {
.split_hdr_size = 0,
},
.txmode = {
.mq_mode = ETH_MQ_TX_NONE,
},
};
#define MAX_QUEUE_NUM 1
#define RX_QUEUE_NUM 1
#define TX_QUEUE_NUM 1
#define MAX_DESC_NUM 0x400
#define RX_DESC_NUM 0x100
#define TX_DESC_NUM 0x200
#define RX_SYNC_NUM 0x20
#define TX_SYNC_NUM 0x20
struct rte_netmap_port_conf port_conf = {
.eth_conf = &eth_conf,
.socket_id = SOCKET_ID_ANY,
.nr_tx_rings = TX_QUEUE_NUM,
.nr_rx_rings = RX_QUEUE_NUM,
.nr_tx_slots = TX_DESC_NUM,
.nr_rx_slots = RX_DESC_NUM,
.tx_burst = TX_SYNC_NUM,
.rx_burst = RX_SYNC_NUM,
};
struct rte_netmap_conf netmap_conf = {
.socket_id = SOCKET_ID_ANY,
.max_bufsz = BUF_SIZE,
.max_rings = MAX_QUEUE_NUM,
.max_slots = MAX_DESC_NUM,
};
static int stop = 0;
#define MAX_PORT_NUM 2
struct netmap_port {
int fd;
struct netmap_if *nmif;
struct netmap_ring *rx_ring;
struct netmap_ring *tx_ring;
const char *str;
uint8_t id;
};
static struct {
uint32_t num;
struct netmap_port p[MAX_PORT_NUM];
void *mem;
} ports;
static void
usage(const char *prgname)
{
fprintf(stderr, "Usage: %s [EAL args] -- [OPTION]...\n"
"-h, --help \t Show this help message and exit\n"
"-i INTERFACE_A \t Interface (DPDK port number) to use\n"
"[ -i INTERFACE_B \t Interface (DPDK port number) to use ]\n",
prgname);
}
static uint8_t
parse_portid(const char *portid_str)
{
char *end;
unsigned id;
id = strtoul(portid_str, &end, 10);
if (end == portid_str || *end != '\0' || id > RTE_MAX_ETHPORTS)
rte_exit(EXIT_FAILURE, "Invalid port number\n");
return (uint8_t) id;
}
static int
parse_args(int argc, char **argv)
{
int opt;
while ((opt = getopt(argc, argv, "hi:")) != -1) {
switch (opt) {
case 'h':
usage(argv[0]);
rte_exit(EXIT_SUCCESS, "exiting...");
break;
case 'i':
if (ports.num >= RTE_DIM(ports.p)) {
usage(argv[0]);
rte_exit(EXIT_FAILURE, "configs with %u "
"ports are not supported\n",
ports.num + 1);
}
ports.p[ports.num].str = optarg;
ports.p[ports.num].id = parse_portid(optarg);
ports.num++;
break;
default:
usage(argv[0]);
rte_exit(EXIT_FAILURE, "invalid option: %c\n", opt);
}
}
return 0;
}
static void sigint_handler(__rte_unused int sig)
{
stop = 1;
signal(SIGINT, SIG_DFL);
}
static void move(int n, struct netmap_ring *rx, struct netmap_ring *tx)
{
uint32_t tmp;
while (n-- > 0) {
tmp = tx->slot[tx->cur].buf_idx;
tx->slot[tx->cur].buf_idx = rx->slot[rx->cur].buf_idx;
tx->slot[tx->cur].len = rx->slot[rx->cur].len;
tx->slot[tx->cur].flags |= NS_BUF_CHANGED;
tx->cur = NETMAP_RING_NEXT(tx, tx->cur);
tx->avail--;
rx->slot[rx->cur].buf_idx = tmp;
rx->slot[rx->cur].flags |= NS_BUF_CHANGED;
rx->cur = NETMAP_RING_NEXT(rx, rx->cur);
rx->avail--;
}
}
static int
netmap_port_open(uint32_t idx)
{
int err;
struct netmap_port *port;
struct nmreq req;
port = ports.p + idx;
port->fd = rte_netmap_open("/dev/netmap", O_RDWR);
strlcpy(req.nr_name, port->str, sizeof(req.nr_name));
req.nr_version = NETMAP_API;
req.nr_ringid = 0;
err = rte_netmap_ioctl(port->fd, NIOCGINFO, &req);
if (err) {
printf("[E] NIOCGINFO ioctl failed (error %d)\n", err);
return err;
}
strlcpy(req.nr_name, port->str, sizeof(req.nr_name));
req.nr_version = NETMAP_API;
req.nr_ringid = 0;
err = rte_netmap_ioctl(port->fd, NIOCREGIF, &req);
if (err) {
printf("[E] NIOCREGIF ioctl failed (error %d)\n", err);
return err;
}
/* mmap only once. */
if (ports.mem == NULL)
ports.mem = rte_netmap_mmap(NULL, req.nr_memsize,
PROT_WRITE | PROT_READ, MAP_PRIVATE, port->fd, 0);
if (ports.mem == MAP_FAILED) {
printf("[E] NETMAP mmap failed for fd: %d)\n", port->fd);
return -ENOMEM;
}
port->nmif = NETMAP_IF(ports.mem, req.nr_offset);
port->tx_ring = NETMAP_TXRING(port->nmif, 0);
port->rx_ring = NETMAP_RXRING(port->nmif, 0);
return 0;
}
int main(int argc, char *argv[])
{
int err, ret;
uint32_t i, pmsk;
struct nmreq req;
struct pollfd pollfd[MAX_PORT_NUM];
struct rte_mempool *pool;
struct netmap_ring *rx_ring, *tx_ring;
ret = rte_eal_init(argc, argv);
if (ret < 0)
rte_exit(EXIT_FAILURE, "Cannot initialize EAL\n");
argc -= ret;
argv += ret;
parse_args(argc, argv);
if (ports.num == 0)
rte_exit(EXIT_FAILURE, "no ports specified\n");
if (rte_eth_dev_count_avail() < 1)
rte_exit(EXIT_FAILURE, "Not enough ethernet ports available\n");
pool = rte_pktmbuf_pool_create("mbuf_pool", MBUF_PER_POOL, 32, 0,
MBUF_DATA_SIZE, rte_socket_id());
if (pool == NULL)
rte_exit(EXIT_FAILURE, "Couldn't create mempool\n");
netmap_conf.socket_id = rte_socket_id();
err = rte_netmap_init(&netmap_conf);
if (err < 0)
rte_exit(EXIT_FAILURE,
"Couldn't initialize librte_compat_netmap\n");
else
printf("librte_compat_netmap initialized\n");
port_conf.pool = pool;
port_conf.socket_id = rte_socket_id();
for (i = 0; i != ports.num; i++) {
err = rte_netmap_init_port(ports.p[i].id, &port_conf);
if (err < 0)
rte_exit(EXIT_FAILURE, "Couldn't setup port %hhu\n",
ports.p[i].id);
err = rte_eth_promiscuous_enable(ports.p[i].id);
if (err != 0)
rte_exit(EXIT_FAILURE,
"Couldn't enable promiscuous mode on port %u: %s\n",
ports.p[i].id, rte_strerror(-err));
}
for (i = 0; i != ports.num; i++) {
err = netmap_port_open(i);
if (err) {
rte_exit(EXIT_FAILURE, "Couldn't set port %hhu "
"under NETMAP control\n",
ports.p[i].id);
}
else
printf("Port %hhu now in Netmap mode\n", ports.p[i].id);
}
memset(pollfd, 0, sizeof(pollfd));
for (i = 0; i != ports.num; i++) {
pollfd[i].fd = ports.p[i].fd;
pollfd[i].events = POLLIN | POLLOUT;
}
signal(SIGINT, sigint_handler);
pmsk = ports.num - 1;
printf("Bridge up and running!\n");
while (!stop) {
uint32_t n_pkts;
pollfd[0].revents = 0;
pollfd[1].revents = 0;
ret = rte_netmap_poll(pollfd, ports.num, 0);
if (ret < 0) {
stop = 1;
printf("[E] poll returned with error %d\n", ret);
}
if (((pollfd[0].revents | pollfd[1].revents) & POLLERR) != 0) {
printf("POLLERR!\n");
}
if ((pollfd[0].revents & POLLIN) != 0 &&
(pollfd[pmsk].revents & POLLOUT) != 0) {
rx_ring = ports.p[0].rx_ring;
tx_ring = ports.p[pmsk].tx_ring;
n_pkts = RTE_MIN(rx_ring->avail, tx_ring->avail);
move(n_pkts, rx_ring, tx_ring);
}
if (pmsk != 0 && (pollfd[pmsk].revents & POLLIN) != 0 &&
(pollfd[0].revents & POLLOUT) != 0) {
rx_ring = ports.p[pmsk].rx_ring;
tx_ring = ports.p[0].tx_ring;
n_pkts = RTE_MIN(rx_ring->avail, tx_ring->avail);
move(n_pkts, rx_ring, tx_ring);
}
}
printf("Bridge stopped!\n");
for (i = 0; i != ports.num; i++) {
err = rte_netmap_ioctl(ports.p[i].fd, NIOCUNREGIF, &req);
if (err) {
printf("[E] NIOCUNREGIF ioctl failed (error %d)\n",
err);
}
else
printf("Port %hhu unregistered from Netmap mode\n", ports.p[i].id);
rte_netmap_close(ports.p[i].fd);
}
return 0;
}

View File

@ -1,906 +0,0 @@
/* SPDX-License-Identifier: BSD-3-Clause
* Copyright(c) 2010-2014 Intel Corporation
*/
#include <errno.h>
#include <inttypes.h>
#include <poll.h>
#include <stdio.h>
#include <stdlib.h>
#include <net/if.h>
#include <sys/types.h>
#include <sys/resource.h>
#include <sys/mman.h>
#include <rte_common.h>
#include <rte_errno.h>
#include <rte_ethdev.h>
#include <rte_log.h>
#include <rte_malloc.h>
#include <rte_mbuf.h>
#include <rte_spinlock.h>
#include <rte_string_fns.h>
#include "compat_netmap.h"
struct netmap_port {
struct rte_mempool *pool;
struct netmap_if *nmif;
struct rte_eth_conf eth_conf;
struct rte_eth_txconf tx_conf;
struct rte_eth_rxconf rx_conf;
int32_t socket_id;
uint16_t nr_tx_rings;
uint16_t nr_rx_rings;
uint32_t nr_tx_slots;
uint32_t nr_rx_slots;
uint16_t tx_burst;
uint16_t rx_burst;
uint32_t fd;
};
struct fd_port {
uint32_t port;
};
#ifndef POLLRDNORM
#define POLLRDNORM 0x0040
#endif
#ifndef POLLWRNORM
#define POLLWRNORM 0x0100
#endif
#define FD_PORT_FREE UINT32_MAX
#define FD_PORT_RSRV (FD_PORT_FREE - 1)
struct netmap_state {
struct rte_netmap_conf conf;
uintptr_t buf_start;
void *mem;
uint32_t mem_sz;
uint32_t netif_memsz;
};
#define COMPAT_NETMAP_MAX_NOFILE (2 * RTE_MAX_ETHPORTS)
#define COMPAT_NETMAP_MAX_BURST 64
#define COMPAT_NETMAP_MAX_PKT_PER_SYNC (2 * COMPAT_NETMAP_MAX_BURST)
static struct netmap_port ports[RTE_MAX_ETHPORTS];
static struct netmap_state netmap;
static struct fd_port fd_port[COMPAT_NETMAP_MAX_NOFILE];
static const int next_fd_start = RLIMIT_NOFILE + 1;
static rte_spinlock_t netmap_lock;
#define IDX_TO_FD(x) ((x) + next_fd_start)
#define FD_TO_IDX(x) ((x) - next_fd_start)
#define FD_VALID(x) ((x) >= next_fd_start && \
(x) < (typeof (x))(RTE_DIM(fd_port) + next_fd_start))
#define PORT_NUM_RINGS (2 * netmap.conf.max_rings)
#define PORT_NUM_SLOTS (PORT_NUM_RINGS * netmap.conf.max_slots)
#define BUF_IDX(port, ring, slot) \
(((port) * PORT_NUM_RINGS + (ring)) * netmap.conf.max_slots + \
(slot))
#define NETMAP_IF_RING_OFS(rid, rings, slots) ({\
struct netmap_if *_if; \
struct netmap_ring *_rg; \
sizeof(*_if) + \
(rings) * sizeof(_if->ring_ofs[0]) + \
(rid) * sizeof(*_rg) + \
(slots) * sizeof(_rg->slot[0]); \
})
static void netmap_unregif(uint32_t idx, uint32_t port);
static int32_t
ifname_to_portid(const char *ifname, uint16_t *port)
{
char *endptr;
uint64_t portid;
errno = 0;
portid = strtoul(ifname, &endptr, 10);
if (endptr == ifname || *endptr != '\0' ||
portid >= RTE_DIM(ports) || errno != 0)
return -EINVAL;
*port = portid;
return 0;
}
/**
* Given a dpdk mbuf, fill in the Netmap slot in ring r and its associated
* buffer with the data held by the mbuf.
* Note that mbuf chains are not supported.
*/
static void
mbuf_to_slot(struct rte_mbuf *mbuf, struct netmap_ring *r, uint32_t index)
{
char *data;
uint16_t length;
data = rte_pktmbuf_mtod(mbuf, char *);
length = rte_pktmbuf_data_len(mbuf);
if (length > r->nr_buf_size)
length = 0;
r->slot[index].len = length;
rte_memcpy(NETMAP_BUF(r, r->slot[index].buf_idx), data, length);
}
/**
* Given a Netmap ring and a slot index for that ring, construct a dpdk mbuf
* from the data held in the buffer associated with the slot.
* Allocation/deallocation of the dpdk mbuf are the responsibility of the
* caller.
* Note that mbuf chains are not supported.
*/
static void
slot_to_mbuf(struct netmap_ring *r, uint32_t index, struct rte_mbuf *mbuf)
{
char *data;
uint16_t length;
rte_pktmbuf_reset(mbuf);
length = r->slot[index].len;
data = rte_pktmbuf_append(mbuf, length);
if (data != NULL)
rte_memcpy(data, NETMAP_BUF(r, r->slot[index].buf_idx), length);
}
static int32_t
fd_reserve(void)
{
uint32_t i;
for (i = 0; i != RTE_DIM(fd_port) && fd_port[i].port != FD_PORT_FREE;
i++)
;
if (i == RTE_DIM(fd_port))
return -ENOMEM;
fd_port[i].port = FD_PORT_RSRV;
return IDX_TO_FD(i);
}
static int32_t
fd_release(int32_t fd)
{
uint32_t idx, port;
idx = FD_TO_IDX(fd);
if (!FD_VALID(fd) || (port = fd_port[idx].port) == FD_PORT_FREE)
return -EINVAL;
/* if we still have a valid port attached, release the port */
if (port < RTE_DIM(ports) && ports[port].fd == idx) {
netmap_unregif(idx, port);
}
fd_port[idx].port = FD_PORT_FREE;
return 0;
}
static int
check_nmreq(struct nmreq *req, uint16_t *port)
{
int32_t rc;
uint16_t portid;
if (req == NULL)
return -EINVAL;
if (req->nr_version != NETMAP_API) {
req->nr_version = NETMAP_API;
return -EINVAL;
}
if ((rc = ifname_to_portid(req->nr_name, &portid)) != 0) {
RTE_LOG(ERR, USER1, "Invalid interface name:\"%s\" "
"in NIOCGINFO call\n", req->nr_name);
return rc;
}
if (ports[portid].pool == NULL) {
RTE_LOG(ERR, USER1, "Misconfigured portid %u\n", portid);
return -EINVAL;
}
*port = portid;
return 0;
}
/**
* Simulate a Netmap NIOCGINFO ioctl: given a struct nmreq holding an interface
* name (a port number in our case), fill the struct nmreq in with advisory
* information about the interface: number of rings and their size, total memory
* required in the map, ...
* Those are preconfigured using rte_eth_{,tx,rx}conf and
* rte_netmap_port_conf structures
* and calls to rte_netmap_init_port() in the Netmap application.
*/
static int
ioctl_niocginfo(__rte_unused int fd, void * param)
{
uint16_t portid;
struct nmreq *req;
int32_t rc;
req = (struct nmreq *)param;
if ((rc = check_nmreq(req, &portid)) != 0)
return rc;
req->nr_tx_rings = (uint16_t)(ports[portid].nr_tx_rings - 1);
req->nr_rx_rings = (uint16_t)(ports[portid].nr_rx_rings - 1);
req->nr_tx_slots = ports[portid].nr_tx_slots;
req->nr_rx_slots = ports[portid].nr_rx_slots;
/* in current implementation we have all NETIFs shared aone region. */
req->nr_memsize = netmap.mem_sz;
req->nr_offset = 0;
return 0;
}
static void
netmap_ring_setup(struct netmap_ring *ring, uint16_t port, uint32_t ringid,
uint32_t num_slots)
{
uint32_t j;
ring->buf_ofs = netmap.buf_start - (uintptr_t)ring;
ring->num_slots = num_slots;
ring->cur = 0;
ring->reserved = 0;
ring->nr_buf_size = netmap.conf.max_bufsz;
ring->flags = 0;
ring->ts.tv_sec = 0;
ring->ts.tv_usec = 0;
for (j = 0; j < ring->num_slots; j++) {
ring->slot[j].buf_idx = BUF_IDX(port, ringid, j);
ring->slot[j].len = 0;
ring->flags = 0;
}
}
static int
netmap_regif(struct nmreq *req, uint32_t idx, uint16_t port)
{
struct netmap_if *nmif;
struct netmap_ring *ring;
uint32_t i, slots, start_ring;
int32_t rc;
if (ports[port].fd < RTE_DIM(fd_port)) {
RTE_LOG(ERR, USER1, "port %u already in use by fd: %u\n",
port, IDX_TO_FD(ports[port].fd));
return -EBUSY;
}
if (fd_port[idx].port != FD_PORT_RSRV) {
RTE_LOG(ERR, USER1, "fd: %u is misconfigured\n",
IDX_TO_FD(idx));
return -EBUSY;
}
nmif = ports[port].nmif;
/* setup netmap_if fields. */
memset(nmif, 0, netmap.netif_memsz);
/* only ALL rings supported right now. */
if (req->nr_ringid != 0)
return -EINVAL;
strlcpy(nmif->ni_name, req->nr_name, sizeof(nmif->ni_name));
nmif->ni_version = req->nr_version;
/* Netmap uses ni_(r|t)x_rings + 1 */
nmif->ni_rx_rings = ports[port].nr_rx_rings - 1;
nmif->ni_tx_rings = ports[port].nr_tx_rings - 1;
/*
* Setup TX rings and slots.
* Refer to the comments in netmap.h for details
*/
slots = 0;
for (i = 0; i < nmif->ni_tx_rings + 1; i++) {
nmif->ring_ofs[i] = NETMAP_IF_RING_OFS(i,
PORT_NUM_RINGS, slots);
ring = NETMAP_TXRING(nmif, i);
netmap_ring_setup(ring, port, i, ports[port].nr_tx_slots);
ring->avail = ring->num_slots;
slots += ports[port].nr_tx_slots;
}
/*
* Setup RX rings and slots.
* Refer to the comments in netmap.h for details
*/
start_ring = i;
for (; i < nmif->ni_rx_rings + 1 + start_ring; i++) {
nmif->ring_ofs[i] = NETMAP_IF_RING_OFS(i,
PORT_NUM_RINGS, slots);
ring = NETMAP_RXRING(nmif, (i - start_ring));
netmap_ring_setup(ring, port, i, ports[port].nr_rx_slots);
ring->avail = 0;
slots += ports[port].nr_rx_slots;
}
if ((rc = rte_eth_dev_start(port)) < 0) {
RTE_LOG(ERR, USER1,
"Couldn't start ethernet device %s (error %d)\n",
req->nr_name, rc);
return rc;
}
/* setup fdi <--> port relationtip. */
ports[port].fd = idx;
fd_port[idx].port = port;
req->nr_memsize = netmap.mem_sz;
req->nr_offset = (uintptr_t)nmif - (uintptr_t)netmap.mem;
return 0;
}
/**
* Simulate a Netmap NIOCREGIF ioctl:
*/
static int
ioctl_niocregif(int32_t fd, void * param)
{
uint16_t portid;
int32_t rc;
uint32_t idx;
struct nmreq *req;
req = (struct nmreq *)param;
if ((rc = check_nmreq(req, &portid)) != 0)
return rc;
idx = FD_TO_IDX(fd);
rte_spinlock_lock(&netmap_lock);
rc = netmap_regif(req, idx, portid);
rte_spinlock_unlock(&netmap_lock);
return rc;
}
static void
netmap_unregif(uint32_t idx, uint32_t port)
{
fd_port[idx].port = FD_PORT_RSRV;
ports[port].fd = UINT32_MAX;
rte_eth_dev_stop(port);
}
/**
* Simulate a Netmap NIOCUNREGIF ioctl: put an interface running in Netmap
* mode back in "normal" mode. In our case, we just stop the port associated
* with this file descriptor.
*/
static int
ioctl_niocunregif(int fd)
{
uint32_t idx, port;
int32_t rc;
idx = FD_TO_IDX(fd);
rte_spinlock_lock(&netmap_lock);
port = fd_port[idx].port;
if (port < RTE_DIM(ports) && ports[port].fd == idx) {
netmap_unregif(idx, port);
rc = 0;
} else {
RTE_LOG(ERR, USER1,
"%s: %d is not associated with valid port\n",
__func__, fd);
rc = -EINVAL;
}
rte_spinlock_unlock(&netmap_lock);
return rc;
}
/**
* A call to rx_sync_ring will try to fill a Netmap RX ring with as many
* packets as it can hold coming from its dpdk port.
*/
static inline int
rx_sync_ring(struct netmap_ring *ring, uint16_t port, uint16_t ring_number,
uint16_t max_burst)
{
int32_t i, n_rx;
uint16_t burst_size;
uint32_t cur_slot, n_free_slots;
struct rte_mbuf *rx_mbufs[COMPAT_NETMAP_MAX_BURST];
n_free_slots = ring->num_slots - (ring->avail + ring->reserved);
n_free_slots = RTE_MIN(n_free_slots, max_burst);
cur_slot = (ring->cur + ring->avail) & (ring->num_slots - 1);
while (n_free_slots) {
burst_size = (uint16_t)RTE_MIN(n_free_slots, RTE_DIM(rx_mbufs));
/* receive up to burst_size packets from the NIC's queue */
n_rx = rte_eth_rx_burst(port, ring_number, rx_mbufs,
burst_size);
if (n_rx == 0)
return 0;
if (unlikely(n_rx < 0))
return -1;
/* Put those n_rx packets in the Netmap structures */
for (i = 0; i < n_rx ; i++) {
mbuf_to_slot(rx_mbufs[i], ring, cur_slot);
rte_pktmbuf_free(rx_mbufs[i]);
cur_slot = NETMAP_RING_NEXT(ring, cur_slot);
}
/* Update the Netmap ring structure to reflect the change */
ring->avail += n_rx;
n_free_slots -= n_rx;
}
return 0;
}
static inline int
rx_sync_if(uint32_t port)
{
uint16_t burst;
uint32_t i, rc;
struct netmap_if *nifp;
struct netmap_ring *r;
nifp = ports[port].nmif;
burst = ports[port].rx_burst;
rc = 0;
for (i = 0; i < nifp->ni_rx_rings + 1; i++) {
r = NETMAP_RXRING(nifp, i);
rx_sync_ring(r, port, (uint16_t)i, burst);
rc += r->avail;
}
return rc;
}
/**
* Simulate a Netmap NIOCRXSYNC ioctl:
*/
static int
ioctl_niocrxsync(int fd)
{
uint32_t idx, port;
idx = FD_TO_IDX(fd);
if ((port = fd_port[idx].port) < RTE_DIM(ports) &&
ports[port].fd == idx) {
return rx_sync_if(fd_port[idx].port);
} else {
return -EINVAL;
}
}
/**
* A call to tx_sync_ring will try to empty a Netmap TX ring by converting its
* buffers into rte_mbufs and sending them out on the rings's dpdk port.
*/
static int
tx_sync_ring(struct netmap_ring *ring, uint16_t port, uint16_t ring_number,
struct rte_mempool *pool, uint16_t max_burst)
{
uint32_t i, n_tx;
uint16_t burst_size;
uint32_t cur_slot, n_used_slots;
struct rte_mbuf *tx_mbufs[COMPAT_NETMAP_MAX_BURST];
n_used_slots = ring->num_slots - ring->avail;
n_used_slots = RTE_MIN(n_used_slots, max_burst);
cur_slot = (ring->cur + ring->avail) & (ring->num_slots - 1);
while (n_used_slots) {
burst_size = (uint16_t)RTE_MIN(n_used_slots, RTE_DIM(tx_mbufs));
for (i = 0; i < burst_size; i++) {
tx_mbufs[i] = rte_pktmbuf_alloc(pool);
if (tx_mbufs[i] == NULL)
goto err;
slot_to_mbuf(ring, cur_slot, tx_mbufs[i]);
cur_slot = NETMAP_RING_NEXT(ring, cur_slot);
}
n_tx = rte_eth_tx_burst(port, ring_number, tx_mbufs,
burst_size);
/* Update the Netmap ring structure to reflect the change */
ring->avail += n_tx;
n_used_slots -= n_tx;
/* Return the mbufs that failed to transmit to their pool */
if (unlikely(n_tx != burst_size)) {
for (i = n_tx; i < burst_size; i++)
rte_pktmbuf_free(tx_mbufs[i]);
break;
}
}
return 0;
err:
for (; i == 0; --i)
rte_pktmbuf_free(tx_mbufs[i]);
RTE_LOG(ERR, USER1,
"Couldn't get mbuf from mempool is the mempool too small?\n");
return -1;
}
static int
tx_sync_if(uint32_t port)
{
uint16_t burst;
uint32_t i, rc;
struct netmap_if *nifp;
struct netmap_ring *r;
struct rte_mempool *mp;
nifp = ports[port].nmif;
mp = ports[port].pool;
burst = ports[port].tx_burst;
rc = 0;
for (i = 0; i < nifp->ni_tx_rings + 1; i++) {
r = NETMAP_TXRING(nifp, i);
tx_sync_ring(r, port, (uint16_t)i, mp, burst);
rc += r->avail;
}
return rc;
}
/**
* Simulate a Netmap NIOCTXSYNC ioctl:
*/
static inline int
ioctl_nioctxsync(int fd)
{
uint32_t idx, port;
idx = FD_TO_IDX(fd);
if ((port = fd_port[idx].port) < RTE_DIM(ports) &&
ports[port].fd == idx) {
return tx_sync_if(fd_port[idx].port);
} else {
return -EINVAL;
}
}
/**
* Give the library a mempool of rte_mbufs with which it can do the
* rte_mbuf <--> netmap slot conversions.
*/
int
rte_netmap_init(const struct rte_netmap_conf *conf)
{
size_t buf_ofs, nmif_sz, sz;
size_t port_rings, port_slots, port_bufs;
uint32_t i, port_num;
port_num = RTE_MAX_ETHPORTS;
port_rings = 2 * conf->max_rings;
port_slots = port_rings * conf->max_slots;
port_bufs = port_slots;
nmif_sz = NETMAP_IF_RING_OFS(port_rings, port_rings, port_slots);
sz = nmif_sz * port_num;
buf_ofs = RTE_ALIGN_CEIL(sz, RTE_CACHE_LINE_SIZE);
sz = buf_ofs + port_bufs * conf->max_bufsz * port_num;
if (sz > UINT32_MAX ||
(netmap.mem = rte_zmalloc_socket(__func__, sz,
RTE_CACHE_LINE_SIZE, conf->socket_id)) == NULL) {
RTE_LOG(ERR, USER1, "%s: failed to allocate %zu bytes\n",
__func__, sz);
return -ENOMEM;
}
netmap.mem_sz = sz;
netmap.netif_memsz = nmif_sz;
netmap.buf_start = (uintptr_t)netmap.mem + buf_ofs;
netmap.conf = *conf;
rte_spinlock_init(&netmap_lock);
/* Mark all ports as unused and set NETIF pointer. */
for (i = 0; i != RTE_DIM(ports); i++) {
ports[i].fd = UINT32_MAX;
ports[i].nmif = (struct netmap_if *)
((uintptr_t)netmap.mem + nmif_sz * i);
}
/* Mark all fd_ports as unused. */
for (i = 0; i != RTE_DIM(fd_port); i++) {
fd_port[i].port = FD_PORT_FREE;
}
return 0;
}
int
rte_netmap_init_port(uint16_t portid, const struct rte_netmap_port_conf *conf)
{
int32_t ret;
uint16_t i;
uint16_t rx_slots, tx_slots;
struct rte_eth_rxconf rxq_conf;
struct rte_eth_txconf txq_conf;
struct rte_eth_dev_info dev_info;
if (conf == NULL ||
portid >= RTE_DIM(ports) ||
conf->nr_tx_rings > netmap.conf.max_rings ||
conf->nr_rx_rings > netmap.conf.max_rings) {
RTE_LOG(ERR, USER1, "%s(%u): invalid parameters\n",
__func__, portid);
return -EINVAL;
}
rx_slots = (uint16_t)rte_align32pow2(conf->nr_rx_slots);
tx_slots = (uint16_t)rte_align32pow2(conf->nr_tx_slots);
if (tx_slots > netmap.conf.max_slots ||
rx_slots > netmap.conf.max_slots) {
RTE_LOG(ERR, USER1, "%s(%u): invalid parameters\n",
__func__, portid);
return -EINVAL;
}
ret = rte_eth_dev_info_get(portid, &dev_info);
if (ret != 0) {
RTE_LOG(ERR, USER1,
"Error during getting device (port %u) info: %s\n",
portid, strerror(-ret));
return ret;
}
if (dev_info.tx_offload_capa & DEV_TX_OFFLOAD_MBUF_FAST_FREE)
conf->eth_conf->txmode.offloads |=
DEV_TX_OFFLOAD_MBUF_FAST_FREE;
ret = rte_eth_dev_configure(portid, conf->nr_rx_rings,
conf->nr_tx_rings, conf->eth_conf);
if (ret < 0) {
RTE_LOG(ERR, USER1, "Couldn't configure port %u\n", portid);
return ret;
}
ret = rte_eth_dev_adjust_nb_rx_tx_desc(portid, &rx_slots, &tx_slots);
if (ret < 0) {
RTE_LOG(ERR, USER1,
"Couldn't ot adjust number of descriptors for port %u\n",
portid);
return ret;
}
rxq_conf = dev_info.default_rxconf;
rxq_conf.offloads = conf->eth_conf->rxmode.offloads;
txq_conf = dev_info.default_txconf;
txq_conf.offloads = conf->eth_conf->txmode.offloads;
for (i = 0; i < conf->nr_tx_rings; i++) {
ret = rte_eth_tx_queue_setup(portid, i, tx_slots,
conf->socket_id, &txq_conf);
if (ret < 0) {
RTE_LOG(ERR, USER1,
"fail to configure TX queue %u of port %u\n",
i, portid);
return ret;
}
ret = rte_eth_rx_queue_setup(portid, i, rx_slots,
conf->socket_id, &rxq_conf, conf->pool);
if (ret < 0) {
RTE_LOG(ERR, USER1,
"fail to configure RX queue %u of port %u\n",
i, portid);
return ret;
}
}
/* copy config to the private storage. */
ports[portid].eth_conf = conf->eth_conf[0];
ports[portid].pool = conf->pool;
ports[portid].socket_id = conf->socket_id;
ports[portid].nr_tx_rings = conf->nr_tx_rings;
ports[portid].nr_rx_rings = conf->nr_rx_rings;
ports[portid].nr_tx_slots = tx_slots;
ports[portid].nr_rx_slots = rx_slots;
ports[portid].tx_burst = conf->tx_burst;
ports[portid].rx_burst = conf->rx_burst;
return 0;
}
int
rte_netmap_close(__rte_unused int fd)
{
int32_t rc;
rte_spinlock_lock(&netmap_lock);
rc = fd_release(fd);
rte_spinlock_unlock(&netmap_lock);
if (rc < 0) {
errno =-rc;
rc = -1;
}
return rc;
}
int rte_netmap_ioctl(int fd, uint32_t op, void *param)
{
int ret;
if (!FD_VALID(fd)) {
errno = EBADF;
return -1;
}
switch (op) {
case NIOCGINFO:
ret = ioctl_niocginfo(fd, param);
break;
case NIOCREGIF:
ret = ioctl_niocregif(fd, param);
break;
case NIOCUNREGIF:
ret = ioctl_niocunregif(fd);
break;
case NIOCRXSYNC:
ret = ioctl_niocrxsync(fd);
break;
case NIOCTXSYNC:
ret = ioctl_nioctxsync(fd);
break;
default:
ret = -ENOTTY;
}
if (ret < 0) {
errno = -ret;
ret = -1;
} else {
ret = 0;
}
return ret;
}
void *
rte_netmap_mmap(void *addr, size_t length,
int prot, int flags, int fd, off_t offset)
{
static const int cprot = PROT_WRITE | PROT_READ;
if (!FD_VALID(fd) || length + offset > netmap.mem_sz ||
(prot & cprot) != cprot ||
((flags & MAP_FIXED) != 0 && addr != NULL)) {
errno = EINVAL;
return MAP_FAILED;
}
return (void *)((uintptr_t)netmap.mem + (uintptr_t)offset);
}
/**
* Return a "fake" file descriptor with a value above RLIMIT_NOFILE so that
* any attempt to use that file descriptor with the usual API will fail.
*/
int
rte_netmap_open(__rte_unused const char *pathname, __rte_unused int flags)
{
int fd;
rte_spinlock_lock(&netmap_lock);
fd = fd_reserve();
rte_spinlock_unlock(&netmap_lock);
if (fd < 0) {
errno = -fd;
fd = -1;
}
return fd;
}
/**
* Doesn't support timeout other than 0 or infinite (negative) timeout
*/
int
rte_netmap_poll(struct pollfd *fds, nfds_t nfds, int timeout)
{
int32_t count_it, ret;
uint32_t i, idx, port;
uint32_t want_rx, want_tx;
if (timeout > 0)
return -1;
ret = 0;
do {
for (i = 0; i < nfds; i++) {
count_it = 0;
if (!FD_VALID(fds[i].fd) || fds[i].events == 0) {
fds[i].revents = 0;
continue;
}
idx = FD_TO_IDX(fds[i].fd);
if ((port = fd_port[idx].port) >= RTE_DIM(ports) ||
ports[port].fd != idx) {
fds[i].revents |= POLLERR;
ret++;
continue;
}
want_rx = fds[i].events & (POLLIN | POLLRDNORM);
want_tx = fds[i].events & (POLLOUT | POLLWRNORM);
if (want_rx && rx_sync_if(port) > 0) {
fds[i].revents = (uint16_t)
(fds[i].revents | want_rx);
count_it = 1;
}
if (want_tx && tx_sync_if(port) > 0) {
fds[i].revents = (uint16_t)
(fds[i].revents | want_tx);
count_it = 1;
}
ret += count_it;
}
}
while ((ret == 0 && timeout < 0) || timeout);
return ret;
}

View File

@ -1,51 +0,0 @@
/* SPDX-License-Identifier: BSD-3-Clause
* Copyright(c) 2010-2014 Intel Corporation
*/
#ifndef _RTE_COMPAT_NETMAP_H_
#include <poll.h>
#include <linux/ioctl.h>
#include <net/if.h>
#include <rte_ethdev.h>
#include <rte_mempool.h>
#include "netmap.h"
#include "netmap_user.h"
/**
* One can overwrite Netmap macros here as needed
*/
struct rte_netmap_conf {
int32_t socket_id;
uint32_t max_rings; /* number of rings(queues) per netmap_if(port) */
uint32_t max_slots; /* number of slots(descriptors) per netmap ring. */
uint16_t max_bufsz; /* size of each netmap buffer. */
};
struct rte_netmap_port_conf {
struct rte_eth_conf *eth_conf;
struct rte_mempool *pool;
int32_t socket_id;
uint16_t nr_tx_rings;
uint16_t nr_rx_rings;
uint32_t nr_tx_slots;
uint32_t nr_rx_slots;
uint16_t tx_burst;
uint16_t rx_burst;
};
int rte_netmap_init(const struct rte_netmap_conf *conf);
int rte_netmap_init_port(uint16_t portid,
const struct rte_netmap_port_conf *conf);
int rte_netmap_close(int fd);
int rte_netmap_ioctl(int fd, uint32_t op, void *param);
int rte_netmap_open(const char *pathname, int flags);
int rte_netmap_poll(struct pollfd *fds, nfds_t nfds, int timeout);
void *rte_netmap_mmap(void *addr, size_t length, int prot, int flags, int fd,
off_t offset);
#endif /* _RTE_COMPAT_NETMAP_H_ */

View File

@ -1,10 +0,0 @@
# SPDX-License-Identifier: BSD-3-Clause
# Copyright(c) 2018 Intel Corporation
# meson file, for building this example as part of a main DPDK build.
#
# To build this example as a standalone application with an already-installed
# DPDK instance, use 'make'
# Example app currently unsupported by meson build
build = false

View File

@ -1,289 +0,0 @@
/*
* Copyright (C) 2011 Matteo Landi, Luigi Rizzo. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. 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.
*
* 3. Neither the name of the authors nor the names of their contributors
* may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY MATTEO LANDI 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 MATTEO LANDI 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.
*/
/*
* $FreeBSD: head/sys/net/netmap.h 231198 2012-02-08 11:43:29Z luigi $
* $Id: netmap.h 10879 2012-04-12 22:48:59Z luigi $
*
* Definitions of constants and the structures used by the netmap
* framework, for the part visible to both kernel and userspace.
* Detailed info on netmap is available with "man netmap" or at
*
* http://info.iet.unipi.it/~luigi/netmap/
*/
#ifndef _NET_NETMAP_H_
#define _NET_NETMAP_H_
/*
* --- Netmap data structures ---
*
* The data structures used by netmap are shown below. Those in
* capital letters are in an mmapp()ed area shared with userspace,
* while others are private to the kernel.
* Shared structures do not contain pointers but only memory
* offsets, so that addressing is portable between kernel and userspace.
softc
+----------------+
| standard fields|
| if_pspare[0] ----------+
+----------------+ |
|
+----------------+<------+
|(netmap_adapter)|
| | netmap_kring
| tx_rings *--------------------------------->+---------------+
| | netmap_kring | ring *---------.
| rx_rings *--------->+---------------+ | nr_hwcur | |
+----------------+ | ring *--------. | nr_hwavail | V
| nr_hwcur | | | selinfo | |
| nr_hwavail | | +---------------+ .
| selinfo | | | ... | .
+---------------+ | |(ntx+1 entries)|
| .... | | | |
|(nrx+1 entries)| | +---------------+
| | |
KERNEL +---------------+ |
|
====================================================================
|
USERSPACE | NETMAP_RING
+---->+-------------+
/ | cur |
NETMAP_IF (nifp, one per file desc.) / | avail |
+---------------+ / | buf_ofs |
| ni_tx_rings | / +=============+
| ni_rx_rings | / | buf_idx | slot[0]
| | / | len, flags |
| | / +-------------+
+===============+ / | buf_idx | slot[1]
| txring_ofs[0] | (rel.to nifp)--' | len, flags |
| txring_ofs[1] | +-------------+
(num_rings+1 entries) (nr_num_slots entries)
| txring_ofs[n] | | buf_idx | slot[n-1]
+---------------+ | len, flags |
| rxring_ofs[0] | +-------------+
| rxring_ofs[1] |
(num_rings+1 entries)
| txring_ofs[n] |
+---------------+
* The private descriptor ('softc' or 'adapter') of each interface
* is extended with a "struct netmap_adapter" containing netmap-related
* info (see description in dev/netmap/netmap_kernel.h.
* Among other things, tx_rings and rx_rings point to the arrays of
* "struct netmap_kring" which in turn reache the various
* "struct netmap_ring", shared with userspace.
* The NETMAP_RING is the userspace-visible replica of the NIC ring.
* Each slot has the index of a buffer, its length and some flags.
* In user space, the buffer address is computed as
* (char *)ring + buf_ofs + index*NETMAP_BUF_SIZE
* In the kernel, buffers do not necessarily need to be contiguous,
* and the virtual and physical addresses are derived through
* a lookup table.
* To associate a different buffer to a slot, applications must
* write the new index in buf_idx, and set NS_BUF_CHANGED flag to
* make sure that the kernel updates the hardware ring as needed.
*
* Normally the driver is not requested to report the result of
* transmissions (this can dramatically speed up operation).
* However the user may request to report completion by setting
* NS_REPORT.
*/
struct netmap_slot {
uint32_t buf_idx; /* buffer index */
uint16_t len; /* packet length, to be copied to/from the hw ring */
uint16_t flags; /* buf changed, etc. */
#define NS_BUF_CHANGED 0x0001 /* must resync the map, buffer changed */
#define NS_REPORT 0x0002 /* ask the hardware to report results
* e.g. by generating an interrupt
*/
};
/*
* Netmap representation of a TX or RX ring (also known as "queue").
* This is a queue implemented as a fixed-size circular array.
* At the software level, two fields are important: avail and cur.
*
* In TX rings:
* avail indicates the number of slots available for transmission.
* It is updated by the kernel after every netmap system call.
* It MUST BE decremented by the application when it appends a
* packet.
* cur indicates the slot to use for the next packet
* to send (i.e. the "tail" of the queue).
* It MUST BE incremented by the application before
* netmap system calls to reflect the number of newly
* sent packets.
* It is checked by the kernel on netmap system calls
* (normally unmodified by the kernel unless invalid).
*
* The kernel side of netmap uses two additional fields in its own
* private ring structure, netmap_kring:
* nr_hwcur is a copy of nr_cur on an NIOCTXSYNC.
* nr_hwavail is the number of slots known as available by the
* hardware. It is updated on an INTR (inc by the
* number of packets sent) and on a NIOCTXSYNC
* (decrease by nr_cur - nr_hwcur)
* A special case, nr_hwavail is -1 if the transmit
* side is idle (no pending transmits).
*
* In RX rings:
* avail is the number of packets available (possibly 0).
* It MUST BE decremented by the application when it consumes
* a packet, and it is updated to nr_hwavail on a NIOCRXSYNC
* cur indicates the first slot that contains a packet not
* processed yet (the "head" of the queue).
* It MUST BE incremented by the software when it consumes
* a packet.
* reserved indicates the number of buffers before 'cur'
* that the application has still in use. Normally 0,
* it MUST BE incremented by the application when it
* does not return the buffer immediately, and decremented
* when the buffer is finally freed.
*
* The kernel side of netmap uses two additional fields in the kring:
* nr_hwcur is a copy of nr_cur on an NIOCRXSYNC
* nr_hwavail is the number of packets available. It is updated
* on INTR (inc by the number of new packets arrived)
* and on NIOCRXSYNC (decreased by nr_cur - nr_hwcur).
*
* DATA OWNERSHIP/LOCKING:
* The netmap_ring is owned by the user program and it is only
* accessed or modified in the upper half of the kernel during
* a system call.
*
* The netmap_kring is only modified by the upper half of the kernel.
*/
struct netmap_ring {
/*
* nr_buf_base_ofs is meant to be used through macros.
* It contains the offset of the buffer region from this
* descriptor.
*/
ssize_t buf_ofs;
uint32_t num_slots; /* number of slots in the ring. */
uint32_t avail; /* number of usable slots */
uint32_t cur; /* 'current' r/w position */
uint32_t reserved; /* not refilled before current */
uint16_t nr_buf_size;
uint16_t flags;
#define NR_TIMESTAMP 0x0002 /* set timestamp on *sync() */
struct timeval ts; /* time of last *sync() */
/* the slots follow. This struct has variable size */
struct netmap_slot slot[0]; /* array of slots. */
};
/*
* Netmap representation of an interface and its queue(s).
* There is one netmap_if for each file descriptor on which we want
* to select/poll. We assume that on each interface has the same number
* of receive and transmit queues.
* select/poll operates on one or all pairs depending on the value of
* nmr_queueid passed on the ioctl.
*/
struct netmap_if {
char ni_name[IFNAMSIZ]; /* name of the interface. */
u_int ni_version; /* API version, currently unused */
u_int ni_rx_rings; /* number of rx rings */
u_int ni_tx_rings; /* if zero, same as ni_rx_rings */
/*
* The following array contains the offset of each netmap ring
* from this structure. The first ni_tx_queues+1 entries refer
* to the tx rings, the next ni_rx_queues+1 refer to the rx rings
* (the last entry in each block refers to the host stack rings).
* The area is filled up by the kernel on NIOCREG,
* and then only read by userspace code.
*/
ssize_t ring_ofs[0];
};
#ifndef NIOCREGIF
/*
* ioctl names and related fields
*
* NIOCGINFO takes a struct ifreq, the interface name is the input,
* the outputs are number of queues and number of descriptor
* for each queue (useful to set number of threads etc.).
*
* NIOCREGIF takes an interface name within a struct ifreq,
* and activates netmap mode on the interface (if possible).
*
* NIOCUNREGIF unregisters the interface associated to the fd.
*
* NIOCTXSYNC, NIOCRXSYNC synchronize tx or rx queues,
* whose identity is set in NIOCREGIF through nr_ringid
*/
/*
* struct nmreq overlays a struct ifreq
*/
struct nmreq {
char nr_name[IFNAMSIZ];
uint32_t nr_version; /* API version */
#define NETMAP_API 3 /* current version */
uint32_t nr_offset; /* nifp offset in the shared region */
uint32_t nr_memsize; /* size of the shared region */
uint32_t nr_tx_slots; /* slots in tx rings */
uint32_t nr_rx_slots; /* slots in rx rings */
uint16_t nr_tx_rings; /* number of tx rings */
uint16_t nr_rx_rings; /* number of rx rings */
uint16_t nr_ringid; /* ring(s) we care about */
#define NETMAP_HW_RING 0x4000 /* low bits indicate one hw ring */
#define NETMAP_SW_RING 0x2000 /* process the sw ring */
#define NETMAP_NO_TX_POLL 0x1000 /* no automatic txsync on poll */
#define NETMAP_RING_MASK 0xfff /* the ring number */
uint16_t spare1;
uint32_t spare2[4];
};
/*
* FreeBSD uses the size value embedded in the _IOWR to determine
* how much to copy in/out. So we need it to match the actual
* data structure we pass. We put some spares in the structure
* to ease compatibility with other versions
*/
#define NIOCGINFO _IOWR('i', 145, struct nmreq) /* return IF info */
#define NIOCREGIF _IOWR('i', 146, struct nmreq) /* interface register */
#define NIOCUNREGIF _IO('i', 147) /* interface unregister */
#define NIOCTXSYNC _IO('i', 148) /* sync tx queues */
#define NIOCRXSYNC _IO('i', 149) /* sync rx queues */
#endif /* !NIOCREGIF */
#endif /* _NET_NETMAP_H_ */

View File

@ -1,95 +0,0 @@
/*
* Copyright (C) 2011 Matteo Landi, Luigi Rizzo. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. 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.
*
* 3. Neither the name of the authors nor the names of their contributors
* may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY MATTEO LANDI 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 MATTEO LANDI 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.
*/
/*
* $FreeBSD: head/sys/net/netmap_user.h 231198 2012-02-08 11:43:29Z luigi $
* $Id: netmap_user.h 10879 2012-04-12 22:48:59Z luigi $
*
* This header contains the macros used to manipulate netmap structures
* and packets in userspace. See netmap(4) for more information.
*
* The address of the struct netmap_if, say nifp, is computed from the
* value returned from ioctl(.., NIOCREG, ...) and the mmap region:
* ioctl(fd, NIOCREG, &req);
* mem = mmap(0, ... );
* nifp = NETMAP_IF(mem, req.nr_nifp);
* (so simple, we could just do it manually)
*
* From there:
* struct netmap_ring *NETMAP_TXRING(nifp, index)
* struct netmap_ring *NETMAP_RXRING(nifp, index)
* we can access ring->nr_cur, ring->nr_avail, ring->nr_flags
*
* ring->slot[i] gives us the i-th slot (we can access
* directly plen, flags, bufindex)
*
* char *buf = NETMAP_BUF(ring, index) returns a pointer to
* the i-th buffer
*
* Since rings are circular, we have macros to compute the next index
* i = NETMAP_RING_NEXT(ring, i);
*/
#ifndef _NET_NETMAP_USER_H_
#define _NET_NETMAP_USER_H_
#define NETMAP_IF(b, o) (struct netmap_if *)((char *)(b) + (o))
#define NETMAP_TXRING(nifp, index) \
((struct netmap_ring *)((char *)(nifp) + \
(nifp)->ring_ofs[index] ) )
#define NETMAP_RXRING(nifp, index) \
((struct netmap_ring *)((char *)(nifp) + \
(nifp)->ring_ofs[index + (nifp)->ni_tx_rings + 1] ) )
#define NETMAP_BUF(ring, index) \
((char *)(ring) + (ring)->buf_ofs + ((index)*(ring)->nr_buf_size))
#define NETMAP_BUF_IDX(ring, buf) \
( ((char *)(buf) - ((char *)(ring) + (ring)->buf_ofs) ) / \
(ring)->nr_buf_size )
#define NETMAP_RING_NEXT(r, i) \
((i)+1 == (r)->num_slots ? 0 : (i) + 1 )
#define NETMAP_RING_FIRST_RESERVED(r) \
( (r)->cur < (r)->reserved ? \
(r)->cur + (r)->num_slots - (r)->reserved : \
(r)->cur - (r)->reserved )
/*
* Return 1 if the given tx ring is empty.
*/
#define NETMAP_TX_RING_EMPTY(r) ((r)->avail >= (r)->num_slots - 1)
#endif /* _NET_NETMAP_USER_H_ */