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
