diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index cf25c16bdba8..f52c46e28e3a 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -259,6 +259,9 @@ struct netdev_hw_addr_list { /* Auxiliary tree for faster lookup on addition and deletion */ struct rb_root tree; + + /* Set for dev->mc only, the device the list belongs to */ + struct net_device *mc_dev; }; #define netdev_hw_addr_list_count(l) ((l)->_count) diff --git a/include/net/net_namespace.h b/include/net/net_namespace.h index 46b4c67e2966..c29a22ca178f 100644 --- a/include/net/net_namespace.h +++ b/include/net/net_namespace.h @@ -71,6 +71,7 @@ struct net { spinlock_t rules_mod_lock; unsigned int dev_base_seq; /* protected by rtnl_mutex */ + atomic_t dev_mc_genid; /* dev->mc membership changes */ u32 ifindex; spinlock_t nsid_lock; diff --git a/net/core/dev_addr_lists.c b/net/core/dev_addr_lists.c index d615192d1c3b..3ab053cbf8b9 100644 --- a/net/core/dev_addr_lists.c +++ b/net/core/dev_addr_lists.c @@ -16,9 +16,19 @@ #include "dev.h" +/* RTM_GETMULTICAST dumps use the netns generation counter to detect + * changes of dev->mc between dump rounds. + */ +static void __hw_addr_changed(struct netdev_hw_addr_list *list) +{ + if (list->mc_dev) + atomic_inc(&dev_net(list->mc_dev)->dev_mc_genid); +} + static void __hw_addr_count_add(struct netdev_hw_addr_list *list, int value) { list->_count += value; + __hw_addr_changed(list); } static void __hw_addr_count_inc(struct netdev_hw_addr_list *list) @@ -33,7 +43,10 @@ static void __hw_addr_count_dec(struct netdev_hw_addr_list *list) static void __hw_addr_count_reset(struct netdev_hw_addr_list *list) { + if (!list->_count) + return; list->_count = 0; + __hw_addr_changed(list); } /* @@ -521,6 +534,7 @@ void __hw_addr_init(struct netdev_hw_addr_list *list) INIT_LIST_HEAD(&list->list); list->_count = 0; list->tree = RB_ROOT; + list->mc_dev = NULL; } EXPORT_SYMBOL(__hw_addr_init); @@ -1197,6 +1211,7 @@ EXPORT_SYMBOL(dev_mc_flush); void dev_mc_init(struct net_device *dev) { __hw_addr_init(&dev->mc); + dev->mc.mc_dev = dev; } EXPORT_SYMBOL(dev_mc_init);