From 1f6e0fbd81b8f1786be0232f83792170a9f476d3 Mon Sep 17 00:00:00 2001 From: Ben Pfaff Date: Wed, 30 Nov 2011 10:59:12 -0800 Subject: [PATCH] netdev-linux: Ref and unref the netdev_linux_cache_notifier for taps too. netdev-linux uses netdev_linux_cache_notifier to flush its cache when the kernel notifies userspace that a particular network device's configuration or status has changed. This is as applicable to tap devices as to system and internal devices, so we should create and destroy the notifier for tap devices also. I doubt that in practice it's possible to run ovs-vswitchd without having a non-tap device open, at least with the kernel datapath, because the local port for a bridge is not a tap device, so there should be no need to backport this to older versions. Reported-by: Gaetano Catalli --- lib/netdev-linux.c | 61 +++++++++++++++++++++++++++++++--------------- 1 file changed, 41 insertions(+), 20 deletions(-) diff --git a/lib/netdev-linux.c b/lib/netdev-linux.c index 44167da4..90e88c76 100644 --- a/lib/netdev-linux.c +++ b/lib/netdev-linux.c @@ -534,13 +534,9 @@ netdev_linux_cache_cb(const struct rtnetlink_link_change *change, } } -/* Creates system and internal devices. */ static int -netdev_linux_create(const struct netdev_class *class, const char *name, - struct netdev_dev **netdev_devp) +cache_notifier_ref(void) { - struct netdev_dev_linux *netdev_dev; - if (!cache_notifier_refcount) { assert(!netdev_linux_cache_notifier); @@ -553,6 +549,33 @@ netdev_linux_create(const struct netdev_class *class, const char *name, } cache_notifier_refcount++; + return 0; +} + +static void +cache_notifier_unref(void) +{ + assert(cache_notifier_refcount > 0); + if (!--cache_notifier_refcount) { + assert(netdev_linux_cache_notifier); + rtnetlink_link_notifier_destroy(netdev_linux_cache_notifier); + netdev_linux_cache_notifier = NULL; + } +} + +/* Creates system and internal devices. */ +static int +netdev_linux_create(const struct netdev_class *class, const char *name, + struct netdev_dev **netdev_devp) +{ + struct netdev_dev_linux *netdev_dev; + int error; + + error = cache_notifier_ref(); + if (error) { + return error; + } + netdev_dev = xzalloc(sizeof *netdev_dev); netdev_dev->change_seq = 1; netdev_dev_init(&netdev_dev->netdev_dev, name, class); @@ -581,12 +604,17 @@ netdev_linux_create_tap(const struct netdev_class *class OVS_UNUSED, netdev_dev = xzalloc(sizeof *netdev_dev); state = &netdev_dev->state.tap; + error = cache_notifier_ref(); + if (error) { + goto error; + } + /* Open tap device. */ state->fd = open(tap_dev, O_RDWR); if (state->fd < 0) { error = errno; VLOG_WARN("opening \"%s\" failed: %s", tap_dev, strerror(error)); - goto error; + goto error_unref_notifier; } /* Create tap device. */ @@ -596,19 +624,21 @@ netdev_linux_create_tap(const struct netdev_class *class OVS_UNUSED, VLOG_WARN("%s: creating tap device failed: %s", name, strerror(errno)); error = errno; - goto error; + goto error_unref_notifier; } /* Make non-blocking. */ error = set_nonblocking(state->fd); if (error) { - goto error; + goto error_unref_notifier; } netdev_dev_init(&netdev_dev->netdev_dev, name, &netdev_tap_class); *netdev_devp = &netdev_dev->netdev_dev; return 0; +error_unref_notifier: + cache_notifier_unref(); error: free(netdev_dev); return error; @@ -635,21 +665,12 @@ netdev_linux_destroy(struct netdev_dev *netdev_dev_) netdev_dev->tc->ops->tc_destroy(netdev_dev->tc); } - if (class == &netdev_linux_class || class == &netdev_internal_class) { - cache_notifier_refcount--; - - if (!cache_notifier_refcount) { - assert(netdev_linux_cache_notifier); - rtnetlink_link_notifier_destroy(netdev_linux_cache_notifier); - netdev_linux_cache_notifier = NULL; - } - } else if (class == &netdev_tap_class) { + if (class == &netdev_tap_class) { destroy_tap(netdev_dev); - } else { - NOT_REACHED(); } - free(netdev_dev); + + cache_notifier_unref(); } static int -- 2.30.2