aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCiprian Barbu <ciprian.barbu@linaro.org>2014-08-19 14:45:04 +0300
committerZoltan Kiss <zoltan.kiss@linaro.org>2015-07-09 14:48:32 +0100
commit15114858675d596623573a9e263a88ff5dbf0f9f (patch)
tree7b8e296a283ab03c67d2d2f2ba233f26161553fc
parenta62f8778231c982278ee07de7e55f44dc4d95c91 (diff)
dpif-netdev: Add ODP netdev
Signed-off-by: Ciprian Barbu <ciprian.barbu@linaro.org> Signed-off-by: Zoltan Kiss <zoltan.kiss@linaro.org>
-rw-r--r--INSTALL.ODP.md161
-rw-r--r--INSTALL.md1
-rw-r--r--lib/netdev-odp.c638
-rw-r--r--lib/netdev-odp.h39
-rw-r--r--lib/netdev.c6
-rw-r--r--lib/ofpbuf.c15
-rw-r--r--lib/ofpbuf.h46
-rw-r--r--vswitchd/bridge.c11
8 files changed, 916 insertions, 1 deletions
diff --git a/INSTALL.ODP.md b/INSTALL.ODP.md
new file mode 100644
index 000000000..243c72a08
--- /dev/null
+++ b/INSTALL.ODP.md
@@ -0,0 +1,161 @@
+ 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:
+------------------------
+Below are a set of steps to help you build OVS on top of ODP for linux-generic
+using basic sockets. It should be possible to compile with ODP for other
+platforms but it hasn't been tested yet.
+
+ODP:
+=============
+Get the code:
+ git clone http://git.linaro.org/git/lng/odp.git
+ cd odp
+ ./bootstrap.sh
+
+it is recommended to disable building shared library because then launching
+ovs with sudo becomes a real pain; you can install ODP anywhere you like
+ ./configure --enable-debug --enable-shared=no --prefix=<odp_install>
+
+to get debug symbols you can
+ ./configure --enable-debug CFLAGS="-g -O0" --enable-shared=no
+
+ make
+ make install
+
+OVS:
+=============
+ ./boot.sh
+ ./configure --with-odp=<odp_install>
+
+to specify a different ODP platform you can use:
+ ./configure --with-odp=<ODP_DIR> --with-odp-platform=<platform>
+
+if you installed ODP you can simply
+ ./configure --with-odp=yes
+ 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
+ export DB_SOCK=/usr/local/var/run/openvswitch/db.sock
+ ./ovsdb/ovsdb-server --remote=punix:$DB_SOCK \
+ --remote=db:Open_vSwitch,Open_vSwitch,manager_options \
+ --private-key=db:Open_vSwitch,SSL,private_key \
+ --certificate=db:Open_vSwitch,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
+
+Enable ODP for OVS (you need to restart ovs-vswitchd):
+
+ ovs-vsctl set Open_vSwitch . other_config:odp=true
+ ovs-vsctl get Open_vSwitch . other_config:odp
+
+Disable it (you need to restart ovs-vswitchd):
+
+ ovs-vsctl set Open_vSwitch . other_config:odp=false
+
+If your platform relies on startup parameters passed through
+ODP_PLATFORM_PARAMS, you can also set it (you need to restart ovs-vswitchd):
+
+ ovs-vsctl set Open_vSwitch . other_config:odp_platform_params="etc"
+ ovs-vsctl get Open_vSwitch . other_config:odp_platform_params
+
+This setting is stored in Open_vSwitch table, which has one record for every
+ovs-vswitch instances. Currently you can't have more than one. It is set by
+ovs-ctl, so if you start ovs-vswitchd in other ways, you have to set
+the environment variable by yourself, like below.
+
+Start vswitchd:
+
+ e.g.
+ export ODP_PLATFORM_PARAMS=<if any>
+ ./vswitchd/ovs-vswitchd 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
+
+Simple test
+-----------
+A simple test would be to add one ODP virtual port and one internal port.
+To make sure that packets arrived on the the ODP virtual port don't come
+through the Linux interface as well you need to remove the IP address from
+the Linux interface. Also set the interface to promisc mode, in case packets
+get rejected otherwise:
+
+ ifconfig eth0 0 promisc
+
+Bring up the bridge internal port and assign some ip (DHCP should work too
+if present):
+
+ ifconfig br0 up
+ dhcp br0
+
+Then run tests as usual, simple ping from another machine, iperf etc.
+Packets should arrive at the physical interface, then at the ODP virtual
+port then forwarded to the br0 internal port and then to the Linux stack.
+
+You can also set up two ODP virtual ports and let the machine run like a
+regular switch, without involving the Linux IP stack.
+
+Testing using flows
+-------------------
+For testing you can setup flows from an ODP virtual port to another port,
+an internal port for example. 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/INSTALL.md b/INSTALL.md
index 941cf37be..bfc800576 100644
--- a/INSTALL.md
+++ b/INSTALL.md
@@ -11,6 +11,7 @@ on a specific platform, please see one of these files:
- [INSTALL.XenServer.md]
- [INSTALL.NetBSD.md]
- [INSTALL.DPDK.md]
+ - [INSTALL.ODP.md]
Build Requirements
------------------
diff --git a/lib/netdev-odp.c b/lib/netdev-odp.c
new file mode 100644
index 000000000..db85aa063
--- /dev/null
+++ b/lib/netdev-odp.c
@@ -0,0 +1,638 @@
+/*
+ * 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 <config.h>
+
+#include <stdio.h>
+#include <string.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <pthread.h>
+#include <config.h>
+#include <errno.h>
+#include <sched.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <stdio.h>
+
+#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 "openvswitch/vlog.h"
+
+VLOG_DEFINE_THIS_MODULE(odp);
+
+#define SHM_PKT_POOL_NUM_BUFS 32768 - 1
+#define SHM_PKT_POOL_BUF_SIZE 1856
+
+#define SHM_OFPBUF_POOL_BUF_SIZE sizeof(struct dpif_packet)
+
+static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 20);
+
+static odp_pool_t pool;
+static odp_pool_t struct_pool;
+
+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_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_NUM_BUFS 512
+#define SHM_STRUCT_POOL_BUF_SIZE STRUCTS_SIZE
+
+void
+free_odp_buf(struct ofpbuf *b)
+{
+ odp_packet_free(b->odp_pkt);
+}
+
+int
+odp_init()
+{
+ int result;
+
+ result = odp_init_global(NULL, NULL);
+ if (result) {
+ VLOG_ERR("Error: ODP global init failed\n");
+ return result;
+ }
+
+ /* Init this thread */
+ if (odp_init_local()) {
+ VLOG_ERR("Error: ODP local init failed.\n");
+ exit(EXIT_FAILURE);
+ }
+
+ return result;
+}
+
+static int
+odp_class_init(void)
+{
+ odp_pool_param_t params;
+ int result = 0;
+
+ /* create packet & ofpbuf pool */
+
+ params.pkt.seg_len = SHM_PKT_POOL_BUF_SIZE;
+ params.pkt.uarea_size = SHM_OFPBUF_POOL_BUF_SIZE;
+ params.pkt.len = 0;
+ params.pkt.num = SHM_PKT_POOL_NUM_BUFS;
+ params.type = ODP_POOL_PACKET;
+
+ pool = odp_pool_create("packet_pool", ODP_SHM_NULL, &params);
+
+ if (pool == ODP_POOL_INVALID) {
+ VLOG_ERR("Error: packet pool create failed.\n");
+ return -1;
+ }
+ odp_pool_print(pool);
+
+ /* create pool for structures */
+
+ params.type = ODP_POOL_BUFFER;
+ params.buf.size = SHM_STRUCT_POOL_BUF_SIZE;
+ params.buf.num = SHM_STRUCT_POOL_NUM_BUFS;
+ params.buf.align = 0;
+
+ struct_pool = odp_pool_create("struct_pool", ODP_SHM_NULL, &params);
+
+ if (struct_pool == ODP_POOL_INVALID) {
+ VLOG_ERR("Error: struct_pool create failed.\n");
+ return -1;
+ }
+ odp_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;
+ struct netdev_odp *netdev = netdev_odp_cast(netdev_);
+ odp_pool_info_t info;
+
+ odp_if = netdev_->name + 4; /* Names always start with "odp:" */
+
+ if (strncmp(netdev_->name, "odp:", 4)) {
+ err = ENODEV;
+ goto out_err;
+ }
+
+ netdev->pktio = odp_pktio_open(odp_if, pool);
+
+ if (netdev->pktio == ODP_PKTIO_INVALID) {
+ VLOG_ERR("Error: odp pktio failed\n");
+ err = ENODEV;
+ goto out_err;
+ }
+
+ netdev->pkt_pool = pool;
+
+ err = odp_pool_info(netdev->pkt_pool, &info);
+ if ( err != 0) {
+ VLOG_ERR("Error: Couldn't get default packet buffer size\n");
+ goto out_err;
+ }
+
+ netdev->max_frame_len = info.params.pkt.len;
+
+ 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
+clone_pkts(struct netdev_odp *dev, struct dpif_packet **pkts,
+ odp_packet_t odp_pkts[], int cnt)
+{
+ int dropped = 0;
+ int newcnt = 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, size);
+
+ if (OVS_UNLIKELY(pkt == ODP_PACKET_INVALID)) {
+ VLOG_WARN_RL(&rl, "Could not allocate packet");
+ dropped += cnt -i;
+ break;
+ }
+
+ odp_packet_l2_offset_set(pkt, 0);
+
+ if (OVS_UNLIKELY(!odp_packet_push_tail(pkt, size)))
+ VLOG_WARN_RL(&rl, "Can't push tail with %lu", size);
+ odp_packet_copydata_in(pkt, 0, size, ofpbuf_data(&pkts[i]->ofpbuf));
+ odp_pkts[newcnt] = pkt;
+ newcnt++;
+ }
+
+ if (OVS_UNLIKELY(dropped)) {
+ ovs_mutex_lock(&dev->mutex);
+ dev->stats.tx_dropped += dropped;
+ ovs_mutex_unlock(&dev->mutex);
+ }
+
+ return newcnt;
+}
+
+static int
+netdev_odp_send(struct netdev *netdev, int qid OVS_UNUSED,
+ 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;
+ 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_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_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, ret = 0;
+ size_t rx_bytes = 0;
+ int i;
+ odp_packet_t pkt_tbl[NETDEV_MAX_RX_BATCH];
+
+ pkts = odp_pktio_recv(netdev->pktio, pkt_tbl, NETDEV_MAX_RX_BATCH);
+ if (OVS_UNLIKELY(pkts < 1)) {
+ *c = 0;
+ if (!pkts)
+ return EAGAIN;
+ VLOG_ERR_RL(&rl, "ODP: Packet receive error (%d)\n", pkts);
+ return EINVAL;
+ }
+
+ /* Build the array of dpif_packet pointers */
+ for (i = 0; i < pkts; i++) {
+ packets[i] = (struct dpif_packet*) odp_packet_user_area(pkt_tbl[i]);
+ packets[i]->ofpbuf.odp_pkt = pkt_tbl[i];
+ ofpbuf_init_odp(&packets[i]->ofpbuf, SHM_PKT_POOL_BUF_SIZE);
+ rx_bytes += odp_packet_len(pkt_tbl[i]);
+ }
+
+ *c = pkts;
+
+ ovs_mutex_lock(&netdev->mutex);
+ netdev->stats.rx_packets += pkts;
+ netdev->stats.rx_bytes += rx_bytes;
+ 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 */
+ NULL, /* build_header */
+ NULL, /* push_header */
+ NULL, /* pop_header */
+ NULL, /* get_numa_id */
+ NULL, /* set_multiq */
+
+ 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_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)
+{
+ static struct ovsthread_once once = OVSTHREAD_ONCE_INITIALIZER;
+
+ if (ovsthread_once_start(&once)) {
+ odp_init();
+ netdev_register_provider(&netdev_odp_class);
+ ovsthread_once_done(&once);
+ }
+}
diff --git a/lib/netdev-odp.h b/lib/netdev-odp.h
new file mode 100644
index 000000000..3069d5f44
--- /dev/null
+++ b/lib/netdev-odp.h
@@ -0,0 +1,39 @@
+#ifndef NETDEV_ODP_H
+#define NETDEV_ODP_H
+
+#include <config.h>
+#include "ofpbuf.h"
+
+extern int odp_initialized;
+#ifdef ODP_NETDEV
+
+#include <odp.h>
+#include <odp/helper/eth.h>
+#include <odp/helper/ip.h>
+
+/* 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(void);
+
+#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 2bda77f51..31e1710ca 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"
@@ -108,7 +109,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
@@ -154,6 +156,8 @@ netdev_initialize(void)
ovsthread_once_done(&once);
}
+ if (odp_initialized)
+ netdev_odp_register();
}
/* Performs periodic work needed by all the various kinds of netdevs.
diff --git a/lib/ofpbuf.c b/lib/ofpbuf.c
index 08fefbeb4..2d81761d6 100644
--- a/lib/ofpbuf.c
+++ b/lib/ofpbuf.c
@@ -20,6 +20,7 @@
#include <string.h>
#include "dynamic-string.h"
#include "netdev-dpdk.h"
+#include "netdev-odp.h"
#include "util.h"
static void
@@ -120,6 +121,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
@@ -143,6 +152,12 @@ 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);
+#else
+ ovs_assert(b->source != OFPBUF_ODP);
+#endif
}
}
}
diff --git a/lib/ofpbuf.h b/lib/ofpbuf.h
index 6ceed88f5..4a4823629 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,9 @@ struct ofpbuf {
#ifdef DPDK_NETDEV
struct rte_mbuf mbuf; /* DPDK mbuf */
#else
+# ifdef ODP_NETDEV
+ 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. */
@@ -113,6 +117,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 *);
@@ -187,6 +194,11 @@ static inline void ofpbuf_delete(struct ofpbuf *b)
return;
}
+ if (b->source == OFPBUF_ODP) {
+ free_odp_buf(b);
+ return;
+ }
+
ofpbuf_uninit(b);
free(b);
}
@@ -414,31 +426,65 @@ 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_data(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) {
+ ovs_abort(0, "ODP: Invalid use of ofpbuf_set_data\n");
+ }
+#endif
+
b->data_ = d;
}
static inline void * ofpbuf_base(const struct ofpbuf *b)
{
+#ifdef ODP_NETDEV
+ if (b->source == OFPBUF_ODP) {
+ ovs_abort(0, "ODP: Invalid use of ofpbuf_base\n");
+ }
+#endif
+
return b->base_;
}
static inline void ofpbuf_set_base(struct ofpbuf *b, void *d)
{
+#ifdef ODP_NETDEV
+ if (b->source == OFPBUF_ODP) {
+ ovs_abort(0, "ODP: Invalid use of ofpbuf_set_base\n\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_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) {
+ ovs_abort(0, "ODP: Invalid use of ofpbuf_set_size\n");
+ }
+#endif
+
b->size_ = v;
}
#endif
diff --git a/vswitchd/bridge.c b/vswitchd/bridge.c
index 4d8473272..e9aac6cc7 100644
--- a/vswitchd/bridge.c
+++ b/vswitchd/bridge.c
@@ -2842,6 +2842,17 @@ bridge_run(void)
}
cfg = ovsrec_open_vswitch_first(idl);
+#ifdef ODP_NETDEV
+ if (cfg) {
+ static struct ovsthread_once once = OVSTHREAD_ONCE_INITIALIZER;
+
+ if (ovsthread_once_start(&once)) {
+ if (smap_get_bool(&cfg->other_config, "odp", false))
+ odp_initialized = 1;
+ ovsthread_once_done(&once);
+ }
+ }
+#endif
/* Initialize the ofproto library. This only needs to run once, but
* it must be done after the configuration is set. If the
* initialization has already occurred, bridge_init_ofproto()