* [PATCH wireless-next v2 01/18] wifi: further RTNL redux
@ 2026-10-05 10:03 Johannes Berg
2026-10-05 10:03 ` [PATCH wireless-next v2 01/18] wifi: cfg80211: track netdev running state under wiphy mutex Johannes Berg
` (17 more replies)
0 siblings, 18 replies; 23+ messages in thread
From: Johannes Berg @ 2026-10-05 10:03 UTC (permalink / raw)
To: linux-wireless
v1: https://lore.kernel.org/linux-wireless/20261004190122.1559001-20-johannes@sipsolutions.net/
v2:
- patch 3: fix netns check/crash, fix race in tests
No new notes, so as before:
We've been discussing some issues around lock contention for a
while now, and syzbot keeps reporting RTNL hangs against wifi.
Inspired by both I looked at further reducing RTNL in the wifi
stack, addressing the two biggest concerns:
1) After this, we no longer hold RTNL on (mostly) nl80211
command paths, unless of course it's required for the
specific command.
2) Regulatory is disentangled and no longer requires RTNL.
johannes
^ permalink raw reply [flat|nested] 23+ messages in thread
* [PATCH wireless-next v2 01/18] wifi: cfg80211: track netdev running state under wiphy mutex
2026-10-05 10:03 [PATCH wireless-next v2 01/18] wifi: further RTNL redux Johannes Berg
@ 2026-10-05 10:03 ` Johannes Berg
2026-10-05 10:03 ` [PATCH wireless-next v2 02/18] wifi: nl80211: allow device lookup under RCU Johannes Berg
` (16 subsequent siblings)
17 siblings, 0 replies; 23+ messages in thread
From: Johannes Berg @ 2026-10-05 10:03 UTC (permalink / raw)
To: linux-wireless; +Cc: Johannes Berg
From: Johannes Berg <johannes.berg@intel.com>
wdev_running() uses netif_running() for netdevs, which
can only be used under RTNL in a completely race-free
way.
Track the state for all wdevs via the existing netdev
notifier so we can check the state under wiphy mutex
in later changes.
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
---
include/net/cfg80211.h | 7 +++----
net/wireless/core.c | 3 +++
2 files changed, 6 insertions(+), 4 deletions(-)
diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h
index b603f1804cc2..9cb536fc8cb8 100644
--- a/include/net/cfg80211.h
+++ b/include/net/cfg80211.h
@@ -7233,8 +7233,9 @@ enum ieee80211_ap_reg_power {
* @mgmt_registrations_need_update: mgmt registrations were updated,
* need to propagate the update to the driver
* @address: The address for this device, valid only if @netdev is %NULL
- * @is_running: true if this is a non-netdev device that has been started, e.g.
- * the P2P Device.
+ * @is_running: true if the device has been started, e.g. the P2P Device;
+ * for netdevs, tracked under the wiphy mutex from NETDEV_UP until
+ * NETDEV_GOING_DOWN
* @ps: powersave mode is enabled
* @ps_timeout: dynamic powersave timeout
* @unexpected_nlportid: (private) netlink port ID of application
@@ -7398,8 +7399,6 @@ static inline const u8 *wdev_address(struct wireless_dev *wdev)
static inline bool wdev_running(struct wireless_dev *wdev)
{
- if (wdev->netdev)
- return netif_running(wdev->netdev);
return wdev->is_running;
}
diff --git a/net/wireless/core.c b/net/wireless/core.c
index dc1e0522340a..805b0958daa8 100644
--- a/net/wireless/core.c
+++ b/net/wireless/core.c
@@ -1824,6 +1824,8 @@ static int cfg80211_netdev_notifier_call(struct notifier_block *nb,
}
break;
case NETDEV_GOING_DOWN:
+ scoped_guard(wiphy, &rdev->wiphy)
+ wdev->is_running = false;
cfg80211_leave(rdev, wdev, -1);
scoped_guard(wiphy, &rdev->wiphy) {
cfg80211_remove_links(wdev);
@@ -1854,6 +1856,7 @@ static int cfg80211_netdev_notifier_call(struct notifier_block *nb,
break;
case NETDEV_UP:
wiphy_lock(&rdev->wiphy);
+ wdev->is_running = true;
cfg80211_update_iface_num(rdev, wdev->iftype, 1);
switch (wdev->iftype) {
#ifdef CONFIG_CFG80211_WEXT
--
2.55.0
^ permalink raw reply related [flat|nested] 23+ messages in thread
* [PATCH wireless-next v2 02/18] wifi: nl80211: allow device lookup under RCU
2026-10-05 10:03 [PATCH wireless-next v2 01/18] wifi: further RTNL redux Johannes Berg
2026-10-05 10:03 ` [PATCH wireless-next v2 01/18] wifi: cfg80211: track netdev running state under wiphy mutex Johannes Berg
@ 2026-10-05 10:03 ` Johannes Berg
2026-10-05 10:03 ` [PATCH wireless-next v2 03/18] wifi: nl80211: avoid rtnl for commands that don't want it Johannes Berg
` (15 subsequent siblings)
17 siblings, 0 replies; 23+ messages in thread
From: Johannes Berg @ 2026-10-05 10:03 UTC (permalink / raw)
To: linux-wireless; +Cc: Johannes Berg
From: Johannes Berg <johannes.berg@intel.com>
We already modify our wiphy/wdev lists in RCU-safe ways,
so we can do lookups in RCU critical sections. Prepare
more of the code to be able to do that.
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
---
net/wireless/core.c | 6 +++---
net/wireless/nl80211.c | 32 +++++++++++++++++---------------
2 files changed, 20 insertions(+), 18 deletions(-)
diff --git a/net/wireless/core.c b/net/wireless/core.c
index 805b0958daa8..a6bba5e83f0d 100644
--- a/net/wireless/core.c
+++ b/net/wireless/core.c
@@ -57,13 +57,13 @@ module_param(cfg80211_disable_40mhz_24ghz, bool, 0644);
MODULE_PARM_DESC(cfg80211_disable_40mhz_24ghz,
"Disable 40MHz support in the 2.4GHz band");
+/* requires RCU or rtnl */
struct cfg80211_registered_device *cfg80211_rdev_by_wiphy_idx(int wiphy_idx)
{
struct cfg80211_registered_device *result = NULL, *rdev;
- ASSERT_RTNL();
-
- for_each_rdev(rdev) {
+ list_for_each_entry_rcu(rdev, &cfg80211_rdev_list, list,
+ lockdep_rtnl_is_held()) {
if (rdev->wiphy_idx == wiphy_idx) {
result = rdev;
break;
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
index a182404557e0..ee4d0112d6b8 100644
--- a/net/wireless/nl80211.c
+++ b/net/wireless/nl80211.c
@@ -63,7 +63,7 @@ static const struct genl_multicast_group nl80211_mcgrps[] = {
#endif
};
-/* returns ERR_PTR values */
+/* returns ERR_PTR values, requires RCU or rtnl if rdev is %NULL */
static struct wireless_dev *
__cfg80211_wdev_from_attrs(struct cfg80211_registered_device *rdev,
struct net *netns, struct nlattr **attrs)
@@ -105,9 +105,8 @@ __cfg80211_wdev_from_attrs(struct cfg80211_registered_device *rdev,
return result ?: ERR_PTR(-ENODEV);
}
- ASSERT_RTNL();
-
- for_each_rdev(rdev) {
+ list_for_each_entry_rcu(rdev, &cfg80211_rdev_list, list,
+ lockdep_rtnl_is_held()) {
struct wireless_dev *wdev;
if (wiphy_net(&rdev->wiphy) != netns)
@@ -116,7 +115,8 @@ __cfg80211_wdev_from_attrs(struct cfg80211_registered_device *rdev,
if (have_wdev_id && rdev->wiphy_idx != wiphy_idx)
continue;
- list_for_each_entry(wdev, &rdev->wiphy.wdev_list, list) {
+ list_for_each_entry_rcu(wdev, &rdev->wiphy.wdev_list, list,
+ lockdep_rtnl_is_held()) {
if (have_ifidx && wdev->netdev &&
wdev->netdev->ifindex == ifidx) {
result = wdev;
@@ -137,14 +137,13 @@ __cfg80211_wdev_from_attrs(struct cfg80211_registered_device *rdev,
return ERR_PTR(-ENODEV);
}
+/* requires RCU or rtnl */
static struct cfg80211_registered_device *
__cfg80211_rdev_from_attrs(struct net *netns, struct nlattr **attrs)
{
struct cfg80211_registered_device *rdev = NULL, *tmp;
struct net_device *netdev;
- ASSERT_RTNL();
-
if (!attrs[NL80211_ATTR_WIPHY] &&
!attrs[NL80211_ATTR_IFINDEX] &&
!attrs[NL80211_ATTR_WDEV])
@@ -162,7 +161,8 @@ __cfg80211_rdev_from_attrs(struct net *netns, struct nlattr **attrs)
tmp = cfg80211_rdev_by_wiphy_idx(wdev_id >> 32);
if (tmp) {
/* make sure wdev exists */
- list_for_each_entry(wdev, &tmp->wiphy.wdev_list, list) {
+ list_for_each_entry_rcu(wdev, &tmp->wiphy.wdev_list,
+ list, lockdep_rtnl_is_held()) {
if (wdev->identifier != (u32)wdev_id)
continue;
found = true;
@@ -181,14 +181,16 @@ __cfg80211_rdev_from_attrs(struct net *netns, struct nlattr **attrs)
if (attrs[NL80211_ATTR_IFINDEX]) {
int ifindex = nla_get_u32(attrs[NL80211_ATTR_IFINDEX]);
- netdev = __dev_get_by_index(netns, ifindex);
- if (netdev) {
- if (netdev->ieee80211_ptr)
- tmp = wiphy_to_rdev(
- netdev->ieee80211_ptr->wiphy);
- else
- tmp = NULL;
+ /* RCU required for dev_get_by_index_rcu() if we have RTNL */
+ rcu_read_lock();
+ netdev = dev_get_by_index_rcu(netns, ifindex);
+ if (netdev && netdev->ieee80211_ptr)
+ tmp = wiphy_to_rdev(netdev->ieee80211_ptr->wiphy);
+ else
+ tmp = NULL;
+ rcu_read_unlock();
+ if (netdev) {
/* not wireless device -- return error */
if (!tmp)
return ERR_PTR(-EINVAL);
--
2.55.0
^ permalink raw reply related [flat|nested] 23+ messages in thread
* [PATCH wireless-next v2 03/18] wifi: nl80211: avoid rtnl for commands that don't want it
2026-10-05 10:03 [PATCH wireless-next v2 01/18] wifi: further RTNL redux Johannes Berg
2026-10-05 10:03 ` [PATCH wireless-next v2 01/18] wifi: cfg80211: track netdev running state under wiphy mutex Johannes Berg
2026-10-05 10:03 ` [PATCH wireless-next v2 02/18] wifi: nl80211: allow device lookup under RCU Johannes Berg
@ 2026-10-05 10:03 ` Johannes Berg
2026-10-05 10:03 ` [PATCH wireless-next v2 04/18] wifi: nl80211: don't take rtnl for most dumps Johannes Berg
` (14 subsequent siblings)
17 siblings, 0 replies; 23+ messages in thread
From: Johannes Berg @ 2026-10-05 10:03 UTC (permalink / raw)
To: linux-wireless; +Cc: Johannes Berg
From: Johannes Berg <johannes.berg@intel.com>
We currently always take the RTNL for the lookup only
to drop it again if the command doesn't want it, but
now that we can do lookups with RCU, do that unless
the command still wants RTNL anyway.
This basically just means looking up under RCU, then
holding a reference briefly, and checking state (and
link IDs) under the wiphy mutex when it's acquired to
avoid races on unregistration etc.
Commands that don't want either mutex can only work
with netdevs since wdevs could go away, so warn on
others. None such can exist since there are no flags
for it, and I noted there it wouldn't be valid.
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
---
v2:
- sync RTNL in !wdev_running()/netdev_running() corner case
- fix wdev netns check
---
net/wireless/core.c | 5 +-
net/wireless/nl80211.c | 153 ++++++++++++++++++++++++++++++++++-------
2 files changed, 132 insertions(+), 26 deletions(-)
diff --git a/net/wireless/core.c b/net/wireless/core.c
index a6bba5e83f0d..ed6b5d3c148e 100644
--- a/net/wireless/core.c
+++ b/net/wireless/core.c
@@ -1259,6 +1259,9 @@ int wiphy_register(struct wiphy *wiphy)
}
cfg80211_debugfs_rdev_add(rdev);
+
+ rdev->wiphy.registered = true;
+
nl80211_notify_wiphy(rdev, NL80211_CMD_NEW_WIPHY);
wiphy_unlock(&rdev->wiphy);
@@ -1305,8 +1308,6 @@ int wiphy_register(struct wiphy *wiphy)
break;
}
}
-
- rdev->wiphy.registered = true;
rtnl_unlock();
res = rfkill_register(rdev->wiphy.rfkill);
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
index ee4d0112d6b8..97411d806383 100644
--- a/net/wireless/nl80211.c
+++ b/net/wireless/nl80211.c
@@ -225,6 +225,48 @@ cfg80211_get_dev_from_info(struct net *netns, struct genl_info *info)
return __cfg80211_rdev_from_attrs(netns, info->attrs);
}
+/*
+ * Lock the given wiphy, put the device and double-check it's
+ * still valid to use.
+ *
+ * Returns with wiphy mutex held unless there was an error.
+ */
+static struct wireless_dev *
+nl80211_lock_and_recheck(struct cfg80211_registered_device *rdev,
+ struct net *netns, u32 wdev_id)
+{
+ struct wireless_dev *wdev;
+
+ wiphy_lock(&rdev->wiphy);
+
+ if (!rdev->wiphy.registered ||
+ !net_eq(wiphy_net(&rdev->wiphy), netns)) {
+ wiphy_unlock(&rdev->wiphy);
+ put_device(&rdev->wiphy.dev);
+ return ERR_PTR(-ENODEV);
+ }
+
+ /*
+ * unregistration blocks on wiphy mutex,
+ * so we don't need this now
+ */
+ put_device(&rdev->wiphy.dev);
+
+ if (!wdev_id)
+ return NULL;
+
+ list_for_each_entry(wdev, &rdev->wiphy.wdev_list, list) {
+ if (wdev->identifier != wdev_id)
+ continue;
+ if (wdev->netdev && !net_eq(dev_net(wdev->netdev), netns))
+ break;
+ return wdev;
+ }
+ wiphy_unlock(&rdev->wiphy);
+
+ return ERR_PTR(-ENODEV);
+}
+
static int validate_beacon_head(const struct nlattr *attr,
struct netlink_ext_ack *extack)
{
@@ -19641,6 +19683,18 @@ nl80211_epcs_cfg(struct sk_buff *skb, struct genl_info *info)
NL80211_FLAG_NO_WIPHY_MTX | \
NL80211_FLAG_NEED_RTNL)
+/*
+ * Note: a
+ * SELECTOR(...,
+ * NL80211_FLAG_NO_WIPHY_MTX |
+ * NL80211_FLAG_NEED_WDEV)
+ * or
+ * SELECTOR(..., NL80211_FLAG_NO_WIPHY_MTX)
+ *
+ * isn't valid - nothing would ensure the device stays around,
+ * need at least a netdev, the wiphy mutex, or RTNL.
+ */
+
enum nl80211_internal_flags_selector {
#define SELECTOR(_, name, value) NL80211_IFL_SEL_##name,
INTERNAL_FLAG_SELECTORS(_)
@@ -19658,41 +19712,88 @@ static int nl80211_pre_doit(const struct genl_split_ops *ops,
struct genl_info *info)
{
struct cfg80211_registered_device *rdev = NULL;
+ struct net *netns = genl_info_net(info);
struct wireless_dev *wdev = NULL;
struct net_device *dev = NULL;
- u32 internal_flags;
+ bool need_rtnl, locked = false;
+ u32 internal_flags, wdev_id = 0;
int err;
if (WARN_ON(ops->internal_flags >= ARRAY_SIZE(nl80211_internal_flags)))
return -EINVAL;
internal_flags = nl80211_internal_flags[ops->internal_flags];
+ need_rtnl = internal_flags & NL80211_FLAG_NEED_RTNL;
+
+ if (need_rtnl)
+ rtnl_lock();
+ else
+ rcu_read_lock();
- rtnl_lock();
if (internal_flags & NL80211_FLAG_NEED_WIPHY) {
- rdev = cfg80211_get_dev_from_info(genl_info_net(info), info);
+ rdev = cfg80211_get_dev_from_info(netns, info);
if (IS_ERR(rdev)) {
err = PTR_ERR(rdev);
- goto out_unlock;
+ goto out_lookup;
}
- info->user_ptr[0] = rdev;
} else if (internal_flags & NL80211_FLAG_NEED_NETDEV ||
internal_flags & NL80211_FLAG_NEED_WDEV) {
- wdev = __cfg80211_wdev_from_attrs(NULL, genl_info_net(info),
- info->attrs);
+ wdev = __cfg80211_wdev_from_attrs(NULL, netns, info->attrs);
if (IS_ERR(wdev)) {
err = PTR_ERR(wdev);
- goto out_unlock;
+ goto out_lookup;
}
dev = wdev->netdev;
dev_hold(dev);
rdev = wiphy_to_rdev(wdev->wiphy);
+ }
+ if (!need_rtnl) {
+ if (rdev)
+ get_device(&rdev->wiphy.dev);
+ if (wdev)
+ wdev_id = wdev->identifier;
+ rcu_read_unlock();
+
+ /* RTM_NEWLINK is sent before NETDEV_UP, wait for dev_open() */
+ if (internal_flags & NL80211_FLAG_CHECK_NETDEV_UP && dev &&
+ !READ_ONCE(wdev->is_running) && netif_running(dev)) {
+ rtnl_lock();
+ rtnl_unlock();
+ }
+ }
+
+ if (rdev && !(internal_flags & NL80211_FLAG_NO_WIPHY_MTX)) {
+ if (need_rtnl) {
+ wiphy_lock(&rdev->wiphy);
+ } else {
+ /* without rtnl, things may have changed since lookup */
+ wdev = nl80211_lock_and_recheck(rdev, netns, wdev_id);
+ if (IS_ERR(wdev)) {
+ err = PTR_ERR(wdev);
+ goto out;
+ }
+ }
+ locked = true;
+ } else if (rdev && !need_rtnl) {
+ /*
+ * Without any mutex need a netdev to keep it alive, see
+ * also the note about selectors - the WARN can't happen
+ * without wrong selectors and those don't exist.
+ */
+ put_device(&rdev->wiphy.dev);
+ if (WARN_ON(!dev)) {
+ err = -EINVAL;
+ goto out;
+ }
+ }
+
+ if (wdev) {
if (internal_flags & NL80211_FLAG_NEED_NETDEV) {
if (!dev) {
err = -EINVAL;
- goto out_unlock;
+ goto out;
}
info->user_ptr[1] = dev;
@@ -19703,10 +19804,8 @@ static int nl80211_pre_doit(const struct genl_split_ops *ops,
if (internal_flags & NL80211_FLAG_CHECK_NETDEV_UP &&
!wdev_running(wdev)) {
err = -ENETDOWN;
- goto out_unlock;
+ goto out;
}
-
- info->user_ptr[0] = rdev;
}
if (internal_flags & NL80211_FLAG_MLO_VALID_LINK_ID) {
@@ -19714,7 +19813,7 @@ static int nl80211_pre_doit(const struct genl_split_ops *ops,
if (!wdev) {
err = -EINVAL;
- goto out_unlock;
+ goto out;
}
/* MLO -> require valid link ID */
@@ -19722,13 +19821,13 @@ static int nl80211_pre_doit(const struct genl_split_ops *ops,
(!link_id ||
!(wdev->valid_links & BIT(nla_get_u8(link_id))))) {
err = -EINVAL;
- goto out_unlock;
+ goto out;
}
/* non-MLO -> no link ID attribute accepted */
if (!wdev->valid_links && link_id) {
err = -EINVAL;
- goto out_unlock;
+ goto out;
}
}
@@ -19736,22 +19835,28 @@ static int nl80211_pre_doit(const struct genl_split_ops *ops,
if (info->attrs[NL80211_ATTR_MLO_LINK_ID] ||
(wdev && wdev->valid_links)) {
err = -EINVAL;
- goto out_unlock;
+ goto out;
}
}
- if (rdev && !(internal_flags & NL80211_FLAG_NO_WIPHY_MTX)) {
- wiphy_lock(&rdev->wiphy);
- /* we keep the mutex locked until post_doit */
+ info->user_ptr[0] = rdev;
+ /* we keep the mutex locked until post_doit */
+ if (locked)
__release(&rdev->wiphy.mtx);
- }
- if (!(internal_flags & NL80211_FLAG_NEED_RTNL))
- rtnl_unlock();
return 0;
-out_unlock:
- rtnl_unlock();
+out:
+ if (locked)
+ wiphy_unlock(&rdev->wiphy);
dev_put(dev);
+ if (need_rtnl)
+ rtnl_unlock();
+ return err;
+out_lookup:
+ if (need_rtnl)
+ rtnl_unlock();
+ else
+ rcu_read_unlock();
return err;
}
--
2.55.0
^ permalink raw reply related [flat|nested] 23+ messages in thread
* [PATCH wireless-next v2 04/18] wifi: nl80211: don't take rtnl for most dumps
2026-10-05 10:03 [PATCH wireless-next v2 01/18] wifi: further RTNL redux Johannes Berg
` (2 preceding siblings ...)
2026-10-05 10:03 ` [PATCH wireless-next v2 03/18] wifi: nl80211: avoid rtnl for commands that don't want it Johannes Berg
@ 2026-10-05 10:03 ` Johannes Berg
2026-10-05 10:03 ` [PATCH wireless-next v2 05/18] wifi: cfg80211: reg: update channels under wiphy mutex Johannes Berg
` (13 subsequent siblings)
17 siblings, 0 replies; 23+ messages in thread
From: Johannes Berg @ 2026-10-05 10:03 UTC (permalink / raw)
To: linux-wireless; +Cc: Johannes Berg
From: Johannes Berg <johannes.berg@intel.com>
Most (wdev-related) dumps also take RTNL just for lookup.
Apply the same logic as for pre_doit() in the previous
commit to avoid the RTNL. Since I refactored the checks
there into a helper to use here, it even simplifies the
code.
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
---
net/wireless/nl80211.c | 67 +++++++++++++++---------------------------
1 file changed, 23 insertions(+), 44 deletions(-)
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
index 97411d806383..00b027620f17 100644
--- a/net/wireless/nl80211.c
+++ b/net/wireless/nl80211.c
@@ -1302,6 +1302,8 @@ static int nl80211_prepare_wdev_dump(struct netlink_callback *cb,
struct wireless_dev **wdev,
struct nlattr **attrbuf)
{
+ struct net *netns = sock_net(cb->skb->sk);
+ u32 wdev_id;
int err;
if (!cb->args[0]) {
@@ -1323,61 +1325,38 @@ static int nl80211_prepare_wdev_dump(struct netlink_callback *cb,
return err;
}
- rtnl_lock();
- *wdev = __cfg80211_wdev_from_attrs(NULL, sock_net(cb->skb->sk),
- attrbuf);
+ rcu_read_lock();
+ *wdev = __cfg80211_wdev_from_attrs(NULL, netns, attrbuf);
kfree(attrbuf_free);
if (IS_ERR(*wdev)) {
- rtnl_unlock();
+ rcu_read_unlock();
return PTR_ERR(*wdev);
}
*rdev = wiphy_to_rdev((*wdev)->wiphy);
- mutex_lock(&(*rdev)->wiphy.mtx);
- rtnl_unlock();
- /* 0 is the first index - add 1 to parse only once */
- cb->args[0] = (*rdev)->wiphy_idx + 1;
- cb->args[1] = (*wdev)->identifier;
+ wdev_id = (*wdev)->identifier;
} else {
/* subtract the 1 again here */
- struct wiphy *wiphy;
- struct wireless_dev *tmp;
-
- rtnl_lock();
- wiphy = wiphy_idx_to_wiphy(cb->args[0] - 1);
- if (!wiphy) {
- rtnl_unlock();
+ rcu_read_lock();
+ *rdev = cfg80211_rdev_by_wiphy_idx(cb->args[0] - 1);
+ if (!*rdev) {
+ rcu_read_unlock();
return -ENODEV;
}
-
- /*
- * The first invocation validated the wdev's netns against
- * the caller via __cfg80211_wdev_from_attrs(). The wiphy
- * may have moved netns between dumpit invocations (via
- * NL80211_CMD_SET_WIPHY_NETNS), so re-check here.
- */
- if (!net_eq(wiphy_net(wiphy), sock_net(cb->skb->sk))) {
- rtnl_unlock();
- return -ENODEV;
- }
-
- *rdev = wiphy_to_rdev(wiphy);
- *wdev = NULL;
-
- list_for_each_entry(tmp, &(*rdev)->wiphy.wdev_list, list) {
- if (tmp->identifier == cb->args[1]) {
- *wdev = tmp;
- break;
- }
- }
-
- if (!*wdev) {
- rtnl_unlock();
- return -ENODEV;
- }
- mutex_lock(&(*rdev)->wiphy.mtx);
- rtnl_unlock();
+ wdev_id = cb->args[1];
}
+ get_device(&(*rdev)->wiphy.dev);
+ rcu_read_unlock();
+
+ /* things may have changed since the lookup or the last dumpit call */
+ *wdev = nl80211_lock_and_recheck(*rdev, netns, wdev_id);
+ if (IS_ERR(*wdev))
+ return PTR_ERR(*wdev);
+
+ /* 0 is the first index - add 1 to parse only once */
+ cb->args[0] = (*rdev)->wiphy_idx + 1;
+ cb->args[1] = wdev_id;
+
return 0;
}
--
2.55.0
^ permalink raw reply related [flat|nested] 23+ messages in thread
* [PATCH wireless-next v2 05/18] wifi: cfg80211: reg: update channels under wiphy mutex
2026-10-05 10:03 [PATCH wireless-next v2 01/18] wifi: further RTNL redux Johannes Berg
` (3 preceding siblings ...)
2026-10-05 10:03 ` [PATCH wireless-next v2 04/18] wifi: nl80211: don't take rtnl for most dumps Johannes Berg
@ 2026-10-05 10:03 ` Johannes Berg
2026-10-05 10:03 ` [PATCH wireless-next v2 06/18] wifi: cfg80211: reg: set intersected regd " Johannes Berg
` (12 subsequent siblings)
17 siblings, 0 replies; 23+ messages in thread
From: Johannes Berg @ 2026-10-05 10:03 UTC (permalink / raw)
To: linux-wireless; +Cc: Johannes Berg
From: Johannes Berg <johannes.berg@intel.com>
Regulatory code updates channels holding only RTNL. Since
I removed RTNL everywhere, most reading code doesn't take
it, so it races with the updates.
Update the relevant regulatory data under wiphy mutex to
avoid (most of?) those races.
Some drivers acquire the wiphy mutex, so don't cover the
calls to the regulatory notifiers.
For beacon hints the order of operation changes, and the
driver notifier will be called only once later, but that
seems acceptable (even better) to drivers as I see it.
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
---
net/wireless/reg.c | 55 ++++++++++++++++++++++++++++------------------
1 file changed, 34 insertions(+), 21 deletions(-)
diff --git a/net/wireless/reg.c b/net/wireless/reg.c
index b7ef479a74f0..1a67d146bf2b 100644
--- a/net/wireless/reg.c
+++ b/net/wireless/reg.c
@@ -2145,31 +2145,32 @@ static void reg_call_notifier(struct wiphy *wiphy,
wiphy->reg_notifier(wiphy, request);
}
-static void handle_reg_beacon(struct wiphy *wiphy, unsigned int chan_idx,
+static bool handle_reg_beacon(struct wiphy *wiphy, unsigned int chan_idx,
struct reg_beacon *reg_beacon)
{
struct ieee80211_supported_band *sband;
struct ieee80211_channel *chan;
bool channel_changed = false;
struct ieee80211_channel chan_before;
- struct regulatory_request *lr = get_last_request();
+
+ lockdep_assert_wiphy(wiphy);
sband = wiphy->bands[reg_beacon->chan.band];
chan = &sband->channels[chan_idx];
if (likely(!ieee80211_channel_equal(chan, ®_beacon->chan)))
- return;
+ return false;
if (chan->beacon_found)
- return;
+ return false;
chan->beacon_found = true;
if (!reg_is_world_roaming(wiphy))
- return;
+ return false;
if (wiphy->regulatory_flags & REGULATORY_DISABLE_BEACON_HINTS)
- return;
+ return false;
chan_before = *chan;
@@ -2178,11 +2179,10 @@ static void handle_reg_beacon(struct wiphy *wiphy, unsigned int chan_idx,
channel_changed = true;
}
- if (channel_changed) {
+ if (channel_changed)
nl80211_send_beacon_hint_event(wiphy, &chan_before, chan);
- if (wiphy->flags & WIPHY_FLAG_CHANNEL_CHANGE_ON_BEACON)
- reg_call_notifier(wiphy, lr);
- }
+
+ return channel_changed;
}
/*
@@ -2194,14 +2194,20 @@ static void wiphy_update_new_beacon(struct wiphy *wiphy,
{
unsigned int i;
struct ieee80211_supported_band *sband;
+ bool changed = false;
if (!wiphy->bands[reg_beacon->chan.band])
return;
sband = wiphy->bands[reg_beacon->chan.band];
- for (i = 0; i < sband->n_channels; i++)
- handle_reg_beacon(wiphy, i, reg_beacon);
+ scoped_guard(wiphy, wiphy) {
+ for (i = 0; i < sband->n_channels; i++)
+ changed |= handle_reg_beacon(wiphy, i, reg_beacon);
+ }
+
+ if (changed && wiphy->flags & WIPHY_FLAG_CHANNEL_CHANGE_ON_BEACON)
+ reg_call_notifier(wiphy, get_last_request());
}
/*
@@ -2546,11 +2552,15 @@ static void wiphy_update_regulatory(struct wiphy *wiphy,
lr->dfs_region = get_cfg80211_regdom()->dfs_region;
- for (band = 0; band < NUM_NL80211_BANDS; band++)
- handle_band(wiphy, initiator, wiphy->bands[band]);
+ /* the notifier is called below, so ignore beacon hint changes */
+ scoped_guard(wiphy, wiphy) {
+ for (band = 0; band < NUM_NL80211_BANDS; band++)
+ handle_band(wiphy, initiator, wiphy->bands[band]);
+
+ reg_process_beacons(wiphy);
+ reg_process_ht_flags(wiphy);
+ }
- reg_process_beacons(wiphy);
- reg_process_ht_flags(wiphy);
reg_call_notifier(wiphy, lr);
}
@@ -3166,12 +3176,14 @@ static void reg_process_pending_beacon_hints(void)
{
struct cfg80211_registered_device *rdev;
struct reg_beacon *pending_beacon, *tmp;
+ LIST_HEAD(pending);
/* This goes through the _pending_ beacon list */
spin_lock_bh(®_pending_beacons_lock);
+ list_splice_tail_init(®_pending_beacons, &pending);
+ spin_unlock_bh(®_pending_beacons_lock);
- list_for_each_entry_safe(pending_beacon, tmp,
- ®_pending_beacons, list) {
+ list_for_each_entry_safe(pending_beacon, tmp, &pending, list) {
list_del_init(&pending_beacon->list);
/* Applies the beacon hint to current wiphys */
@@ -3181,8 +3193,6 @@ static void reg_process_pending_beacon_hints(void)
/* Remembers the beacon hint for new wiphys or reg changes */
list_add_tail(&pending_beacon->list, ®_beacon_list);
}
-
- spin_unlock_bh(®_pending_beacons_lock);
}
static void reg_process_self_managed_hint(struct wiphy *wiphy)
@@ -3571,8 +3581,11 @@ static void restore_regulatory_settings(bool reset_user, bool cached)
for_each_rdev(rdev) {
if (rdev->wiphy.regulatory_flags & REGULATORY_WIPHY_SELF_MANAGED)
continue;
- if (rdev->wiphy.regulatory_flags & REGULATORY_CUSTOM_REG)
+ if (rdev->wiphy.regulatory_flags & REGULATORY_CUSTOM_REG) {
+ guard(wiphy)(&rdev->wiphy);
+
restore_custom_reg_settings(&rdev->wiphy);
+ }
}
if (cached && (!is_an_alpha2(alpha2) ||
--
2.55.0
^ permalink raw reply related [flat|nested] 23+ messages in thread
* [PATCH wireless-next v2 06/18] wifi: cfg80211: reg: set intersected regd under wiphy mutex
2026-10-05 10:03 [PATCH wireless-next v2 01/18] wifi: further RTNL redux Johannes Berg
` (4 preceding siblings ...)
2026-10-05 10:03 ` [PATCH wireless-next v2 05/18] wifi: cfg80211: reg: update channels under wiphy mutex Johannes Berg
@ 2026-10-05 10:03 ` Johannes Berg
2026-10-05 10:03 ` [PATCH wireless-next v2 07/18] wifi: cfg80211: update channel DFS data " Johannes Berg
` (11 subsequent siblings)
17 siblings, 0 replies; 23+ messages in thread
From: Johannes Berg @ 2026-10-05 10:03 UTC (permalink / raw)
To: linux-wireless; +Cc: Johannes Berg
From: Johannes Berg <johannes.berg@intel.com>
When regulatory request is intersected, the wiphy's regd
is replaced holding only the RTNL, while the non-intersected
case holds the wiphy mutex - we document access to need it.
Current readers that don't hold RTNL also don't get into it,
but it's more consistent and I want to remove the RTNL later.
Also move the similar code in reg_process_hint_driver() to
the same pattern as preparation.
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
---
net/wireless/reg.c | 12 +++++++-----
1 file changed, 7 insertions(+), 5 deletions(-)
diff --git a/net/wireless/reg.c b/net/wireless/reg.c
index 1a67d146bf2b..ed4d43a8cb78 100644
--- a/net/wireless/reg.c
+++ b/net/wireless/reg.c
@@ -2859,12 +2859,12 @@ reg_process_hint_driver(struct wiphy *wiphy,
if (IS_ERR(regd))
return REG_REQ_IGNORE;
- tmp = get_wiphy_regdom(wiphy);
ASSERT_RTNL();
scoped_guard(wiphy, wiphy) {
+ tmp = get_wiphy_regdom(wiphy);
rcu_assign_pointer(wiphy->regd, regd);
+ rcu_free_regdom(tmp);
}
- rcu_free_regdom(tmp);
}
@@ -3946,9 +3946,11 @@ static int reg_set_rd_driver(const struct ieee80211_regdomain *rd,
* However if a driver requested this specific regulatory
* domain we keep it for its private use
*/
- tmp = get_wiphy_regdom(request_wiphy);
- rcu_assign_pointer(request_wiphy->regd, rd);
- rcu_free_regdom(tmp);
+ scoped_guard(wiphy, request_wiphy) {
+ tmp = get_wiphy_regdom(request_wiphy);
+ rcu_assign_pointer(request_wiphy->regd, rd);
+ rcu_free_regdom(tmp);
+ }
rd = NULL;
--
2.55.0
^ permalink raw reply related [flat|nested] 23+ messages in thread
* [PATCH wireless-next v2 07/18] wifi: cfg80211: update channel DFS data under wiphy mutex
2026-10-05 10:03 [PATCH wireless-next v2 01/18] wifi: further RTNL redux Johannes Berg
` (5 preceding siblings ...)
2026-10-05 10:03 ` [PATCH wireless-next v2 06/18] wifi: cfg80211: reg: set intersected regd " Johannes Berg
@ 2026-10-05 10:03 ` Johannes Berg
2026-10-05 10:03 ` [PATCH wireless-next v2 08/18] wifi: mac80211_hwsim: call cfg80211 event with " Johannes Berg
` (10 subsequent siblings)
17 siblings, 0 replies; 23+ messages in thread
From: Johannes Berg @ 2026-10-05 10:03 UTC (permalink / raw)
To: linux-wireless; +Cc: Johannes Berg
From: Johannes Berg <johannes.berg@intel.com>
We check the DFS data for a channel under wiphy mutex, but
the code sometimes writes it under RTNL only. Fix that to
avoid data races and to prepare things for later removal
of the RTNL here.
cfg80211_dfs_channels_update_work() must change a bit more
to do this, since it locks other wiphys and nesting locks
is, while possible, too difficult: track the changes in an
array and update everything afterwards.
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
---
net/wireless/mlme.c | 90 +++++++++++++++++++++++++++++++++++++--------
net/wireless/reg.c | 6 ++-
2 files changed, 79 insertions(+), 17 deletions(-)
diff --git a/net/wireless/mlme.c b/net/wireless/mlme.c
index a0d1cde26f0c..131039b675fe 100644
--- a/net/wireless/mlme.c
+++ b/net/wireless/mlme.c
@@ -1070,18 +1070,49 @@ void cfg80211_sched_dfs_chan_update(struct cfg80211_registered_device *rdev)
queue_delayed_work(cfg80211_wq, &rdev->dfs_update_channels_wk, 0);
}
+struct cfg80211_dfs_chan_expiry {
+ struct ieee80211_channel *chan;
+ unsigned long entered;
+ enum nl80211_dfs_state state;
+ enum nl80211_radar_event event;
+};
+
+static bool cfg80211_dfs_chan_expire(struct cfg80211_registered_device *rdev,
+ struct cfg80211_dfs_chan_expiry *exp,
+ struct cfg80211_chan_def *chandef)
+{
+ struct ieee80211_channel *c = exp->chan;
+
+ guard(wiphy)(&rdev->wiphy);
+
+ /* the state may have changed while the wiphy wasn't locked */
+ if (c->dfs_state != exp->state || c->dfs_state_entered != exp->entered)
+ return false;
+
+ c->dfs_state = NL80211_DFS_USABLE;
+ c->dfs_state_entered = jiffies;
+
+ cfg80211_chandef_create(chandef, c, NL80211_CHAN_NO_HT);
+
+ nl80211_radar_notify(rdev, chandef, exp->event, NULL, GFP_KERNEL);
+
+ return true;
+}
+
void cfg80211_dfs_channels_update_work(struct work_struct *work)
{
struct delayed_work *delayed_work = to_delayed_work(work);
+ struct cfg80211_dfs_chan_expiry *expired;
struct cfg80211_registered_device *rdev;
struct cfg80211_chan_def chandef;
struct ieee80211_supported_band *sband;
struct ieee80211_channel *c;
struct wiphy *wiphy;
- bool check_again = false;
+ bool check_again = false, pre_cac_allowed;
unsigned long timeout, next_time = 0;
unsigned long time_dfs_update;
enum nl80211_radar_event radar_event;
+ unsigned int n_chans = 0, n_expired = 0;
int bandid, i;
rdev = container_of(delayed_work, struct cfg80211_registered_device,
@@ -1089,6 +1120,25 @@ void cfg80211_dfs_channels_update_work(struct work_struct *work)
wiphy = &rdev->wiphy;
rtnl_lock();
+ wiphy_lock(wiphy);
+ for (bandid = 0; bandid < NUM_NL80211_BANDS; bandid++) {
+ if (wiphy->bands[bandid])
+ n_chans += wiphy->bands[bandid]->n_channels;
+ }
+
+ expired = kvcalloc(n_chans, sizeof(*expired), GFP_KERNEL);
+ if (!expired) {
+ /* hmm - retry later */
+ queue_delayed_work(cfg80211_wq,
+ &rdev->dfs_update_channels_wk,
+ HZ);
+ wiphy_unlock(wiphy);
+ rtnl_unlock();
+ return;
+ }
+
+ pre_cac_allowed = regulatory_pre_cac_allowed(wiphy);
+
for (bandid = 0; bandid < NUM_NL80211_BANDS; bandid++) {
sband = wiphy->bands[bandid];
if (!sband)
@@ -1108,8 +1158,7 @@ void cfg80211_dfs_channels_update_work(struct work_struct *work)
time_dfs_update = IEEE80211_DFS_MIN_NOP_TIME_MS;
radar_event = NL80211_RADAR_NOP_FINISHED;
} else {
- if (regulatory_pre_cac_allowed(wiphy) ||
- cfg80211_any_wiphy_oper_chan(wiphy, c))
+ if (pre_cac_allowed)
continue;
time_dfs_update = REG_PRE_CAC_EXPIRY_GRACE_MS;
@@ -1120,19 +1169,11 @@ void cfg80211_dfs_channels_update_work(struct work_struct *work)
msecs_to_jiffies(time_dfs_update);
if (time_after_eq(jiffies, timeout)) {
- c->dfs_state = NL80211_DFS_USABLE;
- c->dfs_state_entered = jiffies;
-
- cfg80211_chandef_create(&chandef, c,
- NL80211_CHAN_NO_HT);
-
- nl80211_radar_notify(rdev, &chandef,
- radar_event, NULL,
- GFP_ATOMIC);
-
- regulatory_propagate_dfs_state(wiphy, &chandef,
- c->dfs_state,
- radar_event);
+ expired[n_expired].chan = c;
+ expired[n_expired].state = c->dfs_state;
+ expired[n_expired].entered = c->dfs_state_entered;
+ expired[n_expired].event = radar_event;
+ n_expired++;
continue;
}
@@ -1143,8 +1184,25 @@ void cfg80211_dfs_channels_update_work(struct work_struct *work)
check_again = true;
}
}
+ wiphy_unlock(wiphy);
+
+ /* these lock the wiphys, so must be done without holding ours */
+ for (i = 0; i < n_expired; i++) {
+ if (expired[i].event == NL80211_RADAR_PRE_CAC_EXPIRED &&
+ cfg80211_any_wiphy_oper_chan(wiphy, expired[i].chan))
+ continue;
+
+ if (!cfg80211_dfs_chan_expire(rdev, &expired[i], &chandef))
+ continue;
+
+ regulatory_propagate_dfs_state(wiphy, &chandef,
+ NL80211_DFS_USABLE,
+ expired[i].event);
+ }
rtnl_unlock();
+ kvfree(expired);
+
/* reschedule if there are other channels waiting to be cleared again */
if (check_again)
queue_delayed_work(cfg80211_wq, &rdev->dfs_update_channels_wk,
diff --git a/net/wireless/reg.c b/net/wireless/reg.c
index ed4d43a8cb78..28c431e0c5df 100644
--- a/net/wireless/reg.c
+++ b/net/wireless/reg.c
@@ -3054,6 +3054,8 @@ static void wiphy_all_share_dfs_chan_state(struct wiphy *wiphy)
ASSERT_RTNL();
+ guard(wiphy)(wiphy);
+
for_each_rdev(rdev) {
if (wiphy == &rdev->wiphy)
continue;
@@ -4270,7 +4272,7 @@ static void cfg80211_check_and_end_cac(struct cfg80211_registered_device *rdev)
struct wireless_dev *wdev;
unsigned int link_id;
- guard(wiphy)(&rdev->wiphy);
+ lockdep_assert_wiphy(&rdev->wiphy);
/* If we finished CAC or received radar, we should end any
* CAC running on the same channels.
@@ -4321,6 +4323,8 @@ void regulatory_propagate_dfs_state(struct wiphy *wiphy,
chandef->chan->center_freq))
continue;
+ guard(wiphy)(&rdev->wiphy);
+
cfg80211_set_dfs_state(&rdev->wiphy, chandef, dfs_state);
if (event == NL80211_RADAR_DETECTED ||
--
2.55.0
^ permalink raw reply related [flat|nested] 23+ messages in thread
* [PATCH wireless-next v2 08/18] wifi: mac80211_hwsim: call cfg80211 event with wiphy mutex
2026-10-05 10:03 [PATCH wireless-next v2 01/18] wifi: further RTNL redux Johannes Berg
` (6 preceding siblings ...)
2026-10-05 10:03 ` [PATCH wireless-next v2 07/18] wifi: cfg80211: update channel DFS data " Johannes Berg
@ 2026-10-05 10:03 ` Johannes Berg
2026-10-05 10:03 ` [PATCH wireless-next v2 09/18] wifi: cfg80211: document wiphy mutex for radar/CAC events Johannes Berg
` (9 subsequent siblings)
17 siblings, 0 replies; 23+ messages in thread
From: Johannes Berg @ 2026-10-05 10:03 UTC (permalink / raw)
To: linux-wireless; +Cc: Johannes Berg
From: Johannes Berg <johannes.berg@intel.com>
These functions really require the wiphy mutex (though
they're not yet documented as such), call them with it.
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
---
drivers/net/wireless/virtual/mac80211_hwsim_main.c | 2 ++
1 file changed, 2 insertions(+)
diff --git a/drivers/net/wireless/virtual/mac80211_hwsim_main.c b/drivers/net/wireless/virtual/mac80211_hwsim_main.c
index 762f99eb15e4..59c540d2677b 100644
--- a/drivers/net/wireless/virtual/mac80211_hwsim_main.c
+++ b/drivers/net/wireless/virtual/mac80211_hwsim_main.c
@@ -1133,6 +1133,8 @@ static ssize_t hwsim_background_cac_write(struct file *file,
if (copy_from_user(buf, user_buf, count))
return -EFAULT;
+ guard(wiphy)(data->hw->wiphy);
+
/* Check if background radar channel is configured */
if (!data->radar_background_chandef.chan)
return -ENOENT;
--
2.55.0
^ permalink raw reply related [flat|nested] 23+ messages in thread
* [PATCH wireless-next v2 09/18] wifi: cfg80211: document wiphy mutex for radar/CAC events
2026-10-05 10:03 [PATCH wireless-next v2 01/18] wifi: further RTNL redux Johannes Berg
` (7 preceding siblings ...)
2026-10-05 10:03 ` [PATCH wireless-next v2 08/18] wifi: mac80211_hwsim: call cfg80211 event with " Johannes Berg
@ 2026-10-05 10:03 ` Johannes Berg
2026-10-05 17:46 ` Brian Norris
2026-10-05 10:03 ` [PATCH wireless-next v2 10/18] wifi: cfg80211: add a mutex for regulatory/device list Johannes Berg
` (8 subsequent siblings)
17 siblings, 1 reply; 23+ messages in thread
From: Johannes Berg @ 2026-10-05 10:03 UTC (permalink / raw)
To: linux-wireless; +Cc: Johannes Berg
From: Johannes Berg <johannes.berg@intel.com>
The DFS state of channels and the CAC state of the wdev is
protected by the wiphy mutex, so the radar and CAC events
must be reported by drivers with the wiphy mutex held. In
mac80211 we do this, but some drivers don't yet:
- mwifiex/nxpwifi have an event handling worker,
- qtnfmac also does it from event processing, and
- mt7915 and mt7996 don't acquire a mutex for background
radar events.
Document the requirement.
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
---
include/net/cfg80211.h | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h
index 9cb536fc8cb8..eb376a54a07f 100644
--- a/include/net/cfg80211.h
+++ b/include/net/cfg80211.h
@@ -9794,6 +9794,7 @@ void cfg80211_cqm_beacon_loss_notify(struct net_device *dev, gfp_t gfp);
* @gfp: context flags
*
* This function is called when a radar is detected on the current chanenl.
+ * Must be called with the wiphy mutex held.
*/
void __cfg80211_radar_event(struct wiphy *wiphy,
struct cfg80211_chan_def *chandef,
@@ -9839,7 +9840,7 @@ void cfg80211_sta_opmode_change_notify(struct net_device *dev, const u8 *mac,
*
* This function is called when a Channel availability check (CAC) is finished
* or aborted. This must be called to notify the completion of a CAC process,
- * also by full-MAC drivers.
+ * also by full-MAC drivers. Must be called with the wiphy mutex held.
*/
void cfg80211_cac_event(struct net_device *netdev,
const struct cfg80211_chan_def *chandef,
--
2.55.0
^ permalink raw reply related [flat|nested] 23+ messages in thread
* [PATCH wireless-next v2 10/18] wifi: cfg80211: add a mutex for regulatory/device list
2026-10-05 10:03 [PATCH wireless-next v2 01/18] wifi: further RTNL redux Johannes Berg
` (8 preceding siblings ...)
2026-10-05 10:03 ` [PATCH wireless-next v2 09/18] wifi: cfg80211: document wiphy mutex for radar/CAC events Johannes Berg
@ 2026-10-05 10:03 ` Johannes Berg
2026-10-05 10:03 ` [PATCH wireless-next v2 11/18] wifi: cfg80211: allow walking wiphy list under cfg80211_mutex Johannes Berg
` (7 subsequent siblings)
17 siblings, 0 replies; 23+ messages in thread
From: Johannes Berg @ 2026-10-05 10:03 UTC (permalink / raw)
To: linux-wireless; +Cc: Johannes Berg
From: Johannes Berg <johannes.berg@intel.com>
The global regulatory state is currently protected by the
RTNL (which is the new BKL). Prepare for further reducing
our dependency on it by adding a new mutex that protects
this data (as well, for now held inside RTNL) and the
wiphy list (so that regulatory code can walk it).
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
---
net/wireless/core.c | 13 +++++++++-
net/wireless/core.h | 3 +++
net/wireless/mlme.c | 3 +++
net/wireless/nl80211.c | 2 ++
net/wireless/reg.c | 54 +++++++++++++++++++++++++++++-------------
5 files changed, 58 insertions(+), 17 deletions(-)
diff --git a/net/wireless/core.c b/net/wireless/core.c
index ed6b5d3c148e..e12d8b301a37 100644
--- a/net/wireless/core.c
+++ b/net/wireless/core.c
@@ -42,7 +42,9 @@ MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("wireless configuration support");
MODULE_ALIAS_GENL_FAMILY(NL80211_GENL_NAME);
-/* RCU-protected (and RTNL for writers) */
+DEFINE_MUTEX(cfg80211_mutex);
+
+/* RCU-protected (writes under both RTNL/cfg80211_mutex) */
LIST_HEAD(cfg80211_rdev_list);
int cfg80211_rdev_list_generation;
@@ -565,11 +567,13 @@ static void cfg80211_propagate_radar_detect_wk(struct work_struct *work)
propagate_radar_detect_wk);
rtnl_lock();
+ mutex_lock(&cfg80211_mutex);
regulatory_propagate_dfs_state(&rdev->wiphy, &rdev->radar_chandef,
NL80211_DFS_UNAVAILABLE,
NL80211_RADAR_DETECTED);
+ mutex_unlock(&cfg80211_mutex);
rtnl_unlock();
}
@@ -581,11 +585,13 @@ static void cfg80211_propagate_cac_done_wk(struct work_struct *work)
propagate_cac_done_wk);
rtnl_lock();
+ mutex_lock(&cfg80211_mutex);
regulatory_propagate_dfs_state(&rdev->wiphy, &rdev->cac_done_chandef,
NL80211_DFS_AVAILABLE,
NL80211_RADAR_CAC_FINISHED);
+ mutex_unlock(&cfg80211_mutex);
rtnl_unlock();
}
@@ -1231,10 +1237,12 @@ int wiphy_register(struct wiphy *wiphy)
rdev->wiphy.bss_param_support |= WIPHY_BSS_PARAM_P2P_OPPPS;
rtnl_lock();
+ mutex_lock(&cfg80211_mutex);
wiphy_lock(&rdev->wiphy);
res = device_add(&rdev->wiphy.dev);
if (res) {
wiphy_unlock(&rdev->wiphy);
+ mutex_unlock(&cfg80211_mutex);
rtnl_unlock();
return res;
}
@@ -1308,6 +1316,7 @@ int wiphy_register(struct wiphy *wiphy)
break;
}
}
+ mutex_unlock(&cfg80211_mutex);
rtnl_unlock();
res = rfkill_register(rdev->wiphy.rfkill);
@@ -1379,6 +1388,7 @@ void wiphy_unregister(struct wiphy *wiphy)
rfkill_unregister(rdev->wiphy.rfkill);
rtnl_lock();
+ mutex_lock(&cfg80211_mutex);
wiphy_lock(&rdev->wiphy);
nl80211_notify_wiphy(rdev, NL80211_CMD_DEL_WIPHY);
rdev->wiphy.registered = false;
@@ -1410,6 +1420,7 @@ void wiphy_unregister(struct wiphy *wiphy)
/* surely nothing is reachable now, clean up work */
cfg80211_process_wiphy_works(rdev, NULL);
wiphy_unlock(&rdev->wiphy);
+ mutex_unlock(&cfg80211_mutex);
rtnl_unlock();
/* this has nothing to do now but make sure it's gone */
diff --git a/net/wireless/core.h b/net/wireless/core.h
index 58445c09026a..414ce31af60c 100644
--- a/net/wireless/core.h
+++ b/net/wireless/core.h
@@ -19,6 +19,9 @@
#include "reg.h"
+/* protects the wiphy list and regulatory state */
+extern struct mutex cfg80211_mutex;
+
#define WIPHY_IDX_INVALID -1
struct cfg80211_scan_request_int {
diff --git a/net/wireless/mlme.c b/net/wireless/mlme.c
index 131039b675fe..082590d859e9 100644
--- a/net/wireless/mlme.c
+++ b/net/wireless/mlme.c
@@ -1120,6 +1120,7 @@ void cfg80211_dfs_channels_update_work(struct work_struct *work)
wiphy = &rdev->wiphy;
rtnl_lock();
+ mutex_lock(&cfg80211_mutex);
wiphy_lock(wiphy);
for (bandid = 0; bandid < NUM_NL80211_BANDS; bandid++) {
if (wiphy->bands[bandid])
@@ -1133,6 +1134,7 @@ void cfg80211_dfs_channels_update_work(struct work_struct *work)
&rdev->dfs_update_channels_wk,
HZ);
wiphy_unlock(wiphy);
+ mutex_unlock(&cfg80211_mutex);
rtnl_unlock();
return;
}
@@ -1199,6 +1201,7 @@ void cfg80211_dfs_channels_update_work(struct work_struct *work)
NL80211_DFS_USABLE,
expired[i].event);
}
+ mutex_unlock(&cfg80211_mutex);
rtnl_unlock();
kvfree(expired);
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
index 00b027620f17..a685190d16ac 100644
--- a/net/wireless/nl80211.c
+++ b/net/wireless/nl80211.c
@@ -11044,6 +11044,7 @@ static int nl80211_set_reg(struct sk_buff *skb, struct genl_info *info)
}
rtnl_lock();
+ mutex_lock(&cfg80211_mutex);
if (!reg_is_valid_request(alpha2)) {
r = -EINVAL;
goto out;
@@ -11091,6 +11092,7 @@ static int nl80211_set_reg(struct sk_buff *skb, struct genl_info *info)
bad_reg:
kfree(rd);
out:
+ mutex_unlock(&cfg80211_mutex);
rtnl_unlock();
return r;
}
diff --git a/net/wireless/reg.c b/net/wireless/reg.c
index 28c431e0c5df..6a2bdb3d9d5e 100644
--- a/net/wireless/reg.c
+++ b/net/wireless/reg.c
@@ -88,7 +88,7 @@ static struct regulatory_request core_request_world = {
/*
* Receipt of information from last regulatory request,
- * protected by RTNL (and can be accessed with RCU protection)
+ * protected by cfg80211_mutex (and can be accessed with RCU protection)
*/
static struct regulatory_request __rcu *last_request =
(void __force __rcu *)&core_request_world;
@@ -100,14 +100,14 @@ static struct faux_device *reg_fdev;
* Central wireless core regulatory domains, we only need two,
* the current one and a world regulatory domain in case we have no
* information to give us an alpha2.
- * (protected by RTNL, can be read under RCU)
+ * (protected by cfg80211_mutex, can be read under RCU)
*/
const struct ieee80211_regdomain __rcu *cfg80211_regdomain;
/*
* Number of devices that registered to the core
* that support cellular base station regulatory hints
- * (protected by RTNL)
+ * (protected by cfg80211_mutex)
*/
static int reg_num_devs_support_basehint;
@@ -128,7 +128,9 @@ static void reg_process_hint(struct regulatory_request *reg_request);
static const struct ieee80211_regdomain *get_cfg80211_regdom(void)
{
- return rcu_dereference_rtnl(cfg80211_regdomain);
+ return rcu_dereference_check(cfg80211_regdomain,
+ lockdep_is_held(&cfg80211_mutex) ||
+ lockdep_rtnl_is_held());
}
/*
@@ -204,7 +206,9 @@ static void rcu_free_regdom(const struct ieee80211_regdomain *r)
static struct regulatory_request *get_last_request(void)
{
- return rcu_dereference_rtnl(last_request);
+ return rcu_dereference_check(last_request,
+ lockdep_is_held(&cfg80211_mutex) ||
+ lockdep_rtnl_is_held());
}
/* Used to queue up regulatory hints */
@@ -314,7 +318,7 @@ static void reset_regdomains(bool full_reset,
{
const struct ieee80211_regdomain *r;
- ASSERT_RTNL();
+ lockdep_assert_held(&cfg80211_mutex);
r = get_cfg80211_regdom();
@@ -456,7 +460,7 @@ reg_copy_regd(const struct ieee80211_regdomain *src_regd)
static void cfg80211_save_user_regdom(const struct ieee80211_regdomain *rd)
{
- ASSERT_RTNL();
+ lockdep_assert_held(&cfg80211_mutex);
if (!IS_ERR(cfg80211_user_regdom))
kfree(cfg80211_user_regdom);
@@ -476,6 +480,7 @@ static void reg_regdb_apply(struct work_struct *work)
struct reg_regdb_apply_request *request;
rtnl_lock();
+ mutex_lock(&cfg80211_mutex);
mutex_lock(®_regdb_apply_mutex);
while (!list_empty(®_regdb_apply_list)) {
@@ -489,6 +494,7 @@ static void reg_regdb_apply(struct work_struct *work)
}
mutex_unlock(®_regdb_apply_mutex);
+ mutex_unlock(&cfg80211_mutex);
rtnl_unlock();
}
@@ -527,8 +533,10 @@ static void crda_timeout_work(struct work_struct *work)
{
pr_debug("Timeout while waiting for CRDA to reply, restoring regulatory settings\n");
rtnl_lock();
+ mutex_lock(&cfg80211_mutex);
reg_crda_timeouts++;
restore_regulatory_settings(true, false);
+ mutex_unlock(&cfg80211_mutex);
rtnl_unlock();
}
@@ -973,7 +981,7 @@ static int query_regdb(const char *alpha2)
const struct fwdb_header *hdr = regdb;
const struct fwdb_country *country;
- ASSERT_RTNL();
+ lockdep_assert_held(&cfg80211_mutex);
if (IS_ERR(regdb))
return PTR_ERR(regdb);
@@ -1003,6 +1011,7 @@ static void regdb_fw_cb(const struct firmware *fw, void *context)
}
rtnl_lock();
+ mutex_lock(&cfg80211_mutex);
if (regdb && !IS_ERR(regdb)) {
/* negative case - a bug
* positive case - can happen due to race in case of multiple cb's in
@@ -1025,6 +1034,7 @@ static void regdb_fw_cb(const struct firmware *fw, void *context)
if (restore)
restore_regulatory_settings(true, false);
+ mutex_unlock(&cfg80211_mutex);
rtnl_unlock();
kfree(context);
@@ -1038,7 +1048,7 @@ static int query_regdb_file(const char *alpha2)
{
int err;
- ASSERT_RTNL();
+ lockdep_assert_held(&cfg80211_mutex);
if (regdb)
return query_regdb(alpha2);
@@ -1080,6 +1090,7 @@ int reg_reload_regdb(void)
}
rtnl_lock();
+ mutex_lock(&cfg80211_mutex);
if (!IS_ERR_OR_NULL(regdb))
kfree(regdb);
regdb = db;
@@ -1102,6 +1113,7 @@ int reg_reload_regdb(void)
reg_process_hint(request);
out_unlock:
+ mutex_unlock(&cfg80211_mutex);
rtnl_unlock();
out:
release_firmware(fw);
@@ -2569,7 +2581,7 @@ static void update_all_wiphy_regulatory(enum nl80211_reg_initiator initiator)
struct cfg80211_registered_device *rdev;
struct wiphy *wiphy;
- ASSERT_RTNL();
+ lockdep_assert_held(&cfg80211_mutex);
for_each_rdev(rdev) {
wiphy = &rdev->wiphy;
@@ -2859,7 +2871,7 @@ reg_process_hint_driver(struct wiphy *wiphy,
if (IS_ERR(regd))
return REG_REQ_IGNORE;
- ASSERT_RTNL();
+ lockdep_assert_held(&cfg80211_mutex);
scoped_guard(wiphy, wiphy) {
tmp = get_wiphy_regdom(wiphy);
rcu_assign_pointer(wiphy->regd, regd);
@@ -3052,7 +3064,7 @@ static void wiphy_all_share_dfs_chan_state(struct wiphy *wiphy)
{
struct cfg80211_registered_device *rdev;
- ASSERT_RTNL();
+ lockdep_assert_held(&cfg80211_mutex);
guard(wiphy)(wiphy);
@@ -3240,7 +3252,7 @@ static void reg_process_self_managed_hints(void)
{
struct cfg80211_registered_device *rdev;
- ASSERT_RTNL();
+ lockdep_assert_held(&cfg80211_mutex);
for_each_rdev(rdev) {
guard(wiphy)(&rdev->wiphy);
@@ -3254,9 +3266,11 @@ static void reg_process_self_managed_hints(void)
static void reg_todo(struct work_struct *work)
{
rtnl_lock();
+ mutex_lock(&cfg80211_mutex);
reg_process_pending_hints();
reg_process_pending_beacon_hints();
reg_process_self_managed_hints();
+ mutex_unlock(&cfg80211_mutex);
rtnl_unlock();
}
@@ -3536,7 +3550,7 @@ static void restore_regulatory_settings(bool reset_user, bool cached)
LIST_HEAD(tmp_reg_req_list);
struct cfg80211_registered_device *rdev;
- ASSERT_RTNL();
+ lockdep_assert_held(&cfg80211_mutex);
/*
* Clear the indoor setting in case that it is not controlled by user
@@ -3654,6 +3668,8 @@ static bool is_wiphy_all_set_reg_flag(enum ieee80211_regulatory_flags flag)
void regulatory_hint_disconnect(void)
{
+ guard(mutex)(&cfg80211_mutex);
+
/* Restore of regulatory settings is not required when wiphy(s)
* ignore IE from connected access point but clearance of beacon hints
* is required when wiphy(s) supports beacon hints.
@@ -3922,7 +3938,7 @@ static int reg_set_rd_driver(const struct ieee80211_regdomain *rd,
return -ENODEV;
if (!driver_request->intersect) {
- ASSERT_RTNL();
+ lockdep_assert_held(&cfg80211_mutex);
scoped_guard(wiphy, request_wiphy) {
if (request_wiphy->regd)
tmp = get_wiphy_regdom(request_wiphy);
@@ -4142,6 +4158,8 @@ void wiphy_regulatory_register(struct wiphy *wiphy)
{
struct regulatory_request *lr = get_last_request();
+ lockdep_assert_held(&cfg80211_mutex);
+
/* self-managed devices ignore beacon hints and country IE */
if (wiphy->regulatory_flags & REGULATORY_WIPHY_SELF_MANAGED) {
wiphy->regulatory_flags |= REGULATORY_DISABLE_BEACON_HINTS |
@@ -4169,6 +4187,8 @@ void wiphy_regulatory_deregister(struct wiphy *wiphy)
struct wiphy *request_wiphy = NULL;
struct regulatory_request *lr;
+ lockdep_assert_held(&cfg80211_mutex);
+
lr = get_last_request();
if (!reg_dev_ignore_cell_hint(wiphy))
@@ -4307,7 +4327,7 @@ void regulatory_propagate_dfs_state(struct wiphy *wiphy,
{
struct cfg80211_registered_device *rdev;
- ASSERT_RTNL();
+ lockdep_assert_held(&cfg80211_mutex);
if (WARN_ON(!cfg80211_chandef_valid(chandef)))
return;
@@ -4416,7 +4436,9 @@ void regulatory_exit(void)
/* Lock to suppress warnings */
rtnl_lock();
+ mutex_lock(&cfg80211_mutex);
reset_regdomains(true, NULL);
+ mutex_unlock(&cfg80211_mutex);
rtnl_unlock();
dev_set_uevent_suppress(®_fdev->dev, true);
--
2.55.0
^ permalink raw reply related [flat|nested] 23+ messages in thread
* [PATCH wireless-next v2 11/18] wifi: cfg80211: allow walking wiphy list under cfg80211_mutex
2026-10-05 10:03 [PATCH wireless-next v2 01/18] wifi: further RTNL redux Johannes Berg
` (9 preceding siblings ...)
2026-10-05 10:03 ` [PATCH wireless-next v2 10/18] wifi: cfg80211: add a mutex for regulatory/device list Johannes Berg
@ 2026-10-05 10:03 ` Johannes Berg
2026-10-05 10:03 ` [PATCH wireless-next v2 12/18] wifi: ath: use freq_reg_info() under RCU Johannes Berg
` (6 subsequent siblings)
17 siblings, 0 replies; 23+ messages in thread
From: Johannes Berg @ 2026-10-05 10:03 UTC (permalink / raw)
To: linux-wireless; +Cc: Johannes Berg
From: Johannes Berg <johannes.berg@intel.com>
for_each_rdev() now requires RTNL, but we can relax that and
allow any of RTNL, cfg80211_mutex and RCU, and then use it
in quite a few more places.
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
---
net/wireless/core.c | 16 +++++-----------
net/wireless/core.h | 11 +++--------
net/wireless/nl80211.c | 11 +++++------
net/wireless/reg.c | 2 +-
4 files changed, 14 insertions(+), 26 deletions(-)
diff --git a/net/wireless/core.c b/net/wireless/core.c
index e12d8b301a37..4e167c8c05b0 100644
--- a/net/wireless/core.c
+++ b/net/wireless/core.c
@@ -59,20 +59,16 @@ module_param(cfg80211_disable_40mhz_24ghz, bool, 0644);
MODULE_PARM_DESC(cfg80211_disable_40mhz_24ghz,
"Disable 40MHz support in the 2.4GHz band");
-/* requires RCU or rtnl */
struct cfg80211_registered_device *cfg80211_rdev_by_wiphy_idx(int wiphy_idx)
{
- struct cfg80211_registered_device *result = NULL, *rdev;
+ struct cfg80211_registered_device *rdev;
- list_for_each_entry_rcu(rdev, &cfg80211_rdev_list, list,
- lockdep_rtnl_is_held()) {
- if (rdev->wiphy_idx == wiphy_idx) {
- result = rdev;
- break;
- }
+ for_each_rdev(rdev) {
+ if (rdev->wiphy_idx == wiphy_idx)
+ return rdev;
}
- return result;
+ return NULL;
}
int get_wiphy_idx(struct wiphy *wiphy)
@@ -86,8 +82,6 @@ struct wiphy *wiphy_idx_to_wiphy(int wiphy_idx)
{
struct cfg80211_registered_device *rdev;
- ASSERT_RTNL();
-
rdev = cfg80211_rdev_by_wiphy_idx(wiphy_idx);
if (!rdev)
return NULL;
diff --git a/net/wireless/core.h b/net/wireless/core.h
index 414ce31af60c..dfcb9ed5035b 100644
--- a/net/wireless/core.h
+++ b/net/wireless/core.h
@@ -182,15 +182,10 @@ extern struct workqueue_struct *cfg80211_wq;
extern struct list_head cfg80211_rdev_list;
extern int cfg80211_rdev_list_generation;
-/* This is constructed like this so it can be used in if/else */
-static inline int for_each_rdev_check_rtnl(void)
-{
- ASSERT_RTNL();
- return 0;
-}
#define for_each_rdev(rdev) \
- if (for_each_rdev_check_rtnl()) {} else \
- list_for_each_entry(rdev, &cfg80211_rdev_list, list)
+ list_for_each_entry_rcu(rdev, &cfg80211_rdev_list, list, \
+ lockdep_rtnl_is_held() || \
+ lockdep_is_held(&cfg80211_mutex))
enum bss_source_type {
BSS_SOURCE_DIRECT = 0,
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
index a685190d16ac..1e9d0df6bd3c 100644
--- a/net/wireless/nl80211.c
+++ b/net/wireless/nl80211.c
@@ -63,7 +63,7 @@ static const struct genl_multicast_group nl80211_mcgrps[] = {
#endif
};
-/* returns ERR_PTR values, requires RCU or rtnl if rdev is %NULL */
+/* returns ERR_PTR values, requires RCU/cfg80211_mutex/RTNL if rdev is %NULL */
static struct wireless_dev *
__cfg80211_wdev_from_attrs(struct cfg80211_registered_device *rdev,
struct net *netns, struct nlattr **attrs)
@@ -105,8 +105,7 @@ __cfg80211_wdev_from_attrs(struct cfg80211_registered_device *rdev,
return result ?: ERR_PTR(-ENODEV);
}
- list_for_each_entry_rcu(rdev, &cfg80211_rdev_list, list,
- lockdep_rtnl_is_held()) {
+ for_each_rdev(rdev) {
struct wireless_dev *wdev;
if (wiphy_net(&rdev->wiphy) != netns)
@@ -137,7 +136,7 @@ __cfg80211_wdev_from_attrs(struct cfg80211_registered_device *rdev,
return ERR_PTR(-ENODEV);
}
-/* requires RCU or rtnl */
+/* requires RCU/cfg80211_mutex/RTNL */
static struct cfg80211_registered_device *
__cfg80211_rdev_from_attrs(struct net *netns, struct nlattr **attrs)
{
@@ -10941,7 +10940,7 @@ static int nl80211_get_reg_dump(struct sk_buff *skb,
/* the global regdom is idx 0 */
reg_idx = 1;
- list_for_each_entry_rcu(rdev, &cfg80211_rdev_list, list) {
+ for_each_rdev(rdev) {
regdom = get_wiphy_regdom(&rdev->wiphy);
if (!regdom)
continue;
@@ -23415,7 +23414,7 @@ static int nl80211_netlink_notify(struct notifier_block * nb,
rcu_read_lock();
- list_for_each_entry_rcu(rdev, &cfg80211_rdev_list, list) {
+ for_each_rdev(rdev) {
struct cfg80211_sched_scan_request *sched_scan_req;
list_for_each_entry_rcu(sched_scan_req,
diff --git a/net/wireless/reg.c b/net/wireless/reg.c
index 6a2bdb3d9d5e..7248e36714fe 100644
--- a/net/wireless/reg.c
+++ b/net/wireless/reg.c
@@ -2525,7 +2525,7 @@ static void reg_check_chans_work(struct work_struct *work)
rcu_read_lock();
- list_for_each_entry_rcu(rdev, &cfg80211_rdev_list, list)
+ for_each_rdev(rdev)
wiphy_work_queue(&rdev->wiphy, &rdev->reg_check_chans_wk);
rcu_read_unlock();
--
2.55.0
^ permalink raw reply related [flat|nested] 23+ messages in thread
* [PATCH wireless-next v2 12/18] wifi: ath: use freq_reg_info() under RCU
2026-10-05 10:03 [PATCH wireless-next v2 01/18] wifi: further RTNL redux Johannes Berg
` (10 preceding siblings ...)
2026-10-05 10:03 ` [PATCH wireless-next v2 11/18] wifi: cfg80211: allow walking wiphy list under cfg80211_mutex Johannes Berg
@ 2026-10-05 10:03 ` Johannes Berg
2026-10-05 10:03 ` [PATCH wireless-next v2 13/18] wifi: brcmsmac: " Johannes Berg
` (5 subsequent siblings)
17 siblings, 0 replies; 23+ messages in thread
From: Johannes Berg @ 2026-10-05 10:03 UTC (permalink / raw)
To: linux-wireless; +Cc: Johannes Berg
From: Johannes Berg <johannes.berg@intel.com>
This will soon be required, because there won't be the
RTNL to protect everything. Use RCU for freq_reg_info().
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
---
drivers/net/wireless/ath/regd.c | 2 ++
1 file changed, 2 insertions(+)
diff --git a/drivers/net/wireless/ath/regd.c b/drivers/net/wireless/ath/regd.c
index f15e7bd690b5..cedb5e43b9e4 100644
--- a/drivers/net/wireless/ath/regd.c
+++ b/drivers/net/wireless/ath/regd.c
@@ -268,6 +268,8 @@ static void ath_force_clear_no_ir_chan(struct wiphy *wiphy,
{
const struct ieee80211_reg_rule *reg_rule;
+ guard(rcu)();
+
reg_rule = freq_reg_info(wiphy, MHZ_TO_KHZ(ch->center_freq));
if (IS_ERR(reg_rule))
return;
--
2.55.0
^ permalink raw reply related [flat|nested] 23+ messages in thread
* [PATCH wireless-next v2 13/18] wifi: brcmsmac: use freq_reg_info() under RCU
2026-10-05 10:03 [PATCH wireless-next v2 01/18] wifi: further RTNL redux Johannes Berg
` (11 preceding siblings ...)
2026-10-05 10:03 ` [PATCH wireless-next v2 12/18] wifi: ath: use freq_reg_info() under RCU Johannes Berg
@ 2026-10-05 10:03 ` Johannes Berg
2026-10-05 10:03 ` [PATCH wireless-next v2 14/18] wifi: rtlwifi: " Johannes Berg
` (4 subsequent siblings)
17 siblings, 0 replies; 23+ messages in thread
From: Johannes Berg @ 2026-10-05 10:03 UTC (permalink / raw)
To: linux-wireless; +Cc: Johannes Berg
From: Johannes Berg <johannes.berg@intel.com>
This will soon be required, because there won't be the
RTNL to protect everything. Use RCU for freq_reg_info().
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
---
drivers/net/wireless/broadcom/brcm80211/brcmsmac/channel.c | 2 ++
1 file changed, 2 insertions(+)
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmsmac/channel.c b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/channel.c
index cdfe8635c012..5ca8a0735398 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmsmac/channel.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/channel.c
@@ -676,6 +676,8 @@ brcms_reg_apply_beaconing_flags(struct wiphy *wiphy,
continue;
if (initiator == NL80211_REGDOM_SET_BY_COUNTRY_IE) {
+ guard(rcu)();
+
rule = freq_reg_info(wiphy,
MHZ_TO_KHZ(ch->center_freq));
if (IS_ERR(rule))
--
2.55.0
^ permalink raw reply related [flat|nested] 23+ messages in thread
* [PATCH wireless-next v2 14/18] wifi: rtlwifi: use freq_reg_info() under RCU
2026-10-05 10:03 [PATCH wireless-next v2 01/18] wifi: further RTNL redux Johannes Berg
` (12 preceding siblings ...)
2026-10-05 10:03 ` [PATCH wireless-next v2 13/18] wifi: brcmsmac: " Johannes Berg
@ 2026-10-05 10:03 ` Johannes Berg
2026-10-05 10:03 ` [PATCH wireless-next v2 15/18] wifi: nl80211: read WMM reg rule " Johannes Berg
` (3 subsequent siblings)
17 siblings, 0 replies; 23+ messages in thread
From: Johannes Berg @ 2026-10-05 10:03 UTC (permalink / raw)
To: linux-wireless; +Cc: Johannes Berg
From: Johannes Berg <johannes.berg@intel.com>
This will soon be required, because there won't be the
RTNL to protect everything. Use RCU for freq_reg_info().
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
---
drivers/net/wireless/realtek/rtlwifi/regd.c | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/drivers/net/wireless/realtek/rtlwifi/regd.c b/drivers/net/wireless/realtek/rtlwifi/regd.c
index fd967006b3e1..503625b50fc3 100644
--- a/drivers/net/wireless/realtek/rtlwifi/regd.c
+++ b/drivers/net/wireless/realtek/rtlwifi/regd.c
@@ -150,6 +150,8 @@ static void _rtl_reg_apply_beaconing_flags(struct wiphy *wiphy,
(ch->flags & IEEE80211_CHAN_RADAR))
continue;
if (initiator == NL80211_REGDOM_SET_BY_COUNTRY_IE) {
+ guard(rcu)();
+
reg_rule = freq_reg_info(wiphy,
ch->center_freq);
if (IS_ERR(reg_rule))
@@ -212,6 +214,8 @@ static void _rtl_reg_apply_active_scan_flags(struct wiphy *wiphy,
*custom regulatory domain.
*/
+ guard(rcu)();
+
ch = &sband->channels[11]; /* CH 12 */
reg_rule = freq_reg_info(wiphy, ch->center_freq);
if (!IS_ERR(reg_rule)) {
--
2.55.0
^ permalink raw reply related [flat|nested] 23+ messages in thread
* [PATCH wireless-next v2 15/18] wifi: nl80211: read WMM reg rule under RCU
2026-10-05 10:03 [PATCH wireless-next v2 01/18] wifi: further RTNL redux Johannes Berg
` (13 preceding siblings ...)
2026-10-05 10:03 ` [PATCH wireless-next v2 14/18] wifi: rtlwifi: " Johannes Berg
@ 2026-10-05 10:03 ` Johannes Berg
2026-10-05 10:03 ` [PATCH wireless-next v2 16/18] wifi: ath11k: read wiphy regd " Johannes Berg
` (2 subsequent siblings)
17 siblings, 0 replies; 23+ messages in thread
From: Johannes Berg @ 2026-10-05 10:03 UTC (permalink / raw)
To: linux-wireless; +Cc: Johannes Berg
From: Johannes Berg <johannes.berg@intel.com>
freq_reg_info() returns a pointer into the current regdomain,
so we need RTNL, cfg80211_mutex or RCU, but I want to remove
the RTNL, so just use RCU here.
Also document that this is required, we didn't say when the
function could be used at all.
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
---
include/net/cfg80211.h | 2 ++
net/wireless/nl80211.c | 5 +++--
2 files changed, 5 insertions(+), 2 deletions(-)
diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h
index eb376a54a07f..ef54cc64ddf0 100644
--- a/include/net/cfg80211.h
+++ b/include/net/cfg80211.h
@@ -8183,6 +8183,8 @@ void wiphy_apply_custom_regulatory(struct wiphy *wiphy,
* it wants to follow we respect that unless a country IE has been received
* and processed already.
*
+ * Must be called within an RCU read-side critical section.
+ *
* Return: A valid pointer, or, when an error occurs, for example if no rule
* can be found, the return value is encoded using ERR_PTR(). Use IS_ERR() to
* check and PTR_ERR() to obtain the numeric return value. The numeric return
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
index 1e9d0df6bd3c..3ac3fea6be32 100644
--- a/net/wireless/nl80211.c
+++ b/net/wireless/nl80211.c
@@ -1538,9 +1538,10 @@ static int nl80211_msg_put_channel(struct sk_buff *msg, struct wiphy *wiphy,
goto nla_put_failure;
if (large) {
- const struct ieee80211_reg_rule *rule =
- freq_reg_info(wiphy, MHZ_TO_KHZ(chan->center_freq));
+ const struct ieee80211_reg_rule *rule;
+ guard(rcu)();
+ rule = freq_reg_info(wiphy, MHZ_TO_KHZ(chan->center_freq));
if (!IS_ERR_OR_NULL(rule) && rule->has_wmm) {
if (nl80211_msg_put_wmm_rules(msg, rule))
goto nla_put_failure;
--
2.55.0
^ permalink raw reply related [flat|nested] 23+ messages in thread
* [PATCH wireless-next v2 16/18] wifi: ath11k: read wiphy regd under RCU
2026-10-05 10:03 [PATCH wireless-next v2 01/18] wifi: further RTNL redux Johannes Berg
` (14 preceding siblings ...)
2026-10-05 10:03 ` [PATCH wireless-next v2 15/18] wifi: nl80211: read WMM reg rule " Johannes Berg
@ 2026-10-05 10:03 ` Johannes Berg
2026-10-05 10:03 ` [PATCH wireless-next v2 17/18] wifi: ath12k: " Johannes Berg
2026-10-05 10:03 ` [PATCH wireless-next v2 18/18] wifi: cfg80211: regulatory: stop using RTNL Johannes Berg
17 siblings, 0 replies; 23+ messages in thread
From: Johannes Berg @ 2026-10-05 10:03 UTC (permalink / raw)
To: linux-wireless; +Cc: Johannes Berg
From: Johannes Berg <johannes.berg@intel.com>
Part of the RTNL removal here means the notifier will no
longer hold the RTNL, so rcu_dereference_rtnl() can't be
used any more. Use an RCU critical section which works
either way, we don't have access to the cfg80211_mutex
in the drivers.
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
---
drivers/net/wireless/ath/ath11k/reg.c | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/drivers/net/wireless/ath/ath11k/reg.c b/drivers/net/wireless/ath/ath11k/reg.c
index 0879a132b67c..1030e119fd90 100644
--- a/drivers/net/wireless/ath/ath11k/reg.c
+++ b/drivers/net/wireless/ath/ath11k/reg.c
@@ -33,7 +33,9 @@ static bool ath11k_regdom_changes(struct ath11k *ar, char *alpha2)
{
const struct ieee80211_regdomain *regd;
- regd = rcu_dereference_rtnl(ar->hw->wiphy->regd);
+ guard(rcu)();
+
+ regd = get_wiphy_regdom(ar->hw->wiphy);
/* This can happen during wiphy registration where the previous
* user request is received before we update the regd received
* from firmware.
--
2.55.0
^ permalink raw reply related [flat|nested] 23+ messages in thread
* [PATCH wireless-next v2 17/18] wifi: ath12k: read wiphy regd under RCU
2026-10-05 10:03 [PATCH wireless-next v2 01/18] wifi: further RTNL redux Johannes Berg
` (15 preceding siblings ...)
2026-10-05 10:03 ` [PATCH wireless-next v2 16/18] wifi: ath11k: read wiphy regd " Johannes Berg
@ 2026-10-05 10:03 ` Johannes Berg
2026-10-05 10:03 ` [PATCH wireless-next v2 18/18] wifi: cfg80211: regulatory: stop using RTNL Johannes Berg
17 siblings, 0 replies; 23+ messages in thread
From: Johannes Berg @ 2026-10-05 10:03 UTC (permalink / raw)
To: linux-wireless; +Cc: Johannes Berg
From: Johannes Berg <johannes.berg@intel.com>
Part of the RTNL removal here means the notifier will no
longer hold the RTNL, so rcu_dereference_rtnl() can't be
used any more. Use an RCU critical section which works
either way, we don't have access to the cfg80211_mutex
in the drivers.
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
---
drivers/net/wireless/ath/ath12k/reg.c | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/drivers/net/wireless/ath/ath12k/reg.c b/drivers/net/wireless/ath/ath12k/reg.c
index 89abf2e87ad1..d7065fa73c50 100644
--- a/drivers/net/wireless/ath/ath12k/reg.c
+++ b/drivers/net/wireless/ath/ath12k/reg.c
@@ -33,7 +33,9 @@ static bool ath12k_regdom_changes(struct ieee80211_hw *hw, char *alpha2)
{
const struct ieee80211_regdomain *regd;
- regd = rcu_dereference_rtnl(hw->wiphy->regd);
+ guard(rcu)();
+
+ regd = get_wiphy_regdom(hw->wiphy);
/* This can happen during wiphy registration where the previous
* user request is received before we update the regd received
* from firmware.
--
2.55.0
^ permalink raw reply related [flat|nested] 23+ messages in thread
* [PATCH wireless-next v2 18/18] wifi: cfg80211: regulatory: stop using RTNL
2026-10-05 10:03 [PATCH wireless-next v2 01/18] wifi: further RTNL redux Johannes Berg
` (16 preceding siblings ...)
2026-10-05 10:03 ` [PATCH wireless-next v2 17/18] wifi: ath12k: " Johannes Berg
@ 2026-10-05 10:03 ` Johannes Berg
17 siblings, 0 replies; 23+ messages in thread
From: Johannes Berg @ 2026-10-05 10:03 UTC (permalink / raw)
To: linux-wireless; +Cc: Johannes Berg
From: Johannes Berg <johannes.berg@intel.com>
Now we have cfg80211_mutex everywhere along with wiphy mutex
for per-device bits, so don't need RTNL any more. Remove it
from the regulatory code.
Also, wiphy_apply_custom_regulatory() no longer needs the RTNL,
and get_wiphy_regdom() can no longer rely on it.
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
---
include/net/cfg80211.h | 6 +++---
net/wireless/chan.c | 2 +-
net/wireless/core.c | 12 ++----------
net/wireless/mlme.c | 3 ---
net/wireless/nl80211.c | 11 ++++++-----
net/wireless/reg.c | 37 +++++++++----------------------------
6 files changed, 21 insertions(+), 50 deletions(-)
diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h
index ef54cc64ddf0..76edfe5765c8 100644
--- a/include/net/cfg80211.h
+++ b/include/net/cfg80211.h
@@ -8146,9 +8146,9 @@ int regulatory_set_wiphy_regd(struct wiphy *wiphy,
* @wiphy: the wireless device we want to process the regulatory domain on
* @rd: the regulatory domain information to use for this wiphy
*
- * This functions requires the RTNL and the wiphy mutex to be held and
- * applies the new regdomain synchronously to this wiphy. For more details
- * see regulatory_set_wiphy_regd().
+ * This functions requires the wiphy mutex to be held and applies the new
+ * regdomain synchronously to this wiphy. For more details see
+ * regulatory_set_wiphy_regd().
*
* Return: 0 on success. -EINVAL, -EPERM
*/
diff --git a/net/wireless/chan.c b/net/wireless/chan.c
index c743b6fb7e30..9183cd19dbdc 100644
--- a/net/wireless/chan.c
+++ b/net/wireless/chan.c
@@ -1243,7 +1243,7 @@ bool cfg80211_any_wiphy_oper_chan(struct wiphy *wiphy,
{
struct cfg80211_registered_device *rdev;
- ASSERT_RTNL();
+ lockdep_assert_held(&cfg80211_mutex);
if (!(chan->flags & IEEE80211_CHAN_RADAR))
return false;
diff --git a/net/wireless/core.c b/net/wireless/core.c
index 4e167c8c05b0..dcade9e8c27c 100644
--- a/net/wireless/core.c
+++ b/net/wireless/core.c
@@ -560,15 +560,11 @@ static void cfg80211_propagate_radar_detect_wk(struct work_struct *work)
rdev = container_of(work, struct cfg80211_registered_device,
propagate_radar_detect_wk);
- rtnl_lock();
- mutex_lock(&cfg80211_mutex);
+ guard(mutex)(&cfg80211_mutex);
regulatory_propagate_dfs_state(&rdev->wiphy, &rdev->radar_chandef,
NL80211_DFS_UNAVAILABLE,
NL80211_RADAR_DETECTED);
-
- mutex_unlock(&cfg80211_mutex);
- rtnl_unlock();
}
static void cfg80211_propagate_cac_done_wk(struct work_struct *work)
@@ -578,15 +574,11 @@ static void cfg80211_propagate_cac_done_wk(struct work_struct *work)
rdev = container_of(work, struct cfg80211_registered_device,
propagate_cac_done_wk);
- rtnl_lock();
- mutex_lock(&cfg80211_mutex);
+ guard(mutex)(&cfg80211_mutex);
regulatory_propagate_dfs_state(&rdev->wiphy, &rdev->cac_done_chandef,
NL80211_DFS_AVAILABLE,
NL80211_RADAR_CAC_FINISHED);
-
- mutex_unlock(&cfg80211_mutex);
- rtnl_unlock();
}
static void cfg80211_wiphy_work(struct work_struct *work)
diff --git a/net/wireless/mlme.c b/net/wireless/mlme.c
index 082590d859e9..96fd14f2a305 100644
--- a/net/wireless/mlme.c
+++ b/net/wireless/mlme.c
@@ -1119,7 +1119,6 @@ void cfg80211_dfs_channels_update_work(struct work_struct *work)
dfs_update_channels_wk);
wiphy = &rdev->wiphy;
- rtnl_lock();
mutex_lock(&cfg80211_mutex);
wiphy_lock(wiphy);
for (bandid = 0; bandid < NUM_NL80211_BANDS; bandid++) {
@@ -1135,7 +1134,6 @@ void cfg80211_dfs_channels_update_work(struct work_struct *work)
HZ);
wiphy_unlock(wiphy);
mutex_unlock(&cfg80211_mutex);
- rtnl_unlock();
return;
}
@@ -1202,7 +1200,6 @@ void cfg80211_dfs_channels_update_work(struct work_struct *work)
expired[i].event);
}
mutex_unlock(&cfg80211_mutex);
- rtnl_unlock();
kvfree(expired);
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
index 3ac3fea6be32..5bcfab5c7523 100644
--- a/net/wireless/nl80211.c
+++ b/net/wireless/nl80211.c
@@ -63,7 +63,7 @@ static const struct genl_multicast_group nl80211_mcgrps[] = {
#endif
};
-/* returns ERR_PTR values, requires RCU/cfg80211_mutex/RTNL if rdev is %NULL */
+/* returns ERR_PTR values, requires RCU/cfg80211_mutex if rdev is %NULL */
static struct wireless_dev *
__cfg80211_wdev_from_attrs(struct cfg80211_registered_device *rdev,
struct net *netns, struct nlattr **attrs)
@@ -136,7 +136,7 @@ __cfg80211_wdev_from_attrs(struct cfg80211_registered_device *rdev,
return ERR_PTR(-ENODEV);
}
-/* requires RCU/cfg80211_mutex/RTNL */
+/* requires RCU/cfg80211_mutex */
static struct cfg80211_registered_device *
__cfg80211_rdev_from_attrs(struct net *netns, struct nlattr **attrs)
{
@@ -11043,7 +11043,6 @@ static int nl80211_set_reg(struct sk_buff *skb, struct genl_info *info)
return -EINVAL;
}
- rtnl_lock();
mutex_lock(&cfg80211_mutex);
if (!reg_is_valid_request(alpha2)) {
r = -EINVAL;
@@ -11093,7 +11092,6 @@ static int nl80211_set_reg(struct sk_buff *skb, struct genl_info *info)
kfree(rd);
out:
mutex_unlock(&cfg80211_mutex);
- rtnl_unlock();
return r;
}
#endif /* CONFIG_CFG80211_CRDA_SUPPORT */
@@ -21173,7 +21171,10 @@ static bool nl80211_reg_change_event_fill(struct sk_buff *msg,
}
if (request->wiphy_idx != WIPHY_IDX_INVALID) {
- struct wiphy *wiphy = wiphy_idx_to_wiphy(request->wiphy_idx);
+ struct wiphy *wiphy;
+
+ guard(rcu)();
+ wiphy = wiphy_idx_to_wiphy(request->wiphy_idx);
if (wiphy &&
nla_put_u32(msg, NL80211_ATTR_WIPHY, request->wiphy_idx))
diff --git a/net/wireless/reg.c b/net/wireless/reg.c
index 7248e36714fe..40d43fc6f4b2 100644
--- a/net/wireless/reg.c
+++ b/net/wireless/reg.c
@@ -129,20 +129,18 @@ static void reg_process_hint(struct regulatory_request *reg_request);
static const struct ieee80211_regdomain *get_cfg80211_regdom(void)
{
return rcu_dereference_check(cfg80211_regdomain,
- lockdep_is_held(&cfg80211_mutex) ||
- lockdep_rtnl_is_held());
+ lockdep_is_held(&cfg80211_mutex));
}
/*
* Returns the regulatory domain associated with the wiphy.
*
- * Requires any of RTNL, wiphy mutex or RCU protection.
+ * Requires the wiphy mutex or RCU protection.
*/
const struct ieee80211_regdomain *get_wiphy_regdom(struct wiphy *wiphy)
{
return rcu_dereference_check(wiphy->regd,
- lockdep_is_held(&wiphy->mtx) ||
- lockdep_rtnl_is_held());
+ lockdep_is_held(&wiphy->mtx));
}
EXPORT_SYMBOL(get_wiphy_regdom);
@@ -207,8 +205,7 @@ static void rcu_free_regdom(const struct ieee80211_regdomain *r)
static struct regulatory_request *get_last_request(void)
{
return rcu_dereference_check(last_request,
- lockdep_is_held(&cfg80211_mutex) ||
- lockdep_rtnl_is_held());
+ lockdep_is_held(&cfg80211_mutex));
}
/* Used to queue up regulatory hints */
@@ -479,7 +476,6 @@ static void reg_regdb_apply(struct work_struct *work)
{
struct reg_regdb_apply_request *request;
- rtnl_lock();
mutex_lock(&cfg80211_mutex);
mutex_lock(®_regdb_apply_mutex);
@@ -495,7 +491,6 @@ static void reg_regdb_apply(struct work_struct *work)
mutex_unlock(®_regdb_apply_mutex);
mutex_unlock(&cfg80211_mutex);
- rtnl_unlock();
}
static DECLARE_WORK(reg_regdb_work, reg_regdb_apply);
@@ -532,12 +527,10 @@ static DECLARE_DELAYED_WORK(crda_timeout, crda_timeout_work);
static void crda_timeout_work(struct work_struct *work)
{
pr_debug("Timeout while waiting for CRDA to reply, restoring regulatory settings\n");
- rtnl_lock();
mutex_lock(&cfg80211_mutex);
reg_crda_timeouts++;
restore_regulatory_settings(true, false);
mutex_unlock(&cfg80211_mutex);
- rtnl_unlock();
}
static void cancel_crda_timeout(void)
@@ -1010,7 +1003,6 @@ static void regdb_fw_cb(const struct firmware *fw, void *context)
set_error = -EINVAL;
}
- rtnl_lock();
mutex_lock(&cfg80211_mutex);
if (regdb && !IS_ERR(regdb)) {
/* negative case - a bug
@@ -1035,7 +1027,6 @@ static void regdb_fw_cb(const struct firmware *fw, void *context)
restore_regulatory_settings(true, false);
mutex_unlock(&cfg80211_mutex);
- rtnl_unlock();
kfree(context);
@@ -1089,7 +1080,6 @@ int reg_reload_regdb(void)
goto out;
}
- rtnl_lock();
mutex_lock(&cfg80211_mutex);
if (!IS_ERR_OR_NULL(regdb))
kfree(regdb);
@@ -1114,7 +1104,6 @@ int reg_reload_regdb(void)
out_unlock:
mutex_unlock(&cfg80211_mutex);
- rtnl_unlock();
out:
release_firmware(fw);
return err;
@@ -2698,13 +2687,11 @@ void wiphy_apply_custom_regulatory(struct wiphy *wiphy,
if (IS_ERR(new_regd))
return;
- rtnl_lock();
scoped_guard(wiphy, wiphy) {
tmp = get_wiphy_regdom(wiphy);
rcu_assign_pointer(wiphy->regd, new_regd);
rcu_free_regdom(tmp);
}
- rtnl_unlock();
}
EXPORT_SYMBOL(wiphy_apply_custom_regulatory);
@@ -3217,7 +3204,6 @@ static void reg_process_self_managed_hint(struct wiphy *wiphy)
enum nl80211_band band;
struct regulatory_request request = {};
- ASSERT_RTNL();
lockdep_assert_wiphy(wiphy);
spin_lock(®_requests_lock);
@@ -3265,13 +3251,11 @@ static void reg_process_self_managed_hints(void)
static void reg_todo(struct work_struct *work)
{
- rtnl_lock();
- mutex_lock(&cfg80211_mutex);
+ guard(mutex)(&cfg80211_mutex);
+
reg_process_pending_hints();
reg_process_pending_beacon_hints();
reg_process_self_managed_hints();
- mutex_unlock(&cfg80211_mutex);
- rtnl_unlock();
}
static void queue_regulatory_request(struct regulatory_request *request)
@@ -4141,7 +4125,7 @@ int regulatory_set_wiphy_regd_sync(struct wiphy *wiphy,
{
int ret;
- ASSERT_RTNL();
+ lockdep_assert_wiphy(wiphy);
ret = __regulatory_set_wiphy_regd(wiphy, rd);
if (ret)
@@ -4435,11 +4419,8 @@ void regulatory_exit(void)
cancel_delayed_work_sync(®_check_chans);
/* Lock to suppress warnings */
- rtnl_lock();
- mutex_lock(&cfg80211_mutex);
- reset_regdomains(true, NULL);
- mutex_unlock(&cfg80211_mutex);
- rtnl_unlock();
+ scoped_guard(mutex, &cfg80211_mutex)
+ reset_regdomains(true, NULL);
dev_set_uevent_suppress(®_fdev->dev, true);
--
2.55.0
^ permalink raw reply related [flat|nested] 23+ messages in thread
* Re: [PATCH wireless-next v2 09/18] wifi: cfg80211: document wiphy mutex for radar/CAC events
2026-10-05 10:03 ` [PATCH wireless-next v2 09/18] wifi: cfg80211: document wiphy mutex for radar/CAC events Johannes Berg
@ 2026-10-05 17:46 ` Brian Norris
2026-10-05 17:52 ` Johannes Berg
0 siblings, 1 reply; 23+ messages in thread
From: Brian Norris @ 2026-10-05 17:46 UTC (permalink / raw)
To: Johannes Berg; +Cc: linux-wireless, Johannes Berg, Francesco Dolcini
On Mon, Oct 05, 2026 at 12:03:36PM +0200, Johannes Berg wrote:
> From: Johannes Berg <johannes.berg@intel.com>
>
> The DFS state of channels and the CAC state of the wdev is
> protected by the wiphy mutex, so the radar and CAC events
> must be reported by drivers with the wiphy mutex held. In
> mac80211 we do this, but some drivers don't yet:
> - mwifiex/nxpwifi have an event handling worker,
FWIW, one of the two contexts that call cfg80211_cac_event() in mwifiex
does *not* (by inspection) seem to hold this. Is this something you'd
prefer individual driver users/maintainers resolve?
> - qtnfmac also does it from event processing, and
> - mt7915 and mt7996 don't acquire a mutex for background
> radar events.
>
> Document the requirement.
>
> Signed-off-by: Johannes Berg <johannes.berg@intel.com>
> ---
> include/net/cfg80211.h | 3 ++-
> 1 file changed, 2 insertions(+), 1 deletion(-)
>
> diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h
> index 9cb536fc8cb8..eb376a54a07f 100644
> --- a/include/net/cfg80211.h
> +++ b/include/net/cfg80211.h
> @@ -9794,6 +9794,7 @@ void cfg80211_cqm_beacon_loss_notify(struct net_device *dev, gfp_t gfp);
> * @gfp: context flags
> *
> * This function is called when a radar is detected on the current chanenl.
> + * Must be called with the wiphy mutex held.
> */
> void __cfg80211_radar_event(struct wiphy *wiphy,
> struct cfg80211_chan_def *chandef,
> @@ -9839,7 +9840,7 @@ void cfg80211_sta_opmode_change_notify(struct net_device *dev, const u8 *mac,
> *
> * This function is called when a Channel availability check (CAC) is finished
> * or aborted. This must be called to notify the completion of a CAC process,
> - * also by full-MAC drivers.
> + * also by full-MAC drivers. Must be called with the wiphy mutex held.
> */
> void cfg80211_cac_event(struct net_device *netdev,
> const struct cfg80211_chan_def *chandef,
Should we add an assert to this API?
lockdep_assert_wiphy(wiphy);
> --
> 2.55.0
>
>
^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: [PATCH wireless-next v2 09/18] wifi: cfg80211: document wiphy mutex for radar/CAC events
2026-10-05 17:46 ` Brian Norris
@ 2026-10-05 17:52 ` Johannes Berg
2026-10-05 18:36 ` Brian Norris
0 siblings, 1 reply; 23+ messages in thread
From: Johannes Berg @ 2026-10-05 17:52 UTC (permalink / raw)
To: Brian Norris; +Cc: linux-wireless, Francesco Dolcini
Hi Brian,
Woah, thanks for looking through this! :-)
On Mon, 2026-10-05 at 10:46 -0700, Brian Norris wrote:
> > The DFS state of channels and the CAC state of the wdev is
> > protected by the wiphy mutex, so the radar and CAC events
> > must be reported by drivers with the wiphy mutex held. In
> > mac80211 we do this, but some drivers don't yet:
> > - mwifiex/nxpwifi have an event handling worker,
>
> FWIW, one of the two contexts that call cfg80211_cac_event() in mwifiex
> does *not* (by inspection) seem to hold this.
Yeah I know - that's why I wrote "some drivers __don't__ yet".
It's always been broken though, and I kinda just wanted to move on. It's
racy, but I think mostly wrt. the valid_links warning (which isn't
relevant here) and the data accesses, nothing worse will happen.
> Is this something you'd
> prefer individual driver users/maintainers resolve?
I think so. Or we can discuss it should be async in cfg80211, or an
async version? I guess first we should discuss how to solve it either
way, and look at all the drivers that still have the issue.
> > void cfg80211_cac_event(struct net_device *netdev,
> > const struct cfg80211_chan_def *chandef,
>
> Should we add an assert to this API?
>
> lockdep_assert_wiphy(wiphy);
Well I figured if I do that now then tools (and perhaps people) will
start complaining and that'd just put more pressure on everyone to fix
it ... that's why I didn't yet, but I guess I should've outlined that
better in the commit message (even after a --- marker).
johannes
^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: [PATCH wireless-next v2 09/18] wifi: cfg80211: document wiphy mutex for radar/CAC events
2026-10-05 17:52 ` Johannes Berg
@ 2026-10-05 18:36 ` Brian Norris
2026-10-05 18:43 ` Johannes Berg
0 siblings, 1 reply; 23+ messages in thread
From: Brian Norris @ 2026-10-05 18:36 UTC (permalink / raw)
To: Johannes Berg; +Cc: linux-wireless, Francesco Dolcini
On Mon, Oct 05, 2026 at 07:52:11PM +0200, Johannes Berg wrote:
> Woah, thanks for looking through this! :-)
Ha, well sometimes I read stuff with the "mwifiex" keyword in it :)
> On Mon, 2026-10-05 at 10:46 -0700, Brian Norris wrote:
> > > The DFS state of channels and the CAC state of the wdev is
> > > protected by the wiphy mutex, so the radar and CAC events
> > > must be reported by drivers with the wiphy mutex held. In
> > > mac80211 we do this, but some drivers don't yet:
> > > - mwifiex/nxpwifi have an event handling worker,
> >
> > FWIW, one of the two contexts that call cfg80211_cac_event() in mwifiex
> > does *not* (by inspection) seem to hold this.
>
> Yeah I know - that's why I wrote "some drivers __don't__ yet".
>
> It's always been broken though, and I kinda just wanted to move on. It's
> racy, but I think mostly wrt. the valid_links warning (which isn't
> relevant here) and the data accesses, nothing worse will happen.
OK.
> > Is this something you'd
> > prefer individual driver users/maintainers resolve?
>
> I think so. Or we can discuss it should be async in cfg80211, or an
> async version? I guess first we should discuss how to solve it either
> way, and look at all the drivers that still have the issue.
At first, I thought it'd be trivial to just throw in
wiphy_lock()/unlock() in 1 or 2 places, similar to commit 0d7c2194f17c
("wifi: mwifiex: add missing locking for cfg80211 calls"). But I'm not
sure that's actually sound -- it might introduce some locking inversion
problems, where (for example) mwifiex_del_virtual_intf() expects to be
able to flush/destroy these workers, but it's already holding the wiphy
mutex.
(I wonder if commit 0d7c2194f17c is similarly unsound.)
Either I'm missing something (quite possible), or it'll take a little
more thought on what the right solution should be.
Anyway, I'm fine with your approach of document first, fix later. It's
hard to move anything if you have to reason through every crazy /
lightly-maintained driver for every problem.
> > > void cfg80211_cac_event(struct net_device *netdev,
> > > const struct cfg80211_chan_def *chandef,
> >
> > Should we add an assert to this API?
> >
> > lockdep_assert_wiphy(wiphy);
>
> Well I figured if I do that now then tools (and perhaps people) will
> start complaining and that'd just put more pressure on everyone to fix
> it ... that's why I didn't yet, but I guess I should've outlined that
> better in the commit message (even after a --- marker).
Ack.
Brian
^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: [PATCH wireless-next v2 09/18] wifi: cfg80211: document wiphy mutex for radar/CAC events
2026-10-05 18:36 ` Brian Norris
@ 2026-10-05 18:43 ` Johannes Berg
0 siblings, 0 replies; 23+ messages in thread
From: Johannes Berg @ 2026-10-05 18:43 UTC (permalink / raw)
To: Brian Norris; +Cc: linux-wireless, Francesco Dolcini
On Mon, 2026-10-05 at 11:36 -0700, Brian Norris wrote:
> > I think so. Or we can discuss it should be async in cfg80211, or an
> > async version? I guess first we should discuss how to solve it either
> > way, and look at all the drivers that still have the issue.
>
> At first, I thought it'd be trivial to just throw in
> wiphy_lock()/unlock() in 1 or 2 places, similar to commit 0d7c2194f17c
> ("wifi: mwifiex: add missing locking for cfg80211 calls"). But I'm not
> sure that's actually sound -- it might introduce some locking inversion
> problems, where (for example) mwifiex_del_virtual_intf() expects to be
> able to flush/destroy these workers, but it's already holding the wiphy
> mutex.
>
> (I wonder if commit 0d7c2194f17c is similarly unsound.)
Yeah, I have no idea :-)
Some worker here could maybe move to wiphy work these days? But you
probably wouldn't want arbitrary rx/tx handled with wiphy mutex ...
> Either I'm missing something (quite possible), or it'll take a little
> more thought on what the right solution should be.
>
> Anyway, I'm fine with your approach of document first, fix later. It's
> hard to move anything if you have to reason through every crazy /
> lightly-maintained driver for every problem.
I pretty much try to for all the ones that are correct in the first
place, but ... ;-)
johannes
^ permalink raw reply [flat|nested] 23+ messages in thread
end of thread, other threads:[~2026-10-05 18:43 UTC | newest]
Thread overview: 23+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-10-05 10:03 [PATCH wireless-next v2 01/18] wifi: further RTNL redux Johannes Berg
2026-10-05 10:03 ` [PATCH wireless-next v2 01/18] wifi: cfg80211: track netdev running state under wiphy mutex Johannes Berg
2026-10-05 10:03 ` [PATCH wireless-next v2 02/18] wifi: nl80211: allow device lookup under RCU Johannes Berg
2026-10-05 10:03 ` [PATCH wireless-next v2 03/18] wifi: nl80211: avoid rtnl for commands that don't want it Johannes Berg
2026-10-05 10:03 ` [PATCH wireless-next v2 04/18] wifi: nl80211: don't take rtnl for most dumps Johannes Berg
2026-10-05 10:03 ` [PATCH wireless-next v2 05/18] wifi: cfg80211: reg: update channels under wiphy mutex Johannes Berg
2026-10-05 10:03 ` [PATCH wireless-next v2 06/18] wifi: cfg80211: reg: set intersected regd " Johannes Berg
2026-10-05 10:03 ` [PATCH wireless-next v2 07/18] wifi: cfg80211: update channel DFS data " Johannes Berg
2026-10-05 10:03 ` [PATCH wireless-next v2 08/18] wifi: mac80211_hwsim: call cfg80211 event with " Johannes Berg
2026-10-05 10:03 ` [PATCH wireless-next v2 09/18] wifi: cfg80211: document wiphy mutex for radar/CAC events Johannes Berg
2026-10-05 17:46 ` Brian Norris
2026-10-05 17:52 ` Johannes Berg
2026-10-05 18:36 ` Brian Norris
2026-10-05 18:43 ` Johannes Berg
2026-10-05 10:03 ` [PATCH wireless-next v2 10/18] wifi: cfg80211: add a mutex for regulatory/device list Johannes Berg
2026-10-05 10:03 ` [PATCH wireless-next v2 11/18] wifi: cfg80211: allow walking wiphy list under cfg80211_mutex Johannes Berg
2026-10-05 10:03 ` [PATCH wireless-next v2 12/18] wifi: ath: use freq_reg_info() under RCU Johannes Berg
2026-10-05 10:03 ` [PATCH wireless-next v2 13/18] wifi: brcmsmac: " Johannes Berg
2026-10-05 10:03 ` [PATCH wireless-next v2 14/18] wifi: rtlwifi: " Johannes Berg
2026-10-05 10:03 ` [PATCH wireless-next v2 15/18] wifi: nl80211: read WMM reg rule " Johannes Berg
2026-10-05 10:03 ` [PATCH wireless-next v2 16/18] wifi: ath11k: read wiphy regd " Johannes Berg
2026-10-05 10:03 ` [PATCH wireless-next v2 17/18] wifi: ath12k: " Johannes Berg
2026-10-05 10:03 ` [PATCH wireless-next v2 18/18] wifi: cfg80211: regulatory: stop using RTNL Johannes Berg
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox