diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h
index d037faff7c44..69d1cde0a38a 100644
--- a/include/linux/netdevice.h
+++ b/include/linux/netdevice.h
@@ -256,6 +256,11 @@ struct netdev_hw_addr_list {
/* Auxiliary tree for faster lookup on addition and deletion */
struct rb_root tree;
+
+ /* The device a list belongs to, NULL for snapshots and other
+ * standalone lists
+ */
+ struct net_device *owner;
};
#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..d8c681ab5c74 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; /* bumped on dev->mc changes */
u32 ifindex;
spinlock_t nsid_lock;diff --git a/net/core/dev_addr_lists.c b/net/core/dev_addr_lists.c
index 08528ca0a8b3..c941c3ec1007 100644
--- a/net/core/dev_addr_lists.c
+++ b/net/core/dev_addr_lists.c
@@ -16,6 +16,37 @@
#include "dev.h"
+/* Only dev->mc is tracked, RTM_GETMULTICAST dumps use the netns generation
+ * counter to detect changes between dump rounds.
+ */
+static void __hw_addr_changed(struct netdev_hw_addr_list *list)
+{
+ struct net_device *dev = list->owner;
+
+ if (dev && list == &dev->mc)
+ atomic_inc(&dev_net(dev)->dev_mc_genid);
+}
+
+static void __hw_addr_count_inc(struct netdev_hw_addr_list *list)
+{
+ list->count++;
+ __hw_addr_changed(list);
+}
+
+static void __hw_addr_count_dec(struct netdev_hw_addr_list *list)
+{
+ list->count--;
+ __hw_addr_changed(list);
+}
+
+static void __hw_addr_count_reset(struct netdev_hw_addr_list *list)
+{
+ if (!list->count)
+ return;
+ list->count = 0;
+ __hw_addr_changed(list);
+}
+
/*
* General list handling functions
*/@@ -125,7 +156,7 @@ static int __hw_addr_add_ex(struct netdev_hw_addr_list *list,
rb_insert_color(&ha->node, &list->tree);
list_add_tail_rcu(&ha->list, &list->list);
- list->count++;
+ __hw_addr_count_inc(list);
return 0;
}
@@ -161,7 +192,7 @@ static int __hw_addr_del_entry(struct netdev_hw_addr_list *list,
list_del_rcu(&ha->list);
kfree_rcu(ha, rcu_head);
- list->count--;
+ __hw_addr_count_dec(list);
return 0;
}
@@ -492,7 +523,7 @@ void __hw_addr_flush(struct netdev_hw_addr_list *list)
list_del_rcu(&ha->list);
kfree_rcu(ha, rcu_head);
}
- list->count = 0;
+ __hw_addr_count_reset(list);
}
EXPORT_SYMBOL_IF_KUNIT(__hw_addr_flush);
@@ -501,6 +532,7 @@ void __hw_addr_init(struct netdev_hw_addr_list *list)
INIT_LIST_HEAD(&list->list);
list->count = 0;
list->tree = RB_ROOT;
+ list->owner = NULL;
}
EXPORT_SYMBOL(__hw_addr_init);
@@ -536,7 +568,7 @@ int __hw_addr_list_snapshot(struct netdev_hw_addr_list *snap,
entry = list_first_entry(&cache->list,
struct netdev_hw_addr, list);
list_del(&entry->list);
- cache->count--;
+ __hw_addr_count_dec(cache);
memcpy(entry->addr, ha->addr, addr_len);
entry->type = ha->type;
entry->global_use = false;
@@ -554,7 +586,7 @@ int __hw_addr_list_snapshot(struct netdev_hw_addr_list *snap,
list_add_tail(&entry->list, &snap->list);
__hw_addr_insert(snap, entry, addr_len);
- snap->count++;
+ __hw_addr_count_inc(snap);
}
return 0;
@@ -604,14 +636,14 @@ void __hw_addr_list_reconcile(struct netdev_hw_addr_list *real_list,
if (delta > 0) {
rb_erase(&ref_ha->node, &ref->tree);
list_del(&ref_ha->list);
- ref->count--;
+ __hw_addr_count_dec(ref);
ref_ha->sync_cnt = delta;
ref_ha->refcount = delta;
list_add_tail_rcu(&ref_ha->list,
&real_list->list);
__hw_addr_insert(real_list, ref_ha,
addr_len);
- real_list->count++;
+ __hw_addr_count_inc(real_list);
}
continue;
}@@ -622,7 +654,7 @@ void __hw_addr_list_reconcile(struct netdev_hw_addr_list *real_list,
rb_erase(&real_ha->node, &real_list->tree);
list_del_rcu(&real_ha->list);
kfree_rcu(real_ha, rcu_head);
- real_list->count--;
+ __hw_addr_count_dec(real_list);
}
}
@@ -685,6 +717,7 @@ int dev_addr_init(struct net_device *dev)
/* rtnl_mutex must be held here */
__hw_addr_init(&dev->dev_addrs);
+ dev->dev_addrs.owner = dev;
memset(addr, 0, sizeof(addr));
err = __hw_addr_add(&dev->dev_addrs, addr, sizeof(addr),
NETDEV_HW_ADDR_T_LAN);
@@ -962,6 +995,7 @@ EXPORT_SYMBOL(dev_uc_flush);
void dev_uc_init(struct net_device *dev)
{
__hw_addr_init(&dev->uc);
+ dev->uc.owner = dev;
}
EXPORT_SYMBOL(dev_uc_init);
@@ -1177,6 +1211,7 @@ EXPORT_SYMBOL(dev_mc_flush);
void dev_mc_init(struct net_device *dev)
{
__hw_addr_init(&dev->mc);
+ dev->mc.owner = dev;
}
EXPORT_SYMBOL(dev_mc_init);
@@ -1348,6 +1383,7 @@ static void netif_rx_mode_retry(struct timer_list *t)
void netif_rx_mode_init(struct net_device *dev)
{
__hw_addr_init(&dev->rx_mode_addr_cache);
+ dev->rx_mode_addr_cache.owner = dev;
timer_setup(&dev->rx_mode_retry_timer, netif_rx_mode_retry, 0);
}
--
2.43.0