@@ -51,6 +51,8 @@ AC_ARG_WITH([platform],
AC_SUBST([with_platform])
+AM_CONDITIONAL([ODP_NETMAP_ENABLED], [test x$with_platform = xlinux-netmap ])
+
AC_ARG_WITH([sdk-install-path],
AC_HELP_STRING([--with-sdk-install-path=DIR Path to external libs and headers],
[(or in the default path if not specified).]),
@@ -150,6 +152,7 @@ AC_CHECK_FUNCS([bzero clock_gettime gethostbyname getpagesize gettimeofday memse
AC_CONFIG_FILES([Makefile
platform/Makefile
platform/linux-generic/Makefile
+ platform/linux-netmap/Makefile
example/Makefile
example/generator/Makefile
example/ipsec/Makefile
new file mode 100644
@@ -0,0 +1,85 @@
+include $(top_srcdir)/Makefile.inc
+include $(top_srcdir)/platform/Makefile.inc
+
+PLAT_CFLAGS =
+if SDK_INSTALL_PATH_
+PLAT_CFLAGS += -I$(SDK_INSTALL_PATH)/sys
+endif
+
+AM_CFLAGS += $(PLAT_CFLAGS)
+AM_CFLAGS += -I$(srcdir)/include
+AM_CFLAGS += -I$(srcdir)/include/api
+AM_CFLAGS += -I$(top_srcdir)/platform/linux-generic/include
+AM_CFLAGS += -I$(top_srcdir)/platform/linux-generic/include/api
+AM_CFLAGS += -I$(top_srcdir)/helper/include
+
+include_HEADERS = \
+ $(top_srcdir)/platform/linux-generic/include/api/odp.h \
+ $(top_srcdir)/platform/linux-generic/include/api/odp_align.h \
+ $(top_srcdir)/platform/linux-generic/include/api/odp_atomic.h \
+ $(top_srcdir)/platform/linux-generic/include/api/odp_barrier.h \
+ $(top_srcdir)/platform/linux-generic/include/api/odp_buffer.h \
+ $(top_srcdir)/platform/linux-generic/include/api/odp_buffer_pool.h \
+ $(top_srcdir)/platform/linux-generic/include/api/odp_byteorder.h \
+ $(top_srcdir)/platform/linux-generic/include/api/odp_compiler.h \
+ $(top_srcdir)/platform/linux-generic/include/api/odp_config.h \
+ $(top_srcdir)/platform/linux-generic/include/api/odp_coremask.h \
+ $(top_srcdir)/platform/linux-generic/include/api/odp_crypto.h \
+ $(top_srcdir)/platform/linux-generic/include/api/odp_debug.h \
+ $(top_srcdir)/platform/linux-generic/include/api/odp_hints.h \
+ $(top_srcdir)/platform/linux-generic/include/api/odp_init.h \
+ $(top_srcdir)/platform/linux-generic/include/api/odp_packet_flags.h \
+ $(top_srcdir)/platform/linux-generic/include/api/odp_packet.h \
+ $(top_srcdir)/platform/linux-generic/include/api/odp_packet_io.h \
+ $(top_srcdir)/platform/linux-generic/include/api/odp_queue.h \
+ $(top_srcdir)/platform/linux-generic/include/api/odp_rwlock.h \
+ $(top_srcdir)/platform/linux-generic/include/api/odp_schedule.h \
+ $(top_srcdir)/platform/linux-generic/include/api/odp_shared_memory.h \
+ $(top_srcdir)/platform/linux-generic/include/api/odp_spinlock.h \
+ $(top_srcdir)/platform/linux-generic/include/api/odp_std_types.h \
+ $(top_srcdir)/platform/linux-generic/include/api/odp_sync.h \
+ $(top_srcdir)/platform/linux-generic/include/api/odp_system_info.h \
+ $(top_srcdir)/platform/linux-generic/include/api/odp_thread.h \
+ $(top_srcdir)/platform/linux-generic/include/api/odp_ticketlock.h \
+ $(top_srcdir)/platform/linux-generic/include/api/odp_time.h \
+ $(top_srcdir)/platform/linux-generic/include/api/odp_timer.h \
+ $(top_srcdir)/platform/linux-generic/include/api/odp_version.h \
+ $(srcdir)/include/api/odp_pktio_types.h \
+ $(srcdir)/include/api/odp_pktio_netmap.h \
+ $(top_srcdir)/platform/linux-generic/include/api/odp_pktio_socket.h
+
+subdirheadersdir = $(includedir)/helper
+subdirheaders_HEADERS = \
+ $(top_srcdir)/helper/include/odph_chksum.h \
+ $(top_srcdir)/helper/include/odph_eth.h \
+ $(top_srcdir)/helper/include/odph_ip.h \
+ $(top_srcdir)/helper/include/odph_linux.h \
+ $(top_srcdir)/helper/include/odph_packet.h \
+ $(top_srcdir)/helper/include/odph_ring.h \
+ $(top_srcdir)/helper/include/odph_udp.h
+
+__LIB__libodp_la_SOURCES = \
+ ../linux-generic/odp_barrier.c \
+ ../linux-generic/odp_buffer.c \
+ ../linux-generic/odp_buffer_pool.c \
+ ../linux-generic/odp_coremask.c \
+ ../linux-generic/odp_crypto.c \
+ ../linux-generic/odp_init.c \
+ ../linux-generic/odp_linux.c \
+ ../linux-generic/odp_packet.c \
+ ../linux-generic/odp_packet_flags.c \
+ odp_packet_io.c \
+ ../linux-generic/odp_packet_socket.c \
+ ../linux-generic/odp_queue.c \
+ ../linux-generic/odp_ring.c \
+ ../linux-generic/odp_rwlock.c \
+ ../linux-generic/odp_schedule.c \
+ ../linux-generic/odp_shared_memory.c \
+ ../linux-generic/odp_spinlock.c \
+ ../linux-generic/odp_system_info.c \
+ ../linux-generic/odp_thread.c \
+ ../linux-generic/odp_ticketlock.c \
+ ../linux-generic/odp_time.c \
+ ../linux-generic/odp_timer.c \
+ odp_packet_netmap.c
+
new file mode 100644
new file mode 100644
@@ -0,0 +1,90 @@
+Copyright (c) 2014, Linaro Limited
+All rights reserved.
+
+SPDX-License-Identifier: BSD-3-Clause
+
+1. Intro
+==================================
+
+OpenDataPlane implementation using netmap accelerated packet I/O.
+The current implementation does not yet take advantage of the full
+performance of netmap I/O, the data is copied between netmap slots and
+ODP buffers.
+
+The implementation is based almost entirely on the linux-generic platform
+implementation, only the packet I/O differs to work with netmap enabled
+interfaces.
+
+Also the linux-netmap implementation allows hybrid pktio instances, which
+means that the applications can decide to use either sockets or netmap I/O
+at runtime.
+
+2. Build
+==================================
+
+2.1 Building netmap
+--------------------
+
+Netmap is currently hosted on https://code.google.com/p/netmap/ but
+ODP only works at this point with netmap API version 10 so you will need
+a specific revision of netmap.
+
+ git clone https://code.google.com/p/netmap/
+ cd netmap
+ git reset --hard 1f462ef
+
+Netmap consists of a core kernel module (netmap_lin.ko) and optional modified
+device drivers.
+
+Netmap builds as an out-of-tree kernel module, you need matching kernel sources
+to compile it. General build instructions can be found in the packet README:
+http://code.google.com/p/netmap/source/browse/README
+
+2.1.1 Building netmap on Ubuntu with stock kernel
+
+If you are running Ubuntu/Debian with the stock kernel and you want to compile
+both netmap_lin.ko and modified drivers, these steps will guide you through it.
+
+You will need the linux-headers and linux-source packages for this.
+
+ sudo apt-get install linux-headers
+ sudo apt-get install linux-source
+
+The source archive will be placed in /usr/src/linux-source-<kernel-version>
+You will need to locate it and extract it to a convenient place.
+
+Now compile netmap:
+
+ cd LINUX
+ make SRC=<path_to_kernel_source>
+
+2.1.2 Building netmap for kernel built from sources
+
+ cd LINUX
+ make KSRC=<path_to_kernel_source>
+
+2.2 Building ODP
+----------------
+
+ ./bootstrap
+ ./configure --with-platform=linux-netmap --with-sdk-install-path=<netmap>
+ make
+
+3. Running the example application
+==================================
+
+The example application for netmap-linux is odp_pktio_netmap. The main purpose
+of the example application is to show how to implement a simple packet loopback
+application using ODP. The example also implements a bridge between the Linux
+IP stack and the physical interfaces since netmap disrupts the communication
+between the two. The bridging is achieved by passing packets between the
+"software" ring attached to the host stack and the physical rings of the NIC.
+More information on netmap architecture and software rings can be found in the
+"netmap: a novel framework for fast packet I/O" paper by Luigi Rizzo on his
+research page: http://info.iet.unipi.it/~luigi/research.html
+
+ sudo ./odp_pktio_netmap -i eth0 -m 1
+
+From another machine you can simply run ping and observe the duplicate ICMP
+replies. Also the host running the example application should still have network
+connectivity due to the bridging performed as explained above.
new file mode 100644
@@ -0,0 +1,39 @@
+
+/* Copyright (c) 2013, Linaro Limited
+ * All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+/**
+ * @file
+ *
+ * ODP packet input/output netmap
+ */
+
+#ifndef ODP_PKTIO_NETMAP_H
+#define ODP_PKTIO_NETMAP_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <odp_pktio_types.h>
+
+#define ODP_NETMAP_MODE_HW 0 /**< Netmap mode in hardware */
+#define ODP_NETMAP_MODE_SW 1 /**< Netmap mode in software */
+
+/**
+ * Netmap parameters
+ */
+typedef struct {
+ odp_pktio_type_t type; /**< Packet IO type */
+ int netmap_mode; /**< Netmap Mode */
+ uint16_t ringid; /**< Ring identifiers */
+} netmap_params_t;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
new file mode 100644
@@ -0,0 +1,51 @@
+/* Copyright (c) 2013, Linaro Limited
+ * All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+/**
+ * @file
+ *
+ * ODP packet input/output types
+ */
+
+#ifndef ODP_PKTIO_TYPES_H
+#define ODP_PKTIO_TYPES_H
+
+#ifdef __cplusplus
+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
+ */
+
+/**
+ * Packet IO types
+ */
+typedef enum {
+ ODP_PKTIO_TYPE_SOCKET_BASIC = 0x1,
+ ODP_PKTIO_TYPE_SOCKET_MMSG,
+ ODP_PKTIO_TYPE_SOCKET_MMAP,
+ ODP_PKTIO_TYPE_NETMAP,
+} odp_pktio_type_t;
+
+#include <odp_pktio_socket.h>
+#include <odp_pktio_netmap.h>
+
+/**
+ * Packet IO parameters
+ */
+typedef union odp_pktio_params_t {
+ odp_pktio_type_t type; /**< Packet IO type */
+ socket_params_t sock_params; /**< Socket parameter */
+ netmap_params_t nm_params;
+} odp_pktio_params_t;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
new file mode 100644
@@ -0,0 +1,45 @@
+/* Copyright (c) 2013, Linaro Limited
+ * All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+
+/**
+ * @file
+ *
+ * ODP packet IO - implementation internal
+ */
+
+#ifndef ODP_PACKET_IO_INTERNAL_H_
+#define ODP_PACKET_IO_INTERNAL_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <odp_spinlock.h>
+#include <odp_packet_socket.h>
+#include <odp_packet_netmap.h>
+
+struct pktio_entry {
+ odp_spinlock_t lock; /**< entry spinlock */
+ int taken; /**< is entry taken(1) or free(0) */
+ odp_queue_t inq_default; /**< default input queue, if set */
+ 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 */
+ pkt_sock_mmap_t pkt_sock_mmap; /**< using socket mmap API for IO */
+ pkt_netmap_t pkt_nm; /**< using netmap API for IO */
+};
+
+typedef union {
+ struct pktio_entry s;
+ uint8_t pad[ODP_CACHE_LINE_SIZE_ROUNDUP(sizeof(struct pktio_entry))];
+} pktio_entry_t;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
new file mode 100644
@@ -0,0 +1,523 @@
+/* Copyright (c) 2013, Linaro Limited
+ * All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <odp_packet_io.h>
+#include <odp_packet_io_internal.h>
+#include <odp_packet_io_queue.h>
+#include <odp_packet.h>
+#include <odp_packet_internal.h>
+#include <odp_internal.h>
+#include <odp_spinlock.h>
+#include <odp_shared_memory.h>
+#include <odp_packet_socket.h>
+#include <odp_packet_netmap.h>
+#include <odp_hints.h>
+#include <odp_config.h>
+#include <odp_queue_internal.h>
+#include <odp_schedule_internal.h>
+#include <odp_debug.h>
+
+#include <odp_pktio_socket.h>
+
+#include <string.h>
+
+typedef struct {
+ pktio_entry_t entries[ODP_CONFIG_PKTIO_ENTRIES];
+} pktio_table_t;
+
+static pktio_table_t *pktio_tbl;
+
+
+static pktio_entry_t *get_entry(odp_pktio_t id)
+{
+ if (odp_unlikely(id == ODP_PKTIO_INVALID ||
+ id > ODP_CONFIG_PKTIO_ENTRIES))
+ return NULL;
+
+ return &pktio_tbl->entries[id - 1];
+}
+
+int odp_pktio_init_global(void)
+{
+ char name[ODP_QUEUE_NAME_LEN];
+ pktio_entry_t *pktio_entry;
+ queue_entry_t *queue_entry;
+ odp_queue_t qid;
+ int id;
+ odp_shm_t shm;
+
+ shm = odp_shm_reserve("odp_pktio_entries",
+ sizeof(pktio_table_t),
+ sizeof(pktio_entry_t), 0);
+ pktio_tbl = odp_shm_addr(shm);
+
+ if (pktio_tbl == NULL)
+ return -1;
+
+ memset(pktio_tbl, 0, sizeof(pktio_table_t));
+
+ for (id = 1; id <= ODP_CONFIG_PKTIO_ENTRIES; ++id) {
+ pktio_entry = get_entry(id);
+
+ odp_spinlock_init(&pktio_entry->s.lock);
+
+ /* Create a default output queue for each pktio resource */
+ snprintf(name, sizeof(name), "%i-pktio_outq_default", (int)id);
+ name[ODP_QUEUE_NAME_LEN-1] = '\0';
+
+ qid = odp_queue_create(name, ODP_QUEUE_TYPE_PKTOUT, NULL);
+ if (qid == ODP_QUEUE_INVALID)
+ return -1;
+ pktio_entry->s.outq_default = qid;
+
+ queue_entry = queue_to_qentry(qid);
+ queue_entry->s.pktout = id;
+ }
+
+ return 0;
+}
+
+int odp_pktio_init_local(void)
+{
+ return 0;
+}
+
+static int is_free(pktio_entry_t *entry)
+{
+ return (entry->s.taken == 0);
+}
+
+static void set_free(pktio_entry_t *entry)
+{
+ entry->s.taken = 0;
+}
+
+static void set_taken(pktio_entry_t *entry)
+{
+ entry->s.taken = 1;
+}
+
+static void lock_entry(pktio_entry_t *entry)
+{
+ odp_spinlock_lock(&entry->s.lock);
+}
+
+static void unlock_entry(pktio_entry_t *entry)
+{
+ odp_spinlock_unlock(&entry->s.lock);
+}
+
+static void init_pktio_entry(pktio_entry_t *entry, odp_pktio_params_t *params)
+{
+ set_taken(entry);
+ entry->s.inq_default = ODP_QUEUE_INVALID;
+ switch (params->type) {
+ case ODP_PKTIO_TYPE_SOCKET_BASIC:
+ case ODP_PKTIO_TYPE_SOCKET_MMSG:
+ case ODP_PKTIO_TYPE_SOCKET_MMAP:
+ memset(&entry->s.pkt_sock, 0, sizeof(entry->s.pkt_sock));
+ memset(&entry->s.pkt_sock_mmap, 0,
+ sizeof(entry->s.pkt_sock_mmap));
+ break;
+ case ODP_PKTIO_TYPE_NETMAP:
+ memset(&entry->s.pkt_nm, 0, sizeof(entry->s.pkt_nm));
+ break;
+ default:
+ ODP_ERR("Packet I/O type not supported. Please recompile\n");
+ break;
+ }
+ /* Save pktio parameters, type is the most useful */
+ memcpy(&entry->s.params, params, sizeof(*params));
+}
+
+static odp_pktio_t alloc_lock_pktio_entry(odp_pktio_params_t *params)
+{
+ odp_pktio_t id;
+ pktio_entry_t *entry;
+ int i;
+
+ for (i = 0; i < ODP_CONFIG_PKTIO_ENTRIES; ++i) {
+ entry = &pktio_tbl->entries[i];
+ if (is_free(entry)) {
+ lock_entry(entry);
+ if (is_free(entry)) {
+ init_pktio_entry(entry, params);
+ id = i + 1;
+ return id; /* return with entry locked! */
+ }
+ unlock_entry(entry);
+ }
+ }
+
+ return ODP_PKTIO_INVALID;
+}
+
+static int free_pktio_entry(odp_pktio_t id)
+{
+ pktio_entry_t *entry = get_entry(id);
+
+ if (entry == NULL)
+ return -1;
+
+ set_free(entry);
+
+ return 0;
+}
+
+odp_pktio_t odp_pktio_open(const char *dev, odp_buffer_pool_t pool,
+ odp_pktio_params_t *params)
+{
+ odp_pktio_t id;
+ pktio_entry_t *pktio_entry;
+ int res;
+
+ if (params == NULL) {
+ ODP_ERR("Invalid pktio params\n");
+ return ODP_PKTIO_INVALID;
+ }
+
+ switch (params->type) {
+ case ODP_PKTIO_TYPE_SOCKET_BASIC:
+ case ODP_PKTIO_TYPE_SOCKET_MMSG:
+ case ODP_PKTIO_TYPE_SOCKET_MMAP:
+ ODP_DBG("Allocating socket pktio\n");
+ break;
+ case ODP_PKTIO_TYPE_NETMAP:
+ ODP_DBG("Allocating netmap pktio\n");
+ break;
+ default:
+ ODP_ERR("Invalid pktio type: %02x\n", params->type);
+ return ODP_PKTIO_INVALID;
+ }
+
+ id = alloc_lock_pktio_entry(params);
+ if (id == ODP_PKTIO_INVALID) {
+ ODP_ERR("No resources available.\n");
+ return ODP_PKTIO_INVALID;
+ }
+ /* if successful, alloc_pktio_entry() returns with the entry locked */
+
+ pktio_entry = get_entry(id);
+
+ switch (params->type) {
+ case ODP_PKTIO_TYPE_SOCKET_BASIC:
+ case ODP_PKTIO_TYPE_SOCKET_MMSG:
+ res = setup_pkt_sock(&pktio_entry->s.pkt_sock, dev, pool);
+ if (res == -1) {
+ close_pkt_sock(&pktio_entry->s.pkt_sock);
+ free_pktio_entry(id);
+ id = ODP_PKTIO_INVALID;
+ }
+ break;
+ case ODP_PKTIO_TYPE_SOCKET_MMAP:
+ res = setup_pkt_sock_mmap(&pktio_entry->s.pkt_sock_mmap, dev,
+ pool, params->sock_params.fanout);
+ if (res == -1) {
+ close_pkt_sock_mmap(&pktio_entry->s.pkt_sock_mmap);
+ free_pktio_entry(id);
+ id = ODP_PKTIO_INVALID;
+ }
+ break;
+ case ODP_PKTIO_TYPE_NETMAP:
+
+ res = setup_pkt_netmap(&pktio_entry->s.pkt_nm, dev,
+ pool, ¶ms->nm_params);
+ if (res == -1) {
+ close_pkt_netmap(&pktio_entry->s.pkt_nm);
+ free_pktio_entry(id);
+ id = ODP_PKTIO_INVALID;
+ }
+ break;
+ default:
+ free_pktio_entry(id);
+ id = ODP_PKTIO_INVALID;
+ ODP_ERR("This type of I/O is not supported. Please recompile.\n");
+ break;
+ }
+
+ unlock_entry(pktio_entry);
+ return id;
+}
+
+int odp_pktio_close(odp_pktio_t id)
+{
+ pktio_entry_t *entry;
+ int res = -1;
+
+ entry = get_entry(id);
+ if (entry == NULL)
+ return -1;
+
+ lock_entry(entry);
+ if (!is_free(entry)) {
+ switch (entry->s.params.type) {
+ case ODP_PKTIO_TYPE_SOCKET_BASIC:
+ case ODP_PKTIO_TYPE_SOCKET_MMSG:
+ res = close_pkt_sock(&entry->s.pkt_sock);
+ break;
+ case ODP_PKTIO_TYPE_SOCKET_MMAP:
+ res = close_pkt_sock_mmap(&entry->s.pkt_sock_mmap);
+ break;
+ case ODP_PKTIO_TYPE_NETMAP:
+ res = close_pkt_netmap(&entry->s.pkt_nm);
+ break;
+ default:
+ break;
+ res |= free_pktio_entry(id);
+ }
+ }
+ unlock_entry(entry);
+
+ if (res != 0)
+ return -1;
+
+ return 0;
+}
+
+void odp_pktio_set_input(odp_packet_t pkt, odp_pktio_t pktio)
+{
+ odp_packet_hdr(pkt)->input = pktio;
+}
+
+odp_pktio_t odp_pktio_get_input(odp_packet_t pkt)
+{
+ return odp_packet_hdr(pkt)->input;
+}
+
+int odp_pktio_recv(odp_pktio_t id, odp_packet_t pkt_table[], unsigned len)
+{
+ pktio_entry_t *pktio_entry = get_entry(id);
+ int pkts;
+ int i;
+
+ if (pktio_entry == NULL)
+ return -1;
+
+ lock_entry(pktio_entry);
+ switch (pktio_entry->s.params.type) {
+ case ODP_PKTIO_TYPE_SOCKET_BASIC:
+ pkts = recv_pkt_sock_basic(&pktio_entry->s.pkt_sock,
+ pkt_table, len);
+ break;
+ case ODP_PKTIO_TYPE_SOCKET_MMSG:
+ pkts = recv_pkt_sock_mmsg(&pktio_entry->s.pkt_sock,
+ pkt_table, len);
+ break;
+ case ODP_PKTIO_TYPE_SOCKET_MMAP:
+ pkts = recv_pkt_sock_mmap(&pktio_entry->s.pkt_sock_mmap,
+ pkt_table, len);
+ break;
+ case ODP_PKTIO_TYPE_NETMAP:
+ pkts = recv_pkt_netmap(&pktio_entry->s.pkt_nm, pkt_table, len);
+ break;
+ default:
+ pkts = -1;
+ break;
+ }
+
+ unlock_entry(pktio_entry);
+ if (pkts < 0)
+ return pkts;
+
+ for (i = 0; i < pkts; ++i)
+ odp_pktio_set_input(pkt_table[i], id);
+
+ return pkts;
+}
+
+int odp_pktio_send(odp_pktio_t id, odp_packet_t pkt_table[], unsigned len)
+{
+ pktio_entry_t *pktio_entry = get_entry(id);
+ int pkts;
+
+ if (pktio_entry == NULL)
+ return -1;
+
+ lock_entry(pktio_entry);
+ switch (pktio_entry->s.params.type) {
+ case ODP_PKTIO_TYPE_SOCKET_BASIC:
+ pkts = send_pkt_sock_basic(&pktio_entry->s.pkt_sock,
+ pkt_table, len);
+ break;
+ case ODP_PKTIO_TYPE_SOCKET_MMSG:
+ pkts = send_pkt_sock_mmsg(&pktio_entry->s.pkt_sock,
+ pkt_table, len);
+ break;
+ case ODP_PKTIO_TYPE_SOCKET_MMAP:
+ pkts = send_pkt_sock_mmap(&pktio_entry->s.pkt_sock_mmap,
+ pkt_table, len);
+ break;
+ case ODP_PKTIO_TYPE_NETMAP:
+ pkts = send_pkt_netmap(&pktio_entry->s.pkt_nm,
+ pkt_table, len);
+ break;
+ default:
+ pkts = -1;
+ }
+ unlock_entry(pktio_entry);
+
+ return pkts;
+}
+
+int odp_pktio_inq_setdef(odp_pktio_t id, odp_queue_t queue)
+{
+ pktio_entry_t *pktio_entry = get_entry(id);
+ queue_entry_t *qentry = queue_to_qentry(queue);
+
+ if (pktio_entry == NULL || qentry == NULL)
+ return -1;
+
+ if (qentry->s.type != ODP_QUEUE_TYPE_PKTIN)
+ return -1;
+
+ lock_entry(pktio_entry);
+ pktio_entry->s.inq_default = queue;
+ unlock_entry(pktio_entry);
+
+ queue_lock(qentry);
+ qentry->s.pktin = id;
+ qentry->s.status = QUEUE_STATUS_SCHED;
+ queue_unlock(qentry);
+
+ odp_schedule_queue(queue, qentry->s.param.sched.prio);
+
+ return 0;
+}
+
+int odp_pktio_inq_remdef(odp_pktio_t id)
+{
+ return odp_pktio_inq_setdef(id, ODP_QUEUE_INVALID);
+}
+
+odp_queue_t odp_pktio_inq_getdef(odp_pktio_t id)
+{
+ pktio_entry_t *pktio_entry = get_entry(id);
+
+ if (pktio_entry == NULL)
+ return ODP_QUEUE_INVALID;
+
+ return pktio_entry->s.inq_default;
+}
+
+odp_queue_t odp_pktio_outq_getdef(odp_pktio_t id)
+{
+ pktio_entry_t *pktio_entry = get_entry(id);
+
+ if (pktio_entry == NULL)
+ return ODP_QUEUE_INVALID;
+
+ return pktio_entry->s.outq_default;
+}
+
+int pktout_enqueue(queue_entry_t *qentry, odp_buffer_hdr_t *buf_hdr)
+{
+ odp_packet_t pkt = odp_packet_from_buffer(buf_hdr->handle.handle);
+ int len = 1;
+ int nbr;
+
+ nbr = odp_pktio_send(qentry->s.pktout, &pkt, len);
+ return (nbr == len ? 0 : -1);
+}
+
+odp_buffer_hdr_t *pktout_dequeue(queue_entry_t *qentry)
+{
+ (void)qentry;
+ return NULL;
+}
+
+int pktout_enq_multi(queue_entry_t *qentry, odp_buffer_hdr_t *buf_hdr[],
+ int num)
+{
+ odp_packet_t pkt_tbl[QUEUE_MULTI_MAX];
+ int nbr;
+ int i;
+
+ for (i = 0; i < num; ++i)
+ pkt_tbl[i] = odp_packet_from_buffer(buf_hdr[i]->handle.handle);
+
+ nbr = odp_pktio_send(qentry->s.pktout, pkt_tbl, num);
+ return (nbr == num ? 0 : -1);
+}
+
+int pktout_deq_multi(queue_entry_t *qentry, odp_buffer_hdr_t *buf_hdr[],
+ int num)
+{
+ (void)qentry;
+ (void)buf_hdr;
+ (void)num;
+
+ return 0;
+}
+
+int pktin_enqueue(queue_entry_t *qentry, odp_buffer_hdr_t *buf_hdr)
+{
+ /* Use default action */
+ return queue_enq(qentry, buf_hdr);
+}
+
+odp_buffer_hdr_t *pktin_dequeue(queue_entry_t *qentry)
+{
+ odp_buffer_hdr_t *buf_hdr;
+
+ buf_hdr = queue_deq(qentry);
+
+ if (buf_hdr == NULL) {
+ odp_packet_t pkt;
+ odp_buffer_t buf;
+ odp_packet_t pkt_tbl[QUEUE_MULTI_MAX];
+ odp_buffer_hdr_t *tmp_hdr_tbl[QUEUE_MULTI_MAX];
+ int pkts, i, j;
+
+ pkts = odp_pktio_recv(qentry->s.pktin, pkt_tbl,
+ QUEUE_MULTI_MAX);
+
+ if (pkts > 0) {
+ pkt = pkt_tbl[0];
+ buf = odp_buffer_from_packet(pkt);
+ buf_hdr = odp_buf_to_hdr(buf);
+
+ for (i = 1, j = 0; i < pkts; ++i) {
+ buf = odp_buffer_from_packet(pkt_tbl[i]);
+ tmp_hdr_tbl[j++] = odp_buf_to_hdr(buf);
+ }
+ queue_enq_multi(qentry, tmp_hdr_tbl, j);
+ }
+ }
+
+ return buf_hdr;
+}
+
+int pktin_enq_multi(queue_entry_t *qentry, odp_buffer_hdr_t *buf_hdr[], int num)
+{
+ /* Use default action */
+ return queue_enq_multi(qentry, buf_hdr, num);
+}
+
+int pktin_deq_multi(queue_entry_t *qentry, odp_buffer_hdr_t *buf_hdr[], int num)
+{
+ int nbr;
+
+ nbr = queue_deq_multi(qentry, buf_hdr, num);
+
+ if (nbr < num) {
+ odp_packet_t pkt_tbl[QUEUE_MULTI_MAX];
+ odp_buffer_hdr_t *tmp_hdr_tbl[QUEUE_MULTI_MAX];
+ odp_buffer_t buf;
+ int pkts, i;
+
+ pkts = odp_pktio_recv(qentry->s.pktin, pkt_tbl,
+ QUEUE_MULTI_MAX);
+ if (pkts > 0) {
+ for (i = 0; i < pkts; ++i) {
+ buf = odp_buffer_from_packet(pkt_tbl[i]);
+ tmp_hdr_tbl[i] = odp_buf_to_hdr(buf);
+ }
+ queue_enq_multi(qentry, tmp_hdr_tbl, pkts);
+ }
+ }
+
+ return nbr;
+}
new file mode 100644
@@ -0,0 +1,453 @@
+/* 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 <odph_eth.h>
+#include <odph_ip.h>
+#include <odph_packet.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(ODPH_ETHHDR_LEN, sizeof(uint32_t)) \
+ - ODPH_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)
+
+#define ETH_PROMISC 1 /* TODO: maybe this should be exported to the user */
+#define WAITLINK_TMO 2
+#define POLL_TMO 50
+
+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, const 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 token;
+ uint8_t *pkt_buf;
+ uint16_t ringid;
+ uint8_t *l2_hdr;
+ int ret;
+
+ if (pool == ODP_BUFFER_POOL_INVALID)
+ return -1;
+ pkt_nm->pool = pool;
+
+ pkt = odph_packet_alloc(pool);
+ if (!odph_packet_is_valid(pkt))
+ return -1;
+
+ pkt_buf = odp_packet_buf_addr(pkt);
+ l2_hdr = ETHBUF_ALIGN(pkt_buf);
+ /* Store eth buffer offset for buffers from this pool */
+ pkt_nm->frame_offset = (uintptr_t)l2_hdr - (uintptr_t)pkt_buf;
+ /* pkt buffer size */
+ pkt_nm->buf_size = odph_packet_buf_size(pkt);
+ /* max frame len taking into account the l2-offset */
+ pkt_nm->max_frame_len = pkt_nm->buf_size - pkt_nm->frame_offset;
+ /* save netmap_mode for later use */
+ pkt_nm->netmap_mode = nm_params->netmap_mode;
+
+ odph_packet_free(pkt);
+
+ 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->begin = pkt_nm->nm_desc->req.nr_rx_rings;
+ pkt_nm->end = pkt_nm->begin + 1;
+ 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 if (nm_params->ringid & NETMAP_HW_RING) {
+ pkt_nm->begin = nm_params->ringid & NETMAP_RING_MASK;
+ pkt_nm->end = pkt_nm->begin + 1;
+ pkt_nm->rxring = NETMAP_RXRING(pkt_nm->nm_desc->nifp,
+ pkt_nm->begin);
+ pkt_nm->txring = NETMAP_TXRING(pkt_nm->nm_desc->nifp,
+ pkt_nm->begin);
+ } else {
+ pkt_nm->begin = 0;
+ pkt_nm->end = pkt_nm->nm_desc->req.nr_rx_rings;
+ 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 (ETH_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");
+ /* TODO: This seems to cause the app to not receive frames
+ * first time it is launched after netmap driver is inserted.
+ * 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(WAITLINK_TMO);
+ 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 = pkt_nm->rxring;
+ int fd;
+ unsigned nb_rx = 0;
+ uint32_t limit, rx;
+ uint32_t ringid = pkt_nm->begin;
+ odp_packet_t pkt = ODP_PACKET_INVALID;
+#ifdef NETMAP_BLOCKING_IO
+ struct pollfd fds[1];
+ int ret;
+#endif
+
+ fd = pkt_nm->nm_desc->fd;
+#ifdef NETMAP_BLOCKING_IO
+ fds[0].fd = fd;
+ fds[0].events = POLLIN;
+#endif
+
+ while (nb_rx < len) {
+#ifdef NETMAP_BLOCKING_IO
+ ret = poll(&fds[0], 1, POLL_TMO);
+ if (ret <= 0 || (fds[0].revents & POLLERR))
+ break;
+#else
+ ioctl(fd, NIOCRXSYNC, NULL);
+#endif
+
+ /* Find first ring not empty */
+ while (nm_ring_empty(rxring)) {
+ ringid++;
+
+ /* Return to scheduler if no more data to meet the
+ requested amount (len) */
+ if (ringid == pkt_nm->end) {
+ ODP_DBG("No more data on the wire\n");
+ break;
+ }
+
+ rxring = NETMAP_RXRING(pkt_nm->nm_desc->nifp, ringid);
+ }
+
+ 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);
+
+ for (rx = 0; rx < limit; rx++) {
+ struct netmap_slot *rslot;
+ char *p;
+ uint16_t frame_len;
+ uint8_t *pkt_buf;
+ uint8_t *l2_hdr;
+ uint32_t cur;
+
+ if (odp_likely(pkt == ODP_PACKET_INVALID)) {
+ pkt = odph_packet_alloc(pkt_nm->pool);
+ if (odp_unlikely(pkt == ODP_PACKET_INVALID))
+ break;
+ }
+
+ cur = rxring->cur;
+ rslot = &rxring->slot[cur];
+ p = NETMAP_BUF(rxring, rslot->buf_idx);
+ frame_len = rslot->len;
+
+ rxring->head = nm_ring_next(rxring, cur);
+ rxring->cur = rxring->head;
+
+ pkt_buf = odp_packet_buf_addr(pkt);
+ l2_hdr = pkt_buf + pkt_nm->frame_offset;
+
+ if (frame_len > pkt_nm->max_frame_len) {
+ ODP_ERR("RX: frame too big %u %lu!\n",
+ frame_len, pkt_nm->max_frame_len);
+ /* drop the frame, reuse pkt next interation */
+ continue;
+ }
+ if (odp_unlikely(frame_len < ODPH_ETH_LEN_MIN)) {
+ if (odp_unlikely(pkt_nm->netmap_mode !=
+ ODP_NETMAP_MODE_SW)) {
+ ODP_ERR("RX: Frame truncated: %u\n",
+ (unsigned)frame_len);
+ continue;
+ }
+ memset(l2_hdr + frame_len, 0,
+ ODPH_ETH_LEN_MIN - frame_len);
+ frame_len = ODPH_ETH_LEN_MIN;
+ }
+
+ /* For now copy the data in the mbuf,
+ worry about zero-copy later */
+ memcpy(l2_hdr, p, frame_len);
+
+ /* Initialize, parse and set packet header data */
+ odp_packet_init(pkt);
+ odp_packet_parse(pkt, frame_len, pkt_nm->frame_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)
+{
+ struct netmap_ring *txring = pkt_nm->txring;
+ int fd;
+ unsigned nb_tx = 0;
+ uint32_t limit, tx;
+ uint32_t ringid = pkt_nm->begin;
+ odp_packet_t pkt;
+ odp_buffer_t token;
+
+#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 = POLLOUT;
+#endif
+
+ token = odp_queue_deq(pkt_nm->tx_access);
+
+ while (nb_tx < len) {
+#ifdef NETMAP_BLOCKING_IO
+ ret = poll(&fds[0], 1, POLL_TMO);
+ if (ret <= 0 || (fds[0].revents & POLLERR))
+ break;
+#else
+ ioctl(fd, NIOCTXSYNC, NULL);
+#endif
+
+ /* Find first ring not empty */
+ while (nm_ring_empty(txring)) {
+ ringid++;
+
+ /* Return to scheduler if no more space to meet the
+ requested amount (len) */
+ if (ringid == pkt_nm->end) {
+ ODP_DBG("No more space in TX rings\n");
+ break;
+ }
+
+ txring = NETMAP_TXRING(pkt_nm->nm_desc->nifp, ringid);
+ }
+
+ limit = len - nb_tx;
+ if (nm_ring_space(txring) < limit)
+ limit = nm_ring_space(txring);
+
+ ODP_DBG("Sending %d packets out of %d to netmap %p %u\n",
+ limit, len, txring, txring->cur);
+
+ for (tx = 0; tx < limit; tx++) {
+ struct netmap_slot *tslot;
+ size_t frame_len;
+ uint32_t cur;
+ uint8_t *frame;
+ void *txbuf;
+
+ cur = txring->cur;
+ tslot = &txring->slot[cur];
+ txbuf = NETMAP_BUF(txring, tslot->buf_idx);
+
+ pkt = pkt_table[nb_tx];
+ frame = odp_packet_start(pkt);
+ frame_len = odp_packet_get_len(pkt);
+
+ memcpy(txbuf, frame, frame_len);
+ tslot->len = frame_len;
+ txring->head = nm_ring_next(txring, cur);
+ txring->cur = txring->head;
+ nb_tx++;
+ }
+ }
+
+ odp_queue_enq(pkt_nm->tx_access, token);
+
+#ifndef NETMAP_BLOCKING_IO
+ ioctl(fd, NIOCTXSYNC, NULL);
+#endif
+
+ if (nb_tx)
+ ODP_DBG("===> sent %03u frames to netmap adapter\n", nb_tx);
+
+ for (tx = 0; tx < len; tx++)
+ odph_packet_free(pkt_table[tx]);
+
+ return nb_tx;
+}
new file mode 100644
@@ -0,0 +1,67 @@
+/* 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 frame_offset; /**< frame start offset from start of pkt buf */
+ size_t buf_size; /**< size of buffer payload in 'pool' */
+ int netmap_mode;
+ struct nm_desc_t *nm_desc;
+ uint32_t begin;
+ uint32_t end;
+ struct netmap_ring *rxring;
+ struct netmap_ring *txring;
+ odp_queue_t tx_access; /* Used for exclusive access to send packets */
+ uint32_t if_flags;
+ char ifname[32];
+} pkt_netmap_t;
+
+/**
+ * Configure an interface to work in netmap mode
+ */
+int setup_pkt_netmap(pkt_netmap_t * const pkt_nm, const 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
Signed-off-by: Ciprian Barbu <ciprian.barbu@linaro.org> --- configure.ac | 3 + platform/linux-netmap/Makefile.am | 85 ++++ platform/linux-netmap/Makefile.inc | 0 platform/linux-netmap/README | 90 ++++ .../linux-netmap/include/api/odp_pktio_netmap.h | 39 ++ .../linux-netmap/include/api/odp_pktio_types.h | 51 ++ .../linux-netmap/include/odp_packet_io_internal.h | 45 ++ platform/linux-netmap/odp_packet_io.c | 523 +++++++++++++++++++++ platform/linux-netmap/odp_packet_netmap.c | 453 ++++++++++++++++++ platform/linux-netmap/odp_packet_netmap.h | 67 +++ 10 files changed, 1356 insertions(+) create mode 100644 platform/linux-netmap/Makefile.am create mode 100644 platform/linux-netmap/Makefile.inc create mode 100644 platform/linux-netmap/README create mode 100644 platform/linux-netmap/include/api/odp_pktio_netmap.h create mode 100644 platform/linux-netmap/include/api/odp_pktio_types.h create mode 100644 platform/linux-netmap/include/odp_packet_io_internal.h create mode 100644 platform/linux-netmap/odp_packet_io.c create mode 100644 platform/linux-netmap/odp_packet_netmap.c create mode 100644 platform/linux-netmap/odp_packet_netmap.h