[ovs-dev] [Single DP 15/15] ofproto-dpif: Use a single underlying datapath across multiple bridges.

Justin Pettit jpettit at nicira.com
Thu Oct 18 19:52:00 UTC 2012


This commit switches to using a single backing datapath (called
"ovs-datapath") for all bridges of that datapath's type.  Previously,
resources couldn't be shared across bridges, since each was in its own
datapath.  This change will allow sharing of tunnels and cheaper patch
ports to be added in the future.

Since bridges share a common datapath, the ovs-dpctl commands won't
provide bridge-specific information.  Users wishing to have that
information should use the new "ovs-appctl dpif/*" commands as
documented in ovs-vswitchd(8).

Signed-off-by: Justin Pettit <jpettit at nicira.com>
---
 FAQ                        |    8 +
 NEWS                       |    5 +
 ofproto/ofproto-dpif.c     |  637 +++++++++++++++++++++++++++++++++-----------
 ofproto/ofproto-provider.h |    8 +-
 tests/ofproto-dpif.at      |    6 +-
 vswitchd/bridge.c          |   13 +-
 6 files changed, 507 insertions(+), 170 deletions(-)

diff --git a/FAQ b/FAQ
index b14bfa4..829bb43 100644
--- a/FAQ
+++ b/FAQ
@@ -414,6 +414,14 @@ Q: Is there any documentation on the database tables and fields?
 
 A: Yes.  ovs-vswitchd.conf.db(5) is a comprehensive reference.
 
+Q: When I run ovs-dpctl I no longer see the bridges I created.  Instead,
+   I only see a datapath called "ovs-datapath".  How can I see datapath
+   information about a particular bridge?
+
+A: In version 1.9.0, OVS switched to using a single datapath that is
+   shared by all bridges of that type.  The "ovs-appctl dpif/*"
+   commands provide similar functionality that is scoped by the bridge.
+
 
 VLANs
 -----
diff --git a/NEWS b/NEWS
index e97386a..ab4b3e4 100644
--- a/NEWS
+++ b/NEWS
@@ -37,6 +37,11 @@ post-v1.8.0
     - The ofproto library is now responsible for assigning OpenFlow port
       numbers.  An ofproto implementation should assign them when
       port_construct() is called.
+    - All dpif-based bridges of a particular type share a common
+      datapath called "ovs-datapath".  The ovs-dpctl commands will now
+      return information on that shared datapath.  To get the equivalent
+      bridge-specific information, use the new "ovs-appctl dpif/*"
+      commands.
     - The following features are now deprecated.  They will be removed no
       earlier than February 2013.  Please email dev at openvswitch.org with
       concerns.
diff --git a/ofproto/ofproto-dpif.c b/ofproto/ofproto-dpif.c
index 4b15cf3..39cfb05 100644
--- a/ofproto/ofproto-dpif.c
+++ b/ofproto/ofproto-dpif.c
@@ -28,6 +28,7 @@
 #include "coverage.h"
 #include "cfm.h"
 #include "dpif.h"
+#include "dpif-provider.h"
 #include "dynamic-string.h"
 #include "fail-open.h"
 #include "hmapx.h"
@@ -50,6 +51,7 @@
 #include "poll-loop.h"
 #include "simap.h"
 #include "smap.h"
+#include "svec.h"
 #include "timer.h"
 #include "unaligned.h"
 #include "unixctl.h"
