From d3d22744a7408ec3d6d9eac255862b5761166c2d Mon Sep 17 00:00:00 2001 From: Ben Pfaff Date: Mon, 6 Jul 2009 11:06:36 -0700 Subject: [PATCH] vswitch: Avoid knowledge of details specific to Linux datapaths. At startup, the vswitch needs to delete datapaths that are not configured by the administrator. Until now this was done by knowing the possible names of Linux datapaths. This commit cleans up by allowing each datapath class to enumerate its existing datapaths and their names. --- lib/dpif-linux.c | 44 ++++++++++++++++++++++++++++++++++++ lib/dpif-netdev.c | 2 ++ lib/dpif-provider.h | 28 +++++++++++++++++++++++ lib/dpif.c | 55 +++++++++++++++++++++++++++++++++++++++++++++ lib/dpif.h | 3 +++ vswitchd/bridge.c | 36 ++++++++++++++++------------- 6 files changed, 152 insertions(+), 16 deletions(-) diff --git a/lib/dpif-linux.c b/lib/dpif-linux.c index 8cc213d5..0973b5d9 100644 --- a/lib/dpif-linux.c +++ b/lib/dpif-linux.c @@ -45,6 +45,10 @@ struct dpif_linux { struct dpif dpif; int fd; + /* Used by dpif_linux_get_all_names(). */ + char *local_ifname; + int minor; + /* Change notification. */ int local_ifindex; /* Ifindex of local port. */ struct svec changed_ports; /* Ports that have changed. */ @@ -81,6 +85,30 @@ dpif_linux_wait(void) linux_netdev_notifier_wait(); } +static int +dpif_linux_enumerate(struct svec *all_dps) +{ + int error; + int i; + + error = 0; + for (i = 0; i < ODP_MAX; i++) { + struct dpif *dpif; + char devname[16]; + int retval; + + sprintf(devname, "dp%d", i); + retval = dpif_open(devname, &dpif); + if (!retval) { + svec_add(all_dps, devname); + dpif_close(dpif); + } else if (retval != ENODEV && !error) { + error = retval; + } + } + return error; +} + static int dpif_linux_open(const char *name UNUSED, char *suffix, bool create, struct dpif **dpifp) @@ -146,10 +174,21 @@ dpif_linux_close(struct dpif *dpif_) struct dpif_linux *dpif = dpif_linux_cast(dpif_); linux_netdev_notifier_unregister(&dpif->port_notifier); svec_destroy(&dpif->changed_ports); + free(dpif->local_ifname); close(dpif->fd); free(dpif); } +static int +dpif_linux_get_all_names(const struct dpif *dpif_, struct svec *all_names) +{ + struct dpif_linux *dpif = dpif_linux_cast(dpif_); + + svec_add_nocopy(all_names, xasprintf("dp%d", dpif->minor)); + svec_add(all_names, dpif->local_ifname); + return 0; +} + static int dpif_linux_delete(struct dpif *dpif_) { @@ -415,8 +454,10 @@ const struct dpif_class dpif_linux_class = { "linux", dpif_linux_run, dpif_linux_wait, + dpif_linux_enumerate, dpif_linux_open, dpif_linux_close, + dpif_linux_get_all_names, dpif_linux_delete, dpif_linux_get_stats, dpif_linux_get_drop_frags, @@ -628,6 +669,7 @@ static int finish_open(struct dpif *dpif_, const char *local_ifname) { struct dpif_linux *dpif = dpif_linux_cast(dpif_); + dpif->local_ifname = strdup(local_ifname); dpif->local_ifindex = if_nametoindex(local_ifname); if (!dpif->local_ifindex) { int error = errno; @@ -679,6 +721,8 @@ open_minor(int minor, struct dpif **dpifp) free(name); dpif->fd = fd; + dpif->local_ifname = NULL; + dpif->minor = minor; dpif->local_ifindex = 0; svec_init(&dpif->changed_ports); *dpifp = &dpif->dpif; diff --git a/lib/dpif-netdev.c b/lib/dpif-netdev.c index 285693ab..cae6d231 100644 --- a/lib/dpif-netdev.c +++ b/lib/dpif-netdev.c @@ -1294,8 +1294,10 @@ const struct dpif_class dpif_netdev_class = { "netdev", dp_netdev_run, dp_netdev_wait, + NULL, /* enumerate */ dpif_netdev_open, dpif_netdev_close, + NULL, /* get_all_names */ dpif_netdev_delete, dpif_netdev_get_stats, dpif_netdev_get_drop_frags, diff --git a/lib/dpif-provider.h b/lib/dpif-provider.h index 5abe5310..bd159b2a 100644 --- a/lib/dpif-provider.h +++ b/lib/dpif-provider.h @@ -70,6 +70,17 @@ struct dpif_class { * to be called. */ void (*wait)(void); + /* Enumerates the names of all known created datapaths, if possible, into + * 'all_dps'. The caller has already initialized 'all_dps' and other dpif + * classes might already have added names to it. + * + * This is used by the vswitch at startup, so that it can delete any + * datapaths that are not configured. + * + * Some kinds of datapaths might not be practically enumerable, in which + * case this function may be a null pointer. */ + int (*enumerate)(struct svec *all_dps); + /* Attempts to open an existing dpif, if 'create' is false, or to open an * existing dpif or create a new one, if 'create' is true. 'name' is the * full dpif name provided by the user, e.g. "udatapath:/var/run/mypath". @@ -85,6 +96,23 @@ struct dpif_class { /* Closes 'dpif' and frees associated memory. */ void (*close)(struct dpif *dpif); + /* Enumerates all names that may be used to open 'dpif' into 'all_names'. + * The Linux datapath, for example, supports opening a datapath both by + * number, e.g. "dp0", and by the name of the datapath's local port. For + * some datapaths, this might be an infinite set (e.g. in a file name, + * slashes may be duplicated any number of times), in which case only the + * names most likely to be used should be enumerated. + * + * The caller has already initialized 'all_names' and might already have + * added some names to it. This function should not disturb any existing + * names in 'all_names'. + * + * If a datapath class does not support multiple names for a datapath, this + * function may be a null pointer. + * + * This is used by the vswitch at startup, */ + int (*get_all_names)(const struct dpif *dpif, struct svec *all_names); + /* Attempts to destroy the dpif underlying 'dpif'. * * If successful, 'dpif' will not be used again except as an argument for diff --git a/lib/dpif.c b/lib/dpif.c index 0e83a9e2..73fa4b98 100644 --- a/lib/dpif.c +++ b/lib/dpif.c @@ -33,6 +33,7 @@ #include "ofpbuf.h" #include "packets.h" #include "poll-loop.h" +#include "svec.h" #include "util.h" #include "valgrind.h" @@ -94,6 +95,34 @@ dp_wait(void) } } +/* Initializes 'all_dps' and enumerates the names of all known created + * datapaths, where possible, into it. Returns 0 if successful, otherwise a + * positive errno value. + * + * Some kinds of datapaths might not be practically enumerable. This is not + * considered an error. */ +int +dp_enumerate(struct svec *all_dps) +{ + int error; + int i; + + svec_init(all_dps); + error = 0; + for (i = 0; i < N_DPIF_CLASSES; i++) { + const struct dpif_class *class = dpif_classes[i]; + int retval = class->enumerate ? class->enumerate(all_dps) : 0; + if (retval) { + VLOG_WARN("failed to enumerate %s datapaths: %s", + class->name, strerror(retval)); + if (!error) { + error = retval; + } + } + } + return error; +} + static int do_open(const char *name_, bool create, struct dpif **dpifp) { @@ -166,6 +195,32 @@ dpif_name(const struct dpif *dpif) return dpif->name; } +/* Enumerates all names that may be used to open 'dpif' into 'all_names'. The + * Linux datapath, for example, supports opening a datapath both by number, + * e.g. "dp0", and by the name of the datapath's local port. For some + * datapaths, this might be an infinite set (e.g. in a file name, slashes may + * be duplicated any number of times), in which case only the names most likely + * to be used will be enumerated. + * + * The caller must already have initialized 'all_names'. Any existing names in + * 'all_names' will not be disturbed. */ +int +dpif_get_all_names(const struct dpif *dpif, struct svec *all_names) +{ + if (dpif->class->get_all_names) { + int error = dpif->class->get_all_names(dpif, all_names); + if (error) { + VLOG_WARN_RL(&error_rl, + "failed to retrieve names for datpath %s: %s", + dpif_name(dpif), strerror(error)); + } + return error; + } else { + svec_add(all_names, dpif_name(dpif)); + return 0; + } +} + /* Destroys the datapath that 'dpif' is connected to, first removing all of its * ports. After calling this function, it does not make sense to pass 'dpif' * to any functions other than dpif_name() or dpif_close(). */ diff --git a/lib/dpif.h b/lib/dpif.h index aa4c1a4b..216c0996 100644 --- a/lib/dpif.h +++ b/lib/dpif.h @@ -29,15 +29,18 @@ struct dpif; struct ofpbuf; +struct svec; void dp_run(void); void dp_wait(void); +int dp_enumerate(struct svec *); int dpif_open(const char *name, struct dpif **); int dpif_create(const char *name, struct dpif **); void dpif_close(struct dpif *); const char *dpif_name(const struct dpif *); +int dpif_get_all_names(const struct dpif *, struct svec *); int dpif_delete(struct dpif *); diff --git a/vswitchd/bridge.c b/vswitchd/bridge.c index 00cffbc8..b222152d 100644 --- a/vswitchd/bridge.c +++ b/vswitchd/bridge.c @@ -275,31 +275,35 @@ bridge_get_ifaces(struct svec *svec) void bridge_init(void) { - int retval; - int i; - - bond_init(); + struct svec dpif_names; + size_t i; - for (i = 0; i < DP_MAX; i++) { + dp_enumerate(&dpif_names); + for (i = 0; i < dpif_names.n; i++) { + const char *dpif_name = dpif_names.names[i]; struct dpif *dpif; - char devname[16]; + int retval; - sprintf(devname, "dp%d", i); - retval = dpif_open(devname, &dpif); + retval = dpif_open(dpif_name, &dpif); if (!retval) { - char dpif_name[IF_NAMESIZE]; - if (dpif_port_get_name(dpif, ODPP_LOCAL, - dpif_name, sizeof dpif_name) - || !cfg_has("bridge.%s.port", dpif_name)) { - dpif_delete(dpif); + struct svec all_names; + size_t j; + + svec_init(&all_names); + dpif_get_all_names(dpif, &all_names); + for (j = 0; j < all_names.n; j++) { + if (cfg_has("bridge.%s.port", all_names.names[j])) { + goto found; + } } + dpif_delete(dpif); + found: + svec_destroy(&all_names); dpif_close(dpif); - } else if (retval != ENODEV) { - VLOG_ERR("failed to delete datapath dp%d: %s", - i, strerror(retval)); } } + bond_init(); bridge_reconfigure(); } -- 2.30.2