[PATCH net-next v9 2/6] net: change netdev_hw_addr_list count through helpers

From: Yuyang Huang

Date: Wed Sep 30 2026 - 07:39:41 EST


The count of a netdev_hw_addr_list is changed in several places of
dev_addr_lists.c and a few drivers read it directly. The next patch
needs to account every change of the count of dev->mc.

Add __hw_addr_count_add(), __hw_addr_count_inc(), __hw_addr_count_dec()
and __hw_addr_count_reset(), use them for every change of the count
and rename the field to _count so that a direct write stands out.
Readers keep using netdev_hw_addr_list_count() and the netdev_uc_count()
and netdev_mc_count() helpers, the few that read the field directly
are converted. No functional change.

Signed-off-by: Yuyang Huang <sigefriedhyy@xxxxxxxxx>
---
.../net/ethernet/cavium/octeon/octeon_mgmt.c | 4 +-
.../net/wireless/microchip/wilc1000/netdev.c | 8 ++--
include/linux/netdevice.h | 5 ++-
net/core/dev_addr_lists.c | 44 ++++++++++++++-----
net/core/dev_addr_lists_test.c | 18 ++++----
net/mac80211/driver-ops.h | 2 +-
6 files changed, 51 insertions(+), 30 deletions(-)

diff --git a/drivers/net/ethernet/cavium/octeon/octeon_mgmt.c b/drivers/net/ethernet/cavium/octeon/octeon_mgmt.c
index c190fc6538d4c..5e3c379b24234 100644
--- a/drivers/net/ethernet/cavium/octeon/octeon_mgmt.c
+++ b/drivers/net/ethernet/cavium/octeon/octeon_mgmt.c
@@ -573,14 +573,14 @@ static void octeon_mgmt_set_rx_filtering(struct net_device *netdev)

memset(&cam_state, 0, sizeof(cam_state));

