ofproto: Optimise OpenFlow flow expiry
[sliver-openvswitch.git] / ofproto / ofproto-dpif.c
index b37b482..1d08baf 100644 (file)
@@ -36,6 +36,7 @@
 #include "mac-learning.h"
 #include "meta-flow.h"
 #include "multipath.h"
+#include "netdev-vport.h"
 #include "netdev.h"
 #include "netlink.h"
 #include "nx-match.h"
@@ -295,6 +296,8 @@ static void xlate_actions(struct action_xlate_ctx *,
 static void xlate_actions_for_side_effects(struct action_xlate_ctx *,
                                            const struct ofpact *ofpacts,
                                            size_t ofpacts_len);
+static void xlate_table_action(struct action_xlate_ctx *, uint16_t in_port,
+                               uint8_t table_id, bool may_packet_in);
 
 static size_t put_userspace_action(const struct ofproto_dpif *,
                                    struct ofpbuf *odp_actions,
@@ -619,6 +622,10 @@ struct dpif_backer {
     struct dpif *dpif;
     struct timer next_expiration;
     struct hmap odp_to_ofport_map; /* ODP port to ofport mapping. */
+
+    /* Facet revalidation flags applying to facets which use this backer. */
+    enum revalidate_reason need_revalidate; /* Revalidate every facet. */
+    struct tag_set revalidate_set; /* Revalidate only matching facets. */
 };
 
 /* All existing ofproto_backer instances, indexed by ofproto->up.type. */
@@ -655,8 +662,6 @@ struct ofproto_dpif {
 
     /* Revalidation. */
     struct table_dpif tables[N_TABLES];
-    enum revalidate_reason need_revalidate;
-    struct tag_set revalidate_set;
 
     /* Support for debugging async flow mods. */
     struct list completions;
@@ -674,7 +679,8 @@ struct ofproto_dpif {
     struct hmap vlandev_map;     /* vlandev -> (realdev,vid). */
 
     /* Ports. */
-    struct sset ports;             /* Set of port names. */
+    struct sset ports;             /* Set of standard port names. */
+    struct sset ghost_ports;       /* Ports with no datapath port. */
     struct sset port_poll_set;     /* Queued names for port_poll() reply. */
     int port_poll_errno;           /* Last errno for port_poll() reply. */
 };
@@ -824,6 +830,41 @@ type_run(const char *type)
 
     dpif_run(backer->dpif);
 
+    if (backer->need_revalidate
+        || !tag_set_is_empty(&backer->revalidate_set)) {
+        struct tag_set revalidate_set = backer->revalidate_set;
+        bool need_revalidate = backer->need_revalidate;
+        struct ofproto_dpif *ofproto;
+
+        switch (backer->need_revalidate) {
+        case REV_RECONFIGURE:   COVERAGE_INC(rev_reconfigure);   break;
+        case REV_STP:           COVERAGE_INC(rev_stp);           break;
+        case REV_PORT_TOGGLED:  COVERAGE_INC(rev_port_toggled);  break;
+        case REV_FLOW_TABLE:    COVERAGE_INC(rev_flow_table);    break;
+        case REV_INCONSISTENCY: COVERAGE_INC(rev_inconsistency); break;
+        }
+
+        HMAP_FOR_EACH (ofproto, all_ofproto_dpifs_node, &all_ofproto_dpifs) {
+            struct facet *facet;
+
+            if (ofproto->backer != backer) {
+                continue;
+            }
+
+            /* Clear the revalidation flags. */
+            tag_set_init(&backer->revalidate_set);
+            backer->need_revalidate = 0;
+
+            HMAP_FOR_EACH (facet, hmap_node, &ofproto->facets) {
+                if (need_revalidate
+                    || tag_set_intersects(&revalidate_set, facet->tags)) {
+                    facet_revalidate(facet);
+                }
+            }
+        }
+
+    }
+
     if (timer_expired(&backer->next_expiration)) {
         int delay = expire(backer);
         timer_set_duration(&backer->next_expiration, delay);
@@ -1022,6 +1063,7 @@ open_dpif_backer(const char *type, struct dpif_backer **backerp)
     if (error) {
         VLOG_ERR("failed to open datapath of type %s: %s", type,
                  strerror(error));
+        free(backer);
         return error;
     }
 
@@ -1029,6 +1071,8 @@ open_dpif_backer(const char *type, struct dpif_backer **backerp)
     backer->refcount = 1;
     hmap_init(&backer->odp_to_ofport_map);
     timer_set_duration(&backer->next_expiration, 1000);
+    backer->need_revalidate = 0;
+    tag_set_init(&backer->revalidate_set);
     *backerp = backer;
 
     dpif_flow_flush(backer->dpif);
@@ -1106,8 +1150,6 @@ construct(struct ofproto *ofproto_)
         table->other_table = NULL;
         table->basis = random_uint32();
     }
-    ofproto->need_revalidate = 0;
-    tag_set_init(&ofproto->revalidate_set);
 
     list_init(&ofproto->completions);
 
@@ -1120,6 +1162,7 @@ construct(struct ofproto *ofproto_)
     hmap_init(&ofproto->realdev_vid_map);
 
     sset_init(&ofproto->ports);
+    sset_init(&ofproto->ghost_ports);
     sset_init(&ofproto->port_poll_set);
     ofproto->port_poll_errno = 0;
 
@@ -1264,6 +1307,7 @@ destruct(struct ofproto *ofproto_)
     hmap_destroy(&ofproto->realdev_vid_map);
 
     sset_destroy(&ofproto->ports);
+    sset_destroy(&ofproto->ghost_ports);
     sset_destroy(&ofproto->port_poll_set);
 
     close_dpif_backer(ofproto->backer);
@@ -1316,44 +1360,19 @@ run(struct ofproto *ofproto_)
     }
 
     stp_run(ofproto);
-    mac_learning_run(ofproto->ml, &ofproto->revalidate_set);
-
-    /* Now revalidate if there's anything to do. */
-    if (ofproto->need_revalidate
-        || !tag_set_is_empty(&ofproto->revalidate_set)) {
-        struct tag_set revalidate_set = ofproto->revalidate_set;
-        bool revalidate_all = ofproto->need_revalidate;
-        struct facet *facet;
-
-        switch (ofproto->need_revalidate) {
-        case REV_RECONFIGURE:   COVERAGE_INC(rev_reconfigure);   break;
-        case REV_STP:           COVERAGE_INC(rev_stp);           break;
-        case REV_PORT_TOGGLED:  COVERAGE_INC(rev_port_toggled);  break;
-        case REV_FLOW_TABLE:    COVERAGE_INC(rev_flow_table);    break;
-        case REV_INCONSISTENCY: COVERAGE_INC(rev_inconsistency); break;
-        }
-
-        /* Clear the revalidation flags. */
-        tag_set_init(&ofproto->revalidate_set);
-        ofproto->need_revalidate = 0;
-
-        HMAP_FOR_EACH (facet, hmap_node, &ofproto->facets) {
-            if (revalidate_all
-                || tag_set_intersects(&revalidate_set, facet->tags)) {
-                facet_revalidate(facet);
-            }
-        }
-    }
+    mac_learning_run(ofproto->ml, &ofproto->backer->revalidate_set);
 
     /* Check the consistency of a random facet, to aid debugging. */
-    if (!hmap_is_empty(&ofproto->facets) && !ofproto->need_revalidate) {
+    if (!hmap_is_empty(&ofproto->facets)
+        && !ofproto->backer->need_revalidate) {
         struct facet *facet;
 
         facet = CONTAINER_OF(hmap_random_node(&ofproto->facets),
                              struct facet, hmap_node);
-        if (!tag_set_intersects(&ofproto->revalidate_set, facet->tags)) {
+        if (!tag_set_intersects(&ofproto->backer->revalidate_set,
+                                facet->tags)) {
             if (!facet_check_consistency(facet)) {
-                ofproto->need_revalidate = REV_INCONSISTENCY;
+                ofproto->backer->need_revalidate = REV_INCONSISTENCY;
             }
         }
     }
@@ -1395,7 +1414,7 @@ wait(struct ofproto *ofproto_)
     if (ofproto->sflow) {
         dpif_sflow_wait(ofproto->sflow);
     }
-    if (!tag_set_is_empty(&ofproto->revalidate_set)) {
+    if (!tag_set_is_empty(&ofproto->backer->revalidate_set)) {
         poll_immediate_wake();
     }
     HMAP_FOR_EACH (ofport, up.hmap_node, &ofproto->up.ports) {
@@ -1409,7 +1428,7 @@ wait(struct ofproto *ofproto_)
     }
     mac_learning_wait(ofproto->ml);
     stp_wait(ofproto);
-    if (ofproto->need_revalidate) {
+    if (ofproto->backer->need_revalidate) {
         /* Shouldn't happen, but if it does just go around again. */
         VLOG_DBG_RL(&rl, "need revalidate in ofproto_wait_cb()");
         poll_immediate_wake();
@@ -1510,7 +1529,7 @@ port_construct(struct ofport *port_)
     struct dpif_port dpif_port;
     int error;
 
-    ofproto->need_revalidate = REV_RECONFIGURE;
+    ofproto->backer->need_revalidate = REV_RECONFIGURE;
     port->bundle = NULL;
     port->cfm = NULL;
     port->tag = tag_create_random();
@@ -1522,6 +1541,12 @@ port_construct(struct ofport *port_)
     port->vlandev_vid = 0;
     port->carrier_seq = netdev_get_carrier_resets(port->up.netdev);
 
+    if (netdev_vport_is_patch(port->up.netdev)) {
+        /* XXX By bailing out here, we don't do required sFlow work. */
+        port->odp_port = OVSP_NONE;
+        return 0;
+    }
+
     error = dpif_port_query_by_name(ofproto->backer->dpif,
                                     netdev_get_name(port->up.netdev),
                                     &dpif_port);
@@ -1564,9 +1589,13 @@ port_destruct(struct ofport *port_)
         dpif_port_del(ofproto->backer->dpif, port->odp_port);
     }
 
+    if (port->odp_port != OVSP_NONE) {
+        hmap_remove(&ofproto->backer->odp_to_ofport_map, &port->odp_port_node);
+    }
+
     sset_find_and_delete(&ofproto->ports, devname);
-    hmap_remove(&ofproto->backer->odp_to_ofport_map, &port->odp_port_node);
-    ofproto->need_revalidate = REV_RECONFIGURE;
+    sset_find_and_delete(&ofproto->ghost_ports, devname);
+    ofproto->backer->need_revalidate = REV_RECONFIGURE;
     bundle_remove(port_);
     set_cfm(port_, NULL);
     if (ofproto->sflow) {
@@ -1597,7 +1626,7 @@ port_reconfigured(struct ofport *port_, enum ofputil_port_config old_config)
     if (changed & (OFPUTIL_PC_NO_RECV | OFPUTIL_PC_NO_RECV_STP |
                    OFPUTIL_PC_NO_FWD | OFPUTIL_PC_NO_FLOOD |
                    OFPUTIL_PC_NO_PACKET_IN)) {
-        ofproto->need_revalidate = REV_RECONFIGURE;
+        ofproto->backer->need_revalidate = REV_RECONFIGURE;
 
         if (changed & OFPUTIL_PC_NO_FLOOD && port->bundle) {
             bundle_update(port->bundle);
@@ -1620,13 +1649,13 @@ set_sflow(struct ofproto *ofproto_,
             HMAP_FOR_EACH (ofport, up.hmap_node, &ofproto->up.ports) {
                 dpif_sflow_add_port(ds, &ofport->up, ofport->odp_port);
             }
-            ofproto->need_revalidate = REV_RECONFIGURE;
+            ofproto->backer->need_revalidate = REV_RECONFIGURE;
         }
         dpif_sflow_set_options(ds, sflow_options);
     } else {
         if (ds) {
             dpif_sflow_destroy(ds);
-            ofproto->need_revalidate = REV_RECONFIGURE;
+            ofproto->backer->need_revalidate = REV_RECONFIGURE;
             ofproto->sflow = NULL;
         }
     }
@@ -1646,7 +1675,7 @@ set_cfm(struct ofport *ofport_, const struct cfm_settings *s)
             struct ofproto_dpif *ofproto;
 
             ofproto = ofproto_dpif_cast(ofport->up.ofproto);
-            ofproto->need_revalidate = REV_RECONFIGURE;
+            ofproto->backer->need_revalidate = REV_RECONFIGURE;
             ofport->cfm = cfm_create(netdev_get_name(ofport->up.netdev));
         }
 
@@ -1734,7 +1763,7 @@ set_stp(struct ofproto *ofproto_, const struct ofproto_stp_settings *s)
 
     /* Only revalidate flows if the configuration changed. */
     if (!s != !ofproto->stp) {
-        ofproto->need_revalidate = REV_RECONFIGURE;
+        ofproto->backer->need_revalidate = REV_RECONFIGURE;
     }
 
     if (s) {
@@ -1802,12 +1831,13 @@ update_stp_port_state(struct ofport_dpif *ofport)
         if (stp_learn_in_state(ofport->stp_state)
                 != stp_learn_in_state(state)) {
             /* xxx Learning action flows should also be flushed. */
-            mac_learning_flush(ofproto->ml, &ofproto->revalidate_set);
+            mac_learning_flush(ofproto->ml,
+                               &ofproto->backer->revalidate_set);
         }
         fwd_change = stp_forward_in_state(ofport->stp_state)
                         != stp_forward_in_state(state);
 
-        ofproto->need_revalidate = REV_STP;
+        ofproto->backer->need_revalidate = REV_STP;
         ofport->stp_state = state;
         ofport->stp_state_entered = time_msec();
 
@@ -1907,7 +1937,7 @@ stp_run(struct ofproto_dpif *ofproto)
         }
 
         if (stp_check_and_reset_fdb_flush(ofproto->stp)) {
-            mac_learning_flush(ofproto->ml, &ofproto->revalidate_set);
+            mac_learning_flush(ofproto->ml, &ofproto->backer->revalidate_set);
         }
     }
 }
@@ -2005,12 +2035,12 @@ set_queues(struct ofport *ofport_,
             pdscp = xmalloc(sizeof *pdscp);
             pdscp->priority = priority;
             pdscp->dscp = dscp;
-            ofproto->need_revalidate = REV_RECONFIGURE;
+            ofproto->backer->need_revalidate = REV_RECONFIGURE;
         }
 
         if (pdscp->dscp != dscp) {
             pdscp->dscp = dscp;
-            ofproto->need_revalidate = REV_RECONFIGURE;
+            ofproto->backer->need_revalidate = REV_RECONFIGURE;
         }
 
         hmap_insert(&new, &pdscp->hmap_node, hash_int(pdscp->priority, 0));
@@ -2018,7 +2048,7 @@ set_queues(struct ofport *ofport_,
 
     if (!hmap_is_empty(&ofport->priorities)) {
         ofport_clear_priorities(ofport);
-        ofproto->need_revalidate = REV_RECONFIGURE;
+        ofproto->backer->need_revalidate = REV_RECONFIGURE;
     }
 
     hmap_swap(&new, &ofport->priorities);
@@ -2045,7 +2075,7 @@ bundle_flush_macs(struct ofbundle *bundle, bool all_ofprotos)
     struct mac_learning *ml = ofproto->ml;
     struct mac_entry *mac, *next_mac;
 
-    ofproto->need_revalidate = REV_RECONFIGURE;
+    ofproto->backer->need_revalidate = REV_RECONFIGURE;
     LIST_FOR_EACH_SAFE (mac, next_mac, lru_node, &ml->lrus) {
         if (mac->port.p == bundle) {
             if (all_ofprotos) {
@@ -2058,7 +2088,6 @@ bundle_flush_macs(struct ofbundle *bundle, bool all_ofprotos)
                         e = mac_learning_lookup(o->ml, mac->mac, mac->vlan,
                                                 NULL);
                         if (e) {
-                            tag_set_add(&o->revalidate_set, e->tag);
                             mac_learning_expire(o->ml, e);
                         }
                     }
@@ -2122,7 +2151,7 @@ bundle_del_port(struct ofport_dpif *port)
 {
     struct ofbundle *bundle = port->bundle;
 
-    bundle->ofproto->need_revalidate = REV_RECONFIGURE;
+    bundle->ofproto->backer->need_revalidate = REV_RECONFIGURE;
 
     list_remove(&port->bundle_node);
     port->bundle = NULL;
@@ -2150,7 +2179,7 @@ bundle_add_port(struct ofbundle *bundle, uint32_t ofp_port,
     }
 
     if (port->bundle != bundle) {
-        bundle->ofproto->need_revalidate = REV_RECONFIGURE;
+        bundle->ofproto->backer->need_revalidate = REV_RECONFIGURE;
         if (port->bundle) {
             bundle_del_port(port);
         }
@@ -2163,7 +2192,7 @@ bundle_add_port(struct ofbundle *bundle, uint32_t ofp_port,
         }
     }
     if (lacp) {
-        port->bundle->ofproto->need_revalidate = REV_RECONFIGURE;
+        bundle->ofproto->backer->need_revalidate = REV_RECONFIGURE;
         lacp_slave_register(bundle->lacp, port, lacp);
     }
 
@@ -2191,7 +2220,7 @@ bundle_destroy(struct ofbundle *bundle)
                 mirror_destroy(m);
             } else if (hmapx_find_and_delete(&m->srcs, bundle)
                        || hmapx_find_and_delete(&m->dsts, bundle)) {
-                ofproto->need_revalidate = REV_RECONFIGURE;
+                ofproto->backer->need_revalidate = REV_RECONFIGURE;
             }
         }
     }
@@ -2263,7 +2292,7 @@ bundle_set(struct ofproto *ofproto_, void *aux,
     /* LACP. */
     if (s->lacp) {
         if (!bundle->lacp) {
-            ofproto->need_revalidate = REV_RECONFIGURE;
+            ofproto->backer->need_revalidate = REV_RECONFIGURE;
             bundle->lacp = lacp_create();
         }
         lacp_configure(bundle->lacp, s->lacp);
@@ -2369,11 +2398,11 @@ bundle_set(struct ofproto *ofproto_, void *aux,
         bundle->ofproto->has_bonded_bundles = true;
         if (bundle->bond) {
             if (bond_reconfigure(bundle->bond, s->bond)) {
-                ofproto->need_revalidate = REV_RECONFIGURE;
+                ofproto->backer->need_revalidate = REV_RECONFIGURE;
             }
         } else {
             bundle->bond = bond_create(s->bond);
-            ofproto->need_revalidate = REV_RECONFIGURE;
+            ofproto->backer->need_revalidate = REV_RECONFIGURE;
         }
 
         LIST_FOR_EACH (port, bundle_node, &bundle->ports) {
@@ -2493,7 +2522,7 @@ bundle_run(struct ofbundle *bundle)
             bond_slave_set_may_enable(bundle->bond, port, port->may_enable);
         }
 
-        bond_run(bundle->bond, &bundle->ofproto->revalidate_set,
+        bond_run(bundle->bond, &bundle->ofproto->backer->revalidate_set,
                  lacp_status(bundle->lacp));
         if (bond_should_send_learning_packets(bundle->bond)) {
             bundle_send_learning_packets(bundle);
@@ -2678,9 +2707,10 @@ mirror_set(struct ofproto *ofproto_, void *aux,
         }
     }
 
-    ofproto->need_revalidate = REV_RECONFIGURE;
+    ofproto->backer->need_revalidate = REV_RECONFIGURE;
     ofproto->has_mirrors = true;
-    mac_learning_flush(ofproto->ml, &ofproto->revalidate_set);
+    mac_learning_flush(ofproto->ml,
+                       &ofproto->backer->revalidate_set);
     mirror_update_dups(ofproto);
 
     return 0;
@@ -2699,8 +2729,8 @@ mirror_destroy(struct ofmirror *mirror)
     }
 
     ofproto = mirror->ofproto;
-    ofproto->need_revalidate = REV_RECONFIGURE;
-    mac_learning_flush(ofproto->ml, &ofproto->revalidate_set);
+    ofproto->backer->need_revalidate = REV_RECONFIGURE;
+    mac_learning_flush(ofproto->ml, &ofproto->backer->revalidate_set);
 
     mirror_bit = MIRROR_MASK_C(1) << mirror->idx;
     HMAP_FOR_EACH (bundle, hmap_node, &ofproto->bundles) {
@@ -2751,7 +2781,7 @@ set_flood_vlans(struct ofproto *ofproto_, unsigned long *flood_vlans)
 {
     struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofproto_);
     if (mac_learning_set_flood_vlans(ofproto->ml, flood_vlans)) {
-        mac_learning_flush(ofproto->ml, &ofproto->revalidate_set);
+        mac_learning_flush(ofproto->ml, &ofproto->backer->revalidate_set);
     }
     return 0;
 }
@@ -2768,7 +2798,7 @@ static void
 forward_bpdu_changed(struct ofproto *ofproto_)
 {
     struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofproto_);
-    ofproto->need_revalidate = REV_RECONFIGURE;
+    ofproto->backer->need_revalidate = REV_RECONFIGURE;
 }
 
 static void
@@ -2806,6 +2836,28 @@ ofproto_port_from_dpif_port(struct ofproto_dpif *ofproto,
     ofproto_port->ofp_port = odp_port_to_ofp_port(ofproto, dpif_port->port_no);
 }
 
+static struct ofport_dpif *
+ofport_get_peer(const struct ofport_dpif *ofport_dpif)
+{
+    const struct ofproto_dpif *ofproto;
+    const char *peer;
+
+    peer = netdev_vport_patch_peer(ofport_dpif->up.netdev);
+    if (!peer) {
+        return NULL;
+    }
+
+    HMAP_FOR_EACH (ofproto, all_ofproto_dpifs_node, &all_ofproto_dpifs) {
+        struct ofport *ofport;
+
+        ofport = shash_find_data(&ofproto->up.port_by_name, peer);
+        if (ofport && ofport->ofproto->ofproto_class == &ofproto_dpif_class) {
+            return ofport_dpif_cast(ofport);
+        }
+    }
+    return NULL;
+}
+
 static void
 port_run_fast(struct ofport_dpif *ofport)
 {
@@ -2851,7 +2903,7 @@ port_run(struct ofport_dpif *ofport)
         struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofport->up.ofproto);
 
         if (ofproto->has_bundle_action) {
-            ofproto->need_revalidate = REV_PORT_TOGGLED;
+            ofproto->backer->need_revalidate = REV_PORT_TOGGLED;
         }
     }
 
@@ -2874,6 +2926,24 @@ port_query_by_name(const struct ofproto *ofproto_, const char *devname,
     struct dpif_port dpif_port;
     int error;
 
+    if (sset_contains(&ofproto->ghost_ports, devname)) {
+        const char *type = netdev_get_type_from_name(devname);
+
+        /* We may be called before ofproto->up.port_by_name is populated with
+         * the appropriate ofport.  For this reason, we must get the name and
+         * type from the netdev layer directly. */
+        if (type) {
+            const struct ofport *ofport;
+
+            ofport = shash_find_data(&ofproto->up.port_by_name, devname);
+            ofproto_port->ofp_port = ofport ? ofport->ofp_port : OFPP_NONE;
+            ofproto_port->name = xstrdup(devname);
+            ofproto_port->type = xstrdup(type);
+            return 0;
+        }
+        return ENODEV;
+    }
+
     if (!sset_contains(&ofproto->ports, devname)) {
         return ENODEV;
     }
@@ -2892,6 +2962,11 @@ port_add(struct ofproto *ofproto_, struct netdev *netdev)
     uint32_t odp_port = UINT32_MAX;
     int error;
 
+    if (netdev_vport_is_patch(netdev)) {
+        sset_add(&ofproto->ghost_ports, netdev_get_name(netdev));
+        return 0;
+    }
+
     error = dpif_port_add(ofproto->backer->dpif, netdev, &odp_port);
     if (!error) {
         sset_add(&ofproto->ports, netdev_get_name(netdev));
@@ -2982,16 +3057,13 @@ ofproto_update_local_port_stats(const struct ofproto *ofproto_,
 struct port_dump_state {
     uint32_t bucket;
     uint32_t offset;
+    bool ghost;
 };
 
 static int
 port_dump_start(const struct ofproto *ofproto_ OVS_UNUSED, void **statep)
 {
-    struct port_dump_state *state;
-
-    *statep = state = xmalloc(sizeof *state);
-    state->bucket = 0;
-    state->offset = 0;
+    *statep = xzalloc(sizeof(struct port_dump_state));
     return 0;
 }
 
@@ -3001,10 +3073,11 @@ 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_;
+    const struct sset *sset;
     struct sset_node *node;
 
-    while ((node = sset_at_position(&ofproto->ports, &state->bucket,
-                               &state->offset))) {
+    sset = state->ghost ? &ofproto->ghost_ports : &ofproto->ports;
+    while ((node = sset_at_position(sset, &state->bucket, &state->offset))) {
         int error;
 
         error = port_query_by_name(ofproto_, node->name, port);
@@ -3013,6 +3086,13 @@ port_dump_next(const struct ofproto *ofproto_ OVS_UNUSED, void *state_,
         }
     }
 
+    if (!state->ghost) {
+        state->ghost = true;
+        state->bucket = 0;
+        state->offset = 0;
+        return port_dump_next(ofproto_, state_, port);
+    }
+
     return EOF;
 }
 
@@ -3725,8 +3805,7 @@ expire(struct dpif_backer *backer)
     update_stats(backer);
 
     HMAP_FOR_EACH (ofproto, all_ofproto_dpifs_node, &all_ofproto_dpifs) {
-        struct rule_dpif *rule, *next_rule;
-        struct oftable *table;
+        struct rule *rule, *next_rule;
         int dp_max_idle;
 
         if (ofproto->backer != backer) {
@@ -3741,13 +3820,9 @@ expire(struct dpif_backer *backer)
 
         /* Expire OpenFlow flows whose idle_timeout or hard_timeout
          * has passed. */
-        OFPROTO_FOR_EACH_TABLE (table, &ofproto->up) {
-            struct cls_cursor cursor;
-
-            cls_cursor_init(&cursor, &table->cls, NULL);
-            CLS_CURSOR_FOR_EACH_SAFE (rule, next_rule, up.cr, &cursor) {
-                rule_expire(rule);
-            }
+        LIST_FOR_EACH_SAFE (rule, next_rule, expirable,
+                            &ofproto->up.expirable) {
+            rule_expire(rule_dpif_cast(rule));
         }
 
         /* All outstanding data in existing flows has been accounted, so it's a
@@ -3757,7 +3832,7 @@ expire(struct dpif_backer *backer)
 
             HMAP_FOR_EACH (bundle, hmap_node, &ofproto->bundles) {
                 if (bundle->bond) {
-                    bond_rebalance(bundle->bond, &ofproto->revalidate_set);
+                    bond_rebalance(bundle->bond, &backer->revalidate_set);
                 }
             }
         }
@@ -4089,9 +4164,7 @@ facet_free(struct facet *facet)
 }
 
 /* Executes, within 'ofproto', the 'n_actions' actions in 'actions' on
- * 'packet', which arrived on 'in_port'.
- *
- * Takes ownership of 'packet'. */
+ * 'packet', which arrived on 'in_port'. */
 static bool
 execute_odp_actions(struct ofproto_dpif *ofproto, const struct flow *flow,
                     const struct nlattr *odp_actions, size_t actions_len,
@@ -4107,8 +4180,6 @@ execute_odp_actions(struct ofproto_dpif *ofproto, const struct flow *flow,
 
     error = dpif_execute(ofproto->backer->dpif, key.data, key.size,
                          odp_actions, actions_len, packet);
-
-    ofpbuf_delete(packet);
     return !error;
 }
 
@@ -4321,8 +4392,9 @@ facet_lookup_valid(struct ofproto_dpif *ofproto, const struct flow *flow,
 
     facet = facet_find(ofproto, flow, hash);
     if (facet
-        && (ofproto->need_revalidate
-            || tag_set_intersects(&ofproto->revalidate_set, facet->tags))) {
+        && (ofproto->backer->need_revalidate
+            || tag_set_intersects(&ofproto->backer->revalidate_set,
+                                  facet->tags))) {
         facet_revalidate(facet);
     }
 
@@ -5159,11 +5231,10 @@ rule_get_stats(struct rule *rule_, uint64_t *packets, uint64_t *bytes)
     }
 }
 
-static enum ofperr
-rule_execute(struct rule *rule_, const struct flow *flow,
-             struct ofpbuf *packet)
+static void
+rule_dpif_execute(struct rule_dpif *rule, const struct flow *flow,
+                  struct ofpbuf *packet)
 {
-    struct rule_dpif *rule = rule_dpif_cast(rule_);
     struct ofproto_dpif *ofproto = ofproto_dpif_cast(rule->up.ofproto);
 
     struct dpif_flow_stats stats;
@@ -5185,7 +5256,14 @@ rule_execute(struct rule *rule_, const struct flow *flow,
                         odp_actions.size, packet);
 
     ofpbuf_uninit(&odp_actions);
+}
 
+static enum ofperr
+rule_execute(struct rule *rule, const struct flow *flow,
+             struct ofpbuf *packet)
+{
+    rule_dpif_execute(rule_dpif_cast(rule), flow, packet);
+    ofpbuf_delete(packet);
     return 0;
 }
 
@@ -5211,6 +5289,29 @@ send_packet(const struct ofport_dpif *ofport, struct ofpbuf *packet)
     int error;
 
     flow_extract(packet, 0, 0, NULL, OFPP_LOCAL, &flow);
+    if (netdev_vport_is_patch(ofport->up.netdev)) {
+        struct ofproto_dpif *peer_ofproto;
+        struct dpif_flow_stats stats;
+        struct ofport_dpif *peer;
+        struct rule_dpif *rule;
+
+        peer = ofport_get_peer(ofport);
+        if (!peer) {
+            return ENODEV;
+        }
+
+        dpif_flow_stats_extract(&flow, packet, time_msec(), &stats);
+        netdev_vport_patch_inc_tx(ofport->up.netdev, &stats);
+        netdev_vport_patch_inc_rx(peer->up.netdev, &stats);
+
+        flow.in_port = peer->up.ofp_port;
+        peer_ofproto = ofproto_dpif_cast(peer->up.ofproto);
+        rule = rule_dpif_lookup(peer_ofproto, &flow);
+        rule_dpif_execute(rule, &flow, packet);
+
+        return 0;
+    }
+
     odp_port = vsp_realdev_to_vlandev(ofproto, ofport->odp_port,
                                       flow.vlan_tci);
     if (odp_port != ofport->odp_port) {
@@ -5397,11 +5498,14 @@ compose_output_action__(struct action_xlate_ctx *ctx, uint16_t ofp_port,
                         bool check_stp)
 {
     const struct ofport_dpif *ofport = get_ofp_port(ctx->ofproto, ofp_port);
-    uint32_t odp_port = ofp_port_to_odp_port(ctx->ofproto, ofp_port);
     ovs_be16 flow_vlan_tci = ctx->flow.vlan_tci;
     uint8_t flow_nw_tos = ctx->flow.nw_tos;
     struct priority_to_dscp *pdscp;
-    uint32_t out_port;
+    uint32_t out_port, odp_port;
+
+    /* If 'struct flow' gets additional metadata, we'll need to zero it out
+     * before traversing a patch port. */
+    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 18);
 
     if (!ofport) {
         xlate_report(ctx, "Nonexistent output port");
@@ -5414,12 +5518,46 @@ compose_output_action__(struct action_xlate_ctx *ctx, uint16_t ofp_port,
         return;
     }
 
+    if (netdev_vport_is_patch(ofport->up.netdev)) {
+        struct ofport_dpif *peer = ofport_get_peer(ofport);
+        struct flow old_flow = ctx->flow;
+        const struct ofproto_dpif *peer_ofproto;
+
+        if (!peer) {
+            xlate_report(ctx, "Nonexistent patch port peer");
+            return;
+        }
+
+        peer_ofproto = ofproto_dpif_cast(peer->up.ofproto);
+        if (peer_ofproto->backer != ctx->ofproto->backer) {
+            xlate_report(ctx, "Patch port peer on a different datapath");
+            return;
+        }
+
+        ctx->ofproto = ofproto_dpif_cast(peer->up.ofproto);
+        ctx->flow.in_port = peer->up.ofp_port;
+        ctx->flow.metadata = htonll(0);
+        memset(&ctx->flow.tunnel, 0, sizeof ctx->flow.tunnel);
+        memset(ctx->flow.regs, 0, sizeof ctx->flow.regs);
+        xlate_table_action(ctx, ctx->flow.in_port, 0, true);
+        ctx->flow = old_flow;
+        ctx->ofproto = ofproto_dpif_cast(ofport->up.ofproto);
+
+        if (ctx->resubmit_stats) {
+            netdev_vport_patch_inc_tx(ofport->up.netdev, ctx->resubmit_stats);
+            netdev_vport_patch_inc_rx(peer->up.netdev, ctx->resubmit_stats);
+        }
+
+        return;
+    }
+
     pdscp = get_priority(ofport, ctx->flow.skb_priority);
     if (pdscp) {
         ctx->flow.nw_tos &= ~IP_DSCP_MASK;
         ctx->flow.nw_tos |= pdscp->dscp;
     }
 
+    odp_port = ofp_port_to_odp_port(ctx->ofproto, ofp_port);
     out_port = vsp_realdev_to_vlandev(ctx->ofproto, odp_port,
                                       ctx->flow.vlan_tci);
     if (out_port != odp_port) {
@@ -6614,7 +6752,7 @@ update_learning_table(struct ofproto_dpif *ofproto,
                     in_bundle->name, vlan);
 
         mac->port.p = in_bundle;
-        tag_set_add(&ofproto->revalidate_set,
+        tag_set_add(&ofproto->backer->revalidate_set,
                     mac_learning_changed(ofproto->ml, mac));
     }
 }
@@ -6890,7 +7028,7 @@ table_update_taggable(struct ofproto_dpif *ofproto, uint8_t table_id)
     if (table->catchall_table != catchall || table->other_table != other) {
         table->catchall_table = catchall;
         table->other_table = other;
-        ofproto->need_revalidate = REV_FLOW_TABLE;
+        ofproto->backer->need_revalidate = REV_FLOW_TABLE;
     }
 }
 
@@ -6908,13 +7046,13 @@ rule_invalidate(const struct rule_dpif *rule)
 
     table_update_taggable(ofproto, rule->up.table_id);
 
-    if (!ofproto->need_revalidate) {
+    if (!ofproto->backer->need_revalidate) {
         struct table_dpif *table = &ofproto->tables[rule->up.table_id];
 
         if (table->other_table && rule->tag) {
-            tag_set_add(&ofproto->revalidate_set, rule->tag);
+            tag_set_add(&ofproto->backer->revalidate_set, rule->tag);
         } else {
-            ofproto->need_revalidate = REV_FLOW_TABLE;
+            ofproto->backer->need_revalidate = REV_FLOW_TABLE;
         }
     }
 }
@@ -6924,9 +7062,8 @@ set_frag_handling(struct ofproto *ofproto_,
                   enum ofp_config_flags frag_handling)
 {
     struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofproto_);
-
     if (frag_handling != OFPC_FRAG_REASM) {
-        ofproto->need_revalidate = REV_RECONFIGURE;
+        ofproto->backer->need_revalidate = REV_RECONFIGURE;
         return true;
     } else {
         return false;
@@ -7058,10 +7195,10 @@ ofproto_unixctl_fdb_flush(struct unixctl_conn *conn, int argc,
             unixctl_command_reply_error(conn, "no such bridge");
             return;
         }
-        mac_learning_flush(ofproto->ml, &ofproto->revalidate_set);
+        mac_learning_flush(ofproto->ml, &ofproto->backer->revalidate_set);
     } else {
         HMAP_FOR_EACH (ofproto, all_ofproto_dpifs_node, &all_ofproto_dpifs) {
-            mac_learning_flush(ofproto->ml, &ofproto->revalidate_set);
+            mac_learning_flush(ofproto->ml, &ofproto->backer->revalidate_set);
         }
     }
 
@@ -7429,7 +7566,7 @@ ofproto_dpif_self_check__(struct ofproto_dpif *ofproto, struct ds *reply)
         }
     }
     if (errors) {
-        ofproto->need_revalidate = REV_INCONSISTENCY;
+        ofproto->backer->need_revalidate = REV_INCONSISTENCY;
     }
 
     if (errors) {
@@ -7530,9 +7667,17 @@ show_dp_format(const struct ofproto_dpif *ofproto, struct ds *ds)
         struct ofport *ofport = node->data;
         const char *name = netdev_get_name(ofport->netdev);
         const char *type = netdev_get_type(ofport->netdev);
+        uint32_t odp_port;
+
+        ds_put_format(ds, "\t%s %u/", name, ofport->ofp_port);
+
+        odp_port = ofp_port_to_odp_port(ofproto, ofport->ofp_port);
+        if (odp_port != OVSP_NONE) {
+            ds_put_format(ds, "%"PRIu32":", odp_port);
+        } else {
+            ds_put_cstr(ds, "none:");
+        }
 
-        ds_put_format(ds, "\t%s %u/%u:", name, ofport->ofp_port,
-                      ofp_port_to_odp_port(ofproto, ofport->ofp_port));
         if (strcmp(type, "system")) {
             struct netdev *netdev;
             int error;
@@ -7622,6 +7767,8 @@ ofproto_unixctl_dpif_dump_flows(struct unixctl_conn *conn,
         return;
     }
 
+    update_stats(ofproto->backer);
+
     HMAP_FOR_EACH (subfacet, hmap_node, &ofproto->subfacets) {
         struct odputil_keybuf keybuf;
         struct ofpbuf key;
@@ -7722,7 +7869,7 @@ set_realdev(struct ofport *ofport_, uint16_t realdev_ofp_port, int vid)
         return 0;
     }
 
-    ofproto->need_revalidate = REV_RECONFIGURE;
+    ofproto->backer->need_revalidate = REV_RECONFIGURE;
 
     if (ofport->realdev_ofp_port) {
         vsp_remove(ofport);