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

extras/ezio/ovs-switchui.c
lib/netdev-provider.h
lib/netdev.c
lib/netdev.h

index 31e37f6..4f0843f 100644 (file)
@@ -2458,7 +2458,7 @@ abbreviate_netdevs(const struct svec *netdevs, struct ds *abbrev)
 static void
 choose_netdevs(struct svec *choices)
 {
-    struct svec netdevs;
+    struct svec netdevs = SVEC_EMPTY_INITIALIZER;
     struct menu menu;
     size_t i;
 
index a6c0fd8..43a330c 100644 (file)
@@ -107,12 +107,12 @@ struct netdev_class {
      * the system. */
     const char *type;
 
-    /* Called only once, at program startup.  Returning an error from this
-     * function will prevent any network device in this class from being
-     * opened.
+    /* Called when the netdev provider is registered, typically at program
+     * startup.  Returning an error from this function will prevent any network
+     * device in this class from being opened.
      *
      * This function may be set to null if a network device class needs no
-     * initialization at program startup. */
+     * initialization at registration time. */
     int (*init)(void);
 
     /* Performs periodic work needed by netdevs of this class.  May be null if
index 84efc6b..88ba017 100644 (file)
 #define THIS_MODULE VLM_netdev
 #include "vlog.h"
 
-static const struct netdev_class *netdev_classes[] = {
+static const struct netdev_class *base_netdev_classes[] = {
     &netdev_linux_class,
     &netdev_tap_class,
     &netdev_gre_class,
 };
-static int n_netdev_classes = ARRAY_SIZE(netdev_classes);
+
+static struct shash netdev_classes = SHASH_INITIALIZER(&netdev_classes);
 
 /* All created network devices. */
 static struct shash netdev_dev_shash = SHASH_INITIALIZER(&netdev_dev_shash);
@@ -61,43 +62,21 @@ static void close_all_netdevs(void *aux UNUSED);
 static int restore_flags(struct netdev *netdev);
 void update_device_args(struct netdev_dev *, const struct shash *args);
 
-/* Attempts to initialize the netdev module.  Returns 0 if successful,
- * otherwise a positive errno value.
- *
- * Calling this function is optional.  If not called explicitly, it will
- * automatically be called upon the first attempt to open or create a 
- * network device. */
-int
+static void
 netdev_initialize(void)
 {
     static int status = -1;
 
     if (status < 0) {
-        int i, j;
+        int i;
 
         fatal_signal_add_hook(close_all_netdevs, NULL, NULL, true);
 
         status = 0;
-        for (i = j = 0; i < n_netdev_classes; i++) {
-            const struct netdev_class *class = netdev_classes[i];
-            if (class->init) {
-                int retval = class->init();
-                if (!retval) {
-                    netdev_classes[j++] = class;
-                } else {
-                    VLOG_ERR("failed to initialize %s network device "
-                             "class: %s", class->type, strerror(retval));
-                    if (!status) {
-                        status = retval;
-                    }
-                }
-            } else {
-                netdev_classes[j++] = class;
-            }
+        for (i = 0; i < ARRAY_SIZE(base_netdev_classes); i++) {
+            netdev_register_provider(base_netdev_classes[i]);
         }
-        n_netdev_classes = j;
     }
-    return status;
 }
 
 /* Performs periodic work needed by all the various kinds of netdevs.
@@ -107,9 +86,9 @@ netdev_initialize(void)
 void
 netdev_run(void)
 {
-    int i;
-    for (i = 0; i < n_netdev_classes; i++) {
-        const struct netdev_class *class = netdev_classes[i];
+    struct shash_node *node;
+    SHASH_FOR_EACH(node, &netdev_classes) {
+        const struct netdev_class *class = node->data;
         if (class->run) {
             class->run();
         }
@@ -123,15 +102,91 @@ netdev_run(void)
 void
 netdev_wait(void)
 {
-    int i;
-    for (i = 0; i < n_netdev_classes; i++) {
-        const struct netdev_class *class = netdev_classes[i];
+    struct shash_node *node;
+    SHASH_FOR_EACH(node, &netdev_classes) {
+        const struct netdev_class *class = node->data;
         if (class->wait) {
             class->wait();
         }
     }
 }
 
+/* Initializes and registers a new netdev provider.  After successful
+ * registration, new netdevs of that type can be opened using netdev_open(). */
+int
+netdev_register_provider(const struct netdev_class *new_class)
+{
+    struct netdev_class *new_provider;
+
+    if (shash_find(&netdev_classes, new_class->type)) {
+        VLOG_WARN("attempted to register duplicate netdev provider: %s",
+                   new_class->type);
+        return EEXIST;
+    }
+
+    if (new_class->init) {
+        int error = new_class->init();
+        if (error) {
+            VLOG_ERR("failed to initialize %s network device class: %s",
+                     new_class->type, strerror(error));
+            return error;
+        }
+    }
+
+    new_provider = xmalloc(sizeof *new_provider);
+    memcpy(new_provider, new_class, sizeof *new_provider);
+
+    shash_add(&netdev_classes, new_class->type, new_provider);
+
+    return 0;
+}
+
+/* Unregisters a netdev provider.  'type' must have been previously
+ * registered and not currently be in use by any netdevs.  After unregistration
+ * new netdevs of that type cannot be opened using netdev_open(). */
+int
+netdev_unregister_provider(const char *type)
+{
+    struct shash_node *del_node, *netdev_dev_node;
+
+    del_node = shash_find(&netdev_classes, type);
+    if (!del_node) {
+        VLOG_WARN("attempted to unregister a netdev provider that is not "
+                  "registered: %s", type);
+        return EAFNOSUPPORT;
+    }
+
+    SHASH_FOR_EACH(netdev_dev_node, &netdev_dev_shash) {
+        struct netdev_dev *netdev_dev = netdev_dev_node->data;
+        if (!strcmp(netdev_dev->class->type, type)) {
+            VLOG_WARN("attempted to unregister in use netdev provider: %s",
+                      type);
+            return EBUSY;
+        }
+    }
+
+    shash_delete(&netdev_classes, del_node);
+    free(del_node->data);
+
+    return 0;
+}
+
+/* Clears 'types' and enumerates the types of all currently registered netdev
+ * providers into it.  The caller must first initialize the svec. */
+void
+netdev_enumerate_types(struct svec *types)
+{
+    struct shash_node *node;
+
+    netdev_initialize();
+    svec_clear(types);
+
+    SHASH_FOR_EACH(node, &netdev_classes) {
+        const struct netdev_class *netdev_class = node->data;
+        svec_add(types, netdev_class->type);
+    }
+}
+
 /* Compares 'args' to those used to those used by 'dev'.  Returns true
  * if the arguments are the same, false otherwise.  Does not update the
  * values stored in 'dev'. */
@@ -204,7 +259,7 @@ update_device_args(struct netdev_dev *dev, const struct shash *args)
 static int
 create_device(struct netdev_options *options, struct netdev_dev **netdev_devp)
 {
-    int i;
+    struct netdev_class *netdev_class;
 
     if (!options->may_create) {
         VLOG_WARN("attempted to create a device that may not be created: %s",
@@ -217,18 +272,15 @@ create_device(struct netdev_options *options, struct netdev_dev **netdev_devp)
         options->type = "system";
     }
 
-    for (i = 0; i < n_netdev_classes; i++) {
-        const struct netdev_class *class = netdev_classes[i];
-
-        if (!strcmp(options->type, class->type)) {
-            return class->create(options->name, options->type, options->args,
-                                 netdev_devp);
-        }
+    netdev_class = shash_find_data(&netdev_classes, options->type);
+    if (!netdev_class) {
+        VLOG_WARN("could not create netdev %s of unknown type %s",
+                  options->name, options->type);
+        return EAFNOSUPPORT;
     }
 
-    VLOG_WARN("could not create netdev %s of unknown type %s", options->name,
-                                                                options->type);
-    return EINVAL;
+    return netdev_class->create(options->name, options->type, options->args,
+                                netdev_devp);
 }
 
 /* Opens the network device named 'name' (e.g. "eth0") and returns zero if
@@ -380,31 +432,30 @@ netdev_exists(const char *name)
     }
 }
 
-/* Initializes 'svec' with a list of the names of all known network devices. */
+/*  Clears 'svec' and enumerates the names of all known network devices. */
 int
 netdev_enumerate(struct svec *svec)
 {
-    int error;
-    int i;
-
-    svec_init(svec);
+    struct shash_node *node;
+    int error = 0;
 
     netdev_initialize();
+    svec_clear(svec);
 
-    error = 0;
-    for (i = 0; i < n_netdev_classes; i++) {
-        const struct netdev_class *class = netdev_classes[i];
-        if (class->enumerate) {
-            int retval = class->enumerate(svec);
+    SHASH_FOR_EACH(node, &netdev_classes) {
+        const struct netdev_class *netdev_class = node->data;
+        if (netdev_class->enumerate) {
+            int retval = netdev_class->enumerate(svec);
             if (retval) {
                 VLOG_WARN("failed to enumerate %s network devices: %s",
-                          class->type, strerror(retval));
+                          netdev_class->type, strerror(retval));
                 if (!error) {
                     error = retval;
                 }
             }
         }
     }
+
     return error;
 }
 
@@ -861,7 +912,7 @@ struct netdev *
 netdev_find_dev_by_in4(const struct in_addr *in4)
 {
     struct netdev *netdev;
-    struct svec dev_list;
+    struct svec dev_list = SVEC_EMPTY_INITIALIZER;
     size_t i;
 
     netdev_enumerate(&dev_list);
index 3f6d5ea..e0cdd82 100644 (file)
@@ -86,11 +86,15 @@ struct netdev_options {
 };
 
 struct netdev;
+struct netdev_class;
 
-int netdev_initialize(void);
 void netdev_run(void);
 void netdev_wait(void);
 
+int netdev_register_provider(const struct netdev_class *);
+int netdev_unregister_provider(const char *type);
+void netdev_enumerate_types(struct svec *types);
+
 int netdev_open(struct netdev_options *, struct netdev **);
 int netdev_open_default(const char *name, struct netdev **);
 int netdev_reconfigure(struct netdev *, const struct shash *args);