X-Git-Url: https://pintos-os.org/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=lib%2Fnetdev.c;h=88ba01781fa4b49bd777a752dc19ee3361ae5eea;hb=8fb56e55f65c207ef4034cef123a9eea4aeb974b;hp=6a95e1a4f1eeb9cb42281271b04e855694314f7f;hpb=46415c9085ef6b7386943b1714754150487b6cff;p=openvswitch diff --git a/lib/netdev.c b/lib/netdev.c index 6a95e1a4..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); @@ -59,44 +60,23 @@ static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 20); 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. @@ -106,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(); } @@ -122,59 +102,185 @@ 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(); } } } -static int -create_device(struct netdev_options *options, struct netdev_dev **netdev_devp) +/* 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) { - int i; + struct netdev_class *new_provider; - if (!options->may_create) { - VLOG_WARN("attempted to create a device that may not be created: %s", - options->name); - return ENODEV; + if (shash_find(&netdev_classes, new_class->type)) { + VLOG_WARN("attempted to register duplicate netdev provider: %s", + new_class->type); + return EEXIST; } - if (!options->type || strlen(options->type) == 0) { - /* Default to system. */ - options->type = "system"; + 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; + } } - for (i = 0; i < n_netdev_classes; i++) { - const struct netdev_class *class = netdev_classes[i]; + new_provider = xmalloc(sizeof *new_provider); + memcpy(new_provider, new_class, sizeof *new_provider); - if (!strcmp(options->type, class->type)) { - return class->create(options->name, options->type, options->args, - netdev_devp); + 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; } } - VLOG_WARN("could not create netdev %s of unknown type %s", options->name, - options->type); - return EINVAL; + shash_delete(&netdev_classes, del_node); + free(del_node->data); + + return 0; } -static uint32_t -shash_hash(const struct shash *shash) +/* 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) { - int hash = 0; struct shash_node *node; - uint32_t entry_hash; - SHASH_FOR_EACH(node, shash) { - entry_hash = hash_string(node->name, 0); - entry_hash ^= hash_string(node->data, 10); - hash ^= hash_int(entry_hash, 0); + 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'. */ +static bool +compare_device_args(const struct netdev_dev *dev, const struct shash *args) +{ + const struct shash_node **new_args; + bool result = true; + int i; + + if (shash_count(args) != dev->n_args) { + return false; + } + + new_args = shash_sort(args); + for (i = 0; i < dev->n_args; i++) { + if (strcmp(dev->args[i].key, new_args[i]->name) || + strcmp(dev->args[i].value, new_args[i]->data)) { + result = false; + goto finish; + } } - return hash; +finish: + free(new_args); + return result; +} + +static int +compare_args(const void *a_, const void *b_) +{ + const struct arg *a = a_; + const struct arg *b = b_; + return strcmp(a->key, b->key); +} + +void +update_device_args(struct netdev_dev *dev, const struct shash *args) +{ + struct shash_node *node; + int i; + + if (dev->n_args) { + for (i = 0; i < dev->n_args; i++) { + free(dev->args[i].key); + free(dev->args[i].value); + } + + free(dev->args); + dev->n_args = 0; + } + + if (!args || shash_is_empty(args)) { + return; + } + + dev->n_args = shash_count(args); + dev->args = xmalloc(dev->n_args * sizeof *dev->args); + + i = 0; + SHASH_FOR_EACH(node, args) { + dev->args[i].key = xstrdup(node->name); + dev->args[i].value = xstrdup(node->data); + i++; + } + + qsort(dev->args, dev->n_args, sizeof *dev->args, compare_args); +} + +static int +create_device(struct netdev_options *options, struct netdev_dev **netdev_devp) +{ + struct netdev_class *netdev_class; + + if (!options->may_create) { + VLOG_WARN("attempted to create a device that may not be created: %s", + options->name); + return ENODEV; + } + + if (!options->type || strlen(options->type) == 0) { + /* Default to system. */ + options->type = "system"; + } + + 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; + } + + 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 @@ -218,21 +324,18 @@ netdev_open(struct netdev_options *options, struct netdev **netdevp) if (error) { return error; } - - netdev_dev->args_hash = shash_hash(options->args); + update_device_args(netdev_dev, options->args); } else if (options->may_open) { - if (!shash_is_empty(options->args)) { - uint32_t args_hash = shash_hash(options->args); + if (!shash_is_empty(options->args) && + !compare_device_args(netdev_dev, options->args)) { - if (args_hash != netdev_dev->args_hash) { - VLOG_WARN("attempted to open already created netdev with " - "different arguments: %s", options->name); - return EINVAL; - } + VLOG_WARN("%s: attempted to open already created netdev with " + "different arguments", options->name); + return EINVAL; } } else { - VLOG_WARN("attempted to create a netdev device with bound name: %s", + VLOG_WARN("%s: attempted to create a netdev device with bound name", options->name); return EEXIST; } @@ -278,12 +381,13 @@ netdev_reconfigure(struct netdev *netdev, const struct shash *args) } if (netdev_dev->class->reconfigure) { - uint32_t args_hash = shash_hash(args); - - if (netdev_dev->args_hash != args_hash) { - netdev_dev->args_hash = args_hash; + if (!compare_device_args(netdev_dev, args)) { + update_device_args(netdev_dev, args); return netdev_dev->class->reconfigure(netdev_dev, args); } + } else if (!shash_is_empty(args)) { + VLOG_WARN("%s: arguments provided to device that does not have a " + "reconfigure function", netdev_get_name(netdev)); } return 0; @@ -328,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; } @@ -809,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); @@ -843,8 +946,8 @@ netdev_dev_init(struct netdev_dev *netdev_dev, const char *name, { assert(!shash_find(&netdev_dev_shash, name)); + memset(netdev_dev, 0, sizeof *netdev_dev); netdev_dev->class = class; - netdev_dev->ref_cnt = 0; netdev_dev->name = xstrdup(name); netdev_dev->node = shash_add(&netdev_dev_shash, name, netdev_dev); } @@ -864,6 +967,7 @@ netdev_dev_uninit(struct netdev_dev *netdev_dev, bool destroy) assert(!netdev_dev->ref_cnt); shash_delete(&netdev_dev_shash, netdev_dev->node); + update_device_args(netdev_dev, NULL); if (destroy) { netdev_dev->class->destroy(netdev_dev); @@ -923,9 +1027,8 @@ netdev_dev_get_devices(const struct netdev_class *class, void netdev_init(struct netdev *netdev, struct netdev_dev *netdev_dev) { + memset(netdev, 0, sizeof *netdev); netdev->netdev_dev = netdev_dev; - netdev->save_flags = 0; - netdev->changed_flags = 0; list_push_back(&netdev_list, &netdev->node); }