dpif: Allow providers to be managed at runtime.
authorJesse Gross <jesse@nicira.com>
Mon, 1 Feb 2010 16:36:01 +0000 (11:36 -0500)
committerJesse Gross <jesse@nicira.com>
Mon, 1 Feb 2010 17:00:53 +0000 (12:00 -0500)
The list of datapath providers was previously staticly defined at
compile time.  This allows new providers to be added and removed
at runtime.

lib/dpif-linux.c
lib/dpif-provider.h
lib/dpif.c
lib/dpif.h

index db69589..94c3cf5 100644 (file)
@@ -98,7 +98,7 @@ dpif_linux_enumerate(struct svec *all_dps)
         retval = dpif_open(devname, "system", &dpif);
         if (!retval) {
             svec_add(all_dps, devname);
-            dpif_close(dpif);
+            dpif_uninit(dpif, true);
         } else if (retval != ENODEV && !error) {
             error = retval;
         }
@@ -157,7 +157,7 @@ dpif_linux_open(const char *name, const char *type UNUSED, bool create,
                 VLOG_WARN("%s: probe returned unexpected error: %s",
                           dpif_name(*dpifp), strerror(error));
             }
-            dpif_close(*dpifp);
+            dpif_uninit(*dpifp, true);
             return error;
         }
 
@@ -668,11 +668,11 @@ 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_ifname = xstrdup(local_ifname);
     dpif->local_ifindex = if_nametoindex(local_ifname);
     if (!dpif->local_ifindex) {
         int error = errno;
-        dpif_close(dpif_);
+        dpif_uninit(dpif_, true);
         VLOG_WARN("could not get ifindex of %s device: %s",
                   local_ifname, strerror(errno));
         return error;
@@ -689,7 +689,7 @@ create_minor(const char *name, int minor, struct dpif **dpifp)
         if (!error) {
             error = finish_open(*dpifp, name);
         } else {
-            dpif_close(*dpifp);
+            dpif_uninit(*dpifp, true);
         }
     }
     return error;
