vswitch: Avoid knowledge of details specific to Linux datapaths.
authorBen Pfaff <blp@nicira.com>
Mon, 6 Jul 2009 18:06:36 +0000 (11:06 -0700)
committerBen Pfaff <blp@nicira.com>
Mon, 6 Jul 2009 18:06:36 +0000 (11:06 -0700)
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
lib/dpif-netdev.c
lib/dpif-provider.h
lib/dpif.c
lib/dpif.h
vswitchd/bridge.c

index 8cc213d..0973b5d 100644 (file)
@@ -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;
index 285693a..cae6d23 100644 (file)
@@ -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,
index 5abe531..bd159b2 100644 (file)
@@ -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
index 0e83a9e..73fa4b9 100644 (file)
@@ -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(). */
index aa4c1a4..216c099 100644 (file)
 
 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 *);
 
index 00cffbc..b222152 100644 (file)
@@ -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();
 }