vlog: Fix typo in VLOG_IS_ERR_ENABLED macro definition.
[openvswitch] / lib / netdev-linux.c
index b5d30352379828d32a9f65de4009ac26a9bda503..385c0b81688e62a2d11a0ff6e363ceb5fb23d672 100644 (file)
@@ -346,6 +346,7 @@ struct netdev_dev_linux {
 
     struct shash_node *shash_node;
     unsigned int cache_valid;
+    unsigned int change_seq;
 
     bool miimon;                    /* Link status of last poll. */
     long long int miimon_interval;  /* Miimon Poll rate. Disabled if <= 0. */
@@ -382,15 +383,6 @@ static int af_inet_sock = -1;   /* AF_INET, SOCK_DGRAM. */
 /* A Netlink routing socket that is not subscribed to any multicast groups. */
 static struct nl_sock *rtnl_sock;
 
-struct netdev_linux_notifier {
-    struct netdev_notifier notifier;
-    struct list node;
-};
-
-static struct shash netdev_linux_notifiers =
-    SHASH_INITIALIZER(&netdev_linux_notifiers);
-static struct rtnetlink_notifier netdev_linux_poll_notifier;
-
 /* This is set pretty low because we probably won't learn anything from the
  * additional log messages. */
 static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 20);
@@ -416,7 +408,6 @@ static int set_etheraddr(const char *netdev_name, int hwaddr_family,
 static int get_stats_via_netlink(int ifindex, struct netdev_stats *stats);
 static int get_stats_via_proc(const char *netdev_name, struct netdev_stats *stats);
 static int af_packet_sock(void);
-static void poll_notify(struct list *);
 static void netdev_linux_miimon_run(void);
 static void netdev_linux_miimon_wait(void);
 
@@ -483,6 +474,16 @@ netdev_linux_wait(void)
     netdev_linux_miimon_wait();
 }
 
+static void
+netdev_dev_linux_changed(struct netdev_dev_linux *dev)
+{
+    dev->change_seq++;
+    if (!dev->change_seq) {
+        dev->change_seq++;
+    }
+    dev->cache_valid = 0;
+}
+
 static void
 netdev_linux_cache_cb(const struct rtnetlink_link_change *change,
                       void *aux OVS_UNUSED)