@@ -490,7 +492,7 @@ static void facet_account(struct facet *);
 static bool facet_is_controller_flow(struct facet *);
 
 struct ofport_dpif {
-    struct hmap_node hmap_node; /* In ofproto-dpif's "odp_to_ofport_map". */
+    struct hmap_node hmap_node; /* In dpif_backer's "odp_to_ofport_map". */
     struct ofport up;
 
     uint32_t odp_port;
@@ -606,10 +608,24 @@ COVERAGE_DEFINE(rev_port_toggled);
 COVERAGE_DEFINE(rev_flow_table);
 COVERAGE_DEFINE(rev_inconsistency);
 
+/* All datapaths share a single dpif backer instance. */
+struct dpif_backer {
+    char *type;
+    int refcount;
+    struct dpif *dpif;
+    struct hmap odp_to_ofport_map; /* ODP port to ofport mapping. */
+};
+
+/* All existing ofproto_backer instances, indexed by ofproto->up.type. */
+static struct shash all_dpif_backers = SHASH_INITIALIZER(&all_dpif_backers);
+
+static struct ofproto_dpif *
+odp_port_to_ofproto_dpif(const struct dpif *, uint32_t odp_port);
+
 struct ofproto_dpif {
     struct hmap_node all_ofproto_dpifs_node; /* In 'all_ofproto_dpifs'. */
     struct ofproto up;
-    struct dpif *dpif;
+    struct dpif_backer *backer;
 
     /* Special OpenFlow rules. */
     struct rule_dpif *miss_rule; /* Sends flow table misses to controller. */
@@ -655,8 +671,10 @@ struct ofproto_dpif {
     struct hmap realdev_vid_map; /* (realdev,vid) -> vlandev. */
     struct hmap vlandev_map;     /* vlandev -> (realdev,vid). */
 
-    /* ODP port to ofport mapping. */
-    struct hmap odp_to_ofport_map;
+    /* Ports. */
+    struct svec ports;             /* List of port names. */
+    struct sset port_poll_set;     /* Queued names for port_poll() reply. */
+    int port_poll_errno;           /* Last errno for port_poll() reply. */
 };
 
 /* Defer flow mod completion until "ovs-appctl ofproto/unclog"?  (Useful only
@@ -689,7 +707,7 @@ static void update_learning_table(struct ofproto_dpif *,
                                   struct ofbundle *);
 /* Upcalls. */
 #define FLOW_MISS_MAX_BATCH 50
-static int handle_upcalls(struct ofproto_dpif *, unsigned int max_batch);
+static int handle_upcalls(struct dpif *, unsigned int max_batch);
 
 /* Flow expiration. */
 static int expire(struct ofproto_dpif *);
@@ -706,13 +724,28 @@ static void add_mirror_actions(struct action_xlate_ctx *ctx,
                                const struct flow *flow);
 /* Global variables. */
 static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
+
+/* Initial mappings of port to bridge mappings. */
+static struct shash init_ofp_ports = SHASH_INITIALIZER(&init_ofp_ports);
 
 /* Factory functions. */
 
 static void
-init(const struct shash *iface_hints OVS_UNUSED)
+init(const struct shash *iface_hints)
 {
-    return;
+    struct shash_node *node;
+
+    /* Make a local copy, since we don't own 'iface_hints' elements. */
+    SHASH_FOR_EACH(node, iface_hints) {
+        const struct iface_hint *orig_hint = node->data;
+        struct iface_hint *new_hint = xmalloc(sizeof *new_hint);
+
+        new_hint->br_name = xstrdup(orig_hint->br_name);
+        new_hint->br_type = xstrdup(orig_hint->br_type);
+        new_hint->ofp_port = orig_hint->ofp_port;
+
+        shash_add(&init_ofp_ports, node->name, new_hint);
+    }
 }
 
 static void
@@ -724,7 +757,17 @@ enumerate_types(struct sset *types)
 static int
 enumerate_names(const char *type, struct sset *names)
 {
-    return dp_enumerate_names(type, names);
+    struct ofproto_dpif *ofproto;
+
+    sset_clear(names);
+    HMAP_FOR_EACH (ofproto, all_ofproto_dpifs_node, &all_ofproto_dpifs) {
+        if (strcmp(type, ofproto->up.type)) {
+            continue;
+        }
+        sset_add(names, ofproto->up.name);
+    }
+
+    return 0;
 }
 
 static int
@@ -741,6 +784,113 @@ del(const char *type, const char *name)
     return error;
 }
 
+/* Type functions. */
+
+static int
+type_run(const char *type)
+{
+    struct dpif_backer *backer;
+    char *devname;
+    int error;
+
+    backer = shash_find_data(&all_dpif_backers, type);
+    if (!backer) {
+        /* This is not necessarily a problem, since backers are only
+         * created on demand. */
+        return 0;
+    }
+
+    dpif_run(backer->dpif);
+
+    /* Check for port changes in the dpif. */
+    while ((error = dpif_port_poll(backer->dpif, &devname)) != EAGAIN) {
+        if (!error) {
+            struct ofproto_dpif *ofproto = NULL;
+            struct dpif_port port;
+
+            /* Don't report on the datapath's device. */
+            if (!strcmp(devname, backer->dpif->base_name)) {
+                continue;
+            }
+
+            HMAP_FOR_EACH (ofproto, all_ofproto_dpifs_node,
+                           &all_ofproto_dpifs) {
+                if (svec_contains(&ofproto->ports, devname)) {
+                    break;
+                }
+            }
+
+            if (dpif_port_query_by_name(backer->dpif, devname, &port)) {
+                /* The port was removed.  If we know the datapath,
+                 * report it through poll_set().  If we don't, it may be
+                 * notifying us of a removal we initiated, so ignore it.
+                 * If there's a pending ENOBUFS, let it stand, since
+                 * everything will be reevaluated. */
+                if (ofproto && ofproto->port_poll_errno != ENOBUFS) {
+                    sset_add(&ofproto->port_poll_set, devname);
+                    ofproto->port_poll_errno = 0;
+                }
+                dpif_port_destroy(&port);
+            } else if (!ofproto) {
+                /* The port was added, but we don't know with which
+                 * ofproto we should associate it.  Delete it. */
+                dpif_port_del(backer->dpif, port.port_no);
+            }
+
+            free(devname);
+        } else {
+            struct ofproto_dpif *ofproto;
+
+            /* There was some sort of error, so propagate it to all
+             * ofprotos that use this backer. */
+            HMAP_FOR_EACH (ofproto, all_ofproto_dpifs_node,
+                           &all_ofproto_dpifs) {
+                if (ofproto->backer == backer) {
+                    sset_clear(&ofproto->port_poll_set);
+                    ofproto->port_poll_errno = error;
+                }
+            }
+            break;
+        }
+    }
+
+    return 0;
+}
+
+static int
+type_run_fast(const char *type)
+{
+    struct dpif_backer *backer;
+    unsigned int work;
+
+    backer = shash_find_data(&all_dpif_backers, type);
+    if (!backer) {
+        /* This is not necessarily a problem, since backers are only
+         * created on demand. */
+        return 0;
+    }
+
+    /* Handle one or more batches of upcalls, until there's nothing left to do
+     * or until we do a fixed total amount of work.
+     *
+     * We do work in batches because it can be much cheaper to set up a number
+     * of flows and fire off their patches all at once.  We do multiple batches
+     * because in some cases handling a packet can cause another packet to be
+     * queued almost immediately as part of the return flow.  Both
+     * optimizations can make major improvements on some benchmarks and
+     * presumably for real traffic as well. */
+    work = 0;
+    while (work < FLOW_MISS_MAX_BATCH) {
+        int retval = handle_upcalls(backer->dpif, FLOW_MISS_MAX_BATCH - work);
+        if (retval <= 0) {
+            return -retval;
+        }
+        work += retval;
+    }
+
+    return 0;
+}
+
 /* Basic life-cycle. */
 
 static int add_internal_flows(struct ofproto_dpif *);
@@ -759,36 +909,117 @@ dealloc(struct ofproto *ofproto_)
     free(ofproto);
 }
 
+static void
+close_dpif_backer(struct dpif_backer *backer)
+{
+    struct shash_node *node;
+
+    if (--backer->refcount) {
+        return;
+    }
+
+    hmap_destroy(&backer->odp_to_ofport_map);
+    node = shash_find(&all_dpif_backers, backer->type);
+    free(backer->type);
+    shash_delete(&all_dpif_backers, node);
+    dpif_close(backer->dpif);
+
+    free(backer);
+}
+
+/* Datapath port slated for removal from datapath. */
+struct odp_garbage {
+    struct list list_node;
+    uint32_t odp_port;
+};
+
+static int
+open_dpif_backer(const char *type, struct dpif_backer **backerp)
+{
+    struct dpif_backer *backer;
+    struct dpif_port_dump port_dump;
+    struct dpif_port port;
+    struct shash_node *node;
+    struct list garbage_list;
+    struct odp_garbage *garbage, *next;
+    int error;
+
+    backer = shash_find_data(&all_dpif_backers, type);
+    if (backer) {
+        backer->refcount++;
+        *backerp = backer;
+        return 0;
+    }
+
+    backer = xmalloc(sizeof *backer);
+
+    error = dpif_create_and_open("ovs-datapath", type, &backer->dpif);
+    if (error) {
+        VLOG_ERR("failed to open datapath of type %s: %s", type,
+                 strerror(error));
+        return error;
+    }
+
+    backer->type = xstrdup(type);
+    backer->refcount = 1;
+    hmap_init(&backer->odp_to_ofport_map);
+    *backerp = backer;
+
+    dpif_flow_flush(backer->dpif);
+    dpif_recv_purge(backer->dpif);
+
+    /* Loop through the ports already on the datapath and remove any
+     * that we don't need anymore. */
+    list_init(&garbage_list);
+    dpif_port_dump_start(&port_dump, backer->dpif);
+    while (dpif_port_dump_next(&port_dump, &port)) {
+        node = shash_find(&init_ofp_ports, port.name);
+        if (!node && strcmp(port.name, backer->dpif->base_name)) {
+            garbage = xmalloc(sizeof *garbage);
+            garbage->odp_port = port.port_no;
+            list_push_front(&garbage_list, &garbage->list_node);
+        }
+    }
+    dpif_port_dump_done(&port_dump);
+
+    LIST_FOR_EACH_SAFE (garbage, next, list_node, &garbage_list) {
+        dpif_port_del(backer->dpif, garbage->odp_port);
+        list_remove(&garbage->list_node);
+        free(garbage);
+    }
+
+    shash_add(&all_dpif_backers, type, backer);
+
+    error = dpif_recv_set(backer->dpif, true);
+    if (error) {
+        VLOG_ERR("failed to listen on datapath of type %s: %s",
+                 type, strerror(error));
+        close_dpif_backer(backer);
+        return error;
+    }
+
+    return error;
+}
+
 static int
 construct(struct ofproto *ofproto_)
 {
     struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofproto_);
