diff options
author | Ciprian Barbu <ciprian.barbu@linaro.org> | 2014-08-19 14:45:04 +0300 |
---|---|---|
committer | Zoltan Kiss <zoltan.kiss@linaro.org> | 2015-07-09 14:48:32 +0100 |
commit | 15114858675d596623573a9e263a88ff5dbf0f9f (patch) | |
tree | 7b8e296a283ab03c67d2d2f2ba233f26161553fc | |
parent | a62f8778231c982278ee07de7e55f44dc4d95c91 (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.md | 161 | ||||
-rw-r--r-- | INSTALL.md | 1 | ||||
-rw-r--r-- | lib/netdev-odp.c | 638 | ||||
-rw-r--r-- | lib/netdev-odp.h | 39 | ||||
-rw-r--r-- | lib/netdev.c | 6 | ||||
-rw-r--r-- | lib/ofpbuf.c | 15 | ||||
-rw-r--r-- | lib/ofpbuf.h | 46 | ||||
-rw-r--r-- | vswitchd/bridge.c | 11 |
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, ¶ms); + + 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, ¶ms); + + 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() |