@@ -11,6 +11,7 @@ on a specific platform, please see one of these files:
- INSTALL.XenServer
- INSTALL.NetBSD
- INSTALL.DPDK
+ - INSTALL.ODP
Build Requirements
------------------
new file mode 100644
@@ -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.
new file mode 100644
@@ -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 <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 "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);
+}
new file mode 100644
@@ -0,0 +1,39 @@
+#ifndef NETDEV_ODP_H
+#define NETDEV_ODP_H
+
+#include <config.h>
+#include "ofpbuf.h"
+
+#ifdef ODP_NETDEV
+
+#include <odp.h>
+#include <helper/odp_eth.h>
+#include <helper/odp_ip.h>
+#include <helper/odp_packet_helper.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(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
@@ -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);
}
}
@@ -20,6 +20,7 @@
#include <string.h>
#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
}
}
}
@@ -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
@@ -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();
}
Signed-off-by: Ciprian Barbu <ciprian.barbu@linaro.org> --- 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