aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCiprian Barbu <ciprian.barbu@linaro.org>2014-02-11 14:34:29 +0100
committerMaxim Uvarov <maxim.uvarov@linaro.org>2014-02-12 14:46:10 +0400
commit240c87f9f39df823bdbafbc3e893888318ad5096 (patch)
tree71a53cd8463ae04b84a274ace7def479528a61cb
parent1e3914dffbcd9026fa278eb100ac2cb90ad15779 (diff)
Netmap support
Signed-off-by: Ciprian Barbu <ciprian.barbu@linaro.org>
-rw-r--r--platform/linux-generic/Makefile6
-rw-r--r--platform/linux-generic/include/api/odp_pktio_netmap.h22
-rw-r--r--platform/linux-generic/include/api/odp_pktio_types.h14
-rw-r--r--platform/linux-generic/include/odp_packet_io_internal.h6
-rw-r--r--platform/linux-generic/include/odp_packet_netmap.h68
-rw-r--r--platform/linux-generic/source/odp_packet_io.c42
-rw-r--r--platform/linux-generic/source/odp_packet_netmap.c385
7 files changed, 543 insertions, 0 deletions
diff --git a/platform/linux-generic/Makefile b/platform/linux-generic/Makefile
index 080458603..282a474ca 100644
--- a/platform/linux-generic/Makefile
+++ b/platform/linux-generic/Makefile
@@ -33,6 +33,9 @@ LIB_DIR = ./lib
DOC_DIR = ./doc
CFLAGS += -I./include -I./include/api
+ifeq ($(ODP_HAVE_NETMAP),yes)
+CFLAGS += -DODP_HAVE_NETMAP
+endif
include $(ODP_ROOT)/Makefile.inc
STATIC_LIB = $(ODP_LIB)/lib/libodp.a
@@ -60,6 +63,9 @@ OBJS += $(OBJ_DIR)/odp_ticketlock.o
OBJS += $(OBJ_DIR)/odp_time.o
OBJS += $(OBJ_DIR)/odp_ring.o
OBJS += $(OBJ_DIR)/odp_rwlock.o
+ifeq ($(ODP_HAVE_NETMAP),yes)
+OBJS += $(OBJ_DIR)/odp_packet_netmap.o
+endif
DEPS = $(OBJS:.o=.d)
diff --git a/platform/linux-generic/include/api/odp_pktio_netmap.h b/platform/linux-generic/include/api/odp_pktio_netmap.h
new file mode 100644
index 000000000..d57e2128f
--- /dev/null
+++ b/platform/linux-generic/include/api/odp_pktio_netmap.h
@@ -0,0 +1,22 @@
+
+/* Copyright (c) 2013, Linaro Limited
+ * All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#ifndef ODP_PKTIO_NETMAP_H
+#define ODP_PKTIO_NETMAP_H
+
+#include <odp_pktio_types.h>
+
+#define ODP_NETMAP_MODE_HW 0
+#define ODP_NETMAP_MODE_SW 1
+
+typedef struct {
+ odp_pktio_type_t type;
+ int netmap_mode;
+ uint16_t ringid;
+} netmap_params_t;
+
+#endif
diff --git a/platform/linux-generic/include/api/odp_pktio_types.h b/platform/linux-generic/include/api/odp_pktio_types.h
index 1fd0bc05c..e6b4cbf5e 100644
--- a/platform/linux-generic/include/api/odp_pktio_types.h
+++ b/platform/linux-generic/include/api/odp_pktio_types.h
@@ -11,15 +11,29 @@
extern "C" {
#endif
+/* We should ensure that future enum values will never overlap, otherwise
+ * applications that want netmap suport might get in trouble if the odp lib
+ * was not built with netmap support and there are more types define below
+ */
+
typedef enum {
ODP_PKTIO_TYPE_SOCKET = 0x01,
+#ifdef ODP_HAVE_NETMAP
+ ODP_PKTIO_TYPE_NETMAP = 0x02,
+#endif
} odp_pktio_type_t;
#include <odp_pktio_socket.h>
+#ifdef ODP_HAVE_NETMAP
+#include <odp_pktio_netmap.h>
+#endif
typedef union odp_pktio_params_t {
odp_pktio_type_t type;
socket_params_t sock_params;
+#ifdef ODP_HAVE_NETMAP
+ netmap_params_t nm_params;
+#endif
} odp_pktio_params_t;
#ifdef __cplusplus
diff --git a/platform/linux-generic/include/odp_packet_io_internal.h b/platform/linux-generic/include/odp_packet_io_internal.h
index fc00282fa..ba1ee9b7d 100644
--- a/platform/linux-generic/include/odp_packet_io_internal.h
+++ b/platform/linux-generic/include/odp_packet_io_internal.h
@@ -20,6 +20,9 @@ extern "C" {
#include <odp_spinlock.h>
#include <odp_packet_socket.h>
+#ifdef ODP_HAVE_NETMAP
+#include <odp_packet_netmap.h>
+#endif
struct pktio_entry {
odp_spinlock_t lock; /**< entry spinlock */
@@ -28,6 +31,9 @@ struct pktio_entry {
odp_queue_t outq_default; /**< default out queue */
odp_pktio_params_t params; /**< pktio parameters */
pkt_sock_t pkt_sock; /**< using socket API for IO */
+#ifdef ODP_HAVE_NETMAP
+ pkt_netmap_t pkt_nm; /**< using netmap API for IO */
+#endif
};
typedef union {
diff --git a/platform/linux-generic/include/odp_packet_netmap.h b/platform/linux-generic/include/odp_packet_netmap.h
new file mode 100644
index 000000000..960ccbfa4
--- /dev/null
+++ b/platform/linux-generic/include/odp_packet_netmap.h
@@ -0,0 +1,68 @@
+/* Copyright (c) 2013, Linaro Limited
+ * All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#ifndef ODP_PACKET_NETMAP_H
+#define ODP_PACKET_NETMAP_H
+
+#include <stdint.h>
+
+#include <net/if.h>
+#include <net/netmap.h>
+#include <net/netmap_user.h>
+
+#include <odp_align.h>
+#include <odp_debug.h>
+#include <odp_buffer_pool.h>
+#include <odp_packet.h>
+
+#include <odp_pktio_netmap.h>
+
+#define ODP_NETMAP_MODE_HW 0
+#define ODP_NETMAP_MODE_SW 1
+
+#define NETMAP_BLOCKING_IO
+
+/** Packet socket using netmap mmaped rings for both Rx and Tx */
+typedef struct {
+ odp_buffer_pool_t pool;
+ size_t max_frame_len; /**< max frame len = buf_size - sizeof(pkt_hdr) */
+ size_t l2_offset; /**< l2 hdr start offset from start of pkt payload */
+ size_t buf_size; /**< size of buffer payload in 'pool' */
+ struct nm_desc_t *nm_desc;
+ struct netmap_ring *rxring;
+ struct netmap_ring *txring;
+
+ /********************************/
+ odp_queue_t tx_access; /* Used for exclusive access to send packets */
+ uint32_t if_flags;
+ uint32_t if_reqcap;
+ uint32_t if_curcap;
+ char ifname[32];
+} pkt_netmap_t;
+
+/**
+ * Configure an interface to work in netmap mode
+ */
+int setup_pkt_netmap(pkt_netmap_t * const pkt_nm, char *netdev,
+ odp_buffer_pool_t pool, netmap_params_t *nm_params);
+
+/**
+ * Switch interface from netmap mode to normal mode
+ */
+int close_pkt_netmap(pkt_netmap_t * const pkt_nm);
+
+/**
+ * Receive packets using netmap
+ */
+int recv_pkt_netmap(pkt_netmap_t * const pkt_nm, odp_packet_t pkt_table[],
+ unsigned len);
+
+/**
+ * Send packets using netmap
+ */
+int send_pkt_netmap(pkt_netmap_t * const pkt_nm, odp_packet_t pkt_table[],
+ unsigned len);
+#endif
diff --git a/platform/linux-generic/source/odp_packet_io.c b/platform/linux-generic/source/odp_packet_io.c
index b3551df0a..08d3cbe38 100644
--- a/platform/linux-generic/source/odp_packet_io.c
+++ b/platform/linux-generic/source/odp_packet_io.c
@@ -13,6 +13,9 @@
#include <odp_spinlock.h>
#include <odp_shared_memory.h>
#include <odp_packet_socket.h>
+#ifdef ODP_HAVE_NETMAP
+#include <odp_packet_netmap.h>
+#endif
#include <odp_hints.h>
#include <odp_config.h>
#include <odp_queue_internal.h>
@@ -20,6 +23,9 @@
#include <odp_debug.h>
#include <odp_pktio_socket.h>
+#ifdef ODP_HAVE_NETMAP
+#include <odp_pktio_netmap.h>
+#endif
#include <string.h>
@@ -114,6 +120,11 @@ static void init_pktio_entry(pktio_entry_t *entry, odp_pktio_params_t *params)
case ODP_PKTIO_TYPE_SOCKET:
memset(&entry->s.pkt_sock, 0, sizeof(entry->s.pkt_sock));
break;
+#ifdef ODP_HAVE_NETMAP
+ case ODP_PKTIO_TYPE_NETMAP:
+ memset(&entry->s.pkt_nm, 0, sizeof(entry->s.pkt_nm));
+ break;
+#endif
}
/* Save pktio parameters, type is the most useful */
memcpy(&entry->s.params, params, sizeof(*params));
@@ -169,6 +180,11 @@ odp_pktio_t odp_pktio_open(char *dev, odp_buffer_pool_t pool,
case ODP_PKTIO_TYPE_SOCKET:
ODP_DBG("Allocating socket pktio\n");
break;
+#ifdef ODP_HAVE_NETMAP
+ case ODP_PKTIO_TYPE_NETMAP:
+ ODP_DBG("Allocating netmap pktio\n");
+ break;
+#endif
default:
ODP_ERR("Invalid pktio type: %02x\n", params->type);
return ODP_PKTIO_INVALID;
@@ -192,6 +208,17 @@ odp_pktio_t odp_pktio_open(char *dev, odp_buffer_pool_t pool,
id = ODP_PKTIO_INVALID;
}
break;
+#ifdef ODP_HAVE_NETMAP
+ case ODP_PKTIO_TYPE_NETMAP:
+ res = setup_pkt_netmap(&pktio_entry->s.pkt_nm, dev,
+ pool, &params->nm_params);
+ if (res == -1) {
+ close_pkt_netmap(&pktio_entry->s.pkt_nm);
+ free_pktio_entry(id);
+ id = ODP_PKTIO_INVALID;
+ }
+ break;
+#endif
}
unlock_entry(pktio_entry);
@@ -213,6 +240,11 @@ int odp_pktio_close(odp_pktio_t id)
case ODP_PKTIO_TYPE_SOCKET:
res = close_pkt_sock(&entry->s.pkt_sock);
break;
+#ifdef ODP_HAVE_NETMAP
+ case ODP_PKTIO_TYPE_NETMAP:
+ res = close_pkt_netmap(&entry->s.pkt_nm);
+ break;
+#endif
default:
break;
res |= free_pktio_entry(id);
@@ -250,6 +282,11 @@ int odp_pktio_recv(odp_pktio_t id, odp_packet_t pkt_table[], unsigned len)
case ODP_PKTIO_TYPE_SOCKET:
pkts = recv_pkt_sock(&pktio_entry->s.pkt_sock, pkt_table, len);
break;
+#ifdef ODP_HAVE_NETMAP
+ case ODP_PKTIO_TYPE_NETMAP:
+ pkts = recv_pkt_netmap(&pktio_entry->s.pkt_nm, pkt_table, len);
+ break;
+#endif
default:
pkts = -1;
break;
@@ -278,6 +315,11 @@ int odp_pktio_send(odp_pktio_t id, odp_packet_t pkt_table[], unsigned len)
case ODP_PKTIO_TYPE_SOCKET:
pkts = send_pkt_sock(&pktio_entry->s.pkt_sock, pkt_table, len);
break;
+#ifdef ODP_HAVE_NETMAP
+ case ODP_PKTIO_TYPE_NETMAP:
+ pkts = send_pkt_netmap(&pktio_entry->s.pkt_nm, pkt_table, len);
+ break;
+#endif
default:
pkts = -1;
}
diff --git a/platform/linux-generic/source/odp_packet_netmap.c b/platform/linux-generic/source/odp_packet_netmap.c
new file mode 100644
index 000000000..1cbd84c1f
--- /dev/null
+++ b/platform/linux-generic/source/odp_packet_netmap.c
@@ -0,0 +1,385 @@
+/* Copyright (c) 2013, Linaro Limited
+ * Copyright (c) 2013, Nokia Solutions and Networks
+ * All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+/*
+ * NETMAP I/O code inspired by the pkt-gen example application in netmap by:
+ * Copyright (C) 2011-2014 Matteo Landi, Luigi Rizzo. All rights reserved.
+ * Copyright (C) 2013-2014 Universita` di Pisa. All rights reserved.
+ */
+
+#define _GNU_SOURCE
+#include <stdio.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+#include <sys/mman.h>
+#include <poll.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include <linux/ethtool.h>
+#include <linux/sockios.h>
+
+#include <odp_packet_internal.h>
+#include <odp_hints.h>
+#include <odp_thread.h>
+
+#include <helper/odp_eth.h>
+#include <helper/odp_ip.h>
+
+#define NETMAP_WITH_LIBS
+#include <odp_packet_netmap.h>
+
+/** Eth buffer start offset from u32-aligned address to make sure the following
+ * header (e.g. IP) starts at a 32-bit aligned address.
+ */
+#define ETHBUF_OFFSET (ODP_ALIGN_ROUNDUP(ODP_ETHHDR_LEN, sizeof(uint32_t)) \
+ - ODP_ETHHDR_LEN)
+
+/** Round up buffer address to get a properly aliged eth buffer, i.e. aligned
+ * so that the next header always starts at a 32bit aligned address.
+ */
+#define ETHBUF_ALIGN(buf_ptr) ((uint8_t *)ODP_ALIGN_ROUNDUP_PTR((buf_ptr), \
+ sizeof(uint32_t)) + ETHBUF_OFFSET)
+
+static int nm_do_ioctl(pkt_netmap_t * const pkt_nm, unsigned long cmd,
+ int subcmd)
+{
+ struct ethtool_value eval;
+ struct ifreq ifr;
+ int error;
+ int fd;
+
+ fd = socket(AF_INET, SOCK_DGRAM, 0);
+ if (fd < 0) {
+ ODP_ERR("Error: cannot get device control socket\n");
+ return -1;
+ }
+
+ memset(&ifr, 0, sizeof(ifr));
+ strncpy(ifr.ifr_name, pkt_nm->ifname, sizeof(ifr.ifr_name));
+
+ switch (cmd) {
+ case SIOCSIFFLAGS:
+ ifr.ifr_flags = pkt_nm->if_flags & 0xffff;
+ break;
+ case SIOCETHTOOL:
+ eval.cmd = subcmd;
+ eval.data = 0;
+ ifr.ifr_data = (caddr_t)&eval;
+ break;
+ default:
+ break;
+ }
+ error = ioctl(fd, cmd, &ifr);
+ if (error)
+ goto done;
+
+ switch (cmd) {
+ case SIOCGIFFLAGS:
+ pkt_nm->if_flags = (ifr.ifr_flags << 16) |
+ (0xffff & ifr.ifr_flags);
+ ODP_DBG("flags are 0x%x\n", pkt_nm->if_flags);
+ break;
+ default:
+ break;
+ }
+done:
+ close(fd);
+ if (error)
+ ODP_ERR("ioctl err %d %lu: %s\n", error, cmd, strerror(errno));
+
+ return error;
+}
+
+int setup_pkt_netmap(pkt_netmap_t * const pkt_nm, char *netdev,
+ odp_buffer_pool_t pool, netmap_params_t *nm_params)
+{
+ char qname[ODP_QUEUE_NAME_LEN];
+ char ifname[32];
+ odp_packet_t pkt;
+ odp_buffer_t buf;
+ odp_buffer_t token;
+ uint8_t *pkt_buf;
+ int wait_link = 2;
+ uint16_t ringid;
+ int promisc = 1; /* TODO: maybe this should be exported to the user */
+ uint8_t *l2_hdr;
+ int ret;
+
+ if (pool == ODP_BUFFER_POOL_INVALID)
+ return -1;
+ pkt_nm->pool = pool;
+
+ buf = odp_buffer_alloc(pool);
+ if (!odp_buffer_is_valid(buf))
+ return -1;
+
+ pkt = odp_packet_from_buffer(buf);
+ pkt_buf = odp_packet_buf_addr(pkt);
+ l2_hdr = ETHBUF_ALIGN(pkt_buf);
+ /* Store eth buffer offset for buffers from this pool */
+ pkt_nm->l2_offset = (uintptr_t)l2_hdr - (uintptr_t)pkt_buf;
+ /* pkt buffer size */
+ pkt_nm->buf_size = odp_buffer_size(buf);
+ /* max frame len taking into account the l2-offset */
+ pkt_nm->max_frame_len = pkt_nm->buf_size - pkt_nm->l2_offset;
+
+ odp_buffer_free(buf);
+
+ if (nm_params->netmap_mode == ODP_NETMAP_MODE_SW)
+ ringid = NETMAP_SW_RING;
+ else
+ ringid = nm_params->ringid;
+
+ strncpy(pkt_nm->ifname, netdev, sizeof(pkt_nm->ifname));
+ snprintf(ifname, sizeof(ifname), "netmap:%s", netdev);
+ pkt_nm->nm_desc = nm_open(ifname, NULL, ringid, 0);
+
+ if (pkt_nm->nm_desc == NULL) {
+ ODP_ERR("Error opening nm interface: %s\n", strerror(errno));
+ return -1;
+ }
+
+ ODP_DBG("thread %d mode %s mmap addr %p\n",
+ odp_thread_id(),
+ nm_params->netmap_mode == ODP_NETMAP_MODE_SW ? "SW" : "HW",
+ pkt_nm->nm_desc->mem);
+
+ if (nm_params->netmap_mode == ODP_NETMAP_MODE_SW) {
+ pkt_nm->rxring = NETMAP_RXRING(pkt_nm->nm_desc->nifp,
+ pkt_nm->nm_desc->req.nr_rx_rings);
+ pkt_nm->txring = NETMAP_TXRING(pkt_nm->nm_desc->nifp,
+ pkt_nm->nm_desc->req.nr_tx_rings);
+ } else {
+ pkt_nm->rxring = NETMAP_RXRING(pkt_nm->nm_desc->nifp, 0);
+ pkt_nm->txring = NETMAP_TXRING(pkt_nm->nm_desc->nifp, 0);
+ }
+
+ /* Set TX checksumming if hardware rings */
+ if (nm_params->netmap_mode == ODP_NETMAP_MODE_HW) {
+ ret = nm_do_ioctl(pkt_nm, SIOCGIFFLAGS, 0);
+ if (ret)
+ return ret;
+ if ((pkt_nm->if_flags & IFF_UP) == 0) {
+ ODP_DBG("%s is down, bringing up...\n", pkt_nm->ifname);
+ pkt_nm->if_flags |= IFF_UP;
+ }
+ if (promisc) {
+ pkt_nm->if_flags |= IFF_PROMISC;
+ nm_do_ioctl(pkt_nm, SIOCSIFFLAGS, 0);
+ }
+ ret = nm_do_ioctl(pkt_nm, SIOCETHTOOL, ETHTOOL_SGSO);
+ if (ret)
+ ODP_DBG("ETHTOOL_SGSO not supported\n");
+
+ ret = nm_do_ioctl(pkt_nm, SIOCETHTOOL, ETHTOOL_STSO);
+ if (ret)
+ ODP_DBG("ETHTOOL_STSO not supported\n");
+ /* Not sure why this is needed but the netmap example bridge
+ * uses it; it is possible that this causes some problem at
+ * startup, should be investigated further */
+ /*
+ nm_do_ioctl(pkt_nm, SIOCETHTOOL, ETHTOOL_SRXCSUM);
+ */
+ ret = nm_do_ioctl(pkt_nm, SIOCETHTOOL, ETHTOOL_STXCSUM);
+ if (ret)
+ ODP_DBG("ETHTOOL_STXCSUM not supported\n");
+ }
+
+ /* Set up the TX access queue */
+ snprintf(qname, sizeof(qname), "%s:%s-pktio_tx_access", netdev,
+ nm_params->netmap_mode == ODP_NETMAP_MODE_SW ? "SW" : "HW");
+ pkt_nm->tx_access = odp_queue_create(qname, ODP_QUEUE_TYPE_POLL, NULL);
+ if (pkt_nm->tx_access == ODP_QUEUE_INVALID) {
+ ODP_ERR("Error: pktio queue creation failed\n");
+ return -1;
+ }
+ token = odp_buffer_alloc(pool);
+ if (!odp_buffer_is_valid(token)) {
+ ODP_ERR("Error: token creation failed\n");
+ return -1;
+ }
+
+ odp_queue_enq(pkt_nm->tx_access, token);
+
+ ODP_DBG("Wait for link to come up\n");
+ sleep(wait_link);
+ ODP_DBG("Done\n");
+
+ return 0;
+}
+
+int close_pkt_netmap(pkt_netmap_t * const pkt_nm)
+{
+ if (pkt_nm->nm_desc != NULL) {
+ nm_close(pkt_nm->nm_desc);
+ pkt_nm->nm_desc = NULL;
+ }
+
+ return 0;
+}
+
+int recv_pkt_netmap(pkt_netmap_t * const pkt_nm, odp_packet_t pkt_table[],
+ unsigned len)
+{
+ struct netmap_ring *rxring;
+ int fd;
+ unsigned nb_rx = 0;
+ uint32_t limit, rx, cur;
+ odp_packet_t pkt = ODP_PACKET_INVALID;
+
+#ifdef NETMAP_BLOCKING_IO
+ struct pollfd fds[2];
+ int ret;
+#endif
+
+ fd = pkt_nm->nm_desc->fd;
+#ifdef NETMAP_BLOCKING_IO
+ fds[0].fd = fd;
+ fds[0].events = POLLIN;
+#endif
+
+ rxring = pkt_nm->rxring;
+ while (nb_rx < len) {
+ odp_buffer_t buf;
+
+#ifdef NETMAP_BLOCKING_IO
+ ret = poll(&fds[0], 1, 50);
+ if (ret <= 0 || (fds[0].revents & POLLERR))
+ break;
+#else
+ ioctl(fd, NIOCRXSYNC, NULL);
+#endif
+
+ if (nm_ring_empty(rxring)) {
+ /* No data on the wire, return to scheduler */
+ break;
+ }
+
+ limit = len - nb_rx;
+ if (nm_ring_space(rxring) < limit)
+ limit = nm_ring_space(rxring);
+
+ ODP_DBG("receiving %d frames out of %u\n", limit, len);
+
+ cur = rxring->cur;
+ for (rx = 0; rx < limit; rx++) {
+ struct netmap_slot *rslot;
+ char *p;
+ uint16_t payload_len;
+ uint8_t *pkt_buf;
+ uint8_t *l2_hdr;
+
+ if (odp_likely(pkt == ODP_PACKET_INVALID)) {
+ buf = odp_buffer_alloc(pkt_nm->pool);
+ pkt = odp_packet_from_buffer(buf);
+ if (odp_unlikely(pkt == ODP_PACKET_INVALID))
+ break;
+ }
+
+ cur = rxring->cur;
+ rslot = &rxring->slot[cur];
+ p = NETMAP_BUF(rxring, rslot->buf_idx);
+ payload_len = rslot->len;
+
+ rxring->head = nm_ring_next(rxring, cur);
+ rxring->cur = nm_ring_next(rxring, cur);
+
+ if (payload_len > pkt_nm->max_frame_len) {
+ ODP_ERR("Data partially lost %u %lu!\n",
+ payload_len, pkt_nm->max_frame_len);
+ payload_len = pkt_nm->max_frame_len;
+ }
+
+ pkt_buf = odp_packet_buf_addr(pkt);
+ l2_hdr = pkt_buf + pkt_nm->l2_offset;
+
+ /* For now copy the data in the mbuf,
+ worry about zero-copy later */
+ memcpy(l2_hdr, p, payload_len);
+
+ /* Initialize, parse and set packet header data */
+ odp_packet_init(pkt);
+ odp_packet_parse(pkt, payload_len, pkt_nm->l2_offset);
+
+ pkt_table[nb_rx] = pkt;
+ pkt = ODP_PACKET_INVALID;
+ nb_rx++;
+ }
+
+ if (odp_unlikely(pkt == ODP_PACKET_INVALID))
+ break;
+ }
+
+ if (odp_unlikely(pkt != ODP_PACKET_INVALID))
+ odp_buffer_free((odp_buffer_t) pkt);
+
+ if (nb_rx)
+ ODP_DBG("<=== rcvd %03u frames from netmap adapter\n", nb_rx);
+
+ return nb_rx;
+}
+
+int send_pkt_netmap(pkt_netmap_t * const pkt_nm, odp_packet_t pkt_table[],
+ unsigned len)
+{
+ int fd;
+ uint32_t i;
+ uint32_t limit;
+ void *txbuf;
+ struct netmap_ring *txring;
+ struct netmap_slot *slot;
+ odp_packet_t pkt;
+ odp_buffer_t token;
+
+ fd = pkt_nm->nm_desc->fd;
+
+ txring = pkt_nm->txring;
+ limit = nm_ring_space(txring);
+ if (len < limit)
+ limit = len;
+
+ ODP_DBG("Sending %d packets out of %d to netmap %p %u\n",
+ limit, len, txring, txring->cur);
+ token = odp_queue_deq(pkt_nm->tx_access);
+
+ for (i = 0; i < limit; i++) {
+ size_t frame_len;
+ uint32_t cur;
+ uint8_t *frame;
+
+ cur = txring->cur;
+ slot = &txring->slot[cur];
+ txbuf = NETMAP_BUF(txring, slot->buf_idx);
+
+ pkt = pkt_table[i];
+ frame = odp_packet_l2(pkt);
+ frame_len = odp_packet_get_len(pkt);
+
+ memcpy(txbuf, frame, frame_len);
+ slot->len = frame_len;
+ txring->head = nm_ring_next(txring, cur);
+ txring->cur = nm_ring_next(txring, cur);
+ }
+
+ odp_queue_enq(pkt_nm->tx_access, token);
+
+ /* The netmap examples don't use this anymore, don't know why ... */
+ /* ioctl(fd, NIOCTXSYNC, NULL); */
+ (void)fd;
+ if (limit)
+ ODP_DBG("===> sent %03u frames to netmap adapter\n", limit);
+
+ for (i = 0; i < len; i++)
+ odp_buffer_free(pkt_table[i]);
+
+ return limit;
+}