From patchwork Wed Aug 20 17:11:17 2014 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Ciprian Barbu X-Patchwork-Id: 35708 Return-Path: X-Original-To: linaro@patches.linaro.org Delivered-To: linaro@patches.linaro.org Received: from mail-qc0-f200.google.com (mail-qc0-f200.google.com [209.85.216.200]) by ip-10-151-82-157.ec2.internal (Postfix) with ESMTPS id B37EB2055D for ; Wed, 20 Aug 2014 17:11:56 +0000 (UTC) Received: by mail-qc0-f200.google.com with SMTP id w7sf25264276qcr.7 for ; Wed, 20 Aug 2014 10:11:56 -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=aootHFRMJKBw2QjoD+LzSQ2NER2/5v19nlPA64y32fA=; b=CECQ2wYskstY44bT09qHCMB/4nZ3Ab4JqR1yJvtXjWXZeIMPySInmcK8CF8fmr9Ra6 SauMeGDIF4k/kzfrrpz08D1m0THj4B90otxLT6XQKvxe6oj7YeznrAJRIulA1hZzML9W 6NB1AlmY6m8A4cYB3ifmmrAuIo+OZRQqWKFjsY4D56xJET6ESPyzFGYRc1z8NClWV6B6 AUVJxIuyy5pY8mGCOPmnjsHnHBeW/I0gXTuLKaEPMrrAFiHvcca04J17eF+Z37Y246Uu Cbhbbz9Rf1ZR5FAjfm+cmJxDKuZkkkw2weFH6HE7qTj4RG5o0u6D4wwmbENEykHWETiH Xosg== X-Gm-Message-State: ALoCoQnx6BDc8seisvX27f3QCo6nAuoEYkmCchxVLsI6c7YrrfxTniyW+f9QLGWHnJmiNvi8cEY4 X-Received: by 10.236.209.97 with SMTP id r61mr22816923yho.33.1408554716602; Wed, 20 Aug 2014 10:11:56 -0700 (PDT) X-BeenThere: patchwork-forward@linaro.org Received: by 10.140.95.182 with SMTP id i51ls413207qge.20.gmail; Wed, 20 Aug 2014 10:11:56 -0700 (PDT) X-Received: by 10.53.6.132 with SMTP id cu4mr6411073vdd.62.1408554716468; Wed, 20 Aug 2014 10:11:56 -0700 (PDT) Received: from mail-vc0-f171.google.com (mail-vc0-f171.google.com [209.85.220.171]) by mx.google.com with ESMTPS id r6si11093174vcu.14.2014.08.20.10.11.56 for (version=TLSv1 cipher=ECDHE-RSA-RC4-SHA bits=128/128); Wed, 20 Aug 2014 10:11:56 -0700 (PDT) Received-SPF: pass (google.com: domain of patch+caf_=patchwork-forward=linaro.org@linaro.org designates 209.85.220.171 as permitted sender) client-ip=209.85.220.171; Received: by mail-vc0-f171.google.com with SMTP id hq11so9487571vcb.16 for ; Wed, 20 Aug 2014 10:11:56 -0700 (PDT) X-Received: by 10.220.77.65 with SMTP id f1mr855558vck.48.1408554716329; Wed, 20 Aug 2014 10:11:56 -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.221.45.67 with SMTP id uj3csp69764vcb; Wed, 20 Aug 2014 10:11:55 -0700 (PDT) X-Received: by 10.224.54.205 with SMTP id r13mr80699722qag.59.1408554715596; Wed, 20 Aug 2014 10:11:55 -0700 (PDT) Received: from ip-10-141-164-156.ec2.internal (lists.linaro.org. [54.225.227.206]) by mx.google.com with ESMTPS id p2si34794512qcp.36.2014.08.20.10.11.54 for (version=TLSv1 cipher=RC4-SHA bits=128/128); Wed, 20 Aug 2014 10:11:55 -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-141-164-156.ec2.internal) by ip-10-141-164-156.ec2.internal with esmtp (Exim 4.76) (envelope-from ) id 1XK9QQ-0002nF-Pr; Wed, 20 Aug 2014 17:11:54 +0000 Received: from mail-lb0-f174.google.com ([209.85.217.174]) by ip-10-141-164-156.ec2.internal with esmtp (Exim 4.76) (envelope-from ) id 1XK9QG-0002mW-HI for lng-odp@lists.linaro.org; Wed, 20 Aug 2014 17:11:44 +0000 Received: by mail-lb0-f174.google.com with SMTP id c11so6943823lbj.5 for ; Wed, 20 Aug 2014 10:11:38 -0700 (PDT) X-Received: by 10.152.27.134 with SMTP id t6mr34664627lag.56.1408554698558; Wed, 20 Aug 2014 10:11:38 -0700 (PDT) Received: from sestofb10.enea.se (sestofw01.enea.se. [192.36.1.252]) by mx.google.com with ESMTPSA id dv6sm2008002lbc.32.2014.08.20.10.11.37 for (version=TLSv1.2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Wed, 20 Aug 2014 10:11:37 -0700 (PDT) From: Ciprian Barbu To: lng-odp@lists.linaro.org Date: Wed, 20 Aug 2014 19:11:17 +0200 Message-Id: <1408554678-55317-4-git-send-email-ciprian.barbu@linaro.org> X-Mailer: git-send-email 1.9.1 In-Reply-To: <1408554678-55317-1-git-send-email-ciprian.barbu@linaro.org> References: <1408554678-55317-1-git-send-email-ciprian.barbu@linaro.org> X-Topics: patch Subject: [lng-odp] [APPS PATCH 3/4] dpif-netdev: Add ODP netdev 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.220.171 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 --- INSTALL | 1 + INSTALL.ODP | 101 +++++++ lib/netdev-odp.c | 735 ++++++++++++++++++++++++++++++++++++++++++++++++ lib/netdev-odp.h | 39 +++ lib/netdev.c | 8 +- lib/ofpbuf.c | 16 ++ lib/ofpbuf.h | 52 ++++ vswitchd/ovs-vswitchd.c | 11 + 8 files changed, 962 insertions(+), 1 deletion(-) create mode 100644 INSTALL.ODP create mode 100644 lib/netdev-odp.c create mode 100644 lib/netdev-odp.h diff --git a/INSTALL b/INSTALL index 7e0097b..7d0bcf3 100644 --- a/INSTALL +++ b/INSTALL @@ -11,6 +11,7 @@ on a specific platform, please see one of these files: - INSTALL.XenServer - INSTALL.NetBSD - INSTALL.DPDK + - INSTALL.ODP Build Requirements ------------------ diff --git a/INSTALL.ODP b/INSTALL.ODP new file mode 100644 index 0000000..6dafc34 --- /dev/null +++ b/INSTALL.ODP @@ -0,0 +1,101 @@ + Using Open vSwitch with ODP + =========================== + + +Open vSwitch can be used with the ODP project (http://www.opendataplane.org) +The switch will function entirely in userspace. This file serves as a guide for +building and installing Open vSwitch with ODP. + +The ODP mode is considered experimental, it has not been thoroughly tested. + +This version of Open vSwitch should be built manually with "configure" and +"make". + + +Building and Installing: +------------------------ + +ODP: +Get the code: +git clone http://git.linaro.org/git/lng/odp.git +cd odp +./bootstrap.sh +./configure --enable-debug +make + +optionally: make install + +OVS: +cd $(OVS_DIR)/openvswitch +./boot.sh +./configure --with-odp=$ODP +make + +Refer to INSTALL.userspace for general requirements of building userspace OVS. + +Alternatively go to https://wiki.linaro.org/LNG/Engineering/OVSDPDKOnUbuntu +which explains how to run OVS with DPDK. Similar steps should work with ODP. + +Using ODP with ovs-vswitchd: +---------------------------- + +Start ovsdb-server as discussed in INSTALL doc: + Summary e.g.: + First time only db creation (or clearing): + mkdir -p /usr/local/etc/openvswitch + mkdir -p /usr/local/var/run/openvswitch + rm /usr/local/etc/openvswitch/conf.db + cd $OVS_DIR + ./ovsdb/ovsdb-tool create /usr/local/etc/openvswitch/conf.db \ + ./vswitchd/vswitch.ovsschema + start ovsdb-server + cd $OVS_DIR + ./ovsdb/ovsdb-server --remote=punix:/usr/local/var/run/openvswitch/db.sock \ + --remote=db:OpenOpen_vSwitch,manager_options \ + --private-key=db:Open_vSwitch,SSL,private_key \ + --certificate=dbitch,SSL,certificate \ + --bootstrap-ca-cert=db:Open_vSwitch,SSL,ca_cert --pidfile --detach + First time after db creation, initialize: + cd $OVS_DIR + ./utilities/ovs-vsctl --no-wait init + +Start vswitchd: +ODP configuration arguments can be passed to vswitchd via `--odp`. +For the moment no arguments are available, but is recommended to pass --odp. + + e.g. + export DB_SOCK=/usr/local/var/run/openvswitch/db.sock + ./vswitchd/ovs-vswitch --odp -- unix:$DB_SOCK --pidfile --detach + +To use ovs-vswitchd with ODP, create a bridge with datapath_type +"netdev" in the configuration database. For example: + + ovs-vsctl add-br br0 + ovs-vsctl set bridge br0 datapath_type=netdev + +Now you can add ODP ports. OVS expect ODP port name to start with odp +followed by a colon and then the interface name. + + ovs-vsctl add-port br0 odp:eth0 -- set Interface odp:eth0 type=odp + ovs-vsctl add-port br0 odp:eth1 -- set Interface odp:eth1 type=odp + +For testing you can setup flows from an ODP virtual port to another port, +an internal one for instance. Using an internal port is preferred, because +no other packets will be involed, only what comes from the ODP port. + +First run ovs-ofctl to get the port ids: + ovs-ofctl show br0 + +To remove all flows: + ovs-ofctl del-flows br0 + +Then add a flow to direct packets comming at the ODP port to an internal port. + ovs-ofctl add-flow br0 in_port=1,action=output:LOCAL + +Then you can use tcpdump / wireshark to sniff packets on the LOCAL port. +You might need to bring the virtual interface up: + ifconfig br0 up + +A simple test would be to use ping. In this case you should only see the +ICMP requests showing up at the LOCAL port. Also delete the flow and check that +packets are not forwarded anymore. diff --git a/lib/netdev-odp.c b/lib/netdev-odp.c new file mode 100644 index 0000000..2bbc175 --- /dev/null +++ b/lib/netdev-odp.c @@ -0,0 +1,735 @@ +/* + * Copyright (c) 2014 Nicira, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "dpif-netdev.h" +#include "list.h" +#include "netdev-odp.h" +#include "netdev-provider.h" +#include "netdev-vport.h" +#include "odp-util.h" +#include "ofp-print.h" +#include "ofpbuf.h" +#include "ovs-thread.h" +#include "ovs-rcu.h" +#include "packet-dpif.h" +#include "packets.h" +#include "shash.h" +#include "sset.h" +#include "unaligned.h" +#include "timeval.h" +#include "unixctl.h" +#include "vlog.h" + +VLOG_DEFINE_THIS_MODULE(odp); + +#define SHM_PKT_POOL_SIZE (512*2048) +#define SHM_PKT_POOL_BUF_SIZE 1856 + +#define SHM_OFPBUF_POOL_SIZE (512*256) +#define SHM_OFPBUF_POOL_BUF_SIZE sizeof(struct dpif_packet) + +static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 20); + +static odp_buffer_pool_t pool; +static odp_buffer_pool_t ofpbuf_pool; +static odp_buffer_pool_t struct_pool; + +static int odp_initialized = 0; + +struct netdev_odp { + struct netdev up; + odp_buffer_t odp_buf; /* odp_buffer_t that holds this struct */ + odp_pktio_t pktio; + odp_buffer_pool_t pkt_pool; + size_t frame_offset; + size_t max_frame_len; + + struct ovs_mutex mutex OVS_ACQ_AFTER(odp_mutex); + + uint8_t hwaddr[ETH_ADDR_LEN]; + enum netdev_flags flags; + + struct netdev_stats stats; +}; + +struct netdev_rxq_odp { + struct netdev_rxq up; + odp_buffer_t odp_buf; /* odp_buffer_t that holds this struct */ + odp_queue_t queue_id; +}; + +/* We need a pool of buffers that hold netdev and rxq structures */ +#define STRUCTS_SIZE MAX(sizeof(struct netdev_odp), \ + sizeof(struct netdev_rxq_odp)) +#define SHM_STRUCT_POOL_SIZE (512 * (STRUCTS_SIZE)) +#define SHM_STRUCT_POOL_BUF_SIZE STRUCTS_SIZE + +int +odp_init(int argc, char *argv[]) +{ + int result; + int thr_id; + + if (strcmp(argv[1], "--odp")) + return 0; + + argc--; + argv++; + + result = odp_init_global(); + if (result) { + ODP_ERR("Error: ODP global init failed\n"); + return result; + } + + thr_id = odp_thread_create(0); + odp_init_local(thr_id); + + odp_initialized = 1; + + return result; +} + +static int +odp_class_init(void) +{ + void *pool_base; + int result = 0; + + /* create packet pool */ + pool_base = odp_shm_reserve("shm_packet_pool", SHM_PKT_POOL_SIZE, + ODP_CACHE_LINE_SIZE); + + if (odp_unlikely(pool_base == NULL)) { + ODP_ERR("Error: ODP packet pool mem alloc failed\n"); + out_of_memory(); + return -1; + } + + pool = odp_buffer_pool_create("packet_pool", pool_base, + SHM_PKT_POOL_SIZE, + SHM_PKT_POOL_BUF_SIZE, + ODP_CACHE_LINE_SIZE, + ODP_BUFFER_TYPE_PACKET); + + if (pool == ODP_BUFFER_POOL_INVALID) { + ODP_ERR("Error: packet pool create failed.\n"); + return -1; + } + odp_buffer_pool_print(pool); + + /* create ofpbuf pool */ + pool_base = odp_shm_reserve("shm_ofpbuf_pool", SHM_OFPBUF_POOL_SIZE, + ODP_CACHE_LINE_SIZE); + + if (odp_unlikely(pool_base == NULL)) { + ODP_ERR("Error: ODP packet pool mem alloc failed\n"); + out_of_memory(); + return -1; + } + + ofpbuf_pool = odp_buffer_pool_create("ofpbuf_packet_pool", pool_base, + SHM_OFPBUF_POOL_SIZE, + SHM_OFPBUF_POOL_BUF_SIZE, + ODP_CACHE_LINE_SIZE, + ODP_BUFFER_TYPE_RAW); + + if (ofpbuf_pool == ODP_BUFFER_POOL_INVALID) { + ODP_ERR("Error: ofpbuf pool create failed.\n"); + return -1; + } + odp_buffer_pool_print(ofpbuf_pool); + + /* create pool for structures */ + pool_base = odp_shm_reserve("shm_struct_pool", SHM_STRUCT_POOL_SIZE, + ODP_CACHE_LINE_SIZE); + + if (odp_unlikely(pool_base == NULL)) { + ODP_ERR("Error: ODP packet pool mem alloc failed\n"); + out_of_memory(); + return -1; + } + + struct_pool = odp_buffer_pool_create("packet_pool", pool_base, + SHM_STRUCT_POOL_SIZE, + SHM_STRUCT_POOL_BUF_SIZE, + ODP_CACHE_LINE_SIZE, + ODP_BUFFER_TYPE_RAW); + + if (struct_pool == ODP_BUFFER_POOL_INVALID) { + ODP_ERR("Error: packet pool create failed.\n"); + return -1; + } + odp_buffer_pool_print(struct_pool); + + return result; +} + +static struct netdev * +netdev_odp_alloc(void) +{ + struct netdev_odp *netdev; + odp_buffer_t buf; + buf = odp_buffer_alloc(struct_pool); + netdev = odp_buffer_addr(buf); + memset(netdev, 0, sizeof(*netdev)); + netdev->odp_buf = buf; + return &netdev->up; +} + +static struct netdev_odp * +netdev_odp_cast(const struct netdev *netdev) +{ + return CONTAINER_OF(netdev, struct netdev_odp, up); +} + +static int +netdev_odp_construct(struct netdev *netdev_) +{ + int err = 0; + char *odp_if; + odp_pktio_params_t params; + socket_params_t *sock_params = ¶ms.sock_params; + struct netdev_odp *netdev = netdev_odp_cast(netdev_); + odp_packet_t pkt; + + odp_if = netdev_->name + 4; /* Names always start with "odp:" */ + + if (strncmp(netdev_->name, "odp:", 4)) { + err = ENODEV; + goto out_err; + } + + sock_params->type = ODP_PKTIO_TYPE_SOCKET_BASIC; + + netdev->pktio = odp_pktio_open(odp_if, pool, ¶ms); + + if (netdev->pktio == ODP_PKTIO_INVALID) { + ODP_ERR("Error: odp pktio failed\n"); + err = ENODEV; + goto out_err; + } + + netdev->pkt_pool = pool; + pkt = odp_packet_alloc(netdev->pkt_pool); + if (!odp_packet_is_valid(pkt)) { + out_of_memory(); + goto out_err; + } + + netdev->max_frame_len = odp_packet_buf_size(pkt); + + odp_packet_free(pkt); + + ovs_mutex_init(&netdev->mutex); + +out_err: + + return err; +} + +static void +netdev_odp_destruct(struct netdev *netdev_) +{ + struct netdev_odp *netdev = netdev_odp_cast(netdev_); + + odp_pktio_close(netdev->pktio); +} + +static void +netdev_odp_dealloc(struct netdev *netdev_) +{ + struct netdev_odp *netdev = netdev_odp_cast(netdev_); + odp_buffer_free(netdev->odp_buf); +} + +static int +netdev_odp_get_config(const struct netdev *netdev_, struct smap *args) +{ + struct netdev_odp *netdev = netdev_odp_cast(netdev_); + + ovs_mutex_lock(&netdev->mutex); + + /* TODO: Allow to configure number of queues. */ + smap_add_format(args, "configured_rx_queues", "%u", netdev_->n_rxq); + smap_add_format(args, "configured_tx_queues", "%u", netdev_->n_rxq); + ovs_mutex_unlock(&netdev->mutex); + + return 0; +} + +static int drop_err_pkts(odp_packet_t pkt_tbl[], unsigned len) +{ + odp_packet_t pkt; + unsigned pkt_cnt = len; + unsigned i, j; + + for (i = 0, j = 0; i < len; ++i) { + pkt = pkt_tbl[i]; + + if (odp_unlikely(odp_packet_error(pkt))) { + odp_packet_free(pkt); /* Drop */ + pkt_cnt--; + } else if (odp_unlikely(i != j++)) { + pkt_tbl[j-1] = pkt; + } + } + + return pkt_cnt; +} + +static int +clone_pkts(struct netdev_odp *dev, struct dpif_packet **pkts, + odp_packet_t odp_pkts[], int cnt) +{ + int dropped = 0; + int newcnt = 0; + int pkts_ok = 0; + int i; + + for (i = 0; i < cnt; i++) { + size_t size = ofpbuf_size(&pkts[i]->ofpbuf); + odp_packet_t pkt; + + if (OVS_UNLIKELY(size > dev->max_frame_len)) { + VLOG_WARN_RL(&rl, "Too big size %u max_packet_len %u", + (unsigned)size, + (unsigned)dev->max_frame_len); + dropped++; + continue; + } + pkt = odp_packet_alloc(dev->pkt_pool); + + if (OVS_UNLIKELY(!odp_packet_is_valid(pkt))) { + VLOG_WARN_RL(&rl, "Could not allocate packet"); + dropped += cnt -i; + break; + } + + odp_packet_init(pkt); + odp_packet_set_l2_offset(pkt, 0); + + memcpy(odp_packet_l2(pkt), ofpbuf_data(&pkts[i]->ofpbuf), size); + odp_packet_parse(pkt, size, 0); + + odp_pkts[newcnt] = pkt; + newcnt++; + } + + /* Drop packets with errors */ + pkts_ok = drop_err_pkts(odp_pkts, newcnt); + + if (OVS_UNLIKELY(dropped)) { + ovs_mutex_lock(&dev->mutex); + dev->stats.tx_dropped += dropped; + ovs_mutex_unlock(&dev->mutex); + } + + return pkts_ok; +} + +static int +netdev_odp_send(struct netdev *netdev, struct dpif_packet **pkts, int cnt, + bool may_steal) +{ + struct netdev_odp *dev = netdev_odp_cast(netdev); + odp_packet_t odp_pkts[NETDEV_MAX_RX_BATCH]; + int pkts_ok, i; + + /* Normally NETDEV_MAX_RX_BATCH should be the limit and VLA ar nasty */ + ovs_assert(cnt <= NETDEV_MAX_RX_BATCH); + + if (!may_steal || pkts[0]->ofpbuf.source != OFPBUF_ODP) { + pkts_ok = clone_pkts(dev, pkts, odp_pkts, cnt); + + if (may_steal) { + for (i = 0; i < cnt; i++) { + dpif_packet_delete(pkts[i]); + } + } + } else { + for (i = 0; i < cnt; i++) { + odp_pkts[i] = pkts[i]->ofpbuf.odp_pkt; + odp_packet_free(pkts[i]->ofpbuf.odp_ofpbuf); + } + pkts_ok = cnt; + } + + odp_pktio_send(dev->pktio, odp_pkts, pkts_ok); + + ovs_mutex_lock(&dev->mutex); + dev->stats.tx_packets += pkts_ok; + for (i = 0; i < pkts_ok; i++) { + dev->stats.tx_bytes += odp_packet_get_len(odp_pkts[i]); + } + ovs_mutex_unlock(&dev->mutex); + + return 0; +} + +static int +netdev_odp_set_etheraddr(struct netdev *netdev, + const uint8_t mac[ETH_ADDR_LEN]) +{ + struct netdev_odp *dev = netdev_odp_cast(netdev); + + ovs_mutex_lock(&dev->mutex); + if (!eth_addr_equals(dev->hwaddr, mac)) { + memcpy(dev->hwaddr, mac, ETH_ADDR_LEN); + netdev_change_seq_changed(netdev); + } + ovs_mutex_unlock(&dev->mutex); + + return 0; +} + +static int +netdev_odp_get_etheraddr(const struct netdev *netdev, + uint8_t mac[ETH_ADDR_LEN]) +{ + struct netdev_odp *dev = netdev_odp_cast(netdev); + + ovs_mutex_lock(&dev->mutex); + memcpy(mac, dev->hwaddr, ETH_ADDR_LEN); + ovs_mutex_unlock(&dev->mutex); + + return 0; +} + +static int +netdev_odp_get_mtu(const struct netdev *netdev, int *mtup) +{ + struct netdev_odp *dev = netdev_odp_cast(netdev); + + (void) dev; + (void) mtup; + + return ENOTSUP; +} + +static int +netdev_odp_set_mtu(const struct netdev *netdev, int mtu) +{ + struct netdev_odp *dev = netdev_odp_cast(netdev); + + (void) dev; + (void) mtu; + + return ENOTSUP; +} + +static int +netdev_odp_get_ifindex(const struct netdev *netdev) +{ + struct netdev_odp *dev = netdev_odp_cast(netdev); + + (void) dev; + + return ENOTSUP; +} + +static int +netdev_odp_get_carrier(const struct netdev *netdev, bool *carrier) +{ + struct netdev_odp *dev = netdev_odp_cast(netdev); + + (void) dev; + (void) carrier; + + return ENOTSUP; +} + +static long long int +netdev_odp_get_carrier_resets(const struct netdev *netdev) +{ + struct netdev_odp *dev = netdev_odp_cast(netdev); + + (void) dev; + + return ENOTSUP; +} + +static int +netdev_odp_set_miimon(struct netdev *netdev_ OVS_UNUSED, + long long int interval OVS_UNUSED) +{ + return 0; +} + +static int +netdev_odp_get_stats(const struct netdev *netdev, struct netdev_stats *stats) +{ + struct netdev_odp *dev = netdev_odp_cast(netdev); + + ovs_mutex_lock(&dev->mutex); + *stats = dev->stats; + ovs_mutex_unlock(&dev->mutex); + + return 0; +} + +static int +netdev_odp_set_stats(struct netdev *netdev, const struct netdev_stats *stats) +{ + struct netdev_odp *dev = netdev_odp_cast(netdev); + + ovs_mutex_lock(&dev->mutex); + dev->stats = *stats; + ovs_mutex_unlock(&dev->mutex); + + return 0; +} + +static int +netdev_odp_get_features(const struct netdev *netdev, + enum netdev_features *current, + enum netdev_features *advertised OVS_UNUSED, + enum netdev_features *supported OVS_UNUSED, + enum netdev_features *peer OVS_UNUSED) +{ + struct netdev_odp *dev = netdev_odp_cast(netdev); + + (void) dev; + (void) current; + + return ENOTSUP; +} + +static int +netdev_odp_get_status(const struct netdev *netdev, struct smap *args) +{ + struct netdev_odp *dev = netdev_odp_cast(netdev); + + (void) dev; + (void) args; + + return ENOTSUP; +} + +static int +netdev_odp_update_flags(struct netdev *netdev, + enum netdev_flags off, enum netdev_flags on, + enum netdev_flags *old_flagsp) +{ + struct netdev_odp *dev = netdev_odp_cast(netdev); + + (void) dev; + (void) off; + (void) on; + (void) old_flagsp; + + if ((off | on) & ~(NETDEV_UP | NETDEV_PROMISC)) { + return EINVAL; + } + + *old_flagsp = dev->flags; + dev->flags |= on; + dev->flags &= ~off; + + if (dev->flags == *old_flagsp) { + return 0; + } + + return 0; +} + +static struct netdev_rxq * +netdev_odp_rxq_alloc(void) +{ + struct netdev_rxq_odp *rx; + odp_buffer_t buf; + buf = odp_buffer_alloc(struct_pool); + rx = odp_buffer_addr(buf); + memset(rx, 0, sizeof(*rx)); + rx->odp_buf = buf; + return &rx->up; +} + +static struct netdev_rxq_odp * +netdev_rxq_odp_cast(const struct netdev_rxq *rx) +{ + return CONTAINER_OF(rx, struct netdev_rxq_odp, up); +} + +static int +netdev_odp_rxq_construct(struct netdev_rxq *rxq_) +{ + struct netdev_rxq_odp *rx = netdev_rxq_odp_cast(rxq_); + struct netdev_odp *netdev = netdev_odp_cast(rx->up.netdev); + + rx->queue_id = odp_pktio_inq_getdef(netdev->pktio); + + return 0; +} + +static void +netdev_odp_rxq_destruct(struct netdev_rxq *rxq_ OVS_UNUSED) +{ +} + +static void +netdev_odp_rxq_dealloc(struct netdev_rxq *rxq_) +{ + struct netdev_rxq_odp *rxq = netdev_rxq_odp_cast(rxq_); + odp_buffer_free(rxq->odp_buf); +} + +static int +netdev_odp_rxq_recv(struct netdev_rxq *rxq_, struct dpif_packet **packets, + int *c) +{ + struct netdev_rxq_odp *rx = netdev_rxq_odp_cast(rxq_); + struct netdev_odp *netdev = netdev_odp_cast(rx->up.netdev); + int pkts, pkts_ok, ret = 0; + size_t rx_bytes = 0; + unsigned long err_cnt = 0; + int i; + odp_pktio_t pkt_tbl[NETDEV_MAX_RX_BATCH]; + + pkts = odp_pktio_recv(netdev->pktio, pkt_tbl, NETDEV_MAX_RX_BATCH); + if (pkts < 0) { + return EINVAL; + } + if (!pkts) { + return EAGAIN; + } + + if (pkts > 0) { + pkts_ok = drop_err_pkts(pkt_tbl, pkts); + if (odp_unlikely(pkts_ok != pkts)) + ODP_ERR("Dropped frames:%u - err_cnt:%lu\n", + pkts-pkts_ok, ++err_cnt); + if (!pkts_ok) { + ret = EAGAIN; + goto out_stats; + } + } + + /* Allocate an ofpbuf for each valid packet */ + for (i = 0; i < pkts_ok; i++) { + odp_buffer_t buf; + buf = odp_buffer_alloc(ofpbuf_pool); + if (buf == ODP_BUFFER_INVALID) { + out_of_memory(); + } + packets[i] = (struct dpif_packet*) odp_buffer_addr(buf); + ofpbuf_init_odp(&packets[i]->ofpbuf, odp_packet_buf_size(pkt_tbl[i])); + packets[i]->ofpbuf.odp_pkt = pkt_tbl[i]; + packets[i]->ofpbuf.odp_ofpbuf = buf; + rx_bytes += odp_packet_get_len(pkt_tbl[i]); + } + + *c = pkts_ok; + + printf("ODP: received %d packets\n", pkts_ok); + +out_stats: + ovs_mutex_lock(&netdev->mutex); + netdev->stats.rx_packets += pkts_ok; + netdev->stats.rx_bytes += rx_bytes; + netdev->stats.rx_dropped += pkts - pkts_ok; + ovs_mutex_unlock(&netdev->mutex); + + return ret; +} + +static struct netdev_class netdev_odp_class = { + "odp", + odp_class_init, /* init */ + NULL, /* netdev_odp_run */ + NULL, /* netdev_odp_wait */ + + netdev_odp_alloc, + netdev_odp_construct, + netdev_odp_destruct, + netdev_odp_dealloc, + netdev_odp_get_config, + NULL, /* netdev_odp_set_config */ + NULL, /* get_tunnel_config */ + + netdev_odp_send, /* send */ + NULL, /* send_wait */ + + netdev_odp_set_etheraddr, + netdev_odp_get_etheraddr, + netdev_odp_get_mtu, + netdev_odp_set_mtu, + netdev_odp_get_ifindex, + netdev_odp_get_carrier, + netdev_odp_get_carrier_resets, + netdev_odp_set_miimon, + netdev_odp_get_stats, + netdev_odp_set_stats, + netdev_odp_get_features, + NULL, /* set_advertisements */ + + NULL, /* set_policing */ + NULL, /* get_qos_types */ + NULL, /* get_qos_capabilities */ + NULL, /* get_qos */ + NULL, /* set_qos */ + NULL, /* get_queue */ + NULL, /* set_queue */ + NULL, /* delete_queue */ + NULL, /* get_queue_stats */ + NULL, /* queue_dump_start */ + NULL, /* queue_dump_next */ + NULL, /* queue_dump_done */ + NULL, /* dump_queue_stats */ + + NULL, /* get_in4 */ + NULL, /* set_in4 */ + NULL, /* get_in6 */ + NULL, /* add_router */ + NULL, /* get_next_hop */ + netdev_odp_get_status, + NULL, /* arp_lookup */ + + netdev_odp_update_flags, + + netdev_odp_rxq_alloc, + netdev_odp_rxq_construct, + netdev_odp_rxq_destruct, + netdev_odp_rxq_dealloc, + netdev_odp_rxq_recv, + NULL, /* rxq_wait */ + NULL, /* rxq_drain */ +}; + +void +netdev_odp_register(void) +{ + if (!odp_initialized) { + VLOG_INFO("Not running in ODP mode\n"); + return; + } + + netdev_register_provider(&netdev_odp_class); +} diff --git a/lib/netdev-odp.h b/lib/netdev-odp.h new file mode 100644 index 0000000..a0a2594 --- /dev/null +++ b/lib/netdev-odp.h @@ -0,0 +1,39 @@ +#ifndef NETDEV_ODP_H +#define NETDEV_ODP_H + +#include +#include "ofpbuf.h" + +#ifdef ODP_NETDEV + +#include +#include +#include +#include + +/* This function is not exported, we need another way to deal with + creating a packet from an ofpbuf */ +extern void odp_packet_parse(odp_packet_t pkt, size_t len, size_t l2_offset); + + +void netdev_odp_register(void); +void free_odp_buf(struct ofpbuf *); +int odp_init(int argc, char *argv[]); + +#else + +static inline void +netdev_odp_register(void) +{ + /* Nothing */ +} + +static inline void +free_odp_buf(struct ofpbuf *buf OVS_UNUSED) +{ + /* Nothing */ +} + + +#endif /* ODP_NETDEV */ +#endif diff --git a/lib/netdev.c b/lib/netdev.c index ea16ccb..787b4b3 100644 --- a/lib/netdev.c +++ b/lib/netdev.c @@ -31,6 +31,7 @@ #include "hash.h" #include "list.h" #include "netdev-dpdk.h" +#include "netdev-odp.h" #include "netdev-provider.h" #include "netdev-vport.h" #include "ofpbuf.h" @@ -99,7 +100,8 @@ bool netdev_is_pmd(const struct netdev *netdev) { return (!strcmp(netdev->netdev_class->type, "dpdk") || - !strcmp(netdev->netdev_class->type, "dpdkr")); + !strcmp(netdev->netdev_class->type, "dpdkr") || + !strcmp(netdev->netdev_class->type, "odp")); } static void @@ -138,6 +140,10 @@ netdev_initialize(void) #endif netdev_dpdk_register(); +#ifdef ODP_NETDEV + netdev_odp_register(); +#endif + ovsthread_once_done(&once); } } diff --git a/lib/ofpbuf.c b/lib/ofpbuf.c index 28013d5..55c59e0 100644 --- a/lib/ofpbuf.c +++ b/lib/ofpbuf.c @@ -20,6 +20,7 @@ #include #include "dynamic-string.h" #include "netdev-dpdk.h" +#include "netdev-odp.h" #include "util.h" static void @@ -119,6 +120,14 @@ ofpbuf_init_dpdk(struct ofpbuf *b, size_t allocated) ofpbuf_init__(b, allocated, OFPBUF_DPDK); } +#ifdef ODP_NETDEV +void +ofpbuf_init_odp(struct ofpbuf *b, size_t allocated) +{ + ofpbuf_init__(b, allocated, OFPBUF_ODP); +} +#endif + /* Initializes 'b' as an empty ofpbuf with an initial capacity of 'size' * bytes. */ void @@ -142,6 +151,13 @@ ofpbuf_uninit(struct ofpbuf *b) #else ovs_assert(b->source != OFPBUF_DPDK); #endif + } else if (b->source == OFPBUF_ODP) { +#ifdef ODP_NETDEV + odp_packet_free(b->odp_pkt); + odp_buffer_free(b->odp_ofpbuf); +#else + ovs_assert(b->source != OFPBUF_ODP); +#endif } } } diff --git a/lib/ofpbuf.h b/lib/ofpbuf.h index 6af9c64..ed1f26c 100644 --- a/lib/ofpbuf.h +++ b/lib/ofpbuf.h @@ -23,6 +23,7 @@ #include "packets.h" #include "util.h" #include "netdev-dpdk.h" +#include "netdev-odp.h" #ifdef __cplusplus extern "C" { @@ -62,6 +63,10 @@ struct ofpbuf { #ifdef DPDK_NETDEV struct rte_mbuf mbuf; /* DPDK mbuf */ #else +# ifdef ODP_NETDEV + odp_buffer_t odp_ofpbuf; /* ODP buffer containig this struct ofpbuf */ + odp_packet_t odp_pkt; /* ODP packet containing actual payload */ +# endif void *base_; /* First byte of allocated space. */ void *data_; /* First byte actually in use. */ uint32_t size_; /* Number of bytes in use. */ @@ -109,6 +114,9 @@ void ofpbuf_use_stub(struct ofpbuf *, void *, size_t); void ofpbuf_use_const(struct ofpbuf *, const void *, size_t); void ofpbuf_init_dpdk(struct ofpbuf *b, size_t allocated); +#ifdef ODP_NETDEV +void ofpbuf_init_odp(struct ofpbuf *b, size_t allocated); +#endif void ofpbuf_init(struct ofpbuf *, size_t); void ofpbuf_uninit(struct ofpbuf *); @@ -183,6 +191,12 @@ static inline void ofpbuf_delete(struct ofpbuf *b) return; } + if (b->source == OFPBUF_ODP) { + odp_packet_free(b->odp_pkt); + odp_buffer_free(b->odp_ofpbuf); + return; + } + ofpbuf_uninit(b); free(b); } @@ -395,31 +409,69 @@ static inline void ofpbuf_set_size(struct ofpbuf *b, uint32_t v) #else static inline void * ofpbuf_data(const struct ofpbuf *b) { +#ifdef ODP_NETDEV + if (b->source == OFPBUF_ODP) + return odp_packet_l2(b->odp_pkt); +#endif + return b->data_; } static inline void ofpbuf_set_data(struct ofpbuf *b, void *d) { +#ifdef ODP_NETDEV + if (b->source == OFPBUF_ODP) { + ODP_ERR("ODP: Invalid use of ofpbuf_set_data\n"); + ovs_abort(0, "Invalid function call\n"); + } +#endif + b->data_ = d; } static inline void * ofpbuf_base(const struct ofpbuf *b) { +#ifdef ODP_NETDEV + if (b->source == OFPBUF_ODP) { + ODP_ERR("ODP: Invalid use of ofpbuf_base\n"); + ovs_abort(0, "Invalid function call\n"); + } +#endif + return b->base_; } static inline void ofpbuf_set_base(struct ofpbuf *b, void *d) { +#ifdef ODP_NETDEV + if (b->source == OFPBUF_ODP) { + ODP_ERR("ODP: Invalid use of ofpbuf_set_base\n"); + ovs_abort(0, "Invalid function call\n"); + } +#endif + b->base_ = d; } static inline uint32_t ofpbuf_size(const struct ofpbuf *b) { +#ifdef ODP_NETDEV + if (b->source == OFPBUF_ODP) + return odp_packet_get_len(b->odp_pkt); +#endif + return b->size_; } static inline void ofpbuf_set_size(struct ofpbuf *b, uint32_t v) { +#ifdef ODP_NETDEV + if (b->source == OFPBUF_ODP) { + ODP_ERR("ODP: Invalid use of ofpbuf_set_size\n"); + ovs_abort(0, "Invalid function call\n"); + } +#endif + b->size_ = v; } #endif diff --git a/vswitchd/ovs-vswitchd.c b/vswitchd/ovs-vswitchd.c index 4d7e4f0..ba4635f 100644 --- a/vswitchd/ovs-vswitchd.c +++ b/vswitchd/ovs-vswitchd.c @@ -75,6 +75,12 @@ main(int argc, char *argv[]) argc -= retval; argv += retval; +#ifdef ODP_NETDEV + retval = odp_init(argc, argv); + argc -= retval; + argv += retval; +#endif + proctitle_init(argc, argv); service_start(&argc, &argv); remote = parse_options(argc, argv, &unixctl_path); @@ -149,6 +155,7 @@ parse_options(int argc, char *argv[], char **unixctl_pathp) OPT_DISABLE_SYSTEM, DAEMON_OPTION_ENUMS, OPT_DPDK, + OPT_ODP, }; static const struct option long_options[] = { {"help", no_argument, NULL, 'h'}, @@ -163,6 +170,7 @@ parse_options(int argc, char *argv[], char **unixctl_pathp) {"enable-dummy", optional_argument, NULL, OPT_ENABLE_DUMMY}, {"disable-system", no_argument, NULL, OPT_DISABLE_SYSTEM}, {"dpdk", required_argument, NULL, OPT_DPDK}, + {"odp", required_argument, NULL, OPT_ODP}, {NULL, 0, NULL, 0}, }; char *short_options = long_options_to_short_options(long_options); @@ -217,6 +225,9 @@ parse_options(int argc, char *argv[], char **unixctl_pathp) case OPT_DPDK: break; + case OPT_ODP: + break; + default: abort(); }