@@ -496,7 +497,7 @@ netdev_linux_cache_cb(const struct rtnetlink_link_change *change,
 
             if (is_netdev_linux_class(netdev_class)) {
                 dev = netdev_dev_linux_cast(base_dev);
-                dev->cache_valid = 0;
+                netdev_dev_linux_changed(dev);
             }
         }
     } else {
@@ -507,7 +508,7 @@ netdev_linux_cache_cb(const struct rtnetlink_link_change *change,
         netdev_dev_get_devices(&netdev_linux_class, &device_shash);
         SHASH_FOR_EACH (node, &device_shash) {
             dev = node->data;
-            dev->cache_valid = 0;
+            netdev_dev_linux_changed(dev);
         }
         shash_destroy(&device_shash);
     }
@@ -537,6 +538,7 @@ netdev_linux_create(const struct netdev_class *class,
     cache_notifier_refcount++;
 
     netdev_dev = xzalloc(sizeof *netdev_dev);
+    netdev_dev->change_seq = 1;
     netdev_dev_init(&netdev_dev->netdev_dev, name, args, class);
 
     *netdev_devp = &netdev_dev->netdev_dev;
@@ -1161,14 +1163,8 @@ netdev_linux_miimon_run(void)
 
         netdev_linux_get_miimon(dev->netdev_dev.name, &miimon);
         if (miimon != dev->miimon) {
-            struct list *list;
-
             dev->miimon = miimon;
-            list = shash_find_data(&netdev_linux_notifiers,
-                                   dev->netdev_dev.name);
-            if (list) {
-                poll_notify(list);
-            }
+            netdev_dev_linux_changed(dev);
         }
 
         timer_set_duration(&dev->miimon_timer, dev->miimon_interval);
@@ -2234,88 +2230,10 @@ netdev_linux_update_flags(struct netdev *netdev, enum netdev_flags off,
     return error;
 }
 
-static void
-poll_notify(struct list *list)
-{
-    struct netdev_linux_notifier *notifier;
-    LIST_FOR_EACH (notifier, node, list) {
-        struct netdev_notifier *n = &notifier->notifier;
-        n->cb(n);
-    }
-}
-
-static void
-netdev_linux_poll_cb(const struct rtnetlink_link_change *change,
-                     void *aux OVS_UNUSED)
-{
-    if (change) {
-        struct list *list = shash_find_data(&netdev_linux_notifiers,
-                                            change->ifname);
-        if (list) {
-            poll_notify(list);
-        }
-    } else {
-        struct shash_node *node;
-        SHASH_FOR_EACH (node, &netdev_linux_notifiers) {
-            poll_notify(node->data);
-        }
-    }
-}
-
-static int
-netdev_linux_poll_add(struct netdev *netdev,
-                      void (*cb)(struct netdev_notifier *), void *aux,
-                      struct netdev_notifier **notifierp)
-{
-    const char *netdev_name = netdev_get_name(netdev);
-    struct netdev_linux_notifier *notifier;
-    struct list *list;
-
-    if (shash_is_empty(&netdev_linux_notifiers)) {
-        int error;
-        error = rtnetlink_link_notifier_register(&netdev_linux_poll_notifier,
-                                                 netdev_linux_poll_cb, NULL);
-        if (error) {
-            return error;
-        }
-    }
-
-    list = shash_find_data(&netdev_linux_notifiers, netdev_name);
-    if (!list) {
-        list = xmalloc(sizeof *list);
-        list_init(list);
-        shash_add(&netdev_linux_notifiers, netdev_name, list);
-    }
-
-    notifier = xmalloc(sizeof *notifier);
-    netdev_notifier_init(&notifier->notifier, netdev, cb, aux);
-    list_push_back(list, &notifier->node);
-    *notifierp = &notifier->notifier;
-    return 0;
-}
-
-static void
-netdev_linux_poll_remove(struct netdev_notifier *notifier_)
+static unsigned int
+netdev_linux_change_seq(const struct netdev *netdev)
 {
-    struct netdev_linux_notifier *notifier =
-        CONTAINER_OF(notifier_, struct netdev_linux_notifier, notifier);
-    struct list *list;
-
-    /* Remove 'notifier' from its list. */
-    list = list_remove(&notifier->node);
-    if (list_is_empty(list)) {
-        /* The list is now empty.  Remove it from the hash and free it. */
-        const char *netdev_name = netdev_get_name(notifier->notifier.netdev);
-        shash_delete(&netdev_linux_notifiers,
-                     shash_find(&netdev_linux_notifiers, netdev_name));
-        free(list);
-    }
-    free(notifier);
-
-    /* If that was the last notifier, unregister. */
-    if (shash_is_empty(&netdev_linux_notifiers)) {
-        rtnetlink_link_notifier_unregister(&netdev_linux_poll_notifier);
-    }
+    return netdev_dev_linux_cast(netdev_get_dev(netdev))->change_seq;
 }
 
 #define NETDEV_LINUX_CLASS(NAME, CREATE, ENUMERATE, SET_STATS)  \
@@ -2329,6 +2247,7 @@ netdev_linux_poll_remove(struct netdev_notifier *notifier_)
     CREATE,                                                     \
     netdev_linux_destroy,                                       \
     NULL,                       /* set_config */                \
+    NULL,                       /* config_equal */              \
                                                                 \
     netdev_linux_open,                                          \
     netdev_linux_close,                                         \
@@ -2377,8 +2296,7 @@ netdev_linux_poll_remove(struct netdev_notifier *notifier_)
                                                                 \
     netdev_linux_update_flags,                                  \
                                                                 \
-    netdev_linux_poll_add,                                      \
-    netdev_linux_poll_remove                                    \
+    netdev_linux_change_seq                                     \
 }
 
 const struct netdev_class netdev_linux_class =
@@ -4234,8 +4152,12 @@ get_etheraddr(const char *netdev_name, uint8_t ea[ETH_ADDR_LEN])
     ovs_strzcpy(ifr.ifr_name, netdev_name, sizeof ifr.ifr_name);
     COVERAGE_INC(netdev_get_hwaddr);
     if (ioctl(af_inet_sock, SIOCGIFHWADDR, &ifr) < 0) {
-        VLOG_ERR("ioctl(SIOCGIFHWADDR) on %s device failed: %s",
-                 netdev_name, strerror(errno));
+        /* ENODEV probably means that a vif disappeared asynchronously and
+         * hasn't been removed from the database yet, so reduce the log level
+         * to INFO for that case. */
+        VLOG(errno == ENODEV ? VLL_INFO : VLL_ERR,
+             "ioctl(SIOCGIFHWADDR) on %s device failed: %s",
+             netdev_name, strerror(errno));
         return errno;
     }
     hwaddr_family = ifr.ifr_hwaddr.sa_family;