X-Git-Url: https://pintos-os.org/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=lib%2Fnetdev.c;h=84efc6bc31d3b39f270dbc761ce82df78301f6ab;hb=8f7501e83f4aa3d395287ef91f4910e32b55643e;hp=2a291091ac7dcd5714093b9981ebb3eab25a3995;hpb=149f577a25508779b756515be1f1bdcefa3710fa;p=openvswitch diff --git a/lib/netdev.c b/lib/netdev.c index 2a291091..84efc6bc 100644 --- a/lib/netdev.c +++ b/lib/netdev.c @@ -57,8 +57,9 @@ static struct list netdev_list = LIST_INITIALIZER(&netdev_list); * additional log messages. */ static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 20); -static void restore_all_flags(void *aux); +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. @@ -70,10 +71,11 @@ int netdev_initialize(void) { static int status = -1; + if (status < 0) { int i, j; - fatal_signal_add_hook(restore_all_flags, NULL, true); + fatal_signal_add_hook(close_all_netdevs, NULL, NULL, true); status = 0; for (i = j = 0; i < n_netdev_classes; i++) { @@ -130,6 +132,75 @@ netdev_wait(void) } } +/* 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; + } + } + +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) { @@ -160,22 +231,6 @@ create_device(struct netdev_options *options, struct netdev_dev **netdev_devp) return EINVAL; } -static uint32_t -shash_hash(const struct shash *shash) -{ - 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); - } - - return hash; -} - /* Opens the network device named 'name' (e.g. "eth0") and returns zero if * successful, otherwise a positive errno value. On success, sets '*netdevp' * to the new network device, otherwise to null. @@ -217,21 +272,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; } @@ -277,12 +329,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; @@ -842,8 +895,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); } @@ -863,6 +916,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); @@ -888,6 +942,33 @@ netdev_dev_get_name(const struct netdev_dev *netdev_dev) return netdev_dev->name; } +/* Returns the netdev_dev with 'name' or NULL if there is none. + * + * The caller must not free the returned value. */ +struct netdev_dev * +netdev_dev_from_name(const char *name) +{ + return shash_find_data(&netdev_dev_shash, name); +} + +/* Fills 'device_list' with devices that match 'class'. + * + * The caller is responsible for initializing and destroying 'device_list' + * but the contained netdev_devs must not be freed. */ +void +netdev_dev_get_devices(const struct netdev_class *class, + struct shash *device_list) +{ + struct shash_node *node; + SHASH_FOR_EACH (node, &netdev_dev_shash) { + struct netdev_dev *dev = node->data; + + if (dev->class == class) { + shash_add(device_list, node->name, node->data); + } + } +} + /* Initializes 'netdev' as a instance of the netdev_dev. * * This function adds 'netdev' to a netdev-owned linked list, so it is very @@ -895,9 +976,8 @@ netdev_dev_get_name(const struct netdev_dev *netdev_dev) 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); } @@ -1098,13 +1178,13 @@ restore_flags(struct netdev *netdev) return 0; } -/* Retores all the flags on all network devices that we modified. Called from - * a signal handler, so it does not attempt to report error conditions. */ +/* Close all netdevs on shutdown so they can do any needed cleanup such as + * destroying devices, restoring flags, etc. */ static void -restore_all_flags(void *aux UNUSED) +close_all_netdevs(void *aux UNUSED) { - struct netdev *netdev; - LIST_FOR_EACH (netdev, struct netdev, node, &netdev_list) { - restore_flags(netdev); + struct netdev *netdev, *next; + LIST_FOR_EACH_SAFE(netdev, next, struct netdev, node, &netdev_list) { + netdev_close(netdev); } }