-    const char *name = ofproto->up.name;
+    struct shash_node *node, *next;
     int max_ports;
     int error;
     int i;
 
-    error = dpif_create_and_open(name, ofproto->up.type, &ofproto->dpif);
+    error = open_dpif_backer(ofproto->up.type, &ofproto->backer);
     if (error) {
-        VLOG_ERR("failed to open datapath %s: %s", name, strerror(error));
         return error;
     }
 
-    max_ports = dpif_get_max_ports(ofproto->dpif);
+    max_ports = dpif_get_max_ports(ofproto->backer->dpif);
     ofproto_init_max_ports(ofproto_, MIN(max_ports, OFPP_MAX));
 
     ofproto->n_matches = 0;
 
-    dpif_flow_flush(ofproto->dpif);
-    dpif_recv_purge(ofproto->dpif);
-
-    error = dpif_recv_set(ofproto->dpif, true);
-    if (error) {
-        VLOG_ERR("failed to listen on datapath %s: %s", name, strerror(error));
-        dpif_close(ofproto->dpif);
-        return error;
-    }
-
     ofproto->netflow = NULL;
     ofproto->sflow = NULL;
     ofproto->stp = NULL;
@@ -825,7 +1056,25 @@ construct(struct ofproto *ofproto_)
     hmap_init(&ofproto->vlandev_map);
     hmap_init(&ofproto->realdev_vid_map);
 
-    hmap_init(&ofproto->odp_to_ofport_map);
+    svec_init(&ofproto->ports);
+    sset_init(&ofproto->port_poll_set);
+    ofproto->port_poll_errno = 0;
+
+    SHASH_FOR_EACH_SAFE(node, next, &init_ofp_ports) {
+        const struct iface_hint *iface_hint = node->data;
+
+        if (!strcmp(iface_hint->br_name, ofproto->up.name)) {
+            /* Check if the datapath already has this port. */
+            if (dpif_port_exists(ofproto->backer->dpif, node->name)) {
+                svec_add(&ofproto->ports, node->name);
+                svec_sort(&ofproto->ports);
+            }
+
+            free(iface_hint->br_name);
+            free(iface_hint->br_type);
+            shash_delete(&init_ofp_ports, node);
+        }
+    }
 
     hmap_insert(&all_ofproto_dpifs, &ofproto->all_ofproto_dpifs_node,
                 hash_string(ofproto->up.name, 0));
@@ -951,9 +1200,10 @@ destruct(struct ofproto *ofproto_)
     hmap_destroy(&ofproto->vlandev_map);
     hmap_destroy(&ofproto->realdev_vid_map);
 
-    hmap_destroy(&ofproto->odp_to_ofport_map);
+    svec_destroy(&ofproto->ports);
+    sset_destroy(&ofproto->port_poll_set);
 
-    dpif_close(ofproto->dpif);
+    close_dpif_backer(ofproto->backer);
 }
 
 static int
@@ -961,29 +1211,11 @@ run_fast(struct ofproto *ofproto_)
 {
     struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofproto_);
     struct ofport_dpif *ofport;
-    unsigned int work;
 
     HMAP_FOR_EACH (ofport, up.hmap_node, &ofproto->up.ports) {
         port_run_fast(ofport);
     }
 
-    /* Handle one or more batches of upcalls, until there's nothing left to do
-     * or until we do a fixed total amount of work.
-     *
-     * We do work in batches because it can be much cheaper to set up a number
-     * of flows and fire off their patches all at once.  We do multiple batches
-     * because in some cases handling a packet can cause another packet to be
-     * queued almost immediately as part of the return flow.  Both
-     * optimizations can make major improvements on some benchmarks and
-     * presumably for real traffic as well. */
-    work = 0;
-    while (work < FLOW_MISS_MAX_BATCH) {
-        int retval = handle_upcalls(ofproto, FLOW_MISS_MAX_BATCH - work);
-        if (retval <= 0) {
-            return -retval;
-        }
-        work += retval;
-    }
     return 0;
 }
 
@@ -998,7 +1230,6 @@ run(struct ofproto *ofproto_)
     if (!clogged) {
         complete_operations(ofproto);
     }
-    dpif_run(ofproto->dpif);
 
     error = run_fast(ofproto_);
     if (error) {
@@ -1101,8 +1332,8 @@ wait(struct ofproto *ofproto_)
         poll_immediate_wake();
     }
 
-    dpif_wait(ofproto->dpif);
-    dpif_recv_wait(ofproto->dpif);
+    dpif_wait(ofproto->backer->dpif);
+    dpif_recv_wait(ofproto->backer->dpif);
     if (ofproto->sflow) {
         dpif_sflow_wait(ofproto->sflow);
     }
@@ -1145,23 +1376,27 @@ static void
 flush(struct ofproto *ofproto_)
 {
     struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofproto_);
-    struct facet *facet, *next_facet;
-
-    HMAP_FOR_EACH_SAFE (facet, next_facet, hmap_node, &ofproto->facets) {
-        /* Mark the facet as not installed so that facet_remove() doesn't
-         * bother trying to uninstall it.  There is no point in uninstalling it
-         * individually since we are about to blow away all the facets with
-         * dpif_flow_flush(). */
-        struct subfacet *subfacet;
+    struct subfacet *subfacet, *next_subfacet;
+    struct subfacet *batch[SUBFACET_DESTROY_MAX_BATCH];
+    int n_batch;
 
-        LIST_FOR_EACH (subfacet, list_node, &facet->subfacets) {
-            subfacet->path = SF_NOT_INSTALLED;
-            subfacet->dp_packet_count = 0;
-            subfacet->dp_byte_count = 0;
+    n_batch = 0;
+    HMAP_FOR_EACH_SAFE (subfacet, next_subfacet, hmap_node,
+                        &ofproto->subfacets) {
+        if (subfacet->path != SF_NOT_INSTALLED) {
+            batch[n_batch++] = subfacet;
+            if (n_batch >= SUBFACET_DESTROY_MAX_BATCH) {
+                subfacet_destroy_batch(ofproto, batch, n_batch);
+                n_batch = 0;
+            }
+        } else {
+            subfacet_destroy(subfacet);
         }
-        facet_remove(facet);
     }
-    dpif_flow_flush(ofproto->dpif);
+
+    if (n_batch > 0) {
+        subfacet_destroy_batch(ofproto, batch, n_batch);
+    }
 }
 
 static void
@@ -1191,7 +1426,8 @@ get_tables(struct ofproto *ofproto_, struct ofp12_table_stats *ots)
 
     strcpy(ots->name, "classifier");
 
-    dpif_get_dp_stats(ofproto->dpif, &s);
+    dpif_get_dp_stats(ofproto->backer->dpif, &s);
+
     ots->lookup_count = htonll(s.n_hit + s.n_missed);
     ots->matched_count = htonll(s.n_hit + ofproto->n_matches);
 }
