From patchwork Mon Oct 13 15:27:22 2014 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Ciprian Barbu X-Patchwork-Id: 38677 Return-Path: X-Original-To: linaro@patches.linaro.org Delivered-To: linaro@patches.linaro.org Received: from mail-lb0-f197.google.com (mail-lb0-f197.google.com [209.85.217.197]) by ip-10-151-82-157.ec2.internal (Postfix) with ESMTPS id EC8DC20973 for ; Mon, 13 Oct 2014 15:33:33 +0000 (UTC) Received: by mail-lb0-f197.google.com with SMTP id p9sf4326845lbv.0 for ; Mon, 13 Oct 2014 08:33:32 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20130820; h=x-gm-message-state:delivered-to:from:to:date:message-id:in-reply-to :references:subject:precedence:list-id:list-unsubscribe:list-archive :list-post:list-help:list-subscribe:mime-version:errors-to:sender :x-original-sender:x-original-authentication-results:mailing-list :content-type:content-transfer-encoding; bh=Ifa7n5E4cf0cPNfwUJrNFqmSQ/hfFZxVVkq1MVu8eGQ=; b=DXlGF9xmMn5PGa+DkQSXw/paAKLaLdp9JmpmhaiQZ56sKx+CxqrxstDnISNJBORKaK PutvJQV1MwNuA1rNbeFv9bzgDIG67TtmLsyyoyVoE/0TZIbFnoHOQx66aTWgECtB83DI Q1GIldD2NgZWg3Z97yf/qPRFvpe+uuzwRRn7nU1/65jWBIXdlKThYKIOSzi4HE0sHE2f nusBClsUH4AIulgm2xt64FzdLFFnFZw3761p3px/UWVUoUGYlkL4akwkaSbTjETqJMmv BqLxRF9TrQF0CfvkJ5WrPrfKrgmI9Ay/lTvKOOuwdqhksxQWlC32rkEF8J6P4Em/Z8Hv ibKg== X-Gm-Message-State: ALoCoQmbAZgrb+sO1x87jWP7t8asQe2A5LrLv1n3f1lxv2Zz1h43Njp7lgsM2ZEWvAVnBsJuWZSv X-Received: by 10.112.89.8 with SMTP id bk8mr4130636lbb.6.1413214412488; Mon, 13 Oct 2014 08:33:32 -0700 (PDT) X-BeenThere: patchwork-forward@linaro.org Received: by 10.152.26.72 with SMTP id j8ls416662lag.105.gmail; Mon, 13 Oct 2014 08:33:32 -0700 (PDT) X-Received: by 10.112.122.103 with SMTP id lr7mr24896598lbb.48.1413214412323; Mon, 13 Oct 2014 08:33:32 -0700 (PDT) Received: from mail-lb0-f177.google.com (mail-lb0-f177.google.com [209.85.217.177]) by mx.google.com with ESMTPS id q9si22665358lag.92.2014.10.13.08.33.32 for (version=TLSv1 cipher=ECDHE-RSA-RC4-SHA bits=128/128); Mon, 13 Oct 2014 08:33:32 -0700 (PDT) Received-SPF: pass (google.com: domain of patch+caf_=patchwork-forward=linaro.org@linaro.org designates 209.85.217.177 as permitted sender) client-ip=209.85.217.177; Received: by mail-lb0-f177.google.com with SMTP id w7so6552054lbi.22 for ; Mon, 13 Oct 2014 08:33:32 -0700 (PDT) X-Received: by 10.112.14.34 with SMTP id m2mr24297259lbc.74.1413214412172; Mon, 13 Oct 2014 08:33:32 -0700 (PDT) X-Forwarded-To: patchwork-forward@linaro.org X-Forwarded-For: patch@linaro.org patchwork-forward@linaro.org Delivered-To: patch@linaro.org Received: by 10.112.84.229 with SMTP id c5csp235813lbz; Mon, 13 Oct 2014 08:33:30 -0700 (PDT) X-Received: by 10.140.40.36 with SMTP id w33mr38795650qgw.22.1413214410436; Mon, 13 Oct 2014 08:33:30 -0700 (PDT) Received: from ip-10-35-177-41.ec2.internal (lists.linaro.org. [54.225.227.206]) by mx.google.com with ESMTPS id p39si25441979qgp.16.2014.10.13.08.33.29 for (version=TLSv1 cipher=RC4-SHA bits=128/128); Mon, 13 Oct 2014 08:33:30 -0700 (PDT) Received-SPF: none (google.com: lng-odp-bounces@lists.linaro.org does not designate permitted sender hosts) client-ip=54.225.227.206; Received: from localhost ([127.0.0.1] helo=ip-10-35-177-41.ec2.internal) by ip-10-35-177-41.ec2.internal with esmtp (Exim 4.76) (envelope-from ) id 1Xdhcm-0002dk-Fb; Mon, 13 Oct 2014 15:33:28 +0000 Received: from mail-la0-f51.google.com ([209.85.215.51]) by ip-10-35-177-41.ec2.internal with esmtp (Exim 4.76) (envelope-from ) id 1Xdhcf-0002df-Ny for lng-odp@lists.linaro.org; Mon, 13 Oct 2014 15:33:22 +0000 Received: by mail-la0-f51.google.com with SMTP id ge10so6865951lab.38 for ; Mon, 13 Oct 2014 08:33:15 -0700 (PDT) X-Received: by 10.152.8.194 with SMTP id t2mr3943534laa.85.1413214052176; Mon, 13 Oct 2014 08:27:32 -0700 (PDT) Received: from cipriantemp.enea.se (sestofw01.enea.se. [192.36.1.252]) by mx.google.com with ESMTPSA id uc6sm2684671lac.27.2014.10.13.08.27.31 for (version=TLSv1.1 cipher=ECDHE-RSA-RC4-SHA bits=128/128); Mon, 13 Oct 2014 08:27:31 -0700 (PDT) From: Ciprian Barbu To: lng-odp@lists.linaro.org Date: Mon, 13 Oct 2014 18:27:22 +0300 Message-Id: <1413214043-4864-2-git-send-email-ciprian.barbu@linaro.org> X-Mailer: git-send-email 1.8.3.2 In-Reply-To: <1413214043-4864-1-git-send-email-ciprian.barbu@linaro.org> References: <1413214043-4864-1-git-send-email-ciprian.barbu@linaro.org> X-Topics: patch Subject: [lng-odp] [PATCH NETMAP 1/2] linux-netmap: add linux-netmap X-BeenThere: lng-odp@lists.linaro.org X-Mailman-Version: 2.1.14 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: , List-Help: , List-Subscribe: , MIME-Version: 1.0 Errors-To: lng-odp-bounces@lists.linaro.org Sender: lng-odp-bounces@lists.linaro.org X-Removed-Original-Auth: Dkim didn't pass. X-Original-Sender: ciprian.barbu@linaro.org X-Original-Authentication-Results: mx.google.com; spf=pass (google.com: domain of patch+caf_=patchwork-forward=linaro.org@linaro.org designates 209.85.217.177 as permitted sender) smtp.mail=patch+caf_=patchwork-forward=linaro.org@linaro.org Mailing-list: list patchwork-forward@linaro.org; contact patchwork-forward+owners@linaro.org X-Google-Group-Id: 836684582541 Signed-off-by: Ciprian Barbu --- 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 diff --git a/configure.ac b/configure.ac index 296c9c5..220f89a 100644 --- a/configure.ac +++ b/configure.ac @@ -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 diff --git a/platform/linux-netmap/Makefile.am b/platform/linux-netmap/Makefile.am new file mode 100644 index 0000000..b783521 --- /dev/null +++ b/platform/linux-netmap/Makefile.am @@ -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 + diff --git a/platform/linux-netmap/Makefile.inc b/platform/linux-netmap/Makefile.inc new file mode 100644 index 0000000..e69de29 diff --git a/platform/linux-netmap/README b/platform/linux-netmap/README new file mode 100644 index 0000000..6021445 --- /dev/null +++ b/platform/linux-netmap/README @@ -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- +You will need to locate it and extract it to a convenient place. + +Now compile netmap: + + cd LINUX + make SRC= + +2.1.2 Building netmap for kernel built from sources + + cd LINUX + make KSRC= + +2.2 Building ODP +---------------- + + ./bootstrap + ./configure --with-platform=linux-netmap --with-sdk-install-path= + 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. diff --git a/platform/linux-netmap/include/api/odp_pktio_netmap.h b/platform/linux-netmap/include/api/odp_pktio_netmap.h new file mode 100644 index 0000000..294a13d --- /dev/null +++ b/platform/linux-netmap/include/api/odp_pktio_netmap.h @@ -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 + +#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 diff --git a/platform/linux-netmap/include/api/odp_pktio_types.h b/platform/linux-netmap/include/api/odp_pktio_types.h new file mode 100644 index 0000000..b7c6341 --- /dev/null +++ b/platform/linux-netmap/include/api/odp_pktio_types.h @@ -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 +#include + +/** + * 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 diff --git a/platform/linux-netmap/include/odp_packet_io_internal.h b/platform/linux-netmap/include/odp_packet_io_internal.h new file mode 100644 index 0000000..a11ca31 --- /dev/null +++ b/platform/linux-netmap/include/odp_packet_io_internal.h @@ -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 +#include +#include + +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 diff --git a/platform/linux-netmap/odp_packet_io.c b/platform/linux-netmap/odp_packet_io.c new file mode 100644 index 0000000..8d6e7d6 --- /dev/null +++ b/platform/linux-netmap/odp_packet_io.c @@ -0,0 +1,523 @@ +/* Copyright (c) 2013, Linaro Limited + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +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; +} diff --git a/platform/linux-netmap/odp_packet_netmap.c b/platform/linux-netmap/odp_packet_netmap.c new file mode 100644 index 0000000..524492d --- /dev/null +++ b/platform/linux-netmap/odp_packet_netmap.c @@ -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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include + +#include +#include +#include + +#define NETMAP_WITH_LIBS +#include + +/** 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; +} diff --git a/platform/linux-netmap/odp_packet_netmap.h b/platform/linux-netmap/odp_packet_netmap.h new file mode 100644 index 0000000..1ab50d0 --- /dev/null +++ b/platform/linux-netmap/odp_packet_netmap.h @@ -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 + +#include +#include +#include + +#include +#include +#include +#include + +#include + +#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