aboutsummaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorJesse Gross <jesse@nicira.com>2010-02-01 11:35:54 -0500
committerJesse Gross <jesse@nicira.com>2010-02-01 12:00:49 -0500
commit77909859b05f0d2466069883306e8d75d280bbe7 (patch)
tree79c036827af075dade08576a1a7848c4afeb47e4 /lib
parent5c414a2ed8c21762292776ecee294a11e0b5468b (diff)
netdev: Allow providers to be managed at runtime.
The list of netdev providers was previously staticly defined at compile time. This allows new providers to be added and removed at runtime.
Diffstat (limited to 'lib')
-rw-r--r--lib/netdev-provider.h8
-rw-r--r--lib/netdev.c165
-rw-r--r--lib/netdev.h6
3 files changed, 117 insertions, 62 deletions
diff --git a/lib/netdev-provider.h b/lib/netdev-provider.h
index a6c0fd89..43a330c7 100644
--- a/lib/netdev-provider.h
+++ b/lib/netdev-provider.h
@@ -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
diff --git a/lib/netdev.c b/lib/netdev.c
index 84efc6bc..88ba0178 100644
--- a/lib/netdev.c
+++ b/lib/netdev.c
@@ -40,12 +40,13 @@
#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);
diff --git a/lib/netdev.h b/lib/netdev.h
index 3f6d5eab..e0cdd82f 100644
--- a/lib/netdev.h
+++ b/lib/netdev.h
@@ -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);