In general the network devices don't change very often. By caching the lookup made by netdev_linux_miimon_run() calls to shash_add() and shash_destroy() destroy are avoided reducing the use of malloc(), free() and hash_bytes().
In my test environment this increased the rate at which packets could be received from ~23.3kpps to ~24.4kpps. Signed-off-by: Simon Horman <ho...@verge.net.au> --- lib/netdev-linux.c | 25 +++++++++---------------- lib/netdev-provider.h | 3 +-- lib/netdev.c | 22 ++++++++++++++++++---- 3 files changed, 28 insertions(+), 22 deletions(-) diff --git a/lib/netdev-linux.c b/lib/netdev-linux.c index 433d168..004b5eb 100644 --- a/lib/netdev-linux.c +++ b/lib/netdev-linux.c @@ -561,12 +561,11 @@ netdev_linux_cache_cb(const struct rtnetlink_link_change *change, } } } else { - struct shash device_shash; + const struct shash *device_shash; struct shash_node *node; - shash_init(&device_shash); - netdev_dev_get_devices(&netdev_linux_class, &device_shash); - SHASH_FOR_EACH (node, &device_shash) { + device_shash = netdev_dev_get_devices(&netdev_linux_class); + SHASH_FOR_EACH (node, device_shash) { unsigned int flags; dev = node->data; @@ -574,7 +573,6 @@ netdev_linux_cache_cb(const struct rtnetlink_link_change *change, get_flags(&dev->netdev_dev, &flags); netdev_dev_linux_changed(dev, flags, 0); } - shash_destroy(&device_shash); } } @@ -1230,12 +1228,11 @@ netdev_linux_set_miimon_interval(struct netdev *netdev_, static void netdev_linux_miimon_run(void) { - struct shash device_shash; + const struct shash *device_shash; struct shash_node *node; - shash_init(&device_shash); - netdev_dev_get_devices(&netdev_linux_class, &device_shash); - SHASH_FOR_EACH (node, &device_shash) { + device_shash = netdev_dev_get_devices(&netdev_linux_class); + SHASH_FOR_EACH (node, device_shash) { struct netdev_dev_linux *dev = node->data; bool miimon; @@ -1251,26 +1248,22 @@ netdev_linux_miimon_run(void) timer_set_duration(&dev->miimon_timer, dev->miimon_interval); } - - shash_destroy(&device_shash); } static void netdev_linux_miimon_wait(void) { - struct shash device_shash; + const struct shash *device_shash; struct shash_node *node; - shash_init(&device_shash); - netdev_dev_get_devices(&netdev_linux_class, &device_shash); - SHASH_FOR_EACH (node, &device_shash) { + device_shash = netdev_dev_get_devices(&netdev_linux_class); + SHASH_FOR_EACH (node, device_shash) { struct netdev_dev_linux *dev = node->data; if (dev->miimon_interval > 0) { timer_wait(&dev->miimon_timer); } } - shash_destroy(&device_shash); } /* Check whether we can we use RTM_GETLINK to get network device statistics. diff --git a/lib/netdev-provider.h b/lib/netdev-provider.h index 9db950c..ff388b7 100644 --- a/lib/netdev-provider.h +++ b/lib/netdev-provider.h @@ -47,8 +47,7 @@ const char *netdev_dev_get_type(const struct netdev_dev *); const struct netdev_class *netdev_dev_get_class(const struct netdev_dev *); const char *netdev_dev_get_name(const struct netdev_dev *); struct netdev_dev *netdev_dev_from_name(const char *name); -void netdev_dev_get_devices(const struct netdev_class *, - struct shash *device_list); +const struct shash *netdev_dev_get_devices(const struct netdev_class *); static inline void netdev_dev_assert_class(const struct netdev_dev *netdev_dev, const struct netdev_class *class_) diff --git a/lib/netdev.c b/lib/netdev.c index 0a2e7c5..df2c52d 100644 --- a/lib/netdev.c +++ b/lib/netdev.c @@ -52,6 +52,7 @@ static struct shash netdev_classes = SHASH_INITIALIZER(&netdev_classes); /* All created network devices. */ static struct shash netdev_dev_shash = SHASH_INITIALIZER(&netdev_dev_shash); +bool netdev_dev_shash_changed; /* All open network devices. */ static struct list netdev_list = LIST_INITIALIZER(&netdev_list); @@ -1321,6 +1322,7 @@ netdev_dev_init(struct netdev_dev *netdev_dev, const char *name, netdev_dev->netdev_class = netdev_class; netdev_dev->name = xstrdup(name); netdev_dev->node = shash_add(&netdev_dev_shash, name, netdev_dev); + netdev_dev_shash_changed = true; } /* Undoes the results of initialization. @@ -1338,6 +1340,7 @@ netdev_dev_uninit(struct netdev_dev *netdev_dev, bool destroy) ovs_assert(!netdev_dev->ref_cnt); shash_delete(&netdev_dev_shash, netdev_dev->node); + netdev_dev_shash_changed = true; if (destroy) { netdev_dev->netdev_class->destroy(netdev_dev); @@ -1383,18 +1386,29 @@ netdev_dev_from_name(const char *name) * * 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 *netdev_class, - struct shash *device_list) +const struct shash * +netdev_dev_get_devices(const struct netdev_class *netdev_class) { struct shash_node *node; + static struct shash device_list = SHASH_INITIALIZER(&device_list); + + if (!netdev_dev_shash_changed) { + return &device_list; + } + + shash_destroy(&device_list); + shash_init(&device_list); + SHASH_FOR_EACH (node, &netdev_dev_shash) { struct netdev_dev *dev = node->data; if (dev->netdev_class == netdev_class) { - shash_add(device_list, node->name, node->data); + shash_add(&device_list, node->name, node->data); } } + + netdev_dev_shash_changed = false; + return &device_list; } /* Initializes 'netdev' as a instance of the netdev_dev. -- 1.7.10.4 _______________________________________________ dev mailing list dev@openvswitch.org http://openvswitch.org/mailman/listinfo/dev