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 8cc213d5d45823f99032f9233610758df55a7d37..0973b5d9516b3a9f0e11e35b9363c647f1a5d1dc 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 285693ab2731c06f88ba55d11895ed189af9ffb4..cae6d2319a83a9d1eb4dae5f7116da9e0043a92d 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 5abe5310a7be6a9da51c126f9157faec73d1e63d..bd159b2a9d90ecd2d9a738160ad24fafc98820ac 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 0e83a9e2afaea4403284360cbd41974983be0e24..73fa4b98375d9614f8a0396df16487e1904a34b1 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 aa4c1a4b71179f5fd2b8d972a80eb6869e92cc42..216c0996931484cbb16013f5e41f3342ea9c183b 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 00cffbc822b4f1495d831837be6751a043c38e30..b222152dba4271dbb963f17da4c78eff09b639b2 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();
 }