06371afe39
This provides a sample application and library showing how to use the Intel(R) DPDK with basic netmap applications. The Netmap compatibility library provides a minimal set of APIs to give the ability to programs written against the Netmap APIs to be run with minimal changes to their source code, using the Intel® 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 Intel(R) DPDK one. 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 included. The application requires a single command line option: -i INTERFACE is the number of a valid Intel(R) DPDK port 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 by the other interface. Signed-off-by: Bruce Richardson <bruce.richardson@intel.com>
903 lines
21 KiB
C
903 lines
21 KiB
C
/*-
|
|
* BSD LICENSE
|
|
*
|
|
* Copyright(c) 2010-2014 Intel Corporation. All rights reserved.
|
|
* All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
*
|
|
* * Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* * Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in
|
|
* the documentation and/or other materials provided with the
|
|
* distribution.
|
|
* * Neither the name of Intel Corporation nor the names of its
|
|
* contributors may be used to endorse or promote products derived
|
|
* from this software without specific prior written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
|
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
|
|
#include <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_memzone.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;
|
|
};
|
|
|
|
#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, uint8_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 = (uint8_t)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 responsability 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, uint8_t *port)
|
|
{
|
|
int32_t rc;
|
|
uint8_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 %hhu\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)
|
|
{
|
|
uint8_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, uint8_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, uint8_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 %hhu 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);
|
|
|
|
rte_snprintf(nmif->ni_name, sizeof(nmif->ni_name), "%s", req->nr_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)
|
|
{
|
|
uint8_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((uint8_t)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, uint8_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, (uint8_t)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, uint8_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, (uint8_t)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, CACHE_LINE_SIZE);
|
|
sz = buf_ofs + port_bufs * conf->max_bufsz * port_num;
|
|
|
|
if (sz > UINT32_MAX ||
|
|
(netmap.mem = rte_zmalloc_socket(__func__, sz,
|
|
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(uint8_t portid, const struct rte_netmap_port_conf *conf)
|
|
{
|
|
int32_t ret;
|
|
uint16_t i;
|
|
uint16_t rx_slots, tx_slots;
|
|
|
|
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(%hhu): 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(%hhu): invalid parameters\n",
|
|
__func__, portid);
|
|
return (-EINVAL);
|
|
}
|
|
|
|
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 %hhu\n", portid);
|
|
return (ret);
|
|
}
|
|
|
|
for (i = 0; i < conf->nr_tx_rings; i++) {
|
|
ret = rte_eth_tx_queue_setup(portid, i, tx_slots,
|
|
conf->socket_id, conf->tx_conf);
|
|
|
|
if (ret < 0) {
|
|
RTE_LOG(ERR, USER1,
|
|
"Couldn't configure TX queue %hhu of "
|
|
"port %hu\n",
|
|
i, portid);
|
|
return (ret);
|
|
}
|
|
|
|
ret = rte_eth_rx_queue_setup(portid, i, rx_slots,
|
|
conf->socket_id, conf->rx_conf, conf->pool);
|
|
|
|
if (ret < 0) {
|
|
RTE_LOG(ERR, USER1,
|
|
"Couldn't configure RX queue %hu of "
|
|
"port %hhu\n",
|
|
i, portid);
|
|
return (ret);
|
|
}
|
|
}
|
|
|
|
/* copy config to the private storage. */
|
|
ports[portid].eth_conf = conf->eth_conf[0];
|
|
ports[portid].rx_conf = conf->rx_conf[0];
|
|
ports[portid].tx_conf = conf->tx_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, int 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 + 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;
|
|
|
|
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;
|
|
}
|