- if ((netdev->flags & IFF_PROMISC) || netdev->uc.count > 7) {
+ if ((netdev->flags & IFF_PROMISC) || netdev_uc_count(netdev) > 7) {
cam_mode = 0;
available_cam_entries = 8;
} else {
/* One CAM entry for the primary address, leaves seven
* for the secondary addresses.
*/
- available_cam_entries = 7 - netdev->uc.count;
+ available_cam_entries = 7 - netdev_uc_count(netdev);
}

if (netdev->flags & IFF_MULTICAST) {
diff --git a/drivers/net/wireless/microchip/wilc1000/netdev.c b/drivers/net/wireless/microchip/wilc1000/netdev.c
index 956cb578bf37c..d3343113cea50 100644
--- a/drivers/net/wireless/microchip/wilc1000/netdev.c
+++ b/drivers/net/wireless/microchip/wilc1000/netdev.c
@@ -704,17 +704,17 @@ static void wilc_set_multicast_list(struct net_device *dev)
return;

if (dev->flags & IFF_ALLMULTI ||
- dev->mc.count > WILC_MULTICAST_TABLE_SIZE) {
+ netdev_mc_count(dev) > WILC_MULTICAST_TABLE_SIZE) {
wilc_setup_multicast_filter(vif, 0, 0, NULL);
return;
}

- if (dev->mc.count == 0) {
+ if (netdev_mc_empty(dev)) {
wilc_setup_multicast_filter(vif, 1, 0, NULL);
return;
}

- mc_list = kmalloc_array(dev->mc.count, ETH_ALEN, GFP_ATOMIC);
+ mc_list = kmalloc_array(netdev_mc_count(dev), ETH_ALEN, GFP_ATOMIC);
if (!mc_list)
return;

@@ -727,7 +727,7 @@ static void wilc_set_multicast_list(struct net_device *dev)
cur_mc += ETH_ALEN;
}

- if (wilc_setup_multicast_filter(vif, 1, dev->mc.count, mc_list))
+ if (wilc_setup_multicast_filter(vif, 1, netdev_mc_count(dev), mc_list))
kfree(mc_list);
}

diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h
index d037faff7c44b..97dc053f234cc 100644
--- a/include/linux/netdevice.h
+++ b/include/linux/netdevice.h
@@ -252,13 +252,14 @@ struct netdev_hw_addr {

struct netdev_hw_addr_list {
struct list_head list;
- int count;
+ /* Only changed through the __hw_addr_count_* helpers */
+ int _count;

/* Auxiliary tree for faster lookup on addition and deletion */
struct rb_root tree;
};

-#define netdev_hw_addr_list_count(l) ((l)->count)
+#define netdev_hw_addr_list_count(l) ((l)->_count)
#define netdev_hw_addr_list_empty(l) (netdev_hw_addr_list_count(l) == 0)
#define netdev_hw_addr_list_for_each(ha, l) \
list_for_each_entry(ha, &(l)->list, list)
diff --git a/net/core/dev_addr_lists.c b/net/core/dev_addr_lists.c
index 08528ca0a8b31..23f5db99a702d 100644
--- a/net/core/dev_addr_lists.c
+++ b/net/core/dev_addr_lists.c
@@ -16,6 +16,26 @@

#include "dev.h"

+static void __hw_addr_count_add(struct netdev_hw_addr_list *list, int value)
+{
+ list->_count += value;
+}
+
+static void __hw_addr_count_inc(struct netdev_hw_addr_list *list)
+{
+ __hw_addr_count_add(list, 1);
+}
+
+static void __hw_addr_count_dec(struct netdev_hw_addr_list *list)
+{
+ __hw_addr_count_add(list, -1);
+}
+
+static void __hw_addr_count_reset(struct netdev_hw_addr_list *list)
+{
+ list->_count = 0;
+}
+
/*
* General list handling functions
*/
@@ -125,7 +145,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 +181,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,14 +512,14 @@ 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);

void __hw_addr_init(struct netdev_hw_addr_list *list)
{
INIT_LIST_HEAD(&list->list);
- list->count = 0;
+ list->_count = 0;
list->tree = RB_ROOT;
}
EXPORT_SYMBOL(__hw_addr_init);
@@ -509,8 +529,8 @@ static void __hw_addr_splice(struct netdev_hw_addr_list *dst,
{
src->tree = RB_ROOT;
list_splice_init(&src->list, &dst->list);
- dst->count += src->count;
- src->count = 0;
+ __hw_addr_count_add(dst, src->_count);
+ __hw_addr_count_reset(src);
}

/**
@@ -532,11 +552,11 @@ int __hw_addr_list_snapshot(struct netdev_hw_addr_list *snap,
struct netdev_hw_addr *ha, *entry;

list_for_each_entry(ha, &list->list, list) {
- if (cache->count) {
+ if (cache->_count) {
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 +574,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 +624,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 +642,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);
}
}

diff --git a/net/core/dev_addr_lists_test.c b/net/core/dev_addr_lists_test.c
index 260e71a2399f3..07c35a0af2b4d 100644
--- a/net/core/dev_addr_lists_test.c
+++ b/net/core/dev_addr_lists_test.c
@@ -291,7 +291,7 @@ static void dev_addr_test_snapshot_sync(struct kunit *test)
netif_addr_unlock_bh(netdev);

/* Real entry should now reflect the sync: sync_cnt=1, refcount=2 */
- KUNIT_EXPECT_EQ(test, 1, netdev->uc.count);
+ KUNIT_EXPECT_EQ(test, 1, netdev_uc_count(netdev));
ha = list_first_entry(&netdev->uc.list, struct netdev_hw_addr, list);
KUNIT_EXPECT_MEMEQ(test, ha->addr, addr, ETH_ALEN);
KUNIT_EXPECT_EQ(test, 1, ha->sync_cnt);
@@ -303,7 +303,7 @@ static void dev_addr_test_snapshot_sync(struct kunit *test)
dev_addr_test_unsync);
KUNIT_EXPECT_EQ(test, 0, datp->addr_synced);
KUNIT_EXPECT_EQ(test, 0, datp->addr_unsynced);
- KUNIT_EXPECT_EQ(test, 1, netdev->uc.count);
+ KUNIT_EXPECT_EQ(test, 1, netdev_uc_count(netdev));

__hw_addr_flush(&cache);
rtnl_unlock();
@@ -351,7 +351,7 @@ static void dev_addr_test_snapshot_remove_during_sync(struct kunit *test)
/* Concurrent removal: user deletes ADDR_A while driver was working */
memset(addr, ADDR_A, sizeof(addr));
KUNIT_EXPECT_EQ(test, 0, dev_uc_del(netdev, addr));
- KUNIT_EXPECT_EQ(test, 0, netdev->uc.count);
+ KUNIT_EXPECT_EQ(test, 0, netdev_uc_count(netdev));

/* Reconcile: ADDR_A gone from real list but driver synced it,
* so it gets re-inserted as stale (sync_cnt=1, refcount=1).
@@ -361,7 +361,7 @@ static void dev_addr_test_snapshot_remove_during_sync(struct kunit *test)
&cache);
netif_addr_unlock_bh(netdev);

- KUNIT_EXPECT_EQ(test, 1, netdev->uc.count);
+ KUNIT_EXPECT_EQ(test, 1, netdev_uc_count(netdev));
ha = list_first_entry(&netdev->uc.list, struct netdev_hw_addr, list);
KUNIT_EXPECT_MEMEQ(test, ha->addr, addr, ETH_ALEN);
KUNIT_EXPECT_EQ(test, 1, ha->sync_cnt);
@@ -373,7 +373,7 @@ static void dev_addr_test_snapshot_remove_during_sync(struct kunit *test)
dev_addr_test_unsync);
KUNIT_EXPECT_EQ(test, 0, datp->addr_synced);
KUNIT_EXPECT_EQ(test, 1 << ADDR_A, datp->addr_unsynced);
- KUNIT_EXPECT_EQ(test, 0, netdev->uc.count);
+ KUNIT_EXPECT_EQ(test, 0, netdev_uc_count(netdev));

__hw_addr_flush(&cache);
rtnl_unlock();
@@ -433,7 +433,7 @@ static void dev_addr_test_snapshot_readd_during_unsync(struct kunit *test)
* stale entry and bumps refcount from 1 -> 2. sync_cnt stays 1.
*/
KUNIT_EXPECT_EQ(test, 0, dev_uc_add(netdev, addr));
- KUNIT_EXPECT_EQ(test, 1, netdev->uc.count);
+ KUNIT_EXPECT_EQ(test, 1, netdev_uc_count(netdev));

/* Reconcile: ref sync_cnt=1 matches real sync_cnt=1, delta=-1
* applied. Result: sync_cnt=0, refcount=1 (fresh).
@@ -444,7 +444,7 @@ static void dev_addr_test_snapshot_readd_during_unsync(struct kunit *test)
netif_addr_unlock_bh(netdev);

/* Entry survives as fresh: needs re-sync to HW */
- KUNIT_EXPECT_EQ(test, 1, netdev->uc.count);
+ KUNIT_EXPECT_EQ(test, 1, netdev_uc_count(netdev));
ha = list_first_entry(&netdev->uc.list, struct netdev_hw_addr, list);
KUNIT_EXPECT_MEMEQ(test, ha->addr, addr, ETH_ALEN);
KUNIT_EXPECT_EQ(test, 0, ha->sync_cnt);
@@ -528,7 +528,7 @@ static void dev_addr_test_snapshot_add_and_remove(struct kunit *test)
* ADDR_B: refcount went from 2->1 via dev_uc_del (still present, stale)
* ADDR_C: sync propagated (sync_cnt=1, refcount=2)
*/
- KUNIT_EXPECT_EQ(test, 3, netdev->uc.count);
+ KUNIT_EXPECT_EQ(test, 3, netdev_uc_count(netdev));
netdev_hw_addr_list_for_each(ha, &netdev->uc) {
u8 id = ha->addr[0];

@@ -553,7 +553,7 @@ static void dev_addr_test_snapshot_add_and_remove(struct kunit *test)
dev_addr_test_unsync);
KUNIT_EXPECT_EQ(test, 0, datp->addr_synced);
KUNIT_EXPECT_EQ(test, 1 << ADDR_B, datp->addr_unsynced);
- KUNIT_EXPECT_EQ(test, 2, netdev->uc.count);
+ KUNIT_EXPECT_EQ(test, 2, netdev_uc_count(netdev));

__hw_addr_flush(&cache);
rtnl_unlock();
diff --git a/net/mac80211/driver-ops.h b/net/mac80211/driver-ops.h
index f1c0b87fddd5f..e80731c59ef50 100644
--- a/net/mac80211/driver-ops.h
+++ b/net/mac80211/driver-ops.h
@@ -187,7 +187,7 @@ static inline u64 drv_prepare_multicast(struct ieee80211_local *local,
{
u64 ret = 0;

- trace_drv_prepare_multicast(local, mc_list->count);
+ trace_drv_prepare_multicast(local, netdev_hw_addr_list_count(mc_list));

if (local->ops->prepare_multicast)
ret = local->ops->prepare_multicast(&local->hw, mc_list);
--
2.43.0