index 9a49544..699f724 100644 (file)
@@ -36,6 +36,8 @@ struct dpif {
 
 void dpif_init(struct dpif *, const struct dpif_class *, const char *name,
                uint8_t netflow_engine_type, uint8_t netflow_engine_id);
+void dpif_uninit(struct dpif *dpif, bool close);
+
 static inline void dpif_assert_class(const struct dpif *dpif,
                                      const struct dpif_class *class)
 {
index 5947179..f3d6713 100644 (file)
@@ -33,6 +33,7 @@
 #include "ofpbuf.h"
 #include "packets.h"
 #include "poll-loop.h"
+#include "shash.h"
 #include "svec.h"
 #include "util.h"
 #include "valgrind.h"
 #include "vlog.h"
 #define THIS_MODULE VLM_dpif
 
-static const struct dpif_class *dpif_classes[] = {
+static const struct dpif_class *base_dpif_classes[] = {
     &dpif_linux_class,
     &dpif_netdev_class,
 };
-enum { N_DPIF_CLASSES = ARRAY_SIZE(dpif_classes) };
+
+struct registered_dpif_class {
+    struct dpif_class dpif_class;
+    int refcount;
+};
+static struct shash dpif_classes = SHASH_INITIALIZER(&dpif_classes);
 
 /* Rate limit for individual messages going to or from the datapath, output at
  * DBG level.  This is very high because, if these are enabled, it is because
@@ -63,6 +69,21 @@ static void log_flow_put(struct dpif *, int error,
 static bool should_log_flow_message(int error);
 static void check_rw_odp_flow(struct odp_flow *);
 
+static void
+dp_initialize(void)
+{
+    static int status = -1;
+
+    if (status < 0) {
+        int i;
+
+        status = 0;
+        for (i = 0; i < ARRAY_SIZE(base_dpif_classes); i++) {
+            dp_register_provider(base_dpif_classes[i]);
+        }
+    }
+}
+
 /* Performs periodic work needed by all the various kinds of dpifs.
  *
  * If your program opens any dpifs, it must call both this function and
@@ -70,11 +91,11 @@ static void check_rw_odp_flow(struct odp_flow *);
 void
 dp_run(void)
 {
-    int i;
-    for (i = 0; i < N_DPIF_CLASSES; i++) {
-        const struct dpif_class *class = dpif_classes[i];
-        if (class->run) {
-            class->run();
+    struct shash_node *node;
+    SHASH_FOR_EACH(node, &dpif_classes) {
+        const struct registered_dpif_class *registered_class = node->data;
+        if (registered_class->dpif_class.run) {
+            registered_class->dpif_class.run();
         }
     }
 }
@@ -86,27 +107,79 @@ dp_run(void)
 void
 dp_wait(void)
 {
-    int i;
-    for (i = 0; i < N_DPIF_CLASSES; i++) {
-        const struct dpif_class *class = dpif_classes[i];
-        if (class->wait) {
-            class->wait();
+    struct shash_node *node;
+    SHASH_FOR_EACH(node, &dpif_classes) {
+        const struct registered_dpif_class *registered_class = node->data;
+        if (registered_class->dpif_class.wait) {
+            registered_class->dpif_class.wait();
         }
     }
 }
 
+/* Registers a new datapath provider.  After successful registration, new
+ * datapaths of that type can be opened using dpif_open(). */
+int
+dp_register_provider(const struct dpif_class *new_class)
+{
+    struct registered_dpif_class *registered_class;
+
+    if (shash_find(&dpif_classes, new_class->type)) {
+        VLOG_WARN("attempted to register duplicate datapath provider: %s",
+                  new_class->type);
+        return EEXIST;
+    }
 
-/* Clears 'types' and enumerates the types of all known datapath providers,
- * into it.  The caller must first initialize the svec. */
+    registered_class = xmalloc(sizeof *registered_class);
+    memcpy(&registered_class->dpif_class, new_class,
+           sizeof registered_class->dpif_class);
+    registered_class->refcount = 0;
+
+    shash_add(&dpif_classes, new_class->type, registered_class);
+
+    return 0;
+}
+
+/* Unregisters a datapath provider.  'type' must have been previously
+ * registered and not currently be in use by any dpifs.  After unregistration
+ * new datapaths of that type cannot be opened using dpif_open(). */
+int
+dp_unregister_provider(const char *type)
+{
+    struct shash_node *node;
+    struct registered_dpif_class *registered_class;
+
+    node = shash_find(&dpif_classes, type);
+    if (!node) {
+        VLOG_WARN("attempted to unregister a datapath provider that is not "
+                  "registered: %s", type);
+        return EAFNOSUPPORT;
+    }
+
+    registered_class = node->data;
+    if (registered_class->refcount) {
+        VLOG_WARN("attempted to unregister in use datapath provider: %s", type);
+        return EBUSY;
+    }
+
+    shash_delete(&dpif_classes, node);
+    free(registered_class);
+
+    return 0;
+}
+
+/* Clears 'types' and enumerates the types of all currently registered datapath
+ * providers into it.  The caller must first initialize the svec. */
 void
 dp_enumerate_types(struct svec *types)
 {
-    int i;
+    struct shash_node *node;
 
+    dp_initialize();
     svec_clear(types);
 
-    for (i = 0; i < N_DPIF_CLASSES; i++) {
-        svec_add(types, dpif_classes[i]->type);
+    SHASH_FOR_EACH(node, &dpif_classes) {
+        const struct registered_dpif_class *registered_class = node->data;
+        svec_add(types, registered_class->dpif_class.type);
     }
 }
 
@@ -119,26 +192,28 @@ dp_enumerate_types(struct svec *types)
 int
 dp_enumerate_names(const char *type, struct svec *names)
 {
-    int i;
+    const struct registered_dpif_class *registered_class;
+    const struct dpif_class *dpif_class;
+    int error;
 
+    dp_initialize();
     svec_clear(names);
 
-    for (i = 0; i < N_DPIF_CLASSES; i++) {
-        const struct dpif_class *class = dpif_classes[i];
-
-        if (!strcmp(type, class->type)) {
-            int error = class->enumerate ? class->enumerate(names) : 0;
+    registered_class = shash_find_data(&dpif_classes, type);
+    if (!registered_class) {
+        VLOG_WARN("could not enumerate unknown type: %s", type);
+        return EAFNOSUPPORT;
+    }
 
-            if (error) {
-                VLOG_WARN("failed to enumerate %s datapaths: %s",
-                          class->type, strerror(error));
-            }
+    dpif_class = &registered_class->dpif_class;
+    error = dpif_class->enumerate ? dpif_class->enumerate(names) : 0;
 
-            return error;
-        }
+    if (error) {
+        VLOG_WARN("failed to enumerate %s datapaths: %s", dpif_class->type,
+                   strerror(error));
     }
 
-    return EAFNOSUPPORT;
+    return error;
 }
 
 /* Parses 'datapath name', which is of the form type@name into its
@@ -165,20 +240,26 @@ do_open(const char *name, const char *type, bool create, struct dpif **dpifp)
 {
     struct dpif *dpif = NULL;
     int error;
-    int i;
+    struct registered_dpif_class *registered_class;
+
+    dp_initialize();
 
     if (!type || *type == '\0') {
         type = "system";
     }
 
-    for (i = 0; i < N_DPIF_CLASSES; i++) {
-        const struct dpif_class *class = dpif_classes[i];
-        if (!strcmp(type, class->type)) {
-            error = class->open(name, type, create, &dpif);
-            goto exit;
-        }
+    registered_class = shash_find_data(&dpif_classes, type);
+    if (!registered_class) {
+        VLOG_WARN("could not create datapath %s of unknown type %s", name,
+                  type);
+        error = EAFNOSUPPORT;
+        goto exit;
+    }
+
+    error = registered_class->dpif_class.open(name, type, create, &dpif);
+    if (!error) {
+        registered_class->refcount++;
     }
-    error = EAFNOSUPPORT;
 
 exit:
     *dpifp = error ? NULL : dpif;
@@ -236,11 +317,14 @@ void
 dpif_close(struct dpif *dpif)
 {
     if (dpif) {
-        char *base_name = dpif->base_name;
-        char *full_name = dpif->full_name;
-        dpif->class->close(dpif);
-        free(base_name);
-        free(full_name);
+        struct registered_dpif_class *registered_class;
+
+        registered_class = shash_find_data(&dpif_classes, dpif->class->type);
+        assert(registered_class);
+        assert(registered_class->refcount);
+
+        registered_class->refcount--;
+        dpif_uninit(dpif, true);
     }
 }
 
@@ -975,6 +1059,26 @@ dpif_init(struct dpif *dpif, const struct dpif_class *class, const char *name,
     dpif->netflow_engine_type = netflow_engine_type;
     dpif->netflow_engine_id = netflow_engine_id;
 }
+
+/* Undoes the results of initialization.
+ *
+ * Normally this function only needs to be called from dpif_close().
+ * However, it may be called by providers due to an error on opening
+ * that occurs after initialization.  It this case dpif_close() would
+ * never be called. */
+void
+dpif_uninit(struct dpif *dpif, bool close)
+{
+    char *base_name = dpif->base_name;
+    char *full_name = dpif->full_name;
+
+    if (close) {
+        dpif->class->close(dpif);
+    }
+
+    free(base_name);
+    free(full_name);
+}
 \f
 static void
 log_operation(const struct dpif *dpif, const char *operation, int error)
index aa54a38..dae0ef8 100644 (file)
 struct dpif;
 struct ofpbuf;
 struct svec;
+struct dpif_class;
 
 void dp_run(void);
 void dp_wait(void);
+
+int dp_register_provider(const struct dpif_class *);
+int dp_unregister_provider(const char *type);
 void dp_enumerate_types(struct svec *types);
+
 int dp_enumerate_names(const char *type, struct svec *names);
 void dp_parse_name(const char *datapath_name, char **name, char **type);