--- /dev/null
+From: Johannes Berg <johannes.berg@intel.com>
+Date: Thu, 8 Jan 2026 14:34:31 +0100
+Subject: [PATCH] wifi: mac80211: improve interface iteration ergonomics
+
+Right now, the only way to iterate interfaces is to declare an
+iterator function, possibly data structure to use, and pass all
+that to the iteration helper function. This is annoying, and
+there's really no inherent need for it, except it was easier to
+implement with the iflist mutex, but that's not used much now.
+
+Add a new for_each_interface() macro that does the iteration in
+a more ergonomic way. To avoid even more exported functions, do
+the old ieee80211_iterate_active_interfaces_mtx() as an inline
+using the new way, which may also let the compiler optimise it
+a bit more, e.g. via inlining the iterator function.
+
+Also provide for_each_active_interface() for the common case of
+just iterating active interfaces.
+
+Link: https://patch.msgid.link/20260108143431.f2581e0c381a.Ie387227504c975c109c125b3c57f0bb3fdab2835@changeid
+Signed-off-by: Johannes Berg <johannes.berg@intel.com>
+---
+
+--- a/include/net/mac80211.h
++++ b/include/net/mac80211.h
+@@ -6270,6 +6270,30 @@ void ieee80211_iterate_active_interfaces
+ struct ieee80211_vif *vif),
+ void *data);
+
++struct ieee80211_vif *
++__ieee80211_iterate_interfaces(struct ieee80211_hw *hw,
++ struct ieee80211_vif *prev,
++ u32 iter_flags);
++
++/**
++ * for_each_interface - iterate interfaces under wiphy mutex
++ * @vif: the iterator variable
++ * @hw: the HW to iterate for
++ * @flags: the iteration flags, see &enum ieee80211_interface_iteration_flags
++ */
++#define for_each_interface(vif, hw, flags) \
++ for (vif = __ieee80211_iterate_interfaces(hw, NULL, flags); \
++ vif; \
++ vif = __ieee80211_iterate_interfaces(hw, vif, flags))
++
++/**
++ * for_each_active_interface - iterate active interfaces under wiphy mutex
++ * @vif: the iterator variable
++ * @hw: the HW to iterate for
++ */
++#define for_each_active_interface(vif, hw) \
++ for_each_interface(vif, hw, IEEE80211_IFACE_ITER_ACTIVE)
++
+ /**
+ * ieee80211_iterate_active_interfaces_mtx - iterate active interfaces
+ *
+@@ -6282,12 +6306,18 @@ void ieee80211_iterate_active_interfaces
+ * @iterator: the iterator function to call, cannot sleep
+ * @data: first argument of the iterator function
+ */
+-void ieee80211_iterate_active_interfaces_mtx(struct ieee80211_hw *hw,
+- u32 iter_flags,
+- void (*iterator)(void *data,
+- u8 *mac,
+- struct ieee80211_vif *vif),
+- void *data);
++static inline void
++ieee80211_iterate_active_interfaces_mtx(struct ieee80211_hw *hw,
++ u32 iter_flags,
++ void (*iterator)(void *data, u8 *mac,
++ struct ieee80211_vif *vif),
++ void *data)
++{
++ struct ieee80211_vif *vif;
++
++ for_each_interface(vif, hw, iter_flags | IEEE80211_IFACE_ITER_ACTIVE)
++ iterator(data, vif->addr, vif);
++}
+
+ /**
+ * ieee80211_iterate_stations_atomic - iterate stations
+--- a/net/mac80211/util.c
++++ b/net/mac80211/util.c
+@@ -800,20 +800,56 @@ void ieee80211_iterate_active_interfaces
+ }
+ EXPORT_SYMBOL_GPL(ieee80211_iterate_active_interfaces_atomic);
+
+-void ieee80211_iterate_active_interfaces_mtx(
+- struct ieee80211_hw *hw, u32 iter_flags,
+- void (*iterator)(void *data, u8 *mac,
+- struct ieee80211_vif *vif),
+- void *data)
++struct ieee80211_vif *
++__ieee80211_iterate_interfaces(struct ieee80211_hw *hw,
++ struct ieee80211_vif *prev,
++ u32 iter_flags)
+ {
++ bool active_only = iter_flags & IEEE80211_IFACE_ITER_ACTIVE;
++ struct ieee80211_sub_if_data *sdata = NULL, *monitor;
+ struct ieee80211_local *local = hw_to_local(hw);
+
+ lockdep_assert_wiphy(hw->wiphy);
+
+- __iterate_interfaces(local, iter_flags | IEEE80211_IFACE_ITER_ACTIVE,
+- iterator, data);
++ if (prev)
++ sdata = vif_to_sdata(prev);
++
++ monitor = rcu_dereference_check(local->monitor_sdata,
++ lockdep_is_held(&hw->wiphy->mtx));
++ if (monitor && monitor == sdata)
++ return NULL;
++
++ sdata = list_prepare_entry(sdata, &local->interfaces, list);
++ list_for_each_entry_continue(sdata, &local->interfaces, list) {
++ switch (sdata->vif.type) {
++ case NL80211_IFTYPE_MONITOR:
++ if (!(sdata->u.mntr.flags & MONITOR_FLAG_ACTIVE) &&
++ !ieee80211_hw_check(&local->hw, NO_VIRTUAL_MONITOR))
++ continue;
++ break;
++ case NL80211_IFTYPE_AP_VLAN:
++ continue;
++ default:
++ break;
++ }
++ if (!(iter_flags & IEEE80211_IFACE_ITER_RESUME_ALL) &&
++ active_only && !(sdata->flags & IEEE80211_SDATA_IN_DRIVER))
++ continue;
++ if ((iter_flags & IEEE80211_IFACE_SKIP_SDATA_NOT_IN_DRIVER) &&
++ !(sdata->flags & IEEE80211_SDATA_IN_DRIVER))
++ continue;
++ if (ieee80211_sdata_running(sdata) || !active_only)
++ return &sdata->vif;
++ }
++
++ if (monitor && ieee80211_hw_check(&local->hw, WANT_MONITOR_VIF) &&
++ (iter_flags & IEEE80211_IFACE_ITER_RESUME_ALL || !active_only ||
++ monitor->flags & IEEE80211_SDATA_IN_DRIVER))
++ return &monitor->vif;
++
++ return NULL;
+ }
+-EXPORT_SYMBOL_GPL(ieee80211_iterate_active_interfaces_mtx);
++EXPORT_SYMBOL_GPL(__ieee80211_iterate_interfaces);
+
+ static void __iterate_stations(struct ieee80211_local *local,
+ void (*iterator)(void *data,
--- /dev/null
+From: Johannes Berg <johannes.berg@intel.com>
+Date: Thu, 8 Jan 2026 14:34:32 +0100
+Subject: [PATCH] wifi: mac80211: improve station iteration ergonomics
+
+Right now, the only way to iterate stations is to declare an
+iterator function, possibly data structure to use, and pass all
+that to the iteration helper function. This is annoying, and
+there's really no inherent need for it.
+
+Add a new for_each_station() macro that does the iteration in
+a more ergonomic way. To avoid even more exported functions, do
+the old ieee80211_iterate_stations_mtx() as an inline using the
+new way, which may also let the compiler optimise it a bit more,
+e.g. via inlining the iterator function.
+
+Link: https://patch.msgid.link/20260108143431.d2b641f6f6af.I4470024f7404446052564b15bcf8b3f1ada33655@changeid
+Signed-off-by: Johannes Berg <johannes.berg@intel.com>
+---
+
+--- a/include/net/mac80211.h
++++ b/include/net/mac80211.h
+@@ -6336,6 +6336,20 @@ void ieee80211_iterate_stations_atomic(s
+ struct ieee80211_sta *sta),
+ void *data);
+
++struct ieee80211_sta *
++__ieee80211_iterate_stations(struct ieee80211_hw *hw,
++ struct ieee80211_sta *prev);
++
++/**
++ * for_each_station - iterate stations under wiphy mutex
++ * @sta: the iterator variable
++ * @hw: the HW to iterate for
++ */
++#define for_each_station(sta, hw) \
++ for (sta = __ieee80211_iterate_stations(hw, NULL); \
++ sta; \
++ sta = __ieee80211_iterate_stations(hw, sta))
++
+ /**
+ * ieee80211_iterate_stations_mtx - iterate stations
+ *
+@@ -6348,10 +6362,17 @@ void ieee80211_iterate_stations_atomic(s
+ * @iterator: the iterator function to call
+ * @data: first argument of the iterator function
+ */
+-void ieee80211_iterate_stations_mtx(struct ieee80211_hw *hw,
+- void (*iterator)(void *data,
+- struct ieee80211_sta *sta),
+- void *data);
++static inline void
++ieee80211_iterate_stations_mtx(struct ieee80211_hw *hw,
++ void (*iterator)(void *data,
++ struct ieee80211_sta *sta),
++ void *data)
++{
++ struct ieee80211_sta *sta;
++
++ for_each_station(sta, hw)
++ iterator(data, sta);
++}
+
+ /**
+ * ieee80211_queue_work - add work onto the mac80211 workqueue
+--- a/net/mac80211/util.c
++++ b/net/mac80211/util.c
+@@ -880,18 +880,29 @@ void ieee80211_iterate_stations_atomic(s
+ }
+ EXPORT_SYMBOL_GPL(ieee80211_iterate_stations_atomic);
+
+-void ieee80211_iterate_stations_mtx(struct ieee80211_hw *hw,
+- void (*iterator)(void *data,
+- struct ieee80211_sta *sta),
+- void *data)
++struct ieee80211_sta *
++__ieee80211_iterate_stations(struct ieee80211_hw *hw,
++ struct ieee80211_sta *prev)
+ {
+ struct ieee80211_local *local = hw_to_local(hw);
++ struct sta_info *sta = NULL;
+
+ lockdep_assert_wiphy(local->hw.wiphy);
+
+- __iterate_stations(local, iterator, data);
++ if (prev)
++ sta = container_of(prev, struct sta_info, sta);
++
++ sta = list_prepare_entry(sta, &local->sta_list, list);
++ list_for_each_entry_continue(sta, &local->sta_list, list) {
++ if (!sta->uploaded)
++ continue;
++
++ return &sta->sta;
++ }
++
++ return NULL;
+ }
+-EXPORT_SYMBOL_GPL(ieee80211_iterate_stations_mtx);
++EXPORT_SYMBOL_GPL(__ieee80211_iterate_stations);
+
+ struct ieee80211_vif *wdev_to_ieee80211_vif(struct wireless_dev *wdev)
+ {