@@ -1230,7 +1466,7 @@ port_construct(struct ofport *port_)
     port->vlandev_vid = 0;
     port->carrier_seq = netdev_get_carrier_resets(port->up.netdev);
 
-    error = dpif_port_query_by_name(ofproto->dpif,
+    error = dpif_port_query_by_name(ofproto->backer->dpif,
                                     netdev_get_name(port->up.netdev),
                                     &dpif_port);
     if (error) {
@@ -1238,7 +1474,7 @@ port_construct(struct ofport *port_)
     }
      
     port->odp_port = dpif_port.port_no;
-    hmap_insert(&ofproto->odp_to_ofport_map, &port->hmap_node,
+    hmap_insert(&ofproto->backer->odp_to_ofport_map, &port->hmap_node,
                 hash_int(port->odp_port, 0));
 
     if (ofproto->sflow) {
@@ -1253,8 +1489,17 @@ port_destruct(struct ofport *port_)
 {
     struct ofport_dpif *port = ofport_dpif_cast(port_);
     struct ofproto_dpif *ofproto = ofproto_dpif_cast(port->up.ofproto);
+    struct dpif_port dpif_port;
+
+    if (!dpif_port_query_by_number(ofproto->backer->dpif,
+                                   port->odp_port, &dpif_port)) {
+        /* The underlying device is still there, so delete it. */
+        dpif_port_del(ofproto->backer->dpif, port->odp_port);
+        dpif_port_destroy(&dpif_port);
+    }
 
-    hmap_remove(&ofproto->odp_to_ofport_map, &port->hmap_node);
+    svec_del(&ofproto->ports, netdev_get_name(port->up.netdev));
+    hmap_remove(&ofproto->backer->odp_to_ofport_map, &port->hmap_node);
     ofproto->need_revalidate = REV_RECONFIGURE;
     bundle_remove(port_);
     set_cfm(port_, NULL);
@@ -1305,7 +1550,7 @@ set_sflow(struct ofproto *ofproto_,
         if (!ds) {
             struct ofport_dpif *ofport;
 
-            ds = ofproto->sflow = dpif_sflow_create(ofproto->dpif);
+            ds = ofproto->sflow = dpif_sflow_create(ofproto->backer->dpif);
             HMAP_FOR_EACH (ofport, up.hmap_node, &ofproto->up.ports) {
                 dpif_sflow_add_port(ds, &ofport->up, ofport->odp_port);
             }
@@ -1682,7 +1927,7 @@ set_queues(struct ofport *ofport_,
         uint8_t dscp;
 
         dscp = (qdscp_list[i].dscp << 2) & IP_DSCP_MASK;
-        if (dpif_queue_to_priority(ofproto->dpif, qdscp_list[i].queue,
+        if (dpif_queue_to_priority(ofproto->backer->dpif, qdscp_list[i].queue,
                                    &priority)) {
             continue;
         }
@@ -2560,7 +2805,11 @@ port_query_by_name(const struct ofproto *ofproto_, const char *devname,
     struct dpif_port dpif_port;
     int error;
 
-    error = dpif_port_query_by_name(ofproto->dpif, devname, &dpif_port);
+    if (!svec_contains(&ofproto->ports, devname)) {
+        return ENODEV;
+    }
+    error = dpif_port_query_by_name(ofproto->backer->dpif,
+                                    devname, &dpif_port);
     if (!error) {
         ofproto_port_from_dpif_port(ofproto, ofproto_port, &dpif_port);
     }
@@ -2572,8 +2821,14 @@ port_add(struct ofproto *ofproto_, struct netdev *netdev)
 {
     struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofproto_);
     uint32_t odp_port = UINT32_MAX;
+    int error;
 
-    return dpif_port_add(ofproto->dpif, netdev, &odp_port);
+    error = dpif_port_add(ofproto->backer->dpif, netdev, &odp_port);
+    if (!error) {
+        svec_add(&ofproto->ports, netdev_get_name(netdev));
+        svec_sort(&ofproto->ports);
+    }
+    return error;
 }
 
 static int
@@ -2583,7 +2838,7 @@ port_del(struct ofproto *ofproto_, uint16_t ofp_port)
     uint32_t odp_port = ofp_port_to_odp_port(ofproto, ofp_port);
     int error;
 
-    error = dpif_port_del(ofproto->dpif, odp_port);
+    error = dpif_port_del(ofproto->backer->dpif, odp_port);
     if (!error) {
         struct ofport_dpif *ofport = get_ofp_port(ofproto, ofp_port);
         if (ofport) {
@@ -2655,19 +2910,16 @@ ofproto_update_local_port_stats(const struct ofproto *ofproto_,
 }
 
 struct port_dump_state {
-    struct dpif_port_dump dump;
-    bool done;
+    size_t index;
 };
 
 static int
-port_dump_start(const struct ofproto *ofproto_, void **statep)
+port_dump_start(const struct ofproto *ofproto_ OVS_UNUSED, void **statep)
 {
-    struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofproto_);
     struct port_dump_state *state;
 
     *statep = state = xmalloc(sizeof *state);
-    dpif_port_dump_start(&state->dump, ofproto->dpif);
-    state->done = false;
+    state->index = 0;
     return 0;
 }
 
@@ -2677,15 +2929,17 @@ port_dump_next(const struct ofproto *ofproto_ OVS_UNUSED, void *state_,
 {
     struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofproto_);
     struct port_dump_state *state = state_;
-    struct dpif_port dpif_port;
 
-    if (dpif_port_dump_next(&state->dump, &dpif_port)) {
-        ofproto_port_from_dpif_port(ofproto, port, &dpif_port);
-        return 0;
+    if (state->index < ofproto->ports.n) {
+        int error = port_query_by_name(ofproto_,
+                                       ofproto->ports.names[state->index],
+                                       port);
+        if (!error) {
+            state->index++;
+        }
+        return error;
     } else {
-        int error = dpif_port_dump_done(&state->dump);
-        state->done = true;
-        return error ? error : EOF;
+        return EOF; 
     }
 }
 
@@ -2694,9 +2948,6 @@ port_dump_done(const struct ofproto *ofproto_ OVS_UNUSED, void *state_)
 {
     struct port_dump_state *state = state_;
 
-    if (!state->done) {
-        dpif_port_dump_done(&state->dump);
-    }
     free(state);
     return 0;
 }
@@ -2705,14 +2956,26 @@ static int
 port_poll(const struct ofproto *ofproto_, char **devnamep)
 {
     struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofproto_);
-    return dpif_port_poll(ofproto->dpif, devnamep);
+
+    if (ofproto->port_poll_errno) {
+        int error = ofproto->port_poll_errno;
+        ofproto->port_poll_errno = 0;
+        return error;
+    }
+
+    if (sset_is_empty(&ofproto->port_poll_set)) {
+        return EAGAIN;
+    }
+
+    *devnamep = sset_pop(&ofproto->port_poll_set);
+    return 0;
 }
 
 static void
 port_poll_wait(const struct ofproto *ofproto_)
 {
     struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofproto_);
-    dpif_port_poll_wait(ofproto->dpif);
+    dpif_port_poll_wait(ofproto->backer->dpif);
 }
 
 static int
@@ -2737,6 +3000,7 @@ port_is_lacp_current(const struct ofport *ofport_)
  * It's possible to batch more than that, but the benefit might be minimal. */
 struct flow_miss {
     struct hmap_node hmap_node;
+    struct ofproto_dpif *ofproto;
     struct flow flow;
     enum odp_key_fitness key_fitness;
     const struct nlattr *key;
@@ -3026,12 +3290,13 @@ handle_flow_miss_with_facet(struct flow_miss *miss, struct facet *facet,
     }
 }
 
-/* Handles flow miss 'miss' on 'ofproto'.  May add any required datapath
- * operations to 'ops', incrementing '*n_ops' for each new op. */
+/* Handles flow miss 'miss'.  May add any required datapath operations
+ * to 'ops', incrementing '*n_ops' for each new op. */
 static void
-handle_flow_miss(struct ofproto_dpif *ofproto, struct flow_miss *miss,
-                 struct flow_miss_op *ops, size_t *n_ops)
+handle_flow_miss(struct flow_miss *miss, struct flow_miss_op *ops,
+                 size_t *n_ops)
 {
+    struct ofproto_dpif *ofproto = miss->ofproto;
     struct facet *facet;
     long long int now;
     uint32_t hash;
@@ -3057,31 +3322,25 @@ handle_flow_miss(struct ofproto_dpif *ofproto, struct flow_miss *miss,
     handle_flow_miss_with_facet(miss, facet, now, ops, n_ops);
 }
 
-/* Like odp_flow_key_to_flow(), this function converts the 'key_len' bytes of
- * OVS_KEY_ATTR_* attributes in 'key' to a flow structure in 'flow' and returns
- * an ODP_FIT_* value that indicates how well 'key' fits our expectations for
- * what a flow key should contain.
- *
- * This function also includes some logic to help make VLAN splinters
- * transparent to the rest of the upcall processing logic.  In particular, if
- * the extracted in_port is a VLAN splinter port, it replaces flow->in_port by
- * the "real" port, sets flow->vlan_tci correctly for the VLAN of the VLAN
- * splinter port, and pushes a VLAN header onto 'packet' (if it is nonnull).
+/* This function does post-processing on data returned from
+ * odp_flow_key_to_flow() to help make VLAN splinters transparent to the
+ * rest of the upcall processing logic.  In particular, if the extracted
+ * in_port is a VLAN splinter port, it replaces flow->in_port by the "real"
+ * port, sets flow->vlan_tci correctly for the VLAN of the VLAN splinter
+ * port, and pushes a VLAN header onto 'packet' (if it is nonnull). The
+ * caller must have called odp_flow_key_to_flow() and supply 'fitness' and
+ * 'flow' from its output.
  *
  * Sets '*initial_tci' to the VLAN TCI with which the packet was really
  * received, that is, the actual VLAN TCI extracted by odp_flow_key_to_flow().
  * (This differs from the value returned in flow->vlan_tci only for packets
- * received on VLAN splinters.)
- */
+ * received on VLAN splinters.) */
 static enum odp_key_fitness
-ofproto_dpif_extract_flow_key(const struct ofproto_dpif *ofproto,
-                              const struct nlattr *key, size_t key_len,
-                              struct flow *flow, ovs_be16 *initial_tci,
-                              struct ofpbuf *packet)
+ofproto_dpif_vsp_adjust(const struct ofproto_dpif *ofproto,
+                        enum odp_key_fitness fitness,
+                        struct flow *flow, ovs_be16 *initial_tci,
+                        struct ofpbuf *packet)
 {
-    enum odp_key_fitness fitness;
-
-    fitness = odp_flow_key_to_flow(key, key_len, flow);
     flow->in_port = odp_port_to_ofp_port(ofproto, flow->in_port);
     if (fitness == ODP_FIT_ERROR) {
         return fitness;
@@ -3117,7 +3376,7 @@ ofproto_dpif_extract_flow_key(const struct ofproto_dpif *ofproto,
 }
 
 static void
-handle_miss_upcalls(struct ofproto_dpif *ofproto, struct dpif_upcall *upcalls,
+handle_miss_upcalls(struct dpif *dpif, struct dpif_upcall *upcalls,
                     size_t n_upcalls)
 {
     struct dpif_upcall *upcall;
@@ -3144,14 +3403,28 @@ handle_miss_upcalls(struct ofproto_dpif *ofproto, struct dpif_upcall *upcalls,
     for (upcall = upcalls; upcall < &upcalls[n_upcalls]; upcall++) {
         struct flow_miss *miss = &misses[n_misses];
         struct flow_miss *existing_miss;
+        enum odp_key_fitness fitness;
+        struct ofproto_dpif *ofproto;
         struct flow flow;
         uint32_t hash;
 
+
+        fitness = odp_flow_key_to_flow(upcall->key, upcall->key_len, &flow);
+        ofproto = odp_port_to_ofproto_dpif(dpif, flow.in_port);
+        if (!ofproto) {
+            /* Received packet on port for which we couldn't associate
+             * an ofproto.  This can happen if a port is removed while
+             * traffic is being received.  Print a rate-limited message
+             * in case it happens frequently. */
+            VLOG_INFO_RL(&rl, "Received packet on unassociated port %"PRIu32,
+                         flow.in_port);
+            continue;
+        }
+
         /* Obtain metadata and check userspace/kernel agreement on flow match,
          * then set 'flow''s header pointers. */
-        miss->key_fitness = ofproto_dpif_extract_flow_key(
-            ofproto, upcall->key, upcall->key_len,
-            &flow, &miss->initial_tci, upcall->packet);
+        miss->key_fitness = ofproto_dpif_vsp_adjust(ofproto, fitness,
+                                &flow, &miss->initial_tci, upcall->packet);
         if (miss->key_fitness == ODP_FIT_ERROR) {
             continue;
         }
@@ -3163,6 +3436,7 @@ handle_miss_upcalls(struct ofproto_dpif *ofproto, struct dpif_upcall *upcalls,
         existing_miss = flow_miss_find(&todo, &miss->flow, hash);
         if (!existing_miss) {
             hmap_insert(&todo, &miss->hmap_node, hash);
+            miss->ofproto = ofproto;
             miss->key = upcall->key;
             miss->key_len = upcall->key_len;
             miss->upcall_type = upcall->type;
@@ -3179,7 +3453,7 @@ handle_miss_upcalls(struct ofproto_dpif *ofproto, struct dpif_upcall *upcalls,
      * operations to batch. */
     n_ops = 0;
     HMAP_FOR_EACH (miss, hmap_node, &todo) {
-        handle_flow_miss(ofproto, miss, flow_miss_ops, &n_ops);
+        handle_flow_miss(miss, flow_miss_ops, &n_ops);
     }
     assert(n_ops <= ARRAY_SIZE(flow_miss_ops));
 
@@ -3187,7 +3461,7 @@ handle_miss_upcalls(struct ofproto_dpif *ofproto, struct dpif_upcall *upcalls,
     for (i = 0; i < n_ops; i++) {
         dpif_ops[i] = &flow_miss_ops[i].dpif_op;
     }
-    dpif_operate(ofproto->dpif, dpif_ops, n_ops);
+    dpif_operate(dpif, dpif_ops, n_ops);
 
     /* Free memory and update facets. */
     for (i = 0; i < n_ops; i++) {
@@ -3248,30 +3522,35 @@ classify_upcall(const struct dpif_upcall *upcall)
 }
 
 static void
-handle_sflow_upcall(struct ofproto_dpif *ofproto,
-                    const struct dpif_upcall *upcall)
+handle_sflow_upcall(struct dpif *dpif, const struct dpif_upcall *upcall)
 {
+    struct ofproto_dpif *ofproto;
     union user_action_cookie cookie;
     enum odp_key_fitness fitness;
     ovs_be16 initial_tci;
     struct flow flow;
     uint32_t odp_in_port;
 
-    fitness = ofproto_dpif_extract_flow_key(ofproto, upcall->key,
-                                            upcall->key_len, &flow,
-                                            &initial_tci, upcall->packet);
+    fitness = odp_flow_key_to_flow(upcall->key, upcall->key_len, &flow);
+    odp_in_port = flow.in_port;
+    ofproto = odp_port_to_ofproto_dpif(dpif, odp_in_port);
+    if (!ofproto || !ofproto->sflow) {
+        return;
+    }
+
+    fitness = ofproto_dpif_vsp_adjust(ofproto, fitness, &flow,
+                                      &initial_tci, upcall->packet);
     if (fitness == ODP_FIT_ERROR) {
         return;
     }
 
     memcpy(&cookie, &upcall->userdata, sizeof(cookie));
-    odp_in_port = ofp_port_to_odp_port(ofproto, flow.in_port);
     dpif_sflow_received(ofproto->sflow, upcall->packet, &flow,
                         odp_in_port, &cookie);
 }
 
 static int
-handle_upcalls(struct ofproto_dpif *ofproto, unsigned int max_batch)
+handle_upcalls(struct dpif *dpif, unsigned int max_batch)
 {
     struct dpif_upcall misses[FLOW_MISS_MAX_BATCH];
     struct ofpbuf miss_bufs[FLOW_MISS_MAX_BATCH];
@@ -3290,7 +3569,7 @@ handle_upcalls(struct ofproto_dpif *ofproto, unsigned int max_batch)
 
         ofpbuf_use_stub(buf, miss_buf_stubs[n_misses],
                         sizeof miss_buf_stubs[n_misses]);
-        error = dpif_recv(ofproto->dpif, upcall, buf);
+        error = dpif_recv(dpif, upcall, buf);
         if (error) {
             ofpbuf_uninit(buf);
             break;
@@ -3303,9 +3582,7 @@ handle_upcalls(struct ofproto_dpif *ofproto, unsigned int max_batch)
             break;
 
         case SFLOW_UPCALL:
-            if (ofproto->sflow) {
-                handle_sflow_upcall(ofproto, upcall);
-            }
+            handle_sflow_upcall(dpif, upcall);
             ofpbuf_uninit(buf);
             break;
 
@@ -3316,7 +3593,7 @@ handle_upcalls(struct ofproto_dpif *ofproto, unsigned int max_batch)
     }
 
     /* Handle deferred MISS_UPCALL processing. */
-    handle_miss_upcalls(ofproto, misses, n_misses);
+    handle_miss_upcalls(dpif, misses, n_misses);
     for (i = 0; i < n_misses; i++) {
         ofpbuf_uninit(&miss_bufs[i]);
     }
@@ -3414,7 +3691,7 @@ update_subfacet_stats(struct subfacet *subfacet,
 /* 'key' with length 'key_len' bytes is a flow in 'dpif' that we know nothing
  * about, or a flow that shouldn't be installed but was anyway.  Delete it. */
 static void
-delete_unexpected_flow(struct dpif *dpif,
+delete_unexpected_flow(struct ofproto_dpif *ofproto,
                        const struct nlattr *key, size_t key_len)
 {
     if (!VLOG_DROP_WARN(&rl)) {
@@ -3422,12 +3699,12 @@ delete_unexpected_flow(struct dpif *dpif,
 
         ds_init(&s);
         odp_flow_key_format(key, key_len, &s);
-        VLOG_WARN("unexpected flow from datapath %s", ds_cstr(&s));
+        VLOG_WARN("unexpected flow on %s: %s", ofproto->up.name, ds_cstr(&s));
         ds_destroy(&s);
     }
 
     COVERAGE_INC(facet_unexpected);
-    dpif_flow_del(dpif, key, key_len, NULL);
+    dpif_flow_del(ofproto->backer->dpif, key, key_len, NULL);
 }
 
 /* Update 'packet_count', 'byte_count', and 'used' members of installed facets.
@@ -3449,10 +3726,15 @@ update_stats(struct ofproto_dpif *p)
     const struct nlattr *key;
     size_t key_len;
 
-    dpif_flow_dump_start(&dump, p->dpif);
+    dpif_flow_dump_start(&dump, p->backer->dpif);
     while (dpif_flow_dump_next(&dump, &key, &key_len, NULL, NULL, &stats)) {
+        struct flow flow;
         struct subfacet *subfacet;
 
+        odp_flow_key_to_flow(key, key_len, &flow);
+        if (odp_port_to_ofp_port(p, flow.in_port) == OFPP_NONE) {
+            continue;
+        }
         subfacet = subfacet_find(p, key, key_len);
         switch (subfacet ? subfacet->path : SF_NOT_INSTALLED) {
         case SF_FAST_PATH:
@@ -3465,7 +3747,7 @@ update_stats(struct ofproto_dpif *p)
 
         case SF_NOT_INSTALLED:
         default:
-            delete_unexpected_flow(p->dpif, key, key_len);
+            delete_unexpected_flow(p, key, key_len);
             break;
         }
     }
@@ -3692,7 +3974,7 @@ execute_odp_actions(struct ofproto_dpif *ofproto, const struct flow *flow,
     odp_flow_key_from_flow(&key, flow,
                            ofp_port_to_odp_in_port(ofproto, flow->in_port));
 
-    error = dpif_execute(ofproto->dpif, key.data, key.size,
+    error = dpif_execute(ofproto->backer->dpif, key.data, key.size,
                          odp_actions, actions_len, packet);
 
     ofpbuf_delete(packet);
@@ -4400,7 +4682,7 @@ subfacet_destroy_batch(struct ofproto_dpif *ofproto,
         opsp[i] = &ops[i];
     }
 
-    dpif_operate(ofproto->dpif, opsp, n);
+    dpif_operate(ofproto->backer->dpif, opsp, n);
     for (i = 0; i < n; i++) {
         subfacet_reset_dp_stats(subfacets[i], &stats[i]);
         subfacets[i]->path = SF_NOT_INSTALLED;
@@ -4494,7 +4776,7 @@ subfacet_install(struct subfacet *subfacet,
     }
 
     subfacet_get_key(subfacet, &keybuf, &key);
-    ret = dpif_flow_put(ofproto->dpif, flags, key.data, key.size,
+    ret = dpif_flow_put(ofproto->backer->dpif, flags, key.data, key.size,
                         actions, actions_len, stats);
 
     if (stats) {
@@ -4527,7 +4809,8 @@ subfacet_uninstall(struct subfacet *subfacet)
         int error;
 
         subfacet_get_key(subfacet, &keybuf, &key);
-        error = dpif_flow_del(ofproto->dpif, key.data, key.size, &stats);
+        error = dpif_flow_del(ofproto->backer->dpif,
+                              key.data, key.size, &stats);
         subfacet_reset_dp_stats(subfacet, &stats);
         if (!error) {
             subfacet_update_stats(subfacet, &stats);
@@ -4828,7 +5111,7 @@ send_packet(const struct ofport_dpif *ofport, struct ofpbuf *packet)
     compose_sflow_action(ofproto, &odp_actions, &flow, odp_port);
 
     nl_msg_put_u32(&odp_actions, OVS_ACTION_ATTR_OUTPUT, odp_port);
-    error = dpif_execute(ofproto->dpif,
+    error = dpif_execute(ofproto->backer->dpif,
                          key.data, key.size,
                          odp_actions.data, odp_actions.size,
                          packet);
@@ -4873,7 +5156,7 @@ compose_slow_path(const struct ofproto_dpif *ofproto, const struct flow *flow,
 
     ofpbuf_use_stack(&buf, stub, stub_size);
     if (slow & (SLOW_CFM | SLOW_LACP | SLOW_STP)) {
-        uint32_t pid = dpif_port_get_pid(ofproto->dpif, UINT16_MAX);
+        uint32_t pid = dpif_port_get_pid(ofproto->backer->dpif, UINT16_MAX);
         odp_put_userspace_action(pid, &cookie, &buf);
     } else {
         put_userspace_action(ofproto, &buf, flow, &cookie);
@@ -4890,7 +5173,7 @@ put_userspace_action(const struct ofproto_dpif *ofproto,
 {
     uint32_t pid;
 
-    pid = dpif_port_get_pid(ofproto->dpif,
+    pid = dpif_port_get_pid(ofproto->backer->dpif,
                             ofp_port_to_odp_port(ofproto, flow->in_port));
 
     return odp_put_userspace_action(pid, cookie, odp_actions);
@@ -5321,7 +5604,8 @@ xlate_enqueue_action(struct action_xlate_ctx *ctx,
     int error;
 
     /* Translate queue to priority. */
-    error = dpif_queue_to_priority(ctx->ofproto->dpif, queue_id, &priority);
+    error = dpif_queue_to_priority(ctx->ofproto->backer->dpif,
+                                   queue_id, &priority);
     if (error) {
         /* Fall back to ordinary output action. */
         xlate_output_action(ctx, enqueue->port, 0, false);
@@ -5354,7 +5638,8 @@ xlate_set_queue_action(struct action_xlate_ctx *ctx, uint32_t queue_id)
 {
     uint32_t skb_priority;
 
-    if (!dpif_queue_to_priority(ctx->ofproto->dpif, queue_id, &skb_priority)) {
+    if (!dpif_queue_to_priority(ctx->ofproto->backer->dpif,
+                                queue_id, &skb_priority)) {
         ctx->flow.skb_priority = skb_priority;
     } else {
         /* Couldn't translate queue to a priority.  Nothing to do.  A warning
@@ -6526,7 +6811,7 @@ packet_out(struct ofproto *ofproto_, struct ofpbuf *packet,
     ofpbuf_use_stub(&odp_actions,
                     odp_actions_stub, sizeof odp_actions_stub);
     xlate_actions(&ctx, ofpacts, ofpacts_len, &odp_actions);
-    dpif_execute(ofproto->dpif, key.data, key.size,
+    dpif_execute(ofproto->backer->dpif, key.data, key.size,
                  odp_actions.data, odp_actions.size, packet);
     ofpbuf_uninit(&odp_actions);
 
@@ -6559,7 +6844,7 @@ get_netflow_ids(const struct ofproto *ofproto_,
 {
     struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofproto_);
 
-    dpif_get_netflow_ids(ofproto->dpif, engine_type, engine_id);
+    dpif_get_netflow_ids(ofproto->backer->dpif, engine_type, engine_id);
 }
 
 static void
@@ -6790,6 +7075,7 @@ ofproto_unixctl_trace(struct unixctl_conn *conn, int argc, const char *argv[],
          * you just say "syntax error" or do you present both error messages?
          * Both choices seem lousy. */
         if (strchr(flow_s, '(')) {
+            enum odp_key_fitness fitness;
             int error;
 
             /* Convert string to datapath key. */
@@ -6800,10 +7086,11 @@ ofproto_unixctl_trace(struct unixctl_conn *conn, int argc, const char *argv[],
                 goto exit;
             }
 
+            fitness = odp_flow_key_to_flow(odp_key.data, odp_key.size, &flow);
+
             /* Convert odp_key to flow. */
-            error = ofproto_dpif_extract_flow_key(ofproto, odp_key.data,
-                                                  odp_key.size, &flow,
-                                                  &initial_tci, NULL);
+            error = ofproto_dpif_vsp_adjust(ofproto, fitness, &flow,
+                                            &initial_tci, NULL);
             if (error == ODP_FIT_ERROR) {
                 unixctl_command_reply_error(conn, "Invalid flow");
                 goto exit;
@@ -7064,13 +7351,17 @@ show_dp_format(const struct ofproto_dpif *ofproto, struct ds *ds)
     const struct shash_node **ports;
     int i;
 
-    dpif_get_dp_stats(ofproto->dpif, &s);
+    dpif_get_dp_stats(ofproto->backer->dpif, &s);
 
-    ds_put_format(ds, "%s@%s\n", ofproto->up.type, ofproto->up.name);
+    ds_put_format(ds, "%s (%s)\n", ofproto->up.name,
+                  ofproto->backer->dpif->full_name);
+    /* xxx It would be better to show bridge-specific stats instead
+     * xxx of dp ones. */
     ds_put_format(ds,
                   "\tlookups: hit:%"PRIu64" missed:%"PRIu64" lost:%"PRIu64"\n",
                   s.n_hit, s.n_missed, s.n_lost);
-    ds_put_format(ds, "\tflows: %"PRIu64"\n", s.n_flows);
+    ds_put_format(ds, "\tflows: %"PRIu64"\n",
+                  hmap_count(&ofproto->subfacets));
 
     ports = shash_sort(&ofproto->up.port_by_name);
     for (i = 0; i < shash_count(&ofproto->up.port_by_name); i++) {
@@ -7451,20 +7742,52 @@ ofp_port_to_odp_in_port(const struct ofproto_dpif *ofproto, uint16_t ofp_port)
     return ofp_port_to_odp_port(ofproto, ofp_port);
 }
 
-static uint16_t
-odp_port_to_ofp_port(const struct ofproto_dpif *ofproto, uint32_t odp_port)
+static struct ofport_dpif *
+odp_port_to_ofport(const struct dpif_backer *backer, uint32_t odp_port)
 {
     struct ofport_dpif *port;
 
     HMAP_FOR_EACH_IN_BUCKET (port, hmap_node,
                              hash_int(odp_port, 0),
-                             &ofproto->odp_to_ofport_map) {
+                             &backer->odp_to_ofport_map) {
         if (port->odp_port == odp_port) {
-            return port->up.ofp_port;
+            return port;
         }
     }
 
-    return OFPP_NONE;
+    return NULL;
+}
+
+static uint16_t
+odp_port_to_ofp_port(const struct ofproto_dpif *ofproto, uint32_t odp_port)
+{
+    struct ofport_dpif *port;
+
+    port = odp_port_to_ofport(ofproto->backer, odp_port);
+    if (port && ofproto == ofproto_dpif_cast(port->up.ofproto)) {
+        return port->up.ofp_port;
+    } else {
+        return OFPP_NONE;
+    }
+}
+
+static struct ofproto_dpif *
+odp_port_to_ofproto_dpif(const struct dpif *dpif, uint32_t odp_port)
+{
+    struct dpif_backer *backer;
+    struct ofport_dpif *port;
+
+    backer = shash_find_data(&all_dpif_backers, dpif->dpif_class->type);
+    if (!backer) {
+        return NULL;
+    }
+
+    port = odp_port_to_ofport(backer, odp_port);
+    if (!port) {
+        return NULL;
+    }
+
+    return ofproto_dpif_cast(port->up.ofproto);
 }
 
 const struct ofproto_class ofproto_dpif_class = {
@@ -7472,8 +7795,8 @@ const struct ofproto_class ofproto_dpif_class = {
     enumerate_types,
     enumerate_names,
     del,
-    NULL,                       /* type_run */
-    NULL,                       /* type_run_fast */
+    type_run,
+    type_run_fast,
     alloc,
     construct,
     destruct,
diff --git a/ofproto/ofproto-provider.h b/ofproto/ofproto-provider.h
index db53939..485d216 100644
--- a/ofproto/ofproto-provider.h
+++ b/ofproto/ofproto-provider.h
@@ -644,11 +644,9 @@ struct ofproto_class {
      *
      * The client might not be entirely in control of the ports within an
      * ofproto.  Some hardware implementations, for example, might have a fixed
-     * set of ports in a datapath, and the Linux datapath allows the system
-     * administrator to externally add and remove ports with ovs-dpctl.  For
-     * this reason, the client needs a way to iterate through all the ports
-     * that are actually in a datapath.  These functions provide that
-     * functionality.
+     * set of ports in a datapath For this reason, the client needs a way to
+     * iterate through all the ports that are actually in a datapath.  These
+     * functions provide that functionality.
      *
      * The 'state' pointer provides the implementation a place to
      * keep track of its position.  Its format is opaque to the caller.
diff --git a/tests/ofproto-dpif.at b/tests/ofproto-dpif.at
index 05ba30a..3b5e77d 100644
--- a/tests/ofproto-dpif.at
+++ b/tests/ofproto-dpif.at
@@ -1211,13 +1211,13 @@ ADD_OF_PORTS([test-br0], [1], [2])
 ADD_OF_PORTS([test-br1], [3])
 
 AT_CHECK([ovs-appctl dpif/show], [0], [dnl
-dummy at test-br0
+test-br0 (dummy at ovs-datapath)
 	lookups: hit:0 missed:0 lost:0
 	flows: 0
 	p1 1/1: (dummy)
 	p2 2/2: (dummy)
 	test-br0 65534/100: (dummy)
-dummy at test-br1
+test-br1 (dummy at ovs-datapath)
 	lookups: hit:0 missed:0 lost:0
 	flows: 0
 	p3 3/3: (dummy)
@@ -1225,7 +1225,7 @@ dummy at test-br1
 ])
 
 AT_CHECK([ovs-appctl dpif/show test-br0], [0], [dnl
-dummy at test-br0
+test-br0 (dummy at ovs-datapath)
 	lookups: hit:0 missed:0 lost:0
 	flows: 0
 	p1 1/1: (dummy)
diff --git a/vswitchd/bridge.c b/vswitchd/bridge.c
index 5e2b7af..16f3a46 100644
--- a/vswitchd/bridge.c
+++ b/vswitchd/bridge.c
@@ -3136,11 +3136,14 @@ static const char *
 iface_get_type(const struct ovsrec_interface *iface,
                const struct ovsrec_bridge *br)
 {
-    /* The local port always has type "internal".  Other ports take their type
-     * from the database and default to "system" if none is specified. */
-    return (!strcmp(iface->name, br->name) ? "internal"
-            : iface->type[0] ? iface->type
-            : "system");
+    /* The local port always has type "internal" unless the bridge is of
+     * type "dummy".  Other ports take their type from the database and
+     * default to "system" if none is specified. */
+    if (!strcmp(iface->name, br->name)) {
+        return !strcmp(br->datapath_type, "dummy") ? "dummy" : "internal";
+    } else {
+        return iface->type[0] ? iface->type : "system";
+    }
 }
 
 static void
-- 
1.7.5.4




More information about the dev mailing list