When ovs-vswitchd exits without --cleanup, kernel datapath state can
outlive the daemon.  If the next configuration uses no bridge of that
datapath type, nothing reopens the backer and the orphan persists.

After each bridge_reconfigure(), delete enumerated datapaths for types
that no configured bridge uses and that ovs-vswitchd is not already
managing.  Kernel datapaths are preserved across restarts that use
flow-restore-wait when the database still lists system bridges.

Signed-off-by: Eli Britstein <[email protected]>
---
 ofproto/ofproto-dpif.c | 43 ++++++++++++++++++++++++++++++++++++++++++
 ofproto/ofproto.h      |  1 +
 vswitchd/bridge.c      | 24 +++++++++++++++++++++++
 3 files changed, 68 insertions(+)

diff --git a/ofproto/ofproto-dpif.c b/ofproto/ofproto-dpif.c
index 729bc6f1d..3c5bdbe99 100644
--- a/ofproto/ofproto-dpif.c
+++ b/ofproto/ofproto-dpif.c
@@ -730,6 +730,49 @@ close_dpif_backer(struct dpif_backer *backer, bool del)
     free(backer);
 }
 
+/* Delete datapaths for types not used by any bridge in the database and not
+ * actively managed by ovs-vswitchd.  This removes orphan kernel datapath state
+ * while preserving datapaths for types still configured (e.g. kernel restart
+ * with flow-restore-wait). */
+void
+ofproto_delete_unused_datapaths(const struct sset *types_in_use)
+{
+    struct sset all_types;
+    const char *type;
+
+    sset_init(&all_types);
+    dp_enumerate_types(&all_types);
+
+    SSET_FOR_EACH (type, &all_types) {
+        struct sset names;
+        const char *name;
+
+        if (sset_contains(types_in_use, type)
+            || shash_find(&all_dpif_backers, type)) {
+            continue;
+        }
+
+        sset_init(&names);
+        if (!dp_enumerate_names(type, &names)) {
+            SSET_FOR_EACH (name, &names) {
+                int error;
+
+                error = del(type, name);
+                if (error) {
+                    VLOG_WARN("failed to delete unused datapath %s@%s: %s",
+                              type, name, ovs_strerror(error));
+                } else {
+                    VLOG_INFO("deleted unused datapath %s@%s", type, name);
+                }
+            }
+        }
+
+        sset_destroy(&names);
+    }
+
+    sset_destroy(&all_types);
+}
+
 static void check_support(struct dpif_backer *backer);
 static void copy_support(struct dpif_backer_support *dst,
                          struct dpif_backer_support *src);
diff --git a/ofproto/ofproto.h b/ofproto/ofproto.h
index b1cb8fb33..0cc87e3cf 100644
--- a/ofproto/ofproto.h
+++ b/ofproto/ofproto.h
@@ -280,6 +280,7 @@ int ofproto_create(const char *datapath, const char 
*datapath_type,
     OVS_EXCLUDED(ofproto_mutex);
 void ofproto_destroy(struct ofproto *, bool del);
 int ofproto_delete(const char *name, const char *type);
+void ofproto_delete_unused_datapaths(const struct sset *types_in_use);
 
 int ofproto_run(struct ofproto *);
 void ofproto_wait(struct ofproto *);
diff --git a/vswitchd/bridge.c b/vswitchd/bridge.c
index c00abd295..e7aec5550 100644
--- a/vswitchd/bridge.c
+++ b/vswitchd/bridge.c
@@ -263,6 +263,8 @@ static uint64_t last_ifaces_changed;
 #define BRIDGE_CONTROLLER_PACKET_QUEUE_MAX_SIZE 512
 
 static void add_del_bridges(const struct ovsrec_open_vswitch *);
+static void collect_datapath_types(const struct ovsrec_open_vswitch *,
+                                   struct sset *);
 static void bridge_run__(void);
 static void bridge_create(const struct ovsrec_bridge *);
 static void bridge_destroy(struct bridge *, bool del);
@@ -862,6 +864,7 @@ datapath_reconfigure(const struct ovsrec_open_vswitch *cfg)
 static void
 bridge_reconfigure(const struct ovsrec_open_vswitch *ovs_cfg)
 {
+    static bool unused_datapaths_deleted = false;
     struct sockaddr_in *managers;
     struct bridge *br;
     int sflow_bridge_number;
@@ -1003,6 +1006,16 @@ bridge_reconfigure(const struct ovsrec_open_vswitch 
*ovs_cfg)
     }
     free(managers);
 
+    if (!unused_datapaths_deleted) {
+        struct sset datapath_types;
+
+        sset_init(&datapath_types);
+        collect_datapath_types(ovs_cfg, &datapath_types);
+        ofproto_delete_unused_datapaths(&datapath_types);
+        sset_destroy(&datapath_types);
+        unused_datapaths_deleted = true;
+    }
+
     /* The ofproto-dpif provider does some final reconfiguration in its
      * ->type_run() function.  We have to call it before notifying the database
      * client that reconfiguration is complete, otherwise there is a very
@@ -2104,6 +2117,17 @@ port_is_bond_fake_iface(const struct port *port)
     return port->cfg->bond_fake_iface && !ovs_list_is_short(&port->ifaces);
 }
 
+static void
+collect_datapath_types(const struct ovsrec_open_vswitch *cfg,
+                       struct sset *types)
+{
+    for (size_t i = 0; i < cfg->n_bridges; i++) {
+        const struct ovsrec_bridge *br_cfg = cfg->bridges[i];
+
+        sset_add(types, ofproto_normalize_type(br_cfg->datapath_type));
+    }
+}
+
 static void
 add_del_bridges(const struct ovsrec_open_vswitch *cfg)
 {
-- 
2.43.0

_______________________________________________
dev mailing list
[email protected]
https://mail.openvswitch.org/mailman/listinfo/ovs-dev

Reply via email to