Linux-Wireless Archive on lore.kernel.org
 help / color / Atom feed
* [RFC/RFT] cfg80211: decouple us from the RTNL
@ 2019-07-31 22:08 Johannes Berg
  2019-08-01  3:59 ` Sid Hayn
  2019-08-05 15:20 ` [EXT] " Dedy Lansky
  0 siblings, 2 replies; 4+ messages in thread
From: Johannes Berg @ 2019-07-31 22:08 UTC (permalink / raw)
  To: linux-wireless; +Cc: Florian Westphal, Johannes Berg

From: Johannes Berg <johannes.berg@intel.com>

Currently, _everything_ in cfg80211 holds the RTNL, and if you
have a slow USB device (or a few) you can get some bad lock
contention on that.

Fix that by re-adding a mutex to each wiphy/rdev as we had at
some point, so we have locking for the wireless_dev lists and
all the other things in there, and also so that drivers still
don't have to worry too much about it (they still won't get
parallel calls for a single device).

Then, we can restrict the RTNL to a few cases where we add or
remove interfaces and really need the added protection. Some
of the global list management still also uses the RTNL, since
we need to have it anyway for netdev management.

TODO:
 - re-read everything a few times to make sure it seems right
 - use wiphy_lock()/wiphy_unlock() in all drivers as the code
   changed in mac80211 does
 - address the FIXME

Signed-off-by: Johannes Berg <johannes.berg@intel.com>
---
 include/net/cfg80211.h |  24 +-
 net/mac80211/main.c    |   2 +
 net/wireless/chan.c    |   5 +-
 net/wireless/core.c    |  45 +++-
 net/wireless/core.h    |   4 +-
 net/wireless/ibss.c    |   2 +-
 net/wireless/mlme.c    |   6 +-
 net/wireless/nl80211.c | 547 +++++++++++++++++++++--------------------
 net/wireless/sme.c     |   1 -
 net/wireless/util.c    |   2 +-
 10 files changed, 353 insertions(+), 285 deletions(-)

diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h
index 35ec1f0a2bf9..2ac6cb79f67d 100644
--- a/include/net/cfg80211.h
+++ b/include/net/cfg80211.h
@@ -4726,6 +4726,25 @@ struct cfg80211_internal_bss;
 struct cfg80211_cached_keys;
 struct cfg80211_cqm_config;
 
+/**
+ * wiphy_lock - lock the wiphy
+ * @wiphy: the wiphy to lock
+ *
+ * This is mostly exposed so it can be done around registering and
+ * unregistering netdevs that aren't created through cfg80211 calls,
+ * since that requires locking in cfg80211 when the notifiers is
+ * called, but that cannot differentiate which way it's called.
+ *
+ * When cfg80211 ops are called, the wiphy is already locked.
+ */
+void wiphy_lock(struct wiphy *wiphy);
+
+/**
+ * wiphy_unlock - unlock the wiphy again
+ * @wiphy: the wiphy to unlock
+ */
+void wiphy_unlock(struct wiphy *wiphy);
+
 /**
  * struct wireless_dev - wireless device state
  *
@@ -4733,7 +4752,10 @@ struct cfg80211_cqm_config;
  * that uses the ieee80211_ptr field in struct net_device (this
  * is intentional so it can be allocated along with the netdev.)
  * It need not be registered then as netdev registration will
- * be intercepted by cfg80211 to see the new wireless device.
+ * be intercepted by cfg80211 to see the new wireless device,
+ * however, drivers must lock the wiphy before registering or
+ * unregistering netdevs if they pre-create any netdevs (in ops
+ * called from cfg80211, the wiphy is already locked.)
  *
  * For non-netdev uses, it must also be allocated by the driver
  * in response to the cfg80211 callbacks that require it, as
diff --git a/net/mac80211/main.c b/net/mac80211/main.c
index 29b9d57df1a3..03fc52587bff 100644
--- a/net/mac80211/main.c
+++ b/net/mac80211/main.c
@@ -1252,8 +1252,10 @@ int ieee80211_register_hw(struct ieee80211_hw *hw)
 	    !ieee80211_hw_check(hw, NO_AUTO_VIF)) {
 		struct vif_params params = {0};
 
+		wiphy_lock(hw->wiphy);
 		result = ieee80211_if_add(local, "wlan%d", NET_NAME_ENUM, NULL,
 					  NL80211_IFTYPE_STATION, &params);
+		wiphy_unlock(hw->wiphy);
 		if (result)
 			wiphy_warn(local->hw.wiphy,
 				   "Failed to add default virtual iface\n");
diff --git a/net/wireless/chan.c b/net/wireless/chan.c
index 7dc1bbd0888f..ac83c9030c17 100644
--- a/net/wireless/chan.c
+++ b/net/wireless/chan.c
@@ -839,7 +839,7 @@ static bool cfg80211_ir_permissive_chan(struct wiphy *wiphy,
 	struct wireless_dev *wdev;
 	struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
 
-	ASSERT_RTNL();
+	lockdep_assert_held(&rdev->mtx);
 
 	if (!IS_ENABLED(CONFIG_CFG80211_REG_RELAX_NO_IR) ||
 	    !(wiphy->regulatory_flags & REGULATORY_ENABLE_RELAX_NO_IR))
@@ -961,9 +961,10 @@ bool cfg80211_reg_can_beacon_relax(struct wiphy *wiphy,
 				   struct cfg80211_chan_def *chandef,
 				   enum nl80211_iftype iftype)
 {
+	struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
 	bool check_no_ir;
 
-	ASSERT_RTNL();
+	lockdep_assert_held(&rdev->mtx);
 
 	/*
 	 * Under certain conditions suggested by some regulatory bodies a
diff --git a/net/wireless/core.c b/net/wireless/core.c
index 742986c73490..2d5bd07ae11f 100644
--- a/net/wireless/core.c
+++ b/net/wireless/core.c
@@ -39,7 +39,7 @@ MODULE_LICENSE("GPL");
 MODULE_DESCRIPTION("wireless configuration support");
 MODULE_ALIAS_GENL_FAMILY(NL80211_GENL_NAME);
 
-/* RCU-protected (and RTNL for writers) */
+/* RTNL-protected for writing, RCU-based lookup */
 LIST_HEAD(cfg80211_rdev_list);
 int cfg80211_rdev_list_generation;
 
@@ -222,7 +222,7 @@ static void cfg80211_rfkill_poll(struct rfkill *rfkill, void *data)
 void cfg80211_stop_p2p_device(struct cfg80211_registered_device *rdev,
 			      struct wireless_dev *wdev)
 {
-	ASSERT_RTNL();
+	lockdep_assert_held(&rdev->mtx);
 
 	if (WARN_ON(wdev->iftype != NL80211_IFTYPE_P2P_DEVICE))
 		return;
@@ -245,7 +245,7 @@ void cfg80211_stop_p2p_device(struct cfg80211_registered_device *rdev,
 void cfg80211_stop_nan(struct cfg80211_registered_device *rdev,
 		       struct wireless_dev *wdev)
 {
-	ASSERT_RTNL();
+	lockdep_assert_held(&rdev->mtx);
 
 	if (WARN_ON(wdev->iftype != NL80211_IFTYPE_NAN))
 		return;
@@ -472,6 +472,7 @@ struct wiphy *wiphy_new_nm(const struct cfg80211_ops *ops, int sizeof_priv,
 		}
 	}
 
+	mutex_init(&rdev->mtx);
 	INIT_LIST_HEAD(&rdev->wiphy.wdev_list);
 	INIT_LIST_HEAD(&rdev->beacon_registrations);
 	spin_lock_init(&rdev->beacon_registrations_lock);
@@ -978,21 +979,38 @@ void wiphy_rfkill_stop_polling(struct wiphy *wiphy)
 }
 EXPORT_SYMBOL(wiphy_rfkill_stop_polling);
 
+void wiphy_lock(struct wiphy *wiphy)
+{
+	struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
+
+	mutex_lock(&rdev->mtx);
+}
+EXPORT_SYMBOL(wiphy_lock);
+
+void wiphy_unlock(struct wiphy *wiphy)
+{
+	struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
+
+	mutex_unlock(&rdev->mtx);
+}
+EXPORT_SYMBOL(wiphy_unlock);
+
 void wiphy_unregister(struct wiphy *wiphy)
 {
 	struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
 
 	wait_event(rdev->dev_wait, ({
 		int __count;
-		rtnl_lock();
+		mutex_lock(&rdev->mtx);
 		__count = rdev->opencount;
-		rtnl_unlock();
+		mutex_unlock(&rdev->mtx);
 		__count == 0; }));
 
 	if (rdev->rfkill)
 		rfkill_unregister(rdev->rfkill);
 
 	rtnl_lock();
+	mutex_lock(&rdev->mtx);
 	nl80211_notify_wiphy(rdev, NL80211_CMD_DEL_WIPHY);
 	rdev->wiphy.registered = false;
 
@@ -1015,6 +1033,7 @@ void wiphy_unregister(struct wiphy *wiphy)
 	cfg80211_rdev_list_generation++;
 	device_del(&rdev->wiphy.dev);
 
+	mutex_unlock(&rdev->mtx);
 	rtnl_unlock();
 
 	flush_work(&rdev->scan_done_wk);
@@ -1047,6 +1066,7 @@ void cfg80211_dev_free(struct cfg80211_registered_device *rdev)
 	}
 	list_for_each_entry_safe(scan, tmp, &rdev->bss_list, list)
 		cfg80211_put_bss(&rdev->wiphy, &scan->pub);
+	mutex_destroy(&rdev->mtx);
 	kfree(rdev);
 }
 
@@ -1125,7 +1145,7 @@ static const struct device_type wiphy_type = {
 void cfg80211_update_iface_num(struct cfg80211_registered_device *rdev,
 			       enum nl80211_iftype iftype, int num)
 {
-	ASSERT_RTNL();
+	lockdep_assert_held(&rdev->mtx);
 
 	rdev->num_running_ifaces += num;
 	if (iftype == NL80211_IFTYPE_MONITOR)
@@ -1138,7 +1158,7 @@ void __cfg80211_leave(struct cfg80211_registered_device *rdev,
 	struct net_device *dev = wdev->netdev;
 	struct cfg80211_sched_scan_request *pos, *tmp;
 
-	ASSERT_RTNL();
+	lockdep_assert_held(&rdev->mtx);
 	ASSERT_WDEV_LOCK(wdev);
 
 	cfg80211_pmsr_wdev_down(wdev);
@@ -1234,6 +1254,9 @@ void cfg80211_init_wdev(struct cfg80211_registered_device *rdev,
 	spin_lock_init(&wdev->pmsr_lock);
 	INIT_WORK(&wdev->pmsr_free_wk, cfg80211_pmsr_free_wk);
 
+	ASSERT_RTNL();
+	lockdep_assert_held(&rdev->mtx);
+
 	/*
 	 * We get here also when the interface changes network namespaces,
 	 * as it's registered into the new one, but we don't want it to
@@ -1269,6 +1292,7 @@ static int cfg80211_netdev_notifier_call(struct notifier_block *nb,
 		SET_NETDEV_DEVTYPE(dev, &wiphy_type);
 		break;
 	case NETDEV_REGISTER:
+		lockdep_assert_held(&rdev->mtx);
 		/*
 		 * NB: cannot take rdev->mtx here because this may be
 		 * called within code protected by it when interfaces
@@ -1305,9 +1329,12 @@ static int cfg80211_netdev_notifier_call(struct notifier_block *nb,
 		cfg80211_init_wdev(rdev, wdev);
 		break;
 	case NETDEV_GOING_DOWN:
+		mutex_lock(&rdev->mtx);
 		cfg80211_leave(rdev, wdev);
+		mutex_unlock(&rdev->mtx);
 		break;
 	case NETDEV_DOWN:
+		mutex_lock(&rdev->mtx);
 		cfg80211_update_iface_num(rdev, wdev->iftype, -1);
 		if (rdev->scan_req && rdev->scan_req->wdev == wdev) {
 			if (WARN_ON(!rdev->scan_req->notified))
@@ -1322,9 +1349,11 @@ static int cfg80211_netdev_notifier_call(struct notifier_block *nb,
 		}
 
 		rdev->opencount--;
+		mutex_unlock(&rdev->mtx);
 		wake_up(&rdev->dev_wait);
 		break;
 	case NETDEV_UP:
+		mutex_lock(&rdev->mtx);
 		cfg80211_update_iface_num(rdev, wdev->iftype, 1);
 		wdev_lock(wdev);
 		switch (wdev->iftype) {
@@ -1371,8 +1400,10 @@ static int cfg80211_netdev_notifier_call(struct notifier_block *nb,
 			/* assume this means it's off */
 			wdev->ps = false;
 		}
+		mutex_unlock(&rdev->mtx);
 		break;
 	case NETDEV_UNREGISTER:
+		lockdep_assert_held(&rdev->mtx);
 		/*
 		 * It is possible to get NETDEV_UNREGISTER
 		 * multiple times. To detect that, check
diff --git a/net/wireless/core.h b/net/wireless/core.h
index 77556c58d9ac..9ad586fd7939 100644
--- a/net/wireless/core.h
+++ b/net/wireless/core.h
@@ -25,6 +25,8 @@ struct cfg80211_registered_device {
 	const struct cfg80211_ops *ops;
 	struct list_head list;
 
+	struct mutex mtx;
+
 	/* rfkill support */
 	struct rfkill_ops rfkill_ops;
 	struct rfkill *rfkill;
@@ -231,7 +233,7 @@ static inline void wdev_unlock(struct wireless_dev *wdev)
 
 static inline bool cfg80211_has_monitors_only(struct cfg80211_registered_device *rdev)
 {
-	ASSERT_RTNL();
+	lockdep_assert_held(&rdev->mtx);
 
 	return rdev->num_running_ifaces == rdev->num_running_monitor_ifaces &&
 	       rdev->num_running_ifaces > 0;
diff --git a/net/wireless/ibss.c b/net/wireless/ibss.c
index d1743e6abc34..e8d9162f9010 100644
--- a/net/wireless/ibss.c
+++ b/net/wireless/ibss.c
@@ -92,7 +92,7 @@ int __cfg80211_join_ibss(struct cfg80211_registered_device *rdev,
 	struct wireless_dev *wdev = dev->ieee80211_ptr;
 	int err;
 
-	ASSERT_RTNL();
+	lockdep_assert_held(&rdev->mtx);
 	ASSERT_WDEV_LOCK(wdev);
 
 	if (wdev->ssid_len)
diff --git a/net/wireless/mlme.c b/net/wireless/mlme.c
index f9462010575f..808821d521f9 100644
--- a/net/wireless/mlme.c
+++ b/net/wireless/mlme.c
@@ -433,7 +433,7 @@ cfg80211_process_mlme_unregistrations(struct cfg80211_registered_device *rdev)
 {
 	struct cfg80211_mgmt_registration *reg;
 
-	ASSERT_RTNL();
+	lockdep_assert_held(&rdev->mtx);
 
 	spin_lock_bh(&rdev->mlme_unreg_lock);
 	while ((reg = list_first_entry_or_null(&rdev->mlme_unreg,
@@ -463,9 +463,9 @@ void cfg80211_mlme_unreg_wk(struct work_struct *wk)
 	rdev = container_of(wk, struct cfg80211_registered_device,
 			    mlme_unreg_wk);
 
-	rtnl_lock();
+	mutex_lock(&rdev->mtx);
 	cfg80211_process_mlme_unregistrations(rdev);
-	rtnl_unlock();
+	mutex_unlock(&rdev->mtx);
 }
 
 int cfg80211_mlme_register_mgmt(struct wireless_dev *wdev, u32 snd_portid,
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
index 1a107f29016b..b79ab55f1f53 100644
--- a/net/wireless/nl80211.c
+++ b/net/wireless/nl80211.c
@@ -63,9 +63,9 @@ static const struct genl_multicast_group nl80211_mcgrps[] = {
 
 /* returns ERR_PTR values */
 static struct wireless_dev *
-__cfg80211_wdev_from_attrs(struct net *netns, struct nlattr **attrs)
+__cfg80211_wdev_from_attrs(struct cfg80211_registered_device *rdev,
+			   struct net *netns, struct nlattr **attrs)
 {
-	struct cfg80211_registered_device *rdev;
 	struct wireless_dev *result = NULL;
 	bool have_ifidx = attrs[NL80211_ATTR_IFINDEX];
 	bool have_wdev_id = attrs[NL80211_ATTR_WDEV];
@@ -73,8 +73,6 @@ __cfg80211_wdev_from_attrs(struct net *netns, struct nlattr **attrs)
 	int wiphy_idx = -1;
 	int ifidx = -1;
 
-	ASSERT_RTNL();
-
 	if (!have_ifidx && !have_wdev_id)
 		return ERR_PTR(-EINVAL);
 
@@ -85,6 +83,28 @@ __cfg80211_wdev_from_attrs(struct net *netns, struct nlattr **attrs)
 		wiphy_idx = wdev_id >> 32;
 	}
 
+	if (rdev) {
+		struct wireless_dev *wdev;
+
+		lockdep_assert_held(&rdev->mtx);
+
+		list_for_each_entry(wdev, &rdev->wiphy.wdev_list, list) {
+			if (have_ifidx && wdev->netdev &&
+			    wdev->netdev->ifindex == ifidx) {
+				result = wdev;
+				break;
+			}
+			if (have_wdev_id && wdev->identifier == (u32)wdev_id) {
+				result = wdev;
+				break;
+			}
+		}
+
+		return result ?: ERR_PTR(-ENODEV);
+	}
+
+	ASSERT_RTNL();
+
 	list_for_each_entry(rdev, &cfg80211_rdev_list, list) {
 		struct wireless_dev *wdev;
 
@@ -775,22 +795,31 @@ int nl80211_prepare_wdev_dump(struct netlink_callback *cb,
 			return err;
 		}
 
-		*wdev = __cfg80211_wdev_from_attrs(sock_net(cb->skb->sk),
+		rtnl_lock();
+		*wdev = __cfg80211_wdev_from_attrs(NULL, sock_net(cb->skb->sk),
 						   attrbuf);
 		kfree(attrbuf);
-		if (IS_ERR(*wdev))
+		if (IS_ERR(*wdev)) {
+			rtnl_unlock();
 			return PTR_ERR(*wdev);
+		}
 		*rdev = wiphy_to_rdev((*wdev)->wiphy);
+		mutex_lock(&(*rdev)->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;
 	} else {
 		/* subtract the 1 again here */
-		struct wiphy *wiphy = wiphy_idx_to_wiphy(cb->args[0] - 1);
+		struct wiphy *wiphy;
 		struct wireless_dev *tmp;
 
-		if (!wiphy)
+		rtnl_lock();
+		wiphy = wiphy_idx_to_wiphy(cb->args[0] - 1);
+		if (!wiphy) {
+			rtnl_unlock();
 			return -ENODEV;
+		}
 		*rdev = wiphy_to_rdev(wiphy);
 		*wdev = NULL;
 
@@ -801,8 +830,12 @@ int nl80211_prepare_wdev_dump(struct netlink_callback *cb,
 			}
 		}
 
-		if (!*wdev)
+		if (!*wdev) {
+			rtnl_unlock();
 			return -ENODEV;
+		}
+		mutex_lock(&(*rdev)->mtx);
+		rtnl_unlock();
 	}
 
 	return 0;
@@ -2791,7 +2824,7 @@ static int nl80211_set_wds_peer(struct sk_buff *skb, struct genl_info *info)
 
 static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info)
 {
-	struct cfg80211_registered_device *rdev;
+	struct cfg80211_registered_device *rdev = NULL;
 	struct net_device *netdev = NULL;
 	struct wireless_dev *wdev;
 	int result = 0, rem_txq_params = 0;
@@ -2802,8 +2835,7 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info)
 	u8 coverage_class = 0;
 	u32 txq_limit = 0, txq_memory_limit = 0, txq_quantum = 0;
 
-	ASSERT_RTNL();
-
+	rtnl_lock();
 	/*
 	 * Try to find the wiphy and netdev. Normally this
 	 * function shouldn't need the netdev, but this is
@@ -2827,14 +2859,20 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info)
 	if (!netdev) {
 		rdev = __cfg80211_rdev_from_attrs(genl_info_net(info),
 						  info->attrs);
-		if (IS_ERR(rdev))
+		if (IS_ERR(rdev)) {
+			rtnl_unlock();
 			return PTR_ERR(rdev);
+		}
 		wdev = NULL;
 		netdev = NULL;
 		result = 0;
 	} else
 		wdev = netdev->ieee80211_ptr;
 
+	if (rdev)
+		mutex_lock(&rdev->mtx);
+	rtnl_unlock();
+
 	/*
 	 * end workaround code, by now the rdev is available
 	 * and locked, and wdev may or may not be NULL.
@@ -2844,6 +2882,10 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info)
 		result = cfg80211_dev_rename(
 			rdev, nla_data(info->attrs[NL80211_ATTR_WIPHY_NAME]));
 
+	// FIXME - push this through to all the error paths below
+	if (rdev)
+		mutex_unlock(&rdev->mtx);
+
 	if (result)
 		return result;
 
@@ -3607,6 +3649,17 @@ static int nl80211_del_interface(struct sk_buff *skb, struct genl_info *info)
 	if (!rdev->ops->del_virtual_intf)
 		return -EOPNOTSUPP;
 
+	/*
+	 * We hold RTNL, so this is safe, without RTNL opencount cannot
+	 * reach 0, and thus the rdev cannot be deleted.
+	 *
+	 * We need to do it for the dev_close(), since that will call
+	 * the netdev notifiers, and we need to acquire the mutex there
+	 * but don't know if we get there from here or from some other
+	 * place (e.g. "ip link set ... down").
+	 */
+	mutex_unlock(&rdev->mtx);
+
 	/*
 	 * If we remove a wireless device without a netdev then clear
 	 * user_ptr[1] so that nl80211_post_doit won't dereference it
@@ -3616,6 +3669,10 @@ static int nl80211_del_interface(struct sk_buff *skb, struct genl_info *info)
 	 */
 	if (!wdev->netdev)
 		info->user_ptr[1] = NULL;
+	else
+		dev_close(wdev->netdev);
+
+	mutex_lock(&rdev->mtx);
 
 	return rdev_del_virtual_intf(rdev, wdev);
 }
@@ -5216,10 +5273,9 @@ static int nl80211_dump_station(struct sk_buff *skb,
 	int sta_idx = cb->args[2];
 	int err;
 
-	rtnl_lock();
 	err = nl80211_prepare_wdev_dump(cb, &rdev, &wdev);
 	if (err)
-		goto out_err;
+		return err;
 
 	if (!wdev->netdev) {
 		err = -EINVAL;
@@ -5254,7 +5310,7 @@ static int nl80211_dump_station(struct sk_buff *skb,
 	cb->args[2] = sta_idx;
 	err = skb->len;
  out_err:
-	rtnl_unlock();
+	mutex_unlock(&rdev->mtx);
 
 	return err;
 }
@@ -6099,10 +6155,9 @@ static int nl80211_dump_mpath(struct sk_buff *skb,
 	int path_idx = cb->args[2];
 	int err;
 
-	rtnl_lock();
 	err = nl80211_prepare_wdev_dump(cb, &rdev, &wdev);
 	if (err)
-		goto out_err;
+		return err;
 
 	if (!rdev->ops->dump_mpath) {
 		err = -EOPNOTSUPP;
@@ -6135,7 +6190,7 @@ static int nl80211_dump_mpath(struct sk_buff *skb,
 	cb->args[2] = path_idx;
 	err = skb->len;
  out_err:
-	rtnl_unlock();
+	mutex_unlock(&rdev->mtx);
 	return err;
 }
 
@@ -6295,10 +6350,9 @@ static int nl80211_dump_mpp(struct sk_buff *skb,
 	int path_idx = cb->args[2];
 	int err;
 
-	rtnl_lock();
 	err = nl80211_prepare_wdev_dump(cb, &rdev, &wdev);
 	if (err)
-		goto out_err;
+		return err;
 
 	if (!rdev->ops->dump_mpp) {
 		err = -EOPNOTSUPP;
@@ -6331,7 +6385,7 @@ static int nl80211_dump_mpp(struct sk_buff *skb,
 	cb->args[2] = path_idx;
 	err = skb->len;
  out_err:
-	rtnl_unlock();
+	mutex_unlock(&rdev->mtx);
 	return err;
 }
 
@@ -6939,12 +6993,15 @@ static int nl80211_get_reg_do(struct sk_buff *skb, struct genl_info *info)
 	if (!hdr)
 		goto put_failure;
 
+	rtnl_lock();
+
 	if (info->attrs[NL80211_ATTR_WIPHY]) {
 		bool self_managed;
 
 		rdev = cfg80211_get_dev_from_info(genl_info_net(info), info);
 		if (IS_ERR(rdev)) {
 			nlmsg_free(msg);
+			rtnl_unlock();
 			return PTR_ERR(rdev);
 		}
 
@@ -6953,9 +7010,11 @@ static int nl80211_get_reg_do(struct sk_buff *skb, struct genl_info *info)
 			       REGULATORY_WIPHY_SELF_MANAGED;
 		regdom = get_wiphy_regdom(wiphy);
 
+
 		/* a self-managed-reg device must have a private regdom */
 		if (WARN_ON(!regdom && self_managed)) {
 			nlmsg_free(msg);
+			rtnl_unlock();
 			return -EINVAL;
 		}
 
@@ -6980,11 +7039,13 @@ static int nl80211_get_reg_do(struct sk_buff *skb, struct genl_info *info)
 	rcu_read_unlock();
 
 	genlmsg_end(msg, hdr);
+	rtnl_unlock();
 	return genlmsg_reply(msg, info);
 
 nla_put_failure_rcu:
 	rcu_read_unlock();
 nla_put_failure:
+	rtnl_unlock();
 put_failure:
 	nlmsg_free(msg);
 	return -EMSGSIZE;
@@ -7147,12 +7208,17 @@ static int nl80211_set_reg(struct sk_buff *skb, struct genl_info *info)
 			return -EINVAL;
 	}
 
-	if (!reg_is_valid_request(alpha2))
-		return -EINVAL;
+	rtnl_lock();
+	if (!reg_is_valid_request(alpha2)) {
+		r = -EINVAL;
+		goto out;
+	}
 
 	rd = kzalloc(struct_size(rd, reg_rules, num_rules), GFP_KERNEL);
-	if (!rd)
-		return -ENOMEM;
+	if (!rd) {
+		r = -ENOMEM;
+		goto out;
+	}
 
 	rd->n_reg_rules = num_rules;
 	rd->alpha2[0] = alpha2[0];
@@ -7184,10 +7250,13 @@ static int nl80211_set_reg(struct sk_buff *skb, struct genl_info *info)
 		}
 	}
 
+	r = set_regdom(rd, REGD_SOURCE_CRDA);
 	/* set_regdom takes ownership of rd */
-	return set_regdom(rd, REGD_SOURCE_CRDA);
+	rd = NULL;
  bad_reg:
 	kfree(rd);
+ out:
+	rtnl_unlock();
 	return r;
 }
 #endif /* CONFIG_CFG80211_CRDA_SUPPORT */
@@ -8367,10 +8436,7 @@ static int nl80211_channel_switch(struct sk_buff *skb, struct genl_info *info)
 	struct net_device *dev = info->user_ptr[1];
 	struct wireless_dev *wdev = dev->ieee80211_ptr;
 	struct cfg80211_csa_settings params;
-	/* csa_attrs is defined static to avoid waste of stack size - this
-	 * function is called under RTNL lock, so this should not be a problem.
-	 */
-	static struct nlattr *csa_attrs[NL80211_ATTR_MAX+1];
+	struct nlattr **csa_attrs;
 	int err;
 	bool need_new_beacon = false;
 	bool need_handle_dfs_flag = true;
@@ -8435,28 +8501,39 @@ static int nl80211_channel_switch(struct sk_buff *skb, struct genl_info *info)
 	if (err)
 		return err;
 
+	csa_attrs = kcalloc(NL80211_ATTR_MAX + 1, sizeof(*csa_attrs),
+			    GFP_KERNEL);
+	if (!csa_attrs)
+		return -ENOMEM;
+
 	err = nla_parse_nested_deprecated(csa_attrs, NL80211_ATTR_MAX,
 					  info->attrs[NL80211_ATTR_CSA_IES],
 					  nl80211_policy, info->extack);
 	if (err)
-		return err;
+		goto free;
 
 	err = nl80211_parse_beacon(rdev, csa_attrs, &params.beacon_csa);
 	if (err)
-		return err;
+		goto free;
 
-	if (!csa_attrs[NL80211_ATTR_CSA_C_OFF_BEACON])
-		return -EINVAL;
+	if (!csa_attrs[NL80211_ATTR_CSA_C_OFF_BEACON]) {
+		err = -EINVAL;
+		goto free;
+	}
 
 	len = nla_len(csa_attrs[NL80211_ATTR_CSA_C_OFF_BEACON]);
-	if (!len || (len % sizeof(u16)))
-		return -EINVAL;
+	if (!len || (len % sizeof(u16))) {
+		err = -EINVAL;
+		goto free;
+	}
 
 	params.n_counter_offsets_beacon = len / sizeof(u16);
 	if (rdev->wiphy.max_num_csa_counters &&
 	    (params.n_counter_offsets_beacon >
-	     rdev->wiphy.max_num_csa_counters))
-		return -EINVAL;
+	     rdev->wiphy.max_num_csa_counters)) {
+		err = -EINVAL;
+		goto free;
+	}
 
 	params.counter_offsets_beacon =
 		nla_data(csa_attrs[NL80211_ATTR_CSA_C_OFF_BEACON]);
@@ -8465,23 +8542,31 @@ static int nl80211_channel_switch(struct sk_buff *skb, struct genl_info *info)
 	for (i = 0; i < params.n_counter_offsets_beacon; i++) {
 		u16 offset = params.counter_offsets_beacon[i];
 
-		if (offset >= params.beacon_csa.tail_len)
-			return -EINVAL;
+		if (offset >= params.beacon_csa.tail_len) {
+			err = -EINVAL;
+			goto free;
+		}
 
-		if (params.beacon_csa.tail[offset] != params.count)
-			return -EINVAL;
+		if (params.beacon_csa.tail[offset] != params.count) {
+			err = -EINVAL;
+			goto free;
+		}
 	}
 
 	if (csa_attrs[NL80211_ATTR_CSA_C_OFF_PRESP]) {
 		len = nla_len(csa_attrs[NL80211_ATTR_CSA_C_OFF_PRESP]);
-		if (!len || (len % sizeof(u16)))
-			return -EINVAL;
+		if (!len || (len % sizeof(u16))) {
+			err = -EINVAL;
+			goto free;
+		}
 
 		params.n_counter_offsets_presp = len / sizeof(u16);
 		if (rdev->wiphy.max_num_csa_counters &&
 		    (params.n_counter_offsets_presp >
-		     rdev->wiphy.max_num_csa_counters))
-			return -EINVAL;
+		     rdev->wiphy.max_num_csa_counters)) {
+			err = -EINVAL;
+			goto free;
+		}
 
 		params.counter_offsets_presp =
 			nla_data(csa_attrs[NL80211_ATTR_CSA_C_OFF_PRESP]);
@@ -8490,35 +8575,42 @@ static int nl80211_channel_switch(struct sk_buff *skb, struct genl_info *info)
 		for (i = 0; i < params.n_counter_offsets_presp; i++) {
 			u16 offset = params.counter_offsets_presp[i];
 
-			if (offset >= params.beacon_csa.probe_resp_len)
-				return -EINVAL;
+			if (offset >= params.beacon_csa.probe_resp_len) {
+				err = -EINVAL;
+				goto free;
+			}
 
 			if (params.beacon_csa.probe_resp[offset] !=
-			    params.count)
-				return -EINVAL;
+			    params.count) {
+				err = -EINVAL;
+				goto free;
+			}
 		}
 	}
 
 skip_beacons:
 	err = nl80211_parse_chandef(rdev, info, &params.chandef);
 	if (err)
-		return err;
+		goto free;
 
 	if (!cfg80211_reg_can_beacon_relax(&rdev->wiphy, &params.chandef,
-					   wdev->iftype))
-		return -EINVAL;
+					   wdev->iftype)) {
+		err = -EINVAL;
+		goto free;
+	}
 
 	err = cfg80211_chandef_dfs_required(wdev->wiphy,
 					    &params.chandef,
 					    wdev->iftype);
 	if (err < 0)
-		return err;
+		goto free;
 
 	if (err > 0) {
 		params.radar_required = true;
 		if (need_handle_dfs_flag &&
 		    !nla_get_flag(info->attrs[NL80211_ATTR_HANDLE_DFS])) {
-			return -EINVAL;
+			err = -EINVAL;
+			goto free;
 		}
 	}
 
@@ -8529,6 +8621,8 @@ static int nl80211_channel_switch(struct sk_buff *skb, struct genl_info *info)
 	err = rdev_channel_switch(rdev, dev, &params);
 	wdev_unlock(wdev);
 
+free:
+	kfree(csa_attrs);
 	return err;
 }
 
@@ -8677,12 +8771,9 @@ static int nl80211_dump_scan(struct sk_buff *skb, struct netlink_callback *cb)
 	int start = cb->args[2], idx = 0;
 	int err;
 
-	rtnl_lock();
 	err = nl80211_prepare_wdev_dump(cb, &rdev, &wdev);
-	if (err) {
-		rtnl_unlock();
+	if (err)
 		return err;
-	}
 
 	wdev_lock(wdev);
 	spin_lock_bh(&rdev->bss_lock);
@@ -8713,7 +8804,7 @@ static int nl80211_dump_scan(struct sk_buff *skb, struct netlink_callback *cb)
 	wdev_unlock(wdev);
 
 	cb->args[2] = idx;
-	rtnl_unlock();
+	mutex_unlock(&rdev->mtx);
 
 	return skb->len;
 }
@@ -8802,10 +8893,11 @@ static int nl80211_dump_survey(struct sk_buff *skb, struct netlink_callback *cb)
 	if (!attrbuf)
 		return -ENOMEM;
 
-	rtnl_lock();
 	res = nl80211_prepare_wdev_dump(cb, &rdev, &wdev);
-	if (res)
-		goto out_err;
+	if (res) {
+		kfree(attrbuf);
+		return res;
+	}
 
 	/* prepare_wdev_dump parsed the attributes */
 	radio_stats = attrbuf[NL80211_ATTR_SURVEY_RADIO_STATS];
@@ -8847,7 +8939,7 @@ static int nl80211_dump_survey(struct sk_buff *skb, struct netlink_callback *cb)
 	res = skb->len;
  out_err:
 	kfree(attrbuf);
-	rtnl_unlock();
+	mutex_unlock(&rdev->mtx);
 	return res;
 }
 
@@ -9674,10 +9766,14 @@ EXPORT_SYMBOL(__cfg80211_send_event_skb);
 static int nl80211_testmode_do(struct sk_buff *skb, struct genl_info *info)
 {
 	struct cfg80211_registered_device *rdev = info->user_ptr[0];
-	struct wireless_dev *wdev =
-		__cfg80211_wdev_from_attrs(genl_info_net(info), info->attrs);
+	struct wireless_dev *wdev;
 	int err;
 
+	lockdep_assert_held(&rdev->mtx);
+
+	wdev = __cfg80211_wdev_from_attrs(rdev, genl_info_net(info),
+					  info->attrs);
+
 	if (!rdev->ops->testmode_cmd)
 		return -EOPNOTSUPP;
 
@@ -12817,7 +12913,8 @@ static int nl80211_vendor_cmd(struct sk_buff *skb, struct genl_info *info)
 {
 	struct cfg80211_registered_device *rdev = info->user_ptr[0];
 	struct wireless_dev *wdev =
-		__cfg80211_wdev_from_attrs(genl_info_net(info), info->attrs);
+		__cfg80211_wdev_from_attrs(rdev, genl_info_net(info),
+					   info->attrs);
 	int i, err;
 	u32 vid, subcmd;
 
@@ -12941,7 +13038,7 @@ static int nl80211_prepare_vendor_dump(struct sk_buff *skb,
 		goto out;
 	}
 
-	*wdev = __cfg80211_wdev_from_attrs(sock_net(skb->sk), attrbuf);
+	*wdev = __cfg80211_wdev_from_attrs(NULL, sock_net(skb->sk), attrbuf);
 	if (IS_ERR(*wdev))
 		*wdev = NULL;
 
@@ -13723,31 +13820,24 @@ static int nl80211_probe_mesh_link(struct sk_buff *skb, struct genl_info *info)
 static int nl80211_pre_doit(const struct genl_ops *ops, struct sk_buff *skb,
 			    struct genl_info *info)
 {
-	struct cfg80211_registered_device *rdev;
+	struct cfg80211_registered_device *rdev = NULL;
 	struct wireless_dev *wdev;
 	struct net_device *dev;
-	bool rtnl = ops->internal_flags & NL80211_FLAG_NEED_RTNL;
-
-	if (rtnl)
-		rtnl_lock();
 
+	rtnl_lock();
 	if (ops->internal_flags & NL80211_FLAG_NEED_WIPHY) {
 		rdev = cfg80211_get_dev_from_info(genl_info_net(info), info);
 		if (IS_ERR(rdev)) {
-			if (rtnl)
-				rtnl_unlock();
+			rtnl_unlock();
 			return PTR_ERR(rdev);
 		}
 		info->user_ptr[0] = rdev;
 	} else if (ops->internal_flags & NL80211_FLAG_NEED_NETDEV ||
 		   ops->internal_flags & NL80211_FLAG_NEED_WDEV) {
-		ASSERT_RTNL();
-
-		wdev = __cfg80211_wdev_from_attrs(genl_info_net(info),
+		wdev = __cfg80211_wdev_from_attrs(NULL, genl_info_net(info),
 						  info->attrs);
 		if (IS_ERR(wdev)) {
-			if (rtnl)
-				rtnl_unlock();
+			rtnl_unlock();
 			return PTR_ERR(wdev);
 		}
 
@@ -13756,8 +13846,7 @@ static int nl80211_pre_doit(const struct genl_ops *ops, struct sk_buff *skb,
 
 		if (ops->internal_flags & NL80211_FLAG_NEED_NETDEV) {
 			if (!dev) {
-				if (rtnl)
-					rtnl_unlock();
+				rtnl_unlock();
 				return -EINVAL;
 			}
 
@@ -13768,8 +13857,7 @@ static int nl80211_pre_doit(const struct genl_ops *ops, struct sk_buff *skb,
 
 		if (ops->internal_flags & NL80211_FLAG_CHECK_NETDEV_UP &&
 		    !wdev_running(wdev)) {
-			if (rtnl)
-				rtnl_unlock();
+			rtnl_unlock();
 			return -ENETDOWN;
 		}
 
@@ -13779,6 +13867,11 @@ static int nl80211_pre_doit(const struct genl_ops *ops, struct sk_buff *skb,
 		info->user_ptr[0] = rdev;
 	}
 
+	if (rdev)
+		mutex_lock(&rdev->mtx);
+	if (!(ops->internal_flags & NL80211_FLAG_NEED_RTNL))
+		rtnl_unlock();
+
 	return 0;
 }
 
@@ -13796,6 +13889,12 @@ static void nl80211_post_doit(const struct genl_ops *ops, struct sk_buff *skb,
 		}
 	}
 
+	if (info->user_ptr[0]) {
+		struct cfg80211_registered_device *rdev = info->user_ptr[0];
+
+		mutex_unlock(&rdev->mtx);
+	}
+
 	if (ops->internal_flags & NL80211_FLAG_NEED_RTNL)
 		rtnl_unlock();
 
@@ -13819,15 +13918,13 @@ static const struct genl_ops nl80211_ops[] = {
 		.dumpit = nl80211_dump_wiphy,
 		.done = nl80211_dump_wiphy_done,
 		/* can be retrieved by unprivileged users */
-		.internal_flags = NL80211_FLAG_NEED_WIPHY |
-				  NL80211_FLAG_NEED_RTNL,
+		.internal_flags = NL80211_FLAG_NEED_WIPHY,
 	},
 	{
 		.cmd = NL80211_CMD_SET_WIPHY,
 		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
 		.doit = nl80211_set_wiphy,
 		.flags = GENL_UNS_ADMIN_PERM,
-		.internal_flags = NL80211_FLAG_NEED_RTNL,
 	},
 	{
 		.cmd = NL80211_CMD_GET_INTERFACE,
@@ -13835,8 +13932,7 @@ static const struct genl_ops nl80211_ops[] = {
 		.doit = nl80211_get_interface,
 		.dumpit = nl80211_dump_interface,
 		/* can be retrieved by unprivileged users */
-		.internal_flags = NL80211_FLAG_NEED_WDEV |
-				  NL80211_FLAG_NEED_RTNL,
+		.internal_flags = NL80211_FLAG_NEED_WDEV,
 	},
 	{
 		.cmd = NL80211_CMD_SET_INTERFACE,
@@ -13867,8 +13963,7 @@ static const struct genl_ops nl80211_ops[] = {
 		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
 		.doit = nl80211_get_key,
 		.flags = GENL_UNS_ADMIN_PERM,
-		.internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
-				  NL80211_FLAG_NEED_RTNL,
+		.internal_flags = NL80211_FLAG_NEED_NETDEV_UP,
 	},
 	{
 		.cmd = NL80211_CMD_SET_KEY,
@@ -13876,7 +13971,6 @@ static const struct genl_ops nl80211_ops[] = {
 		.doit = nl80211_set_key,
 		.flags = GENL_UNS_ADMIN_PERM,
 		.internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
-				  NL80211_FLAG_NEED_RTNL |
 				  NL80211_FLAG_CLEAR_SKB,
 	},
 	{
@@ -13885,7 +13979,6 @@ static const struct genl_ops nl80211_ops[] = {
 		.doit = nl80211_new_key,
 		.flags = GENL_UNS_ADMIN_PERM,
 		.internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
-				  NL80211_FLAG_NEED_RTNL |
 				  NL80211_FLAG_CLEAR_SKB,
 	},
 	{
@@ -13893,64 +13986,56 @@ static const struct genl_ops nl80211_ops[] = {
 		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
 		.doit = nl80211_del_key,
 		.flags = GENL_UNS_ADMIN_PERM,
-		.internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
-				  NL80211_FLAG_NEED_RTNL,
+		.internal_flags = NL80211_FLAG_NEED_NETDEV_UP,
 	},
 	{
 		.cmd = NL80211_CMD_SET_BEACON,
 		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
 		.flags = GENL_UNS_ADMIN_PERM,
 		.doit = nl80211_set_beacon,
-		.internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
-				  NL80211_FLAG_NEED_RTNL,
+		.internal_flags = NL80211_FLAG_NEED_NETDEV_UP,
 	},
 	{
 		.cmd = NL80211_CMD_START_AP,
 		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
 		.flags = GENL_UNS_ADMIN_PERM,
 		.doit = nl80211_start_ap,
-		.internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
-				  NL80211_FLAG_NEED_RTNL,
+		.internal_flags = NL80211_FLAG_NEED_NETDEV_UP,
 	},
 	{
 		.cmd = NL80211_CMD_STOP_AP,
 		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
 		.flags = GENL_UNS_ADMIN_PERM,
 		.doit = nl80211_stop_ap,
-		.internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
-				  NL80211_FLAG_NEED_RTNL,
+		.internal_flags = NL80211_FLAG_NEED_NETDEV_UP,
 	},
 	{
 		.cmd = NL80211_CMD_GET_STATION,
 		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
 		.doit = nl80211_get_station,
 		.dumpit = nl80211_dump_station,
-		.internal_flags = NL80211_FLAG_NEED_NETDEV |
-				  NL80211_FLAG_NEED_RTNL,
+		.internal_flags = NL80211_FLAG_NEED_NETDEV,
 	},
 	{
 		.cmd = NL80211_CMD_SET_STATION,
 		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
 		.doit = nl80211_set_station,
 		.flags = GENL_UNS_ADMIN_PERM,
-		.internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
-				  NL80211_FLAG_NEED_RTNL,
+		.internal_flags = NL80211_FLAG_NEED_NETDEV_UP,
 	},
 	{
 		.cmd = NL80211_CMD_NEW_STATION,
 		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
 		.doit = nl80211_new_station,
 		.flags = GENL_UNS_ADMIN_PERM,
-		.internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
-				  NL80211_FLAG_NEED_RTNL,
+		.internal_flags = NL80211_FLAG_NEED_NETDEV_UP,
 	},
 	{
 		.cmd = NL80211_CMD_DEL_STATION,
 		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
 		.doit = nl80211_del_station,
 		.flags = GENL_UNS_ADMIN_PERM,
-		.internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
-				  NL80211_FLAG_NEED_RTNL,
+		.internal_flags = NL80211_FLAG_NEED_NETDEV_UP,
 	},
 	{
 		.cmd = NL80211_CMD_GET_MPATH,
@@ -13958,8 +14043,7 @@ static const struct genl_ops nl80211_ops[] = {
 		.doit = nl80211_get_mpath,
 		.dumpit = nl80211_dump_mpath,
 		.flags = GENL_UNS_ADMIN_PERM,
-		.internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
-				  NL80211_FLAG_NEED_RTNL,
+		.internal_flags = NL80211_FLAG_NEED_NETDEV_UP,
 	},
 	{
 		.cmd = NL80211_CMD_GET_MPP,
@@ -13967,47 +14051,42 @@ static const struct genl_ops nl80211_ops[] = {
 		.doit = nl80211_get_mpp,
 		.dumpit = nl80211_dump_mpp,
 		.flags = GENL_UNS_ADMIN_PERM,
-		.internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
-				  NL80211_FLAG_NEED_RTNL,
+		.internal_flags = NL80211_FLAG_NEED_NETDEV_UP,
 	},
 	{
 		.cmd = NL80211_CMD_SET_MPATH,
 		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
 		.doit = nl80211_set_mpath,
 		.flags = GENL_UNS_ADMIN_PERM,
-		.internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
-				  NL80211_FLAG_NEED_RTNL,
+		.internal_flags = NL80211_FLAG_NEED_NETDEV_UP,
 	},
 	{
 		.cmd = NL80211_CMD_NEW_MPATH,
 		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
 		.doit = nl80211_new_mpath,
 		.flags = GENL_UNS_ADMIN_PERM,
-		.internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
-				  NL80211_FLAG_NEED_RTNL,
+		.internal_flags = NL80211_FLAG_NEED_NETDEV_UP,
 	},
 	{
 		.cmd = NL80211_CMD_DEL_MPATH,
 		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
 		.doit = nl80211_del_mpath,
 		.flags = GENL_UNS_ADMIN_PERM,
-		.internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
-				  NL80211_FLAG_NEED_RTNL,
+		.internal_flags = NL80211_FLAG_NEED_NETDEV_UP,
 	},
 	{
 		.cmd = NL80211_CMD_SET_BSS,
 		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
 		.doit = nl80211_set_bss,
 		.flags = GENL_UNS_ADMIN_PERM,
-		.internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
-				  NL80211_FLAG_NEED_RTNL,
+		.internal_flags = NL80211_FLAG_NEED_NETDEV_UP,
 	},
 	{
 		.cmd = NL80211_CMD_GET_REG,
 		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
 		.doit = nl80211_get_reg_do,
 		.dumpit = nl80211_get_reg_dump,
-		.internal_flags = NL80211_FLAG_NEED_RTNL,
+		.internal_flags = 0,
 		/* can be retrieved by unprivileged users */
 	},
 #ifdef CONFIG_CFG80211_CRDA_SUPPORT
@@ -14016,7 +14095,7 @@ static const struct genl_ops nl80211_ops[] = {
 		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
 		.doit = nl80211_set_reg,
 		.flags = GENL_ADMIN_PERM,
-		.internal_flags = NL80211_FLAG_NEED_RTNL,
+		.internal_flags = 0,
 	},
 #endif
 	{
@@ -14036,32 +14115,28 @@ static const struct genl_ops nl80211_ops[] = {
 		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
 		.doit = nl80211_get_mesh_config,
 		/* can be retrieved by unprivileged users */
-		.internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
-				  NL80211_FLAG_NEED_RTNL,
+		.internal_flags = NL80211_FLAG_NEED_NETDEV_UP,
 	},
 	{
 		.cmd = NL80211_CMD_SET_MESH_CONFIG,
 		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
 		.doit = nl80211_update_mesh_config,
 		.flags = GENL_UNS_ADMIN_PERM,
-		.internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
-				  NL80211_FLAG_NEED_RTNL,
+		.internal_flags = NL80211_FLAG_NEED_NETDEV_UP,
 	},
 	{
 		.cmd = NL80211_CMD_TRIGGER_SCAN,
 		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
 		.doit = nl80211_trigger_scan,
 		.flags = GENL_UNS_ADMIN_PERM,
-		.internal_flags = NL80211_FLAG_NEED_WDEV_UP |
-				  NL80211_FLAG_NEED_RTNL,
+		.internal_flags = NL80211_FLAG_NEED_WDEV_UP,
 	},
 	{
 		.cmd = NL80211_CMD_ABORT_SCAN,
 		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
 		.doit = nl80211_abort_scan,
 		.flags = GENL_UNS_ADMIN_PERM,
-		.internal_flags = NL80211_FLAG_NEED_WDEV_UP |
-				  NL80211_FLAG_NEED_RTNL,
+		.internal_flags = NL80211_FLAG_NEED_WDEV_UP,
 	},
 	{
 		.cmd = NL80211_CMD_GET_SCAN,
@@ -14073,16 +14148,14 @@ static const struct genl_ops nl80211_ops[] = {
 		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
 		.doit = nl80211_start_sched_scan,
 		.flags = GENL_UNS_ADMIN_PERM,
-		.internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
-				  NL80211_FLAG_NEED_RTNL,
+		.internal_flags = NL80211_FLAG_NEED_NETDEV_UP,
 	},
 	{
 		.cmd = NL80211_CMD_STOP_SCHED_SCAN,
 		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
 		.doit = nl80211_stop_sched_scan,
 		.flags = GENL_UNS_ADMIN_PERM,
-		.internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
-				  NL80211_FLAG_NEED_RTNL,
+		.internal_flags = NL80211_FLAG_NEED_NETDEV_UP,
 	},
 	{
 		.cmd = NL80211_CMD_AUTHENTICATE,
@@ -14090,7 +14163,7 @@ static const struct genl_ops nl80211_ops[] = {
 		.doit = nl80211_authenticate,
 		.flags = GENL_UNS_ADMIN_PERM,
 		.internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
-				  NL80211_FLAG_NEED_RTNL |
+				  0 |
 				  NL80211_FLAG_CLEAR_SKB,
 	},
 	{
@@ -14099,7 +14172,7 @@ static const struct genl_ops nl80211_ops[] = {
 		.doit = nl80211_associate,
 		.flags = GENL_UNS_ADMIN_PERM,
 		.internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
-				  NL80211_FLAG_NEED_RTNL |
+				  0 |
 				  NL80211_FLAG_CLEAR_SKB,
 	},
 	{
@@ -14107,32 +14180,28 @@ static const struct genl_ops nl80211_ops[] = {
 		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
 		.doit = nl80211_deauthenticate,
 		.flags = GENL_UNS_ADMIN_PERM,
-		.internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
-				  NL80211_FLAG_NEED_RTNL,
+		.internal_flags = NL80211_FLAG_NEED_NETDEV_UP,
 	},
 	{
 		.cmd = NL80211_CMD_DISASSOCIATE,
 		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
 		.doit = nl80211_disassociate,
 		.flags = GENL_UNS_ADMIN_PERM,
-		.internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
-				  NL80211_FLAG_NEED_RTNL,
+		.internal_flags = NL80211_FLAG_NEED_NETDEV_UP,
 	},
 	{
 		.cmd = NL80211_CMD_JOIN_IBSS,
 		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
 		.doit = nl80211_join_ibss,
 		.flags = GENL_UNS_ADMIN_PERM,
-		.internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
-				  NL80211_FLAG_NEED_RTNL,
+		.internal_flags = NL80211_FLAG_NEED_NETDEV_UP,
 	},
 	{
 		.cmd = NL80211_CMD_LEAVE_IBSS,
 		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
 		.doit = nl80211_leave_ibss,
 		.flags = GENL_UNS_ADMIN_PERM,
-		.internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
-				  NL80211_FLAG_NEED_RTNL,
+		.internal_flags = NL80211_FLAG_NEED_NETDEV_UP,
 	},
 #ifdef CONFIG_NL80211_TESTMODE
 	{
@@ -14141,8 +14210,7 @@ static const struct genl_ops nl80211_ops[] = {
 		.doit = nl80211_testmode_do,
 		.dumpit = nl80211_testmode_dump,
 		.flags = GENL_UNS_ADMIN_PERM,
-		.internal_flags = NL80211_FLAG_NEED_WIPHY |
-				  NL80211_FLAG_NEED_RTNL,
+		.internal_flags = NL80211_FLAG_NEED_WIPHY,
 	},
 #endif
 	{
@@ -14151,7 +14219,7 @@ static const struct genl_ops nl80211_ops[] = {
 		.doit = nl80211_connect,
 		.flags = GENL_UNS_ADMIN_PERM,
 		.internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
-				  NL80211_FLAG_NEED_RTNL |
+				  0 |
 				  NL80211_FLAG_CLEAR_SKB,
 	},
 	{
@@ -14160,7 +14228,7 @@ static const struct genl_ops nl80211_ops[] = {
 		.doit = nl80211_update_connect_params,
 		.flags = GENL_ADMIN_PERM,
 		.internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
-				  NL80211_FLAG_NEED_RTNL |
+				  0 |
 				  NL80211_FLAG_CLEAR_SKB,
 	},
 	{
@@ -14168,16 +14236,14 @@ static const struct genl_ops nl80211_ops[] = {
 		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
 		.doit = nl80211_disconnect,
 		.flags = GENL_UNS_ADMIN_PERM,
-		.internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
-				  NL80211_FLAG_NEED_RTNL,
+		.internal_flags = NL80211_FLAG_NEED_NETDEV_UP,
 	},
 	{
 		.cmd = NL80211_CMD_SET_WIPHY_NETNS,
 		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
 		.doit = nl80211_wiphy_netns,
 		.flags = GENL_UNS_ADMIN_PERM,
-		.internal_flags = NL80211_FLAG_NEED_WIPHY |
-				  NL80211_FLAG_NEED_RTNL,
+		.internal_flags = NL80211_FLAG_NEED_WIPHY,
 	},
 	{
 		.cmd = NL80211_CMD_GET_SURVEY,
@@ -14190,7 +14256,7 @@ static const struct genl_ops nl80211_ops[] = {
 		.doit = nl80211_setdel_pmksa,
 		.flags = GENL_UNS_ADMIN_PERM,
 		.internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
-				  NL80211_FLAG_NEED_RTNL |
+				  0 |
 				  NL80211_FLAG_CLEAR_SKB,
 	},
 	{
@@ -14198,136 +14264,119 @@ static const struct genl_ops nl80211_ops[] = {
 		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
 		.doit = nl80211_setdel_pmksa,
 		.flags = GENL_UNS_ADMIN_PERM,
-		.internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
-				  NL80211_FLAG_NEED_RTNL,
+		.internal_flags = NL80211_FLAG_NEED_NETDEV_UP,
 	},
 	{
 		.cmd = NL80211_CMD_FLUSH_PMKSA,
 		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
 		.doit = nl80211_flush_pmksa,
 		.flags = GENL_UNS_ADMIN_PERM,
-		.internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
-				  NL80211_FLAG_NEED_RTNL,
+		.internal_flags = NL80211_FLAG_NEED_NETDEV_UP,
 	},
 	{
 		.cmd = NL80211_CMD_REMAIN_ON_CHANNEL,
 		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
 		.doit = nl80211_remain_on_channel,
 		.flags = GENL_UNS_ADMIN_PERM,
-		.internal_flags = NL80211_FLAG_NEED_WDEV_UP |
-				  NL80211_FLAG_NEED_RTNL,
+		.internal_flags = NL80211_FLAG_NEED_WDEV_UP,
 	},
 	{
 		.cmd = NL80211_CMD_CANCEL_REMAIN_ON_CHANNEL,
 		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
 		.doit = nl80211_cancel_remain_on_channel,
 		.flags = GENL_UNS_ADMIN_PERM,
-		.internal_flags = NL80211_FLAG_NEED_WDEV_UP |
-				  NL80211_FLAG_NEED_RTNL,
+		.internal_flags = NL80211_FLAG_NEED_WDEV_UP,
 	},
 	{
 		.cmd = NL80211_CMD_SET_TX_BITRATE_MASK,
 		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
 		.doit = nl80211_set_tx_bitrate_mask,
 		.flags = GENL_UNS_ADMIN_PERM,
-		.internal_flags = NL80211_FLAG_NEED_NETDEV |
-				  NL80211_FLAG_NEED_RTNL,
+		.internal_flags = NL80211_FLAG_NEED_NETDEV,
 	},
 	{
 		.cmd = NL80211_CMD_REGISTER_FRAME,
 		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
 		.doit = nl80211_register_mgmt,
 		.flags = GENL_UNS_ADMIN_PERM,
-		.internal_flags = NL80211_FLAG_NEED_WDEV |
-				  NL80211_FLAG_NEED_RTNL,
+		.internal_flags = NL80211_FLAG_NEED_WDEV,
 	},
 	{
 		.cmd = NL80211_CMD_FRAME,
 		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
 		.doit = nl80211_tx_mgmt,
 		.flags = GENL_UNS_ADMIN_PERM,
-		.internal_flags = NL80211_FLAG_NEED_WDEV_UP |
-				  NL80211_FLAG_NEED_RTNL,
+		.internal_flags = NL80211_FLAG_NEED_WDEV_UP,
 	},
 	{
 		.cmd = NL80211_CMD_FRAME_WAIT_CANCEL,
 		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
 		.doit = nl80211_tx_mgmt_cancel_wait,
 		.flags = GENL_UNS_ADMIN_PERM,
-		.internal_flags = NL80211_FLAG_NEED_WDEV_UP |
-				  NL80211_FLAG_NEED_RTNL,
+		.internal_flags = NL80211_FLAG_NEED_WDEV_UP,
 	},
 	{
 		.cmd = NL80211_CMD_SET_POWER_SAVE,
 		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
 		.doit = nl80211_set_power_save,
 		.flags = GENL_UNS_ADMIN_PERM,
-		.internal_flags = NL80211_FLAG_NEED_NETDEV |
-				  NL80211_FLAG_NEED_RTNL,
+		.internal_flags = NL80211_FLAG_NEED_NETDEV,
 	},
 	{
 		.cmd = NL80211_CMD_GET_POWER_SAVE,
 		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
 		.doit = nl80211_get_power_save,
 		/* can be retrieved by unprivileged users */
-		.internal_flags = NL80211_FLAG_NEED_NETDEV |
-				  NL80211_FLAG_NEED_RTNL,
+		.internal_flags = NL80211_FLAG_NEED_NETDEV,
 	},
 	{
 		.cmd = NL80211_CMD_SET_CQM,
 		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
 		.doit = nl80211_set_cqm,
 		.flags = GENL_UNS_ADMIN_PERM,
-		.internal_flags = NL80211_FLAG_NEED_NETDEV |
-				  NL80211_FLAG_NEED_RTNL,
+		.internal_flags = NL80211_FLAG_NEED_NETDEV,
 	},
 	{
 		.cmd = NL80211_CMD_SET_CHANNEL,
 		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
 		.doit = nl80211_set_channel,
 		.flags = GENL_UNS_ADMIN_PERM,
-		.internal_flags = NL80211_FLAG_NEED_NETDEV |
-				  NL80211_FLAG_NEED_RTNL,
+		.internal_flags = NL80211_FLAG_NEED_NETDEV,
 	},
 	{
 		.cmd = NL80211_CMD_SET_WDS_PEER,
 		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
 		.doit = nl80211_set_wds_peer,
 		.flags = GENL_UNS_ADMIN_PERM,
-		.internal_flags = NL80211_FLAG_NEED_NETDEV |
-				  NL80211_FLAG_NEED_RTNL,
+		.internal_flags = NL80211_FLAG_NEED_NETDEV,
 	},
 	{
 		.cmd = NL80211_CMD_JOIN_MESH,
 		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
 		.doit = nl80211_join_mesh,
 		.flags = GENL_UNS_ADMIN_PERM,
-		.internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
-				  NL80211_FLAG_NEED_RTNL,
+		.internal_flags = NL80211_FLAG_NEED_NETDEV_UP,
 	},
 	{
 		.cmd = NL80211_CMD_LEAVE_MESH,
 		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
 		.doit = nl80211_leave_mesh,
 		.flags = GENL_UNS_ADMIN_PERM,
-		.internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
-				  NL80211_FLAG_NEED_RTNL,
+		.internal_flags = NL80211_FLAG_NEED_NETDEV_UP,
 	},
 	{
 		.cmd = NL80211_CMD_JOIN_OCB,
 		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
 		.doit = nl80211_join_ocb,
 		.flags = GENL_UNS_ADMIN_PERM,
-		.internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
-				  NL80211_FLAG_NEED_RTNL,
+		.internal_flags = NL80211_FLAG_NEED_NETDEV_UP,
 	},
 	{
 		.cmd = NL80211_CMD_LEAVE_OCB,
 		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
 		.doit = nl80211_leave_ocb,
 		.flags = GENL_UNS_ADMIN_PERM,
-		.internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
-				  NL80211_FLAG_NEED_RTNL,
+		.internal_flags = NL80211_FLAG_NEED_NETDEV_UP,
 	},
 #ifdef CONFIG_PM
 	{
@@ -14335,16 +14384,14 @@ static const struct genl_ops nl80211_ops[] = {
 		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
 		.doit = nl80211_get_wowlan,
 		/* can be retrieved by unprivileged users */
-		.internal_flags = NL80211_FLAG_NEED_WIPHY |
-				  NL80211_FLAG_NEED_RTNL,
+		.internal_flags = NL80211_FLAG_NEED_WIPHY,
 	},
 	{
 		.cmd = NL80211_CMD_SET_WOWLAN,
 		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
 		.doit = nl80211_set_wowlan,
 		.flags = GENL_UNS_ADMIN_PERM,
-		.internal_flags = NL80211_FLAG_NEED_WIPHY |
-				  NL80211_FLAG_NEED_RTNL,
+		.internal_flags = NL80211_FLAG_NEED_WIPHY,
 	},
 #endif
 	{
@@ -14353,7 +14400,7 @@ static const struct genl_ops nl80211_ops[] = {
 		.doit = nl80211_set_rekey_data,
 		.flags = GENL_UNS_ADMIN_PERM,
 		.internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
-				  NL80211_FLAG_NEED_RTNL |
+				  0 |
 				  NL80211_FLAG_CLEAR_SKB,
 	},
 	{
@@ -14361,128 +14408,112 @@ static const struct genl_ops nl80211_ops[] = {
 		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
 		.doit = nl80211_tdls_mgmt,
 		.flags = GENL_UNS_ADMIN_PERM,
-		.internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
-				  NL80211_FLAG_NEED_RTNL,
+		.internal_flags = NL80211_FLAG_NEED_NETDEV_UP,
 	},
 	{
 		.cmd = NL80211_CMD_TDLS_OPER,
 		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
 		.doit = nl80211_tdls_oper,
 		.flags = GENL_UNS_ADMIN_PERM,
-		.internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
-				  NL80211_FLAG_NEED_RTNL,
+		.internal_flags = NL80211_FLAG_NEED_NETDEV_UP,
 	},
 	{
 		.cmd = NL80211_CMD_UNEXPECTED_FRAME,
 		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
 		.doit = nl80211_register_unexpected_frame,
 		.flags = GENL_UNS_ADMIN_PERM,
-		.internal_flags = NL80211_FLAG_NEED_NETDEV |
-				  NL80211_FLAG_NEED_RTNL,
+		.internal_flags = NL80211_FLAG_NEED_NETDEV,
 	},
 	{
 		.cmd = NL80211_CMD_PROBE_CLIENT,
 		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
 		.doit = nl80211_probe_client,
 		.flags = GENL_UNS_ADMIN_PERM,
-		.internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
-				  NL80211_FLAG_NEED_RTNL,
+		.internal_flags = NL80211_FLAG_NEED_NETDEV_UP,
 	},
 	{
 		.cmd = NL80211_CMD_REGISTER_BEACONS,
 		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
 		.doit = nl80211_register_beacons,
 		.flags = GENL_UNS_ADMIN_PERM,
-		.internal_flags = NL80211_FLAG_NEED_WIPHY |
-				  NL80211_FLAG_NEED_RTNL,
+		.internal_flags = NL80211_FLAG_NEED_WIPHY,
 	},
 	{
 		.cmd = NL80211_CMD_SET_NOACK_MAP,
 		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
 		.doit = nl80211_set_noack_map,
 		.flags = GENL_UNS_ADMIN_PERM,
-		.internal_flags = NL80211_FLAG_NEED_NETDEV |
-				  NL80211_FLAG_NEED_RTNL,
+		.internal_flags = NL80211_FLAG_NEED_NETDEV,
 	},
 	{
 		.cmd = NL80211_CMD_START_P2P_DEVICE,
 		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
 		.doit = nl80211_start_p2p_device,
 		.flags = GENL_UNS_ADMIN_PERM,
-		.internal_flags = NL80211_FLAG_NEED_WDEV |
-				  NL80211_FLAG_NEED_RTNL,
+		.internal_flags = NL80211_FLAG_NEED_WDEV,
 	},
 	{
 		.cmd = NL80211_CMD_STOP_P2P_DEVICE,
 		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
 		.doit = nl80211_stop_p2p_device,
 		.flags = GENL_UNS_ADMIN_PERM,
-		.internal_flags = NL80211_FLAG_NEED_WDEV_UP |
-				  NL80211_FLAG_NEED_RTNL,
+		.internal_flags = NL80211_FLAG_NEED_WDEV_UP,
 	},
 	{
 		.cmd = NL80211_CMD_START_NAN,
 		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
 		.doit = nl80211_start_nan,
 		.flags = GENL_ADMIN_PERM,
-		.internal_flags = NL80211_FLAG_NEED_WDEV |
-				  NL80211_FLAG_NEED_RTNL,
+		.internal_flags = NL80211_FLAG_NEED_WDEV,
 	},
 	{
 		.cmd = NL80211_CMD_STOP_NAN,
 		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
 		.doit = nl80211_stop_nan,
 		.flags = GENL_ADMIN_PERM,
-		.internal_flags = NL80211_FLAG_NEED_WDEV_UP |
-				  NL80211_FLAG_NEED_RTNL,
+		.internal_flags = NL80211_FLAG_NEED_WDEV_UP,
 	},
 	{
 		.cmd = NL80211_CMD_ADD_NAN_FUNCTION,
 		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
 		.doit = nl80211_nan_add_func,
 		.flags = GENL_ADMIN_PERM,
-		.internal_flags = NL80211_FLAG_NEED_WDEV_UP |
-				  NL80211_FLAG_NEED_RTNL,
+		.internal_flags = NL80211_FLAG_NEED_WDEV_UP,
 	},
 	{
 		.cmd = NL80211_CMD_DEL_NAN_FUNCTION,
 		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
 		.doit = nl80211_nan_del_func,
 		.flags = GENL_ADMIN_PERM,
-		.internal_flags = NL80211_FLAG_NEED_WDEV_UP |
-				  NL80211_FLAG_NEED_RTNL,
+		.internal_flags = NL80211_FLAG_NEED_WDEV_UP,
 	},
 	{
 		.cmd = NL80211_CMD_CHANGE_NAN_CONFIG,
 		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
 		.doit = nl80211_nan_change_config,
 		.flags = GENL_ADMIN_PERM,
-		.internal_flags = NL80211_FLAG_NEED_WDEV_UP |
-				  NL80211_FLAG_NEED_RTNL,
+		.internal_flags = NL80211_FLAG_NEED_WDEV_UP,
 	},
 	{
 		.cmd = NL80211_CMD_SET_MCAST_RATE,
 		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
 		.doit = nl80211_set_mcast_rate,
 		.flags = GENL_UNS_ADMIN_PERM,
-		.internal_flags = NL80211_FLAG_NEED_NETDEV |
-				  NL80211_FLAG_NEED_RTNL,
+		.internal_flags = NL80211_FLAG_NEED_NETDEV,
 	},
 	{
 		.cmd = NL80211_CMD_SET_MAC_ACL,
 		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
 		.doit = nl80211_set_mac_acl,
 		.flags = GENL_UNS_ADMIN_PERM,
-		.internal_flags = NL80211_FLAG_NEED_NETDEV |
-				  NL80211_FLAG_NEED_RTNL,
+		.internal_flags = NL80211_FLAG_NEED_NETDEV,
 	},
 	{
 		.cmd = NL80211_CMD_RADAR_DETECT,
 		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
 		.doit = nl80211_start_radar_detection,
 		.flags = GENL_UNS_ADMIN_PERM,
-		.internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
-				  NL80211_FLAG_NEED_RTNL,
+		.internal_flags = NL80211_FLAG_NEED_NETDEV_UP,
 	},
 	{
 		.cmd = NL80211_CMD_GET_PROTOCOL_FEATURES,
@@ -14494,47 +14525,41 @@ static const struct genl_ops nl80211_ops[] = {
 		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
 		.doit = nl80211_update_ft_ies,
 		.flags = GENL_UNS_ADMIN_PERM,
-		.internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
-				  NL80211_FLAG_NEED_RTNL,
+		.internal_flags = NL80211_FLAG_NEED_NETDEV_UP,
 	},
 	{
 		.cmd = NL80211_CMD_CRIT_PROTOCOL_START,
 		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
 		.doit = nl80211_crit_protocol_start,
 		.flags = GENL_UNS_ADMIN_PERM,
-		.internal_flags = NL80211_FLAG_NEED_WDEV_UP |
-				  NL80211_FLAG_NEED_RTNL,
+		.internal_flags = NL80211_FLAG_NEED_WDEV_UP,
 	},
 	{
 		.cmd = NL80211_CMD_CRIT_PROTOCOL_STOP,
 		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
 		.doit = nl80211_crit_protocol_stop,
 		.flags = GENL_UNS_ADMIN_PERM,
-		.internal_flags = NL80211_FLAG_NEED_WDEV_UP |
-				  NL80211_FLAG_NEED_RTNL,
+		.internal_flags = NL80211_FLAG_NEED_WDEV_UP,
 	},
 	{
 		.cmd = NL80211_CMD_GET_COALESCE,
 		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
 		.doit = nl80211_get_coalesce,
-		.internal_flags = NL80211_FLAG_NEED_WIPHY |
-				  NL80211_FLAG_NEED_RTNL,
+		.internal_flags = NL80211_FLAG_NEED_WIPHY,
 	},
 	{
 		.cmd = NL80211_CMD_SET_COALESCE,
 		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
 		.doit = nl80211_set_coalesce,
 		.flags = GENL_UNS_ADMIN_PERM,
-		.internal_flags = NL80211_FLAG_NEED_WIPHY |
-				  NL80211_FLAG_NEED_RTNL,
+		.internal_flags = NL80211_FLAG_NEED_WIPHY,
 	},
 	{
 		.cmd = NL80211_CMD_CHANNEL_SWITCH,
 		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
 		.doit = nl80211_channel_switch,
 		.flags = GENL_UNS_ADMIN_PERM,
-		.internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
-				  NL80211_FLAG_NEED_RTNL,
+		.internal_flags = NL80211_FLAG_NEED_NETDEV_UP,
 	},
 	{
 		.cmd = NL80211_CMD_VENDOR,
@@ -14543,7 +14568,7 @@ static const struct genl_ops nl80211_ops[] = {
 		.dumpit = nl80211_vendor_cmd_dump,
 		.flags = GENL_UNS_ADMIN_PERM,
 		.internal_flags = NL80211_FLAG_NEED_WIPHY |
-				  NL80211_FLAG_NEED_RTNL |
+				  0 |
 				  NL80211_FLAG_CLEAR_SKB,
 	},
 	{
@@ -14551,116 +14576,102 @@ static const struct genl_ops nl80211_ops[] = {
 		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
 		.doit = nl80211_set_qos_map,
 		.flags = GENL_UNS_ADMIN_PERM,
-		.internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
-				  NL80211_FLAG_NEED_RTNL,
+		.internal_flags = NL80211_FLAG_NEED_NETDEV_UP,
 	},
 	{
 		.cmd = NL80211_CMD_ADD_TX_TS,
 		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
 		.doit = nl80211_add_tx_ts,
 		.flags = GENL_UNS_ADMIN_PERM,
-		.internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
-				  NL80211_FLAG_NEED_RTNL,
+		.internal_flags = NL80211_FLAG_NEED_NETDEV_UP,
 	},
 	{
 		.cmd = NL80211_CMD_DEL_TX_TS,
 		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
 		.doit = nl80211_del_tx_ts,
 		.flags = GENL_UNS_ADMIN_PERM,
-		.internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
-				  NL80211_FLAG_NEED_RTNL,
+		.internal_flags = NL80211_FLAG_NEED_NETDEV_UP,
 	},
 	{
 		.cmd = NL80211_CMD_TDLS_CHANNEL_SWITCH,
 		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
 		.doit = nl80211_tdls_channel_switch,
 		.flags = GENL_UNS_ADMIN_PERM,
-		.internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
-				  NL80211_FLAG_NEED_RTNL,
+		.internal_flags = NL80211_FLAG_NEED_NETDEV_UP,
 	},
 	{
 		.cmd = NL80211_CMD_TDLS_CANCEL_CHANNEL_SWITCH,
 		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
 		.doit = nl80211_tdls_cancel_channel_switch,
 		.flags = GENL_UNS_ADMIN_PERM,
-		.internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
-				  NL80211_FLAG_NEED_RTNL,
+		.internal_flags = NL80211_FLAG_NEED_NETDEV_UP,
 	},
 	{
 		.cmd = NL80211_CMD_SET_MULTICAST_TO_UNICAST,
 		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
 		.doit = nl80211_set_multicast_to_unicast,
 		.flags = GENL_UNS_ADMIN_PERM,
-		.internal_flags = NL80211_FLAG_NEED_NETDEV |
-				  NL80211_FLAG_NEED_RTNL,
+		.internal_flags = NL80211_FLAG_NEED_NETDEV,
 	},
 	{
 		.cmd = NL80211_CMD_SET_PMK,
 		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
 		.doit = nl80211_set_pmk,
 		.internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
-				  NL80211_FLAG_NEED_RTNL |
+				  0 |
 				  NL80211_FLAG_CLEAR_SKB,
 	},
 	{
 		.cmd = NL80211_CMD_DEL_PMK,
 		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
 		.doit = nl80211_del_pmk,
-		.internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
-				  NL80211_FLAG_NEED_RTNL,
+		.internal_flags = NL80211_FLAG_NEED_NETDEV_UP,
 	},
 	{
 		.cmd = NL80211_CMD_EXTERNAL_AUTH,
 		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
 		.doit = nl80211_external_auth,
 		.flags = GENL_ADMIN_PERM,
-		.internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
-				  NL80211_FLAG_NEED_RTNL,
+		.internal_flags = NL80211_FLAG_NEED_NETDEV_UP,
 	},
 	{
 		.cmd = NL80211_CMD_CONTROL_PORT_FRAME,
 		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
 		.doit = nl80211_tx_control_port,
 		.flags = GENL_UNS_ADMIN_PERM,
-		.internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
-				  NL80211_FLAG_NEED_RTNL,
+		.internal_flags = NL80211_FLAG_NEED_NETDEV_UP,
 	},
 	{
 		.cmd = NL80211_CMD_GET_FTM_RESPONDER_STATS,
 		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
 		.doit = nl80211_get_ftm_responder_stats,
-		.internal_flags = NL80211_FLAG_NEED_NETDEV |
-				  NL80211_FLAG_NEED_RTNL,
+		.internal_flags = NL80211_FLAG_NEED_NETDEV,
 	},
 	{
 		.cmd = NL80211_CMD_PEER_MEASUREMENT_START,
 		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
 		.doit = nl80211_pmsr_start,
 		.flags = GENL_UNS_ADMIN_PERM,
-		.internal_flags = NL80211_FLAG_NEED_WDEV_UP |
-				  NL80211_FLAG_NEED_RTNL,
+		.internal_flags = NL80211_FLAG_NEED_WDEV_UP,
 	},
 	{
 		.cmd = NL80211_CMD_NOTIFY_RADAR,
 		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
 		.doit = nl80211_notify_radar_detection,
 		.flags = GENL_UNS_ADMIN_PERM,
-		.internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
-				  NL80211_FLAG_NEED_RTNL,
+		.internal_flags = NL80211_FLAG_NEED_NETDEV_UP,
 	},
 	{
 		.cmd = NL80211_CMD_UPDATE_OWE_INFO,
 		.doit = nl80211_update_owe_info,
 		.flags = GENL_ADMIN_PERM,
-		.internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
-				  NL80211_FLAG_NEED_RTNL,
+		.internal_flags = NL80211_FLAG_NEED_NETDEV_UP,
 	},
 	{
 		.cmd = NL80211_CMD_PROBE_MESH_LINK,
 		.doit = nl80211_probe_mesh_link,
 		.flags = GENL_UNS_ADMIN_PERM,
-		.internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
-				  NL80211_FLAG_NEED_RTNL,
+		.internal_flags = NL80211_FLAG_NEED_NETDEV_UP,
 	},
 };
 
diff --git a/net/wireless/sme.c b/net/wireless/sme.c
index 7a6c38ddc65a..f6b68685189f 100644
--- a/net/wireless/sme.c
+++ b/net/wireless/sme.c
@@ -67,7 +67,6 @@ static int cfg80211_conn_scan(struct wireless_dev *wdev)
 	struct cfg80211_scan_request *request;
 	int n_channels, err;
 
-	ASSERT_RTNL();
 	ASSERT_WDEV_LOCK(wdev);
 
 	if (rdev->scan_req || rdev->scan_msg)
diff --git a/net/wireless/util.c b/net/wireless/util.c
index 1c39d6a2e850..39587ad2fb48 100644
--- a/net/wireless/util.c
+++ b/net/wireless/util.c
@@ -891,7 +891,7 @@ void cfg80211_process_rdev_events(struct cfg80211_registered_device *rdev)
 {
 	struct wireless_dev *wdev;
 
-	ASSERT_RTNL();
+	lockdep_assert_held(&rdev->mtx);
 
 	list_for_each_entry(wdev, &rdev->wiphy.wdev_list, list)
 		cfg80211_process_wdev_events(wdev);
-- 
2.20.1


^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: [RFC/RFT] cfg80211: decouple us from the RTNL
  2019-07-31 22:08 [RFC/RFT] cfg80211: decouple us from the RTNL Johannes Berg
@ 2019-08-01  3:59 ` Sid Hayn
  2019-08-05 15:20 ` [EXT] " Dedy Lansky
  1 sibling, 0 replies; 4+ messages in thread
From: Sid Hayn @ 2019-08-01  3:59 UTC (permalink / raw)
  To: Johannes Berg; +Cc: linux-wireless, Florian Westphal, Johannes Berg

To give an indication of what this does.  I built a small nuc computer
with 13 USB wifi cards and 1 pci-e wifi card.  I used kismet to
channel hop every card twice per second while in monitor mode.

Before this patch, the load average of my 4 core nuc was 11-12 (fairly
stable, with actual cpu use fairly low, 2-10%)
After this patch, the load average is 3.5-4 under the same usage
conditions.  Quick math suggest this is a very worthwhile improvement.
Interestingly, with the patch I bumped it back up to the default hop
speed of 5 per second and the load average only hit 5-6

I'll leave the code review to those who are much better at this than
I, but hopefully this demonstrates at least some of the need.  I have
another computer with ~15 wifi clients connecting and disconnecting
once per minute and it hits D state long enough to time out
connections.  I look forward to testing this patch on that system as
well.

Thanks Johannes!

-Zero

On Wed, Jul 31, 2019 at 6:21 PM Johannes Berg <johannes@sipsolutions.net> wrote:
>
> From: Johannes Berg <johannes.berg@intel.com>
>
> Currently, _everything_ in cfg80211 holds the RTNL, and if you
> have a slow USB device (or a few) you can get some bad lock
> contention on that.
>
> Fix that by re-adding a mutex to each wiphy/rdev as we had at
> some point, so we have locking for the wireless_dev lists and
> all the other things in there, and also so that drivers still
> don't have to worry too much about it (they still won't get
> parallel calls for a single device).
>
> Then, we can restrict the RTNL to a few cases where we add or
> remove interfaces and really need the added protection. Some
> of the global list management still also uses the RTNL, since
> we need to have it anyway for netdev management.
>
> TODO:
>  - re-read everything a few times to make sure it seems right
>  - use wiphy_lock()/wiphy_unlock() in all drivers as the code
>    changed in mac80211 does
>  - address the FIXME
>
> Signed-off-by: Johannes Berg <johannes.berg@intel.com>
> ---
>  include/net/cfg80211.h |  24 +-
>  net/mac80211/main.c    |   2 +
>  net/wireless/chan.c    |   5 +-
>  net/wireless/core.c    |  45 +++-
>  net/wireless/core.h    |   4 +-
>  net/wireless/ibss.c    |   2 +-
>  net/wireless/mlme.c    |   6 +-
>  net/wireless/nl80211.c | 547 +++++++++++++++++++++--------------------
>  net/wireless/sme.c     |   1 -
>  net/wireless/util.c    |   2 +-
>  10 files changed, 353 insertions(+), 285 deletions(-)
>
> diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h
> index 35ec1f0a2bf9..2ac6cb79f67d 100644
> --- a/include/net/cfg80211.h
> +++ b/include/net/cfg80211.h
> @@ -4726,6 +4726,25 @@ struct cfg80211_internal_bss;
>  struct cfg80211_cached_keys;
>  struct cfg80211_cqm_config;
>
> +/**
> + * wiphy_lock - lock the wiphy
> + * @wiphy: the wiphy to lock
> + *
> + * This is mostly exposed so it can be done around registering and
> + * unregistering netdevs that aren't created through cfg80211 calls,
> + * since that requires locking in cfg80211 when the notifiers is
> + * called, but that cannot differentiate which way it's called.
> + *
> + * When cfg80211 ops are called, the wiphy is already locked.
> + */
> +void wiphy_lock(struct wiphy *wiphy);
> +
> +/**
> + * wiphy_unlock - unlock the wiphy again
> + * @wiphy: the wiphy to unlock
> + */
> +void wiphy_unlock(struct wiphy *wiphy);
> +
>  /**
>   * struct wireless_dev - wireless device state
>   *
> @@ -4733,7 +4752,10 @@ struct cfg80211_cqm_config;
>   * that uses the ieee80211_ptr field in struct net_device (this
>   * is intentional so it can be allocated along with the netdev.)
>   * It need not be registered then as netdev registration will
> - * be intercepted by cfg80211 to see the new wireless device.
> + * be intercepted by cfg80211 to see the new wireless device,
> + * however, drivers must lock the wiphy before registering or
> + * unregistering netdevs if they pre-create any netdevs (in ops
> + * called from cfg80211, the wiphy is already locked.)
>   *
>   * For non-netdev uses, it must also be allocated by the driver
>   * in response to the cfg80211 callbacks that require it, as
> diff --git a/net/mac80211/main.c b/net/mac80211/main.c
> index 29b9d57df1a3..03fc52587bff 100644
> --- a/net/mac80211/main.c
> +++ b/net/mac80211/main.c
> @@ -1252,8 +1252,10 @@ int ieee80211_register_hw(struct ieee80211_hw *hw)
>             !ieee80211_hw_check(hw, NO_AUTO_VIF)) {
>                 struct vif_params params = {0};
>
> +               wiphy_lock(hw->wiphy);
>                 result = ieee80211_if_add(local, "wlan%d", NET_NAME_ENUM, NULL,
>                                           NL80211_IFTYPE_STATION, &params);
> +               wiphy_unlock(hw->wiphy);
>                 if (result)
>                         wiphy_warn(local->hw.wiphy,
>                                    "Failed to add default virtual iface\n");
> diff --git a/net/wireless/chan.c b/net/wireless/chan.c
> index 7dc1bbd0888f..ac83c9030c17 100644
> --- a/net/wireless/chan.c
> +++ b/net/wireless/chan.c
> @@ -839,7 +839,7 @@ static bool cfg80211_ir_permissive_chan(struct wiphy *wiphy,
>         struct wireless_dev *wdev;
>         struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
>
> -       ASSERT_RTNL();
> +       lockdep_assert_held(&rdev->mtx);
>
>         if (!IS_ENABLED(CONFIG_CFG80211_REG_RELAX_NO_IR) ||
>             !(wiphy->regulatory_flags & REGULATORY_ENABLE_RELAX_NO_IR))
> @@ -961,9 +961,10 @@ bool cfg80211_reg_can_beacon_relax(struct wiphy *wiphy,
>                                    struct cfg80211_chan_def *chandef,
>                                    enum nl80211_iftype iftype)
>  {
> +       struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
>         bool check_no_ir;
>
> -       ASSERT_RTNL();
> +       lockdep_assert_held(&rdev->mtx);
>
>         /*
>          * Under certain conditions suggested by some regulatory bodies a
> diff --git a/net/wireless/core.c b/net/wireless/core.c
> index 742986c73490..2d5bd07ae11f 100644
> --- a/net/wireless/core.c
> +++ b/net/wireless/core.c
> @@ -39,7 +39,7 @@ MODULE_LICENSE("GPL");
>  MODULE_DESCRIPTION("wireless configuration support");
>  MODULE_ALIAS_GENL_FAMILY(NL80211_GENL_NAME);
>
> -/* RCU-protected (and RTNL for writers) */
> +/* RTNL-protected for writing, RCU-based lookup */
>  LIST_HEAD(cfg80211_rdev_list);
>  int cfg80211_rdev_list_generation;
>
> @@ -222,7 +222,7 @@ static void cfg80211_rfkill_poll(struct rfkill *rfkill, void *data)
>  void cfg80211_stop_p2p_device(struct cfg80211_registered_device *rdev,
>                               struct wireless_dev *wdev)
>  {
> -       ASSERT_RTNL();
> +       lockdep_assert_held(&rdev->mtx);
>
>         if (WARN_ON(wdev->iftype != NL80211_IFTYPE_P2P_DEVICE))
>                 return;
> @@ -245,7 +245,7 @@ void cfg80211_stop_p2p_device(struct cfg80211_registered_device *rdev,
>  void cfg80211_stop_nan(struct cfg80211_registered_device *rdev,
>                        struct wireless_dev *wdev)
>  {
> -       ASSERT_RTNL();
> +       lockdep_assert_held(&rdev->mtx);
>
>         if (WARN_ON(wdev->iftype != NL80211_IFTYPE_NAN))
>                 return;
> @@ -472,6 +472,7 @@ struct wiphy *wiphy_new_nm(const struct cfg80211_ops *ops, int sizeof_priv,
>                 }
>         }
>
> +       mutex_init(&rdev->mtx);
>         INIT_LIST_HEAD(&rdev->wiphy.wdev_list);
>         INIT_LIST_HEAD(&rdev->beacon_registrations);
>         spin_lock_init(&rdev->beacon_registrations_lock);
> @@ -978,21 +979,38 @@ void wiphy_rfkill_stop_polling(struct wiphy *wiphy)
>  }
>  EXPORT_SYMBOL(wiphy_rfkill_stop_polling);
>
> +void wiphy_lock(struct wiphy *wiphy)
> +{
> +       struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
> +
> +       mutex_lock(&rdev->mtx);
> +}
> +EXPORT_SYMBOL(wiphy_lock);
> +
> +void wiphy_unlock(struct wiphy *wiphy)
> +{
> +       struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
> +
> +       mutex_unlock(&rdev->mtx);
> +}
> +EXPORT_SYMBOL(wiphy_unlock);
> +
>  void wiphy_unregister(struct wiphy *wiphy)
>  {
>         struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
>
>         wait_event(rdev->dev_wait, ({
>                 int __count;
> -               rtnl_lock();
> +               mutex_lock(&rdev->mtx);
>                 __count = rdev->opencount;
> -               rtnl_unlock();
> +               mutex_unlock(&rdev->mtx);
>                 __count == 0; }));
>
>         if (rdev->rfkill)
>                 rfkill_unregister(rdev->rfkill);
>
>         rtnl_lock();
> +       mutex_lock(&rdev->mtx);
>         nl80211_notify_wiphy(rdev, NL80211_CMD_DEL_WIPHY);
>         rdev->wiphy.registered = false;
>
> @@ -1015,6 +1033,7 @@ void wiphy_unregister(struct wiphy *wiphy)
>         cfg80211_rdev_list_generation++;
>         device_del(&rdev->wiphy.dev);
>
> +       mutex_unlock(&rdev->mtx);
>         rtnl_unlock();
>
>         flush_work(&rdev->scan_done_wk);
> @@ -1047,6 +1066,7 @@ void cfg80211_dev_free(struct cfg80211_registered_device *rdev)
>         }
>         list_for_each_entry_safe(scan, tmp, &rdev->bss_list, list)
>                 cfg80211_put_bss(&rdev->wiphy, &scan->pub);
> +       mutex_destroy(&rdev->mtx);
>         kfree(rdev);
>  }
>
> @@ -1125,7 +1145,7 @@ static const struct device_type wiphy_type = {
>  void cfg80211_update_iface_num(struct cfg80211_registered_device *rdev,
>                                enum nl80211_iftype iftype, int num)
>  {
> -       ASSERT_RTNL();
> +       lockdep_assert_held(&rdev->mtx);
>
>         rdev->num_running_ifaces += num;
>         if (iftype == NL80211_IFTYPE_MONITOR)
> @@ -1138,7 +1158,7 @@ void __cfg80211_leave(struct cfg80211_registered_device *rdev,
>         struct net_device *dev = wdev->netdev;
>         struct cfg80211_sched_scan_request *pos, *tmp;
>
> -       ASSERT_RTNL();
> +       lockdep_assert_held(&rdev->mtx);
>         ASSERT_WDEV_LOCK(wdev);
>
>         cfg80211_pmsr_wdev_down(wdev);
> @@ -1234,6 +1254,9 @@ void cfg80211_init_wdev(struct cfg80211_registered_device *rdev,
>         spin_lock_init(&wdev->pmsr_lock);
>         INIT_WORK(&wdev->pmsr_free_wk, cfg80211_pmsr_free_wk);
>
> +       ASSERT_RTNL();
> +       lockdep_assert_held(&rdev->mtx);
> +
>         /*
>          * We get here also when the interface changes network namespaces,
>          * as it's registered into the new one, but we don't want it to
> @@ -1269,6 +1292,7 @@ static int cfg80211_netdev_notifier_call(struct notifier_block *nb,
>                 SET_NETDEV_DEVTYPE(dev, &wiphy_type);
>                 break;
>         case NETDEV_REGISTER:
> +               lockdep_assert_held(&rdev->mtx);
>                 /*
>                  * NB: cannot take rdev->mtx here because this may be
>                  * called within code protected by it when interfaces
> @@ -1305,9 +1329,12 @@ static int cfg80211_netdev_notifier_call(struct notifier_block *nb,
>                 cfg80211_init_wdev(rdev, wdev);
>                 break;
>         case NETDEV_GOING_DOWN:
> +               mutex_lock(&rdev->mtx);
>                 cfg80211_leave(rdev, wdev);
> +               mutex_unlock(&rdev->mtx);
>                 break;
>         case NETDEV_DOWN:
> +               mutex_lock(&rdev->mtx);
>                 cfg80211_update_iface_num(rdev, wdev->iftype, -1);
>                 if (rdev->scan_req && rdev->scan_req->wdev == wdev) {
>                         if (WARN_ON(!rdev->scan_req->notified))
> @@ -1322,9 +1349,11 @@ static int cfg80211_netdev_notifier_call(struct notifier_block *nb,
>                 }
>
>                 rdev->opencount--;
> +               mutex_unlock(&rdev->mtx);
>                 wake_up(&rdev->dev_wait);
>                 break;
>         case NETDEV_UP:
> +               mutex_lock(&rdev->mtx);
>                 cfg80211_update_iface_num(rdev, wdev->iftype, 1);
>                 wdev_lock(wdev);
>                 switch (wdev->iftype) {
> @@ -1371,8 +1400,10 @@ static int cfg80211_netdev_notifier_call(struct notifier_block *nb,
>                         /* assume this means it's off */
>                         wdev->ps = false;
>                 }
> +               mutex_unlock(&rdev->mtx);
>                 break;
>         case NETDEV_UNREGISTER:
> +               lockdep_assert_held(&rdev->mtx);
>                 /*
>                  * It is possible to get NETDEV_UNREGISTER
>                  * multiple times. To detect that, check
> diff --git a/net/wireless/core.h b/net/wireless/core.h
> index 77556c58d9ac..9ad586fd7939 100644
> --- a/net/wireless/core.h
> +++ b/net/wireless/core.h
> @@ -25,6 +25,8 @@ struct cfg80211_registered_device {
>         const struct cfg80211_ops *ops;
>         struct list_head list;
>
> +       struct mutex mtx;
> +
>         /* rfkill support */
>         struct rfkill_ops rfkill_ops;
>         struct rfkill *rfkill;
> @@ -231,7 +233,7 @@ static inline void wdev_unlock(struct wireless_dev *wdev)
>
>  static inline bool cfg80211_has_monitors_only(struct cfg80211_registered_device *rdev)
>  {
> -       ASSERT_RTNL();
> +       lockdep_assert_held(&rdev->mtx);
>
>         return rdev->num_running_ifaces == rdev->num_running_monitor_ifaces &&
>                rdev->num_running_ifaces > 0;
> diff --git a/net/wireless/ibss.c b/net/wireless/ibss.c
> index d1743e6abc34..e8d9162f9010 100644
> --- a/net/wireless/ibss.c
> +++ b/net/wireless/ibss.c
> @@ -92,7 +92,7 @@ int __cfg80211_join_ibss(struct cfg80211_registered_device *rdev,
>         struct wireless_dev *wdev = dev->ieee80211_ptr;
>         int err;
>
> -       ASSERT_RTNL();
> +       lockdep_assert_held(&rdev->mtx);
>         ASSERT_WDEV_LOCK(wdev);
>
>         if (wdev->ssid_len)
> diff --git a/net/wireless/mlme.c b/net/wireless/mlme.c
> index f9462010575f..808821d521f9 100644
> --- a/net/wireless/mlme.c
> +++ b/net/wireless/mlme.c
> @@ -433,7 +433,7 @@ cfg80211_process_mlme_unregistrations(struct cfg80211_registered_device *rdev)
>  {
>         struct cfg80211_mgmt_registration *reg;
>
> -       ASSERT_RTNL();
> +       lockdep_assert_held(&rdev->mtx);
>
>         spin_lock_bh(&rdev->mlme_unreg_lock);
>         while ((reg = list_first_entry_or_null(&rdev->mlme_unreg,
> @@ -463,9 +463,9 @@ void cfg80211_mlme_unreg_wk(struct work_struct *wk)
>         rdev = container_of(wk, struct cfg80211_registered_device,
>                             mlme_unreg_wk);
>
> -       rtnl_lock();
> +       mutex_lock(&rdev->mtx);
>         cfg80211_process_mlme_unregistrations(rdev);
> -       rtnl_unlock();
> +       mutex_unlock(&rdev->mtx);
>  }
>
>  int cfg80211_mlme_register_mgmt(struct wireless_dev *wdev, u32 snd_portid,
> diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
> index 1a107f29016b..b79ab55f1f53 100644
> --- a/net/wireless/nl80211.c
> +++ b/net/wireless/nl80211.c
> @@ -63,9 +63,9 @@ static const struct genl_multicast_group nl80211_mcgrps[] = {
>
>  /* returns ERR_PTR values */
>  static struct wireless_dev *
> -__cfg80211_wdev_from_attrs(struct net *netns, struct nlattr **attrs)
> +__cfg80211_wdev_from_attrs(struct cfg80211_registered_device *rdev,
> +                          struct net *netns, struct nlattr **attrs)
>  {
> -       struct cfg80211_registered_device *rdev;
>         struct wireless_dev *result = NULL;
>         bool have_ifidx = attrs[NL80211_ATTR_IFINDEX];
>         bool have_wdev_id = attrs[NL80211_ATTR_WDEV];
> @@ -73,8 +73,6 @@ __cfg80211_wdev_from_attrs(struct net *netns, struct nlattr **attrs)
>         int wiphy_idx = -1;
>         int ifidx = -1;
>
> -       ASSERT_RTNL();
> -
>         if (!have_ifidx && !have_wdev_id)
>                 return ERR_PTR(-EINVAL);
>
> @@ -85,6 +83,28 @@ __cfg80211_wdev_from_attrs(struct net *netns, struct nlattr **attrs)
>                 wiphy_idx = wdev_id >> 32;
>         }
>
> +       if (rdev) {
> +               struct wireless_dev *wdev;
> +
> +               lockdep_assert_held(&rdev->mtx);
> +
> +               list_for_each_entry(wdev, &rdev->wiphy.wdev_list, list) {
> +                       if (have_ifidx && wdev->netdev &&
> +                           wdev->netdev->ifindex == ifidx) {
> +                               result = wdev;
> +                               break;
> +                       }
> +                       if (have_wdev_id && wdev->identifier == (u32)wdev_id) {
> +                               result = wdev;
> +                               break;
> +                       }
> +               }
> +
> +               return result ?: ERR_PTR(-ENODEV);
> +       }
> +
> +       ASSERT_RTNL();
> +
>         list_for_each_entry(rdev, &cfg80211_rdev_list, list) {
>                 struct wireless_dev *wdev;
>
> @@ -775,22 +795,31 @@ int nl80211_prepare_wdev_dump(struct netlink_callback *cb,
>                         return err;
>                 }
>
> -               *wdev = __cfg80211_wdev_from_attrs(sock_net(cb->skb->sk),
> +               rtnl_lock();
> +               *wdev = __cfg80211_wdev_from_attrs(NULL, sock_net(cb->skb->sk),
>                                                    attrbuf);
>                 kfree(attrbuf);
> -               if (IS_ERR(*wdev))
> +               if (IS_ERR(*wdev)) {
> +                       rtnl_unlock();
>                         return PTR_ERR(*wdev);
> +               }
>                 *rdev = wiphy_to_rdev((*wdev)->wiphy);
> +               mutex_lock(&(*rdev)->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;
>         } else {
>                 /* subtract the 1 again here */
> -               struct wiphy *wiphy = wiphy_idx_to_wiphy(cb->args[0] - 1);
> +               struct wiphy *wiphy;
>                 struct wireless_dev *tmp;
>
> -               if (!wiphy)
> +               rtnl_lock();
> +               wiphy = wiphy_idx_to_wiphy(cb->args[0] - 1);
> +               if (!wiphy) {
> +                       rtnl_unlock();
>                         return -ENODEV;
> +               }
>                 *rdev = wiphy_to_rdev(wiphy);
>                 *wdev = NULL;
>
> @@ -801,8 +830,12 @@ int nl80211_prepare_wdev_dump(struct netlink_callback *cb,
>                         }
>                 }
>
> -               if (!*wdev)
> +               if (!*wdev) {
> +                       rtnl_unlock();
>                         return -ENODEV;
> +               }
> +               mutex_lock(&(*rdev)->mtx);
> +               rtnl_unlock();
>         }
>
>         return 0;
> @@ -2791,7 +2824,7 @@ static int nl80211_set_wds_peer(struct sk_buff *skb, struct genl_info *info)
>
>  static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info)
>  {
> -       struct cfg80211_registered_device *rdev;
> +       struct cfg80211_registered_device *rdev = NULL;
>         struct net_device *netdev = NULL;
>         struct wireless_dev *wdev;
>         int result = 0, rem_txq_params = 0;
> @@ -2802,8 +2835,7 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info)
>         u8 coverage_class = 0;
>         u32 txq_limit = 0, txq_memory_limit = 0, txq_quantum = 0;
>
> -       ASSERT_RTNL();
> -
> +       rtnl_lock();
>         /*
>          * Try to find the wiphy and netdev. Normally this
>          * function shouldn't need the netdev, but this is
> @@ -2827,14 +2859,20 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info)
>         if (!netdev) {
>                 rdev = __cfg80211_rdev_from_attrs(genl_info_net(info),
>                                                   info->attrs);
> -               if (IS_ERR(rdev))
> +               if (IS_ERR(rdev)) {
> +                       rtnl_unlock();
>                         return PTR_ERR(rdev);
> +               }
>                 wdev = NULL;
>                 netdev = NULL;
>                 result = 0;
>         } else
>                 wdev = netdev->ieee80211_ptr;
>
> +       if (rdev)
> +               mutex_lock(&rdev->mtx);
> +       rtnl_unlock();
> +
>         /*
>          * end workaround code, by now the rdev is available
>          * and locked, and wdev may or may not be NULL.
> @@ -2844,6 +2882,10 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info)
>                 result = cfg80211_dev_rename(
>                         rdev, nla_data(info->attrs[NL80211_ATTR_WIPHY_NAME]));
>
> +       // FIXME - push this through to all the error paths below
> +       if (rdev)
> +               mutex_unlock(&rdev->mtx);
> +
>         if (result)
>                 return result;
>
> @@ -3607,6 +3649,17 @@ static int nl80211_del_interface(struct sk_buff *skb, struct genl_info *info)
>         if (!rdev->ops->del_virtual_intf)
>                 return -EOPNOTSUPP;
>
> +       /*
> +        * We hold RTNL, so this is safe, without RTNL opencount cannot
> +        * reach 0, and thus the rdev cannot be deleted.
> +        *
> +        * We need to do it for the dev_close(), since that will call
> +        * the netdev notifiers, and we need to acquire the mutex there
> +        * but don't know if we get there from here or from some other
> +        * place (e.g. "ip link set ... down").
> +        */
> +       mutex_unlock(&rdev->mtx);
> +
>         /*
>          * If we remove a wireless device without a netdev then clear
>          * user_ptr[1] so that nl80211_post_doit won't dereference it
> @@ -3616,6 +3669,10 @@ static int nl80211_del_interface(struct sk_buff *skb, struct genl_info *info)
>          */
>         if (!wdev->netdev)
>                 info->user_ptr[1] = NULL;
> +       else
> +               dev_close(wdev->netdev);
> +
> +       mutex_lock(&rdev->mtx);
>
>         return rdev_del_virtual_intf(rdev, wdev);
>  }
> @@ -5216,10 +5273,9 @@ static int nl80211_dump_station(struct sk_buff *skb,
>         int sta_idx = cb->args[2];
>         int err;
>
> -       rtnl_lock();
>         err = nl80211_prepare_wdev_dump(cb, &rdev, &wdev);
>         if (err)
> -               goto out_err;
> +               return err;
>
>         if (!wdev->netdev) {
>                 err = -EINVAL;
> @@ -5254,7 +5310,7 @@ static int nl80211_dump_station(struct sk_buff *skb,
>         cb->args[2] = sta_idx;
>         err = skb->len;
>   out_err:
> -       rtnl_unlock();
> +       mutex_unlock(&rdev->mtx);
>
>         return err;
>  }
> @@ -6099,10 +6155,9 @@ static int nl80211_dump_mpath(struct sk_buff *skb,
>         int path_idx = cb->args[2];
>         int err;
>
> -       rtnl_lock();
>         err = nl80211_prepare_wdev_dump(cb, &rdev, &wdev);
>         if (err)
> -               goto out_err;
> +               return err;
>
>         if (!rdev->ops->dump_mpath) {
>                 err = -EOPNOTSUPP;
> @@ -6135,7 +6190,7 @@ static int nl80211_dump_mpath(struct sk_buff *skb,
>         cb->args[2] = path_idx;
>         err = skb->len;
>   out_err:
> -       rtnl_unlock();
> +       mutex_unlock(&rdev->mtx);
>         return err;
>  }
>
> @@ -6295,10 +6350,9 @@ static int nl80211_dump_mpp(struct sk_buff *skb,
>         int path_idx = cb->args[2];
>         int err;
>
> -       rtnl_lock();
>         err = nl80211_prepare_wdev_dump(cb, &rdev, &wdev);
>         if (err)
> -               goto out_err;
> +               return err;
>
>         if (!rdev->ops->dump_mpp) {
>                 err = -EOPNOTSUPP;
> @@ -6331,7 +6385,7 @@ static int nl80211_dump_mpp(struct sk_buff *skb,
>         cb->args[2] = path_idx;
>         err = skb->len;
>   out_err:
> -       rtnl_unlock();
> +       mutex_unlock(&rdev->mtx);
>         return err;
>  }
>
> @@ -6939,12 +6993,15 @@ static int nl80211_get_reg_do(struct sk_buff *skb, struct genl_info *info)
>         if (!hdr)
>                 goto put_failure;
>
> +       rtnl_lock();
> +
>         if (info->attrs[NL80211_ATTR_WIPHY]) {
>                 bool self_managed;
>
>                 rdev = cfg80211_get_dev_from_info(genl_info_net(info), info);
>                 if (IS_ERR(rdev)) {
>                         nlmsg_free(msg);
> +                       rtnl_unlock();
>                         return PTR_ERR(rdev);
>                 }
>
> @@ -6953,9 +7010,11 @@ static int nl80211_get_reg_do(struct sk_buff *skb, struct genl_info *info)
>                                REGULATORY_WIPHY_SELF_MANAGED;
>                 regdom = get_wiphy_regdom(wiphy);
>
> +
>                 /* a self-managed-reg device must have a private regdom */
>                 if (WARN_ON(!regdom && self_managed)) {
>                         nlmsg_free(msg);
> +                       rtnl_unlock();
>                         return -EINVAL;
>                 }
>
> @@ -6980,11 +7039,13 @@ static int nl80211_get_reg_do(struct sk_buff *skb, struct genl_info *info)
>         rcu_read_unlock();
>
>         genlmsg_end(msg, hdr);
> +       rtnl_unlock();
>         return genlmsg_reply(msg, info);
>
>  nla_put_failure_rcu:
>         rcu_read_unlock();
>  nla_put_failure:
> +       rtnl_unlock();
>  put_failure:
>         nlmsg_free(msg);
>         return -EMSGSIZE;
> @@ -7147,12 +7208,17 @@ static int nl80211_set_reg(struct sk_buff *skb, struct genl_info *info)
>                         return -EINVAL;
>         }
>
> -       if (!reg_is_valid_request(alpha2))
> -               return -EINVAL;
> +       rtnl_lock();
> +       if (!reg_is_valid_request(alpha2)) {
> +               r = -EINVAL;
> +               goto out;
> +       }
>
>         rd = kzalloc(struct_size(rd, reg_rules, num_rules), GFP_KERNEL);
> -       if (!rd)
> -               return -ENOMEM;
> +       if (!rd) {
> +               r = -ENOMEM;
> +               goto out;
> +       }
>
>         rd->n_reg_rules = num_rules;
>         rd->alpha2[0] = alpha2[0];
> @@ -7184,10 +7250,13 @@ static int nl80211_set_reg(struct sk_buff *skb, struct genl_info *info)
>                 }
>         }
>
> +       r = set_regdom(rd, REGD_SOURCE_CRDA);
>         /* set_regdom takes ownership of rd */
> -       return set_regdom(rd, REGD_SOURCE_CRDA);
> +       rd = NULL;
>   bad_reg:
>         kfree(rd);
> + out:
> +       rtnl_unlock();
>         return r;
>  }
>  #endif /* CONFIG_CFG80211_CRDA_SUPPORT */
> @@ -8367,10 +8436,7 @@ static int nl80211_channel_switch(struct sk_buff *skb, struct genl_info *info)
>         struct net_device *dev = info->user_ptr[1];
>         struct wireless_dev *wdev = dev->ieee80211_ptr;
>         struct cfg80211_csa_settings params;
> -       /* csa_attrs is defined static to avoid waste of stack size - this
> -        * function is called under RTNL lock, so this should not be a problem.
> -        */
> -       static struct nlattr *csa_attrs[NL80211_ATTR_MAX+1];
> +       struct nlattr **csa_attrs;
>         int err;
>         bool need_new_beacon = false;
>         bool need_handle_dfs_flag = true;
> @@ -8435,28 +8501,39 @@ static int nl80211_channel_switch(struct sk_buff *skb, struct genl_info *info)
>         if (err)
>                 return err;
>
> +       csa_attrs = kcalloc(NL80211_ATTR_MAX + 1, sizeof(*csa_attrs),
> +                           GFP_KERNEL);
> +       if (!csa_attrs)
> +               return -ENOMEM;
> +
>         err = nla_parse_nested_deprecated(csa_attrs, NL80211_ATTR_MAX,
>                                           info->attrs[NL80211_ATTR_CSA_IES],
>                                           nl80211_policy, info->extack);
>         if (err)
> -               return err;
> +               goto free;
>
>         err = nl80211_parse_beacon(rdev, csa_attrs, &params.beacon_csa);
>         if (err)
> -               return err;
> +               goto free;
>
> -       if (!csa_attrs[NL80211_ATTR_CSA_C_OFF_BEACON])
> -               return -EINVAL;
> +       if (!csa_attrs[NL80211_ATTR_CSA_C_OFF_BEACON]) {
> +               err = -EINVAL;
> +               goto free;
> +       }
>
>         len = nla_len(csa_attrs[NL80211_ATTR_CSA_C_OFF_BEACON]);
> -       if (!len || (len % sizeof(u16)))
> -               return -EINVAL;
> +       if (!len || (len % sizeof(u16))) {
> +               err = -EINVAL;
> +               goto free;
> +       }
>
>         params.n_counter_offsets_beacon = len / sizeof(u16);
>         if (rdev->wiphy.max_num_csa_counters &&
>             (params.n_counter_offsets_beacon >
> -            rdev->wiphy.max_num_csa_counters))
> -               return -EINVAL;
> +            rdev->wiphy.max_num_csa_counters)) {
> +               err = -EINVAL;
> +               goto free;
> +       }
>
>         params.counter_offsets_beacon =
>                 nla_data(csa_attrs[NL80211_ATTR_CSA_C_OFF_BEACON]);
> @@ -8465,23 +8542,31 @@ static int nl80211_channel_switch(struct sk_buff *skb, struct genl_info *info)
>         for (i = 0; i < params.n_counter_offsets_beacon; i++) {
>                 u16 offset = params.counter_offsets_beacon[i];
>
> -               if (offset >= params.beacon_csa.tail_len)
> -                       return -EINVAL;
> +               if (offset >= params.beacon_csa.tail_len) {
> +                       err = -EINVAL;
> +                       goto free;
> +               }
>
> -               if (params.beacon_csa.tail[offset] != params.count)
> -                       return -EINVAL;
> +               if (params.beacon_csa.tail[offset] != params.count) {
> +                       err = -EINVAL;
> +                       goto free;
> +               }
>         }
>
>         if (csa_attrs[NL80211_ATTR_CSA_C_OFF_PRESP]) {
>                 len = nla_len(csa_attrs[NL80211_ATTR_CSA_C_OFF_PRESP]);
> -               if (!len || (len % sizeof(u16)))
> -                       return -EINVAL;
> +               if (!len || (len % sizeof(u16))) {
> +                       err = -EINVAL;
> +                       goto free;
> +               }
>
>                 params.n_counter_offsets_presp = len / sizeof(u16);
>                 if (rdev->wiphy.max_num_csa_counters &&
>                     (params.n_counter_offsets_presp >
> -                    rdev->wiphy.max_num_csa_counters))
> -                       return -EINVAL;
> +                    rdev->wiphy.max_num_csa_counters)) {
> +                       err = -EINVAL;
> +                       goto free;
> +               }
>
>                 params.counter_offsets_presp =
>                         nla_data(csa_attrs[NL80211_ATTR_CSA_C_OFF_PRESP]);
> @@ -8490,35 +8575,42 @@ static int nl80211_channel_switch(struct sk_buff *skb, struct genl_info *info)
>                 for (i = 0; i < params.n_counter_offsets_presp; i++) {
>                         u16 offset = params.counter_offsets_presp[i];
>
> -                       if (offset >= params.beacon_csa.probe_resp_len)
> -                               return -EINVAL;
> +                       if (offset >= params.beacon_csa.probe_resp_len) {
> +                               err = -EINVAL;
> +                               goto free;
> +                       }
>
>                         if (params.beacon_csa.probe_resp[offset] !=
> -                           params.count)
> -                               return -EINVAL;
> +                           params.count) {
> +                               err = -EINVAL;
> +                               goto free;
> +                       }
>                 }
>         }
>
>  skip_beacons:
>         err = nl80211_parse_chandef(rdev, info, &params.chandef);
>         if (err)
> -               return err;
> +               goto free;
>
>         if (!cfg80211_reg_can_beacon_relax(&rdev->wiphy, &params.chandef,
> -                                          wdev->iftype))
> -               return -EINVAL;
> +                                          wdev->iftype)) {
> +               err = -EINVAL;
> +               goto free;
> +       }
>
>         err = cfg80211_chandef_dfs_required(wdev->wiphy,
>                                             &params.chandef,
>                                             wdev->iftype);
>         if (err < 0)
> -               return err;
> +               goto free;
>
>         if (err > 0) {
>                 params.radar_required = true;
>                 if (need_handle_dfs_flag &&
>                     !nla_get_flag(info->attrs[NL80211_ATTR_HANDLE_DFS])) {
> -                       return -EINVAL;
> +                       err = -EINVAL;
> +                       goto free;
>                 }
>         }
>
> @@ -8529,6 +8621,8 @@ static int nl80211_channel_switch(struct sk_buff *skb, struct genl_info *info)
>         err = rdev_channel_switch(rdev, dev, &params);
>         wdev_unlock(wdev);
>
> +free:
> +       kfree(csa_attrs);
>         return err;
>  }
>
> @@ -8677,12 +8771,9 @@ static int nl80211_dump_scan(struct sk_buff *skb, struct netlink_callback *cb)
>         int start = cb->args[2], idx = 0;
>         int err;
>
> -       rtnl_lock();
>         err = nl80211_prepare_wdev_dump(cb, &rdev, &wdev);
> -       if (err) {
> -               rtnl_unlock();
> +       if (err)
>                 return err;
> -       }
>
>         wdev_lock(wdev);
>         spin_lock_bh(&rdev->bss_lock);
> @@ -8713,7 +8804,7 @@ static int nl80211_dump_scan(struct sk_buff *skb, struct netlink_callback *cb)
>         wdev_unlock(wdev);
>
>         cb->args[2] = idx;
> -       rtnl_unlock();
> +       mutex_unlock(&rdev->mtx);
>
>         return skb->len;
>  }
> @@ -8802,10 +8893,11 @@ static int nl80211_dump_survey(struct sk_buff *skb, struct netlink_callback *cb)
>         if (!attrbuf)
>                 return -ENOMEM;
>
> -       rtnl_lock();
>         res = nl80211_prepare_wdev_dump(cb, &rdev, &wdev);
> -       if (res)
> -               goto out_err;
> +       if (res) {
> +               kfree(attrbuf);
> +               return res;
> +       }
>
>         /* prepare_wdev_dump parsed the attributes */
>         radio_stats = attrbuf[NL80211_ATTR_SURVEY_RADIO_STATS];
> @@ -8847,7 +8939,7 @@ static int nl80211_dump_survey(struct sk_buff *skb, struct netlink_callback *cb)
>         res = skb->len;
>   out_err:
>         kfree(attrbuf);
> -       rtnl_unlock();
> +       mutex_unlock(&rdev->mtx);
>         return res;
>  }
>
> @@ -9674,10 +9766,14 @@ EXPORT_SYMBOL(__cfg80211_send_event_skb);
>  static int nl80211_testmode_do(struct sk_buff *skb, struct genl_info *info)
>  {
>         struct cfg80211_registered_device *rdev = info->user_ptr[0];
> -       struct wireless_dev *wdev =
> -               __cfg80211_wdev_from_attrs(genl_info_net(info), info->attrs);
> +       struct wireless_dev *wdev;
>         int err;
>
> +       lockdep_assert_held(&rdev->mtx);
> +
> +       wdev = __cfg80211_wdev_from_attrs(rdev, genl_info_net(info),
> +                                         info->attrs);
> +
>         if (!rdev->ops->testmode_cmd)
>                 return -EOPNOTSUPP;
>
> @@ -12817,7 +12913,8 @@ static int nl80211_vendor_cmd(struct sk_buff *skb, struct genl_info *info)
>  {
>         struct cfg80211_registered_device *rdev = info->user_ptr[0];
>         struct wireless_dev *wdev =
> -               __cfg80211_wdev_from_attrs(genl_info_net(info), info->attrs);
> +               __cfg80211_wdev_from_attrs(rdev, genl_info_net(info),
> +                                          info->attrs);
>         int i, err;
>         u32 vid, subcmd;
>
> @@ -12941,7 +13038,7 @@ static int nl80211_prepare_vendor_dump(struct sk_buff *skb,
>                 goto out;
>         }
>
> -       *wdev = __cfg80211_wdev_from_attrs(sock_net(skb->sk), attrbuf);
> +       *wdev = __cfg80211_wdev_from_attrs(NULL, sock_net(skb->sk), attrbuf);
>         if (IS_ERR(*wdev))
>                 *wdev = NULL;
>
> @@ -13723,31 +13820,24 @@ static int nl80211_probe_mesh_link(struct sk_buff *skb, struct genl_info *info)
>  static int nl80211_pre_doit(const struct genl_ops *ops, struct sk_buff *skb,
>                             struct genl_info *info)
>  {
> -       struct cfg80211_registered_device *rdev;
> +       struct cfg80211_registered_device *rdev = NULL;
>         struct wireless_dev *wdev;
>         struct net_device *dev;
> -       bool rtnl = ops->internal_flags & NL80211_FLAG_NEED_RTNL;
> -
> -       if (rtnl)
> -               rtnl_lock();
>
> +       rtnl_lock();
>         if (ops->internal_flags & NL80211_FLAG_NEED_WIPHY) {
>                 rdev = cfg80211_get_dev_from_info(genl_info_net(info), info);
>                 if (IS_ERR(rdev)) {
> -                       if (rtnl)
> -                               rtnl_unlock();
> +                       rtnl_unlock();
>                         return PTR_ERR(rdev);
>                 }
>                 info->user_ptr[0] = rdev;
>         } else if (ops->internal_flags & NL80211_FLAG_NEED_NETDEV ||
>                    ops->internal_flags & NL80211_FLAG_NEED_WDEV) {
> -               ASSERT_RTNL();
> -
> -               wdev = __cfg80211_wdev_from_attrs(genl_info_net(info),
> +               wdev = __cfg80211_wdev_from_attrs(NULL, genl_info_net(info),
>                                                   info->attrs);
>                 if (IS_ERR(wdev)) {
> -                       if (rtnl)
> -                               rtnl_unlock();
> +                       rtnl_unlock();
>                         return PTR_ERR(wdev);
>                 }
>
> @@ -13756,8 +13846,7 @@ static int nl80211_pre_doit(const struct genl_ops *ops, struct sk_buff *skb,
>
>                 if (ops->internal_flags & NL80211_FLAG_NEED_NETDEV) {
>                         if (!dev) {
> -                               if (rtnl)
> -                                       rtnl_unlock();
> +                               rtnl_unlock();
>                                 return -EINVAL;
>                         }
>
> @@ -13768,8 +13857,7 @@ static int nl80211_pre_doit(const struct genl_ops *ops, struct sk_buff *skb,
>
>                 if (ops->internal_flags & NL80211_FLAG_CHECK_NETDEV_UP &&
>                     !wdev_running(wdev)) {
> -                       if (rtnl)
> -                               rtnl_unlock();
> +                       rtnl_unlock();
>                         return -ENETDOWN;
>                 }
>
> @@ -13779,6 +13867,11 @@ static int nl80211_pre_doit(const struct genl_ops *ops, struct sk_buff *skb,
>                 info->user_ptr[0] = rdev;
>         }
>
> +       if (rdev)
> +               mutex_lock(&rdev->mtx);
> +       if (!(ops->internal_flags & NL80211_FLAG_NEED_RTNL))
> +               rtnl_unlock();
> +
>         return 0;
>  }
>
> @@ -13796,6 +13889,12 @@ static void nl80211_post_doit(const struct genl_ops *ops, struct sk_buff *skb,
>                 }
>         }
>
> +       if (info->user_ptr[0]) {
> +               struct cfg80211_registered_device *rdev = info->user_ptr[0];
> +
> +               mutex_unlock(&rdev->mtx);
> +       }
> +
>         if (ops->internal_flags & NL80211_FLAG_NEED_RTNL)
>                 rtnl_unlock();
>
> @@ -13819,15 +13918,13 @@ static const struct genl_ops nl80211_ops[] = {
>                 .dumpit = nl80211_dump_wiphy,
>                 .done = nl80211_dump_wiphy_done,
>                 /* can be retrieved by unprivileged users */
> -               .internal_flags = NL80211_FLAG_NEED_WIPHY |
> -                                 NL80211_FLAG_NEED_RTNL,
> +               .internal_flags = NL80211_FLAG_NEED_WIPHY,
>         },
>         {
>                 .cmd = NL80211_CMD_SET_WIPHY,
>                 .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
>                 .doit = nl80211_set_wiphy,
>                 .flags = GENL_UNS_ADMIN_PERM,
> -               .internal_flags = NL80211_FLAG_NEED_RTNL,
>         },
>         {
>                 .cmd = NL80211_CMD_GET_INTERFACE,
> @@ -13835,8 +13932,7 @@ static const struct genl_ops nl80211_ops[] = {
>                 .doit = nl80211_get_interface,
>                 .dumpit = nl80211_dump_interface,
>                 /* can be retrieved by unprivileged users */
> -               .internal_flags = NL80211_FLAG_NEED_WDEV |
> -                                 NL80211_FLAG_NEED_RTNL,
> +               .internal_flags = NL80211_FLAG_NEED_WDEV,
>         },
>         {
>                 .cmd = NL80211_CMD_SET_INTERFACE,
> @@ -13867,8 +13963,7 @@ static const struct genl_ops nl80211_ops[] = {
>                 .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
>                 .doit = nl80211_get_key,
>                 .flags = GENL_UNS_ADMIN_PERM,
> -               .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
> -                                 NL80211_FLAG_NEED_RTNL,
> +               .internal_flags = NL80211_FLAG_NEED_NETDEV_UP,
>         },
>         {
>                 .cmd = NL80211_CMD_SET_KEY,
> @@ -13876,7 +13971,6 @@ static const struct genl_ops nl80211_ops[] = {
>                 .doit = nl80211_set_key,
>                 .flags = GENL_UNS_ADMIN_PERM,
>                 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
> -                                 NL80211_FLAG_NEED_RTNL |
>                                   NL80211_FLAG_CLEAR_SKB,
>         },
>         {
> @@ -13885,7 +13979,6 @@ static const struct genl_ops nl80211_ops[] = {
>                 .doit = nl80211_new_key,
>                 .flags = GENL_UNS_ADMIN_PERM,
>                 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
> -                                 NL80211_FLAG_NEED_RTNL |
>                                   NL80211_FLAG_CLEAR_SKB,
>         },
>         {
> @@ -13893,64 +13986,56 @@ static const struct genl_ops nl80211_ops[] = {
>                 .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
>                 .doit = nl80211_del_key,
>                 .flags = GENL_UNS_ADMIN_PERM,
> -               .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
> -                                 NL80211_FLAG_NEED_RTNL,
> +               .internal_flags = NL80211_FLAG_NEED_NETDEV_UP,
>         },
>         {
>                 .cmd = NL80211_CMD_SET_BEACON,
>                 .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
>                 .flags = GENL_UNS_ADMIN_PERM,
>                 .doit = nl80211_set_beacon,
> -               .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
> -                                 NL80211_FLAG_NEED_RTNL,
> +               .internal_flags = NL80211_FLAG_NEED_NETDEV_UP,
>         },
>         {
>                 .cmd = NL80211_CMD_START_AP,
>                 .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
>                 .flags = GENL_UNS_ADMIN_PERM,
>                 .doit = nl80211_start_ap,
> -               .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
> -                                 NL80211_FLAG_NEED_RTNL,
> +               .internal_flags = NL80211_FLAG_NEED_NETDEV_UP,
>         },
>         {
>                 .cmd = NL80211_CMD_STOP_AP,
>                 .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
>                 .flags = GENL_UNS_ADMIN_PERM,
>                 .doit = nl80211_stop_ap,
> -               .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
> -                                 NL80211_FLAG_NEED_RTNL,
> +               .internal_flags = NL80211_FLAG_NEED_NETDEV_UP,
>         },
>         {
>                 .cmd = NL80211_CMD_GET_STATION,
>                 .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
>                 .doit = nl80211_get_station,
>                 .dumpit = nl80211_dump_station,
> -               .internal_flags = NL80211_FLAG_NEED_NETDEV |
> -                                 NL80211_FLAG_NEED_RTNL,
> +               .internal_flags = NL80211_FLAG_NEED_NETDEV,
>         },
>         {
>                 .cmd = NL80211_CMD_SET_STATION,
>                 .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
>                 .doit = nl80211_set_station,
>                 .flags = GENL_UNS_ADMIN_PERM,
> -               .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
> -                                 NL80211_FLAG_NEED_RTNL,
> +               .internal_flags = NL80211_FLAG_NEED_NETDEV_UP,
>         },
>         {
>                 .cmd = NL80211_CMD_NEW_STATION,
>                 .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
>                 .doit = nl80211_new_station,
>                 .flags = GENL_UNS_ADMIN_PERM,
> -               .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
> -                                 NL80211_FLAG_NEED_RTNL,
> +               .internal_flags = NL80211_FLAG_NEED_NETDEV_UP,
>         },
>         {
>                 .cmd = NL80211_CMD_DEL_STATION,
>                 .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
>                 .doit = nl80211_del_station,
>                 .flags = GENL_UNS_ADMIN_PERM,
> -               .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
> -                                 NL80211_FLAG_NEED_RTNL,
> +               .internal_flags = NL80211_FLAG_NEED_NETDEV_UP,
>         },
>         {
>                 .cmd = NL80211_CMD_GET_MPATH,
> @@ -13958,8 +14043,7 @@ static const struct genl_ops nl80211_ops[] = {
>                 .doit = nl80211_get_mpath,
>                 .dumpit = nl80211_dump_mpath,
>                 .flags = GENL_UNS_ADMIN_PERM,
> -               .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
> -                                 NL80211_FLAG_NEED_RTNL,
> +               .internal_flags = NL80211_FLAG_NEED_NETDEV_UP,
>         },
>         {
>                 .cmd = NL80211_CMD_GET_MPP,
> @@ -13967,47 +14051,42 @@ static const struct genl_ops nl80211_ops[] = {
>                 .doit = nl80211_get_mpp,
>                 .dumpit = nl80211_dump_mpp,
>                 .flags = GENL_UNS_ADMIN_PERM,
> -               .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
> -                                 NL80211_FLAG_NEED_RTNL,
> +               .internal_flags = NL80211_FLAG_NEED_NETDEV_UP,
>         },
>         {
>                 .cmd = NL80211_CMD_SET_MPATH,
>                 .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
>                 .doit = nl80211_set_mpath,
>                 .flags = GENL_UNS_ADMIN_PERM,
> -               .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
> -                                 NL80211_FLAG_NEED_RTNL,
> +               .internal_flags = NL80211_FLAG_NEED_NETDEV_UP,
>         },
>         {
>                 .cmd = NL80211_CMD_NEW_MPATH,
>                 .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
>                 .doit = nl80211_new_mpath,
>                 .flags = GENL_UNS_ADMIN_PERM,
> -               .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
> -                                 NL80211_FLAG_NEED_RTNL,
> +               .internal_flags = NL80211_FLAG_NEED_NETDEV_UP,
>         },
>         {
>                 .cmd = NL80211_CMD_DEL_MPATH,
>                 .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
>                 .doit = nl80211_del_mpath,
>                 .flags = GENL_UNS_ADMIN_PERM,
> -               .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
> -                                 NL80211_FLAG_NEED_RTNL,
> +               .internal_flags = NL80211_FLAG_NEED_NETDEV_UP,
>         },
>         {
>                 .cmd = NL80211_CMD_SET_BSS,
>                 .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
>                 .doit = nl80211_set_bss,
>                 .flags = GENL_UNS_ADMIN_PERM,
> -               .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
> -                                 NL80211_FLAG_NEED_RTNL,
> +               .internal_flags = NL80211_FLAG_NEED_NETDEV_UP,
>         },
>         {
>                 .cmd = NL80211_CMD_GET_REG,
>                 .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
>                 .doit = nl80211_get_reg_do,
>                 .dumpit = nl80211_get_reg_dump,
> -               .internal_flags = NL80211_FLAG_NEED_RTNL,
> +               .internal_flags = 0,
>                 /* can be retrieved by unprivileged users */
>         },
>  #ifdef CONFIG_CFG80211_CRDA_SUPPORT
> @@ -14016,7 +14095,7 @@ static const struct genl_ops nl80211_ops[] = {
>                 .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
>                 .doit = nl80211_set_reg,
>                 .flags = GENL_ADMIN_PERM,
> -               .internal_flags = NL80211_FLAG_NEED_RTNL,
> +               .internal_flags = 0,
>         },
>  #endif
>         {
> @@ -14036,32 +14115,28 @@ static const struct genl_ops nl80211_ops[] = {
>                 .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
>                 .doit = nl80211_get_mesh_config,
>                 /* can be retrieved by unprivileged users */
> -               .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
> -                                 NL80211_FLAG_NEED_RTNL,
> +               .internal_flags = NL80211_FLAG_NEED_NETDEV_UP,
>         },
>         {
>                 .cmd = NL80211_CMD_SET_MESH_CONFIG,
>                 .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
>                 .doit = nl80211_update_mesh_config,
>                 .flags = GENL_UNS_ADMIN_PERM,
> -               .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
> -                                 NL80211_FLAG_NEED_RTNL,
> +               .internal_flags = NL80211_FLAG_NEED_NETDEV_UP,
>         },
>         {
>                 .cmd = NL80211_CMD_TRIGGER_SCAN,
>                 .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
>                 .doit = nl80211_trigger_scan,
>                 .flags = GENL_UNS_ADMIN_PERM,
> -               .internal_flags = NL80211_FLAG_NEED_WDEV_UP |
> -                                 NL80211_FLAG_NEED_RTNL,
> +               .internal_flags = NL80211_FLAG_NEED_WDEV_UP,
>         },
>         {
>                 .cmd = NL80211_CMD_ABORT_SCAN,
>                 .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
>                 .doit = nl80211_abort_scan,
>                 .flags = GENL_UNS_ADMIN_PERM,
> -               .internal_flags = NL80211_FLAG_NEED_WDEV_UP |
> -                                 NL80211_FLAG_NEED_RTNL,
> +               .internal_flags = NL80211_FLAG_NEED_WDEV_UP,
>         },
>         {
>                 .cmd = NL80211_CMD_GET_SCAN,
> @@ -14073,16 +14148,14 @@ static const struct genl_ops nl80211_ops[] = {
>                 .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
>                 .doit = nl80211_start_sched_scan,
>                 .flags = GENL_UNS_ADMIN_PERM,
> -               .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
> -                                 NL80211_FLAG_NEED_RTNL,
> +               .internal_flags = NL80211_FLAG_NEED_NETDEV_UP,
>         },
>         {
>                 .cmd = NL80211_CMD_STOP_SCHED_SCAN,
>                 .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
>                 .doit = nl80211_stop_sched_scan,
>                 .flags = GENL_UNS_ADMIN_PERM,
> -               .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
> -                                 NL80211_FLAG_NEED_RTNL,
> +               .internal_flags = NL80211_FLAG_NEED_NETDEV_UP,
>         },
>         {
>                 .cmd = NL80211_CMD_AUTHENTICATE,
> @@ -14090,7 +14163,7 @@ static const struct genl_ops nl80211_ops[] = {
>                 .doit = nl80211_authenticate,
>                 .flags = GENL_UNS_ADMIN_PERM,
>                 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
> -                                 NL80211_FLAG_NEED_RTNL |
> +                                 0 |
>                                   NL80211_FLAG_CLEAR_SKB,
>         },
>         {
> @@ -14099,7 +14172,7 @@ static const struct genl_ops nl80211_ops[] = {
>                 .doit = nl80211_associate,
>                 .flags = GENL_UNS_ADMIN_PERM,
>                 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
> -                                 NL80211_FLAG_NEED_RTNL |
> +                                 0 |
>                                   NL80211_FLAG_CLEAR_SKB,
>         },
>         {
> @@ -14107,32 +14180,28 @@ static const struct genl_ops nl80211_ops[] = {
>                 .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
>                 .doit = nl80211_deauthenticate,
>                 .flags = GENL_UNS_ADMIN_PERM,
> -               .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
> -                                 NL80211_FLAG_NEED_RTNL,
> +               .internal_flags = NL80211_FLAG_NEED_NETDEV_UP,
>         },
>         {
>                 .cmd = NL80211_CMD_DISASSOCIATE,
>                 .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
>                 .doit = nl80211_disassociate,
>                 .flags = GENL_UNS_ADMIN_PERM,
> -               .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
> -                                 NL80211_FLAG_NEED_RTNL,
> +               .internal_flags = NL80211_FLAG_NEED_NETDEV_UP,
>         },
>         {
>                 .cmd = NL80211_CMD_JOIN_IBSS,
>                 .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
>                 .doit = nl80211_join_ibss,
>                 .flags = GENL_UNS_ADMIN_PERM,
> -               .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
> -                                 NL80211_FLAG_NEED_RTNL,
> +               .internal_flags = NL80211_FLAG_NEED_NETDEV_UP,
>         },
>         {
>                 .cmd = NL80211_CMD_LEAVE_IBSS,
>                 .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
>                 .doit = nl80211_leave_ibss,
>                 .flags = GENL_UNS_ADMIN_PERM,
> -               .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
> -                                 NL80211_FLAG_NEED_RTNL,
> +               .internal_flags = NL80211_FLAG_NEED_NETDEV_UP,
>         },
>  #ifdef CONFIG_NL80211_TESTMODE
>         {
> @@ -14141,8 +14210,7 @@ static const struct genl_ops nl80211_ops[] = {
>                 .doit = nl80211_testmode_do,
>                 .dumpit = nl80211_testmode_dump,
>                 .flags = GENL_UNS_ADMIN_PERM,
> -               .internal_flags = NL80211_FLAG_NEED_WIPHY |
> -                                 NL80211_FLAG_NEED_RTNL,
> +               .internal_flags = NL80211_FLAG_NEED_WIPHY,
>         },
>  #endif
>         {
> @@ -14151,7 +14219,7 @@ static const struct genl_ops nl80211_ops[] = {
>                 .doit = nl80211_connect,
>                 .flags = GENL_UNS_ADMIN_PERM,
>                 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
> -                                 NL80211_FLAG_NEED_RTNL |
> +                                 0 |
>                                   NL80211_FLAG_CLEAR_SKB,
>         },
>         {
> @@ -14160,7 +14228,7 @@ static const struct genl_ops nl80211_ops[] = {
>                 .doit = nl80211_update_connect_params,
>                 .flags = GENL_ADMIN_PERM,
>                 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
> -                                 NL80211_FLAG_NEED_RTNL |
> +                                 0 |
>                                   NL80211_FLAG_CLEAR_SKB,
>         },
>         {
> @@ -14168,16 +14236,14 @@ static const struct genl_ops nl80211_ops[] = {
>                 .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
>                 .doit = nl80211_disconnect,
>                 .flags = GENL_UNS_ADMIN_PERM,
> -               .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
> -                                 NL80211_FLAG_NEED_RTNL,
> +               .internal_flags = NL80211_FLAG_NEED_NETDEV_UP,
>         },
>         {
>                 .cmd = NL80211_CMD_SET_WIPHY_NETNS,
>                 .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
>                 .doit = nl80211_wiphy_netns,
>                 .flags = GENL_UNS_ADMIN_PERM,
> -               .internal_flags = NL80211_FLAG_NEED_WIPHY |
> -                                 NL80211_FLAG_NEED_RTNL,
> +               .internal_flags = NL80211_FLAG_NEED_WIPHY,
>         },
>         {
>                 .cmd = NL80211_CMD_GET_SURVEY,
> @@ -14190,7 +14256,7 @@ static const struct genl_ops nl80211_ops[] = {
>                 .doit = nl80211_setdel_pmksa,
>                 .flags = GENL_UNS_ADMIN_PERM,
>                 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
> -                                 NL80211_FLAG_NEED_RTNL |
> +                                 0 |
>                                   NL80211_FLAG_CLEAR_SKB,
>         },
>         {
> @@ -14198,136 +14264,119 @@ static const struct genl_ops nl80211_ops[] = {
>                 .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
>                 .doit = nl80211_setdel_pmksa,
>                 .flags = GENL_UNS_ADMIN_PERM,
> -               .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
> -                                 NL80211_FLAG_NEED_RTNL,
> +               .internal_flags = NL80211_FLAG_NEED_NETDEV_UP,
>         },
>         {
>                 .cmd = NL80211_CMD_FLUSH_PMKSA,
>                 .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
>                 .doit = nl80211_flush_pmksa,
>                 .flags = GENL_UNS_ADMIN_PERM,
> -               .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
> -                                 NL80211_FLAG_NEED_RTNL,
> +               .internal_flags = NL80211_FLAG_NEED_NETDEV_UP,
>         },
>         {
>                 .cmd = NL80211_CMD_REMAIN_ON_CHANNEL,
>                 .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
>                 .doit = nl80211_remain_on_channel,
>                 .flags = GENL_UNS_ADMIN_PERM,
> -               .internal_flags = NL80211_FLAG_NEED_WDEV_UP |
> -                                 NL80211_FLAG_NEED_RTNL,
> +               .internal_flags = NL80211_FLAG_NEED_WDEV_UP,
>         },
>         {
>                 .cmd = NL80211_CMD_CANCEL_REMAIN_ON_CHANNEL,
>                 .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
>                 .doit = nl80211_cancel_remain_on_channel,
>                 .flags = GENL_UNS_ADMIN_PERM,
> -               .internal_flags = NL80211_FLAG_NEED_WDEV_UP |
> -                                 NL80211_FLAG_NEED_RTNL,
> +               .internal_flags = NL80211_FLAG_NEED_WDEV_UP,
>         },
>         {
>                 .cmd = NL80211_CMD_SET_TX_BITRATE_MASK,
>                 .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
>                 .doit = nl80211_set_tx_bitrate_mask,
>                 .flags = GENL_UNS_ADMIN_PERM,
> -               .internal_flags = NL80211_FLAG_NEED_NETDEV |
> -                                 NL80211_FLAG_NEED_RTNL,
> +               .internal_flags = NL80211_FLAG_NEED_NETDEV,
>         },
>         {
>                 .cmd = NL80211_CMD_REGISTER_FRAME,
>                 .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
>                 .doit = nl80211_register_mgmt,
>                 .flags = GENL_UNS_ADMIN_PERM,
> -               .internal_flags = NL80211_FLAG_NEED_WDEV |
> -                                 NL80211_FLAG_NEED_RTNL,
> +               .internal_flags = NL80211_FLAG_NEED_WDEV,
>         },
>         {
>                 .cmd = NL80211_CMD_FRAME,
>                 .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
>                 .doit = nl80211_tx_mgmt,
>                 .flags = GENL_UNS_ADMIN_PERM,
> -               .internal_flags = NL80211_FLAG_NEED_WDEV_UP |
> -                                 NL80211_FLAG_NEED_RTNL,
> +               .internal_flags = NL80211_FLAG_NEED_WDEV_UP,
>         },
>         {
>                 .cmd = NL80211_CMD_FRAME_WAIT_CANCEL,
>                 .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
>                 .doit = nl80211_tx_mgmt_cancel_wait,
>                 .flags = GENL_UNS_ADMIN_PERM,
> -               .internal_flags = NL80211_FLAG_NEED_WDEV_UP |
> -                                 NL80211_FLAG_NEED_RTNL,
> +               .internal_flags = NL80211_FLAG_NEED_WDEV_UP,
>         },
>         {
>                 .cmd = NL80211_CMD_SET_POWER_SAVE,
>                 .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
>                 .doit = nl80211_set_power_save,
>                 .flags = GENL_UNS_ADMIN_PERM,
> -               .internal_flags = NL80211_FLAG_NEED_NETDEV |
> -                                 NL80211_FLAG_NEED_RTNL,
> +               .internal_flags = NL80211_FLAG_NEED_NETDEV,
>         },
>         {
>                 .cmd = NL80211_CMD_GET_POWER_SAVE,
>                 .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
>                 .doit = nl80211_get_power_save,
>                 /* can be retrieved by unprivileged users */
> -               .internal_flags = NL80211_FLAG_NEED_NETDEV |
> -                                 NL80211_FLAG_NEED_RTNL,
> +               .internal_flags = NL80211_FLAG_NEED_NETDEV,
>         },
>         {
>                 .cmd = NL80211_CMD_SET_CQM,
>                 .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
>                 .doit = nl80211_set_cqm,
>                 .flags = GENL_UNS_ADMIN_PERM,
> -               .internal_flags = NL80211_FLAG_NEED_NETDEV |
> -                                 NL80211_FLAG_NEED_RTNL,
> +               .internal_flags = NL80211_FLAG_NEED_NETDEV,
>         },
>         {
>                 .cmd = NL80211_CMD_SET_CHANNEL,
>                 .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
>                 .doit = nl80211_set_channel,
>                 .flags = GENL_UNS_ADMIN_PERM,
> -               .internal_flags = NL80211_FLAG_NEED_NETDEV |
> -                                 NL80211_FLAG_NEED_RTNL,
> +               .internal_flags = NL80211_FLAG_NEED_NETDEV,
>         },
>         {
>                 .cmd = NL80211_CMD_SET_WDS_PEER,
>                 .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
>                 .doit = nl80211_set_wds_peer,
>                 .flags = GENL_UNS_ADMIN_PERM,
> -               .internal_flags = NL80211_FLAG_NEED_NETDEV |
> -                                 NL80211_FLAG_NEED_RTNL,
> +               .internal_flags = NL80211_FLAG_NEED_NETDEV,
>         },
>         {
>                 .cmd = NL80211_CMD_JOIN_MESH,
>                 .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
>                 .doit = nl80211_join_mesh,
>                 .flags = GENL_UNS_ADMIN_PERM,
> -               .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
> -                                 NL80211_FLAG_NEED_RTNL,
> +               .internal_flags = NL80211_FLAG_NEED_NETDEV_UP,
>         },
>         {
>                 .cmd = NL80211_CMD_LEAVE_MESH,
>                 .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
>                 .doit = nl80211_leave_mesh,
>                 .flags = GENL_UNS_ADMIN_PERM,
> -               .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
> -                                 NL80211_FLAG_NEED_RTNL,
> +               .internal_flags = NL80211_FLAG_NEED_NETDEV_UP,
>         },
>         {
>                 .cmd = NL80211_CMD_JOIN_OCB,
>                 .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
>                 .doit = nl80211_join_ocb,
>                 .flags = GENL_UNS_ADMIN_PERM,
> -               .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
> -                                 NL80211_FLAG_NEED_RTNL,
> +               .internal_flags = NL80211_FLAG_NEED_NETDEV_UP,
>         },
>         {
>                 .cmd = NL80211_CMD_LEAVE_OCB,
>                 .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
>                 .doit = nl80211_leave_ocb,
>                 .flags = GENL_UNS_ADMIN_PERM,
> -               .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
> -                                 NL80211_FLAG_NEED_RTNL,
> +               .internal_flags = NL80211_FLAG_NEED_NETDEV_UP,
>         },
>  #ifdef CONFIG_PM
>         {
> @@ -14335,16 +14384,14 @@ static const struct genl_ops nl80211_ops[] = {
>                 .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
>                 .doit = nl80211_get_wowlan,
>                 /* can be retrieved by unprivileged users */
> -               .internal_flags = NL80211_FLAG_NEED_WIPHY |
> -                                 NL80211_FLAG_NEED_RTNL,
> +               .internal_flags = NL80211_FLAG_NEED_WIPHY,
>         },
>         {
>                 .cmd = NL80211_CMD_SET_WOWLAN,
>                 .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
>                 .doit = nl80211_set_wowlan,
>                 .flags = GENL_UNS_ADMIN_PERM,
> -               .internal_flags = NL80211_FLAG_NEED_WIPHY |
> -                                 NL80211_FLAG_NEED_RTNL,
> +               .internal_flags = NL80211_FLAG_NEED_WIPHY,
>         },
>  #endif
>         {
> @@ -14353,7 +14400,7 @@ static const struct genl_ops nl80211_ops[] = {
>                 .doit = nl80211_set_rekey_data,
>                 .flags = GENL_UNS_ADMIN_PERM,
>                 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
> -                                 NL80211_FLAG_NEED_RTNL |
> +                                 0 |
>                                   NL80211_FLAG_CLEAR_SKB,
>         },
>         {
> @@ -14361,128 +14408,112 @@ static const struct genl_ops nl80211_ops[] = {
>                 .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
>                 .doit = nl80211_tdls_mgmt,
>                 .flags = GENL_UNS_ADMIN_PERM,
> -               .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
> -                                 NL80211_FLAG_NEED_RTNL,
> +               .internal_flags = NL80211_FLAG_NEED_NETDEV_UP,
>         },
>         {
>                 .cmd = NL80211_CMD_TDLS_OPER,
>                 .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
>                 .doit = nl80211_tdls_oper,
>                 .flags = GENL_UNS_ADMIN_PERM,
> -               .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
> -                                 NL80211_FLAG_NEED_RTNL,
> +               .internal_flags = NL80211_FLAG_NEED_NETDEV_UP,
>         },
>         {
>                 .cmd = NL80211_CMD_UNEXPECTED_FRAME,
>                 .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
>                 .doit = nl80211_register_unexpected_frame,
>                 .flags = GENL_UNS_ADMIN_PERM,
> -               .internal_flags = NL80211_FLAG_NEED_NETDEV |
> -                                 NL80211_FLAG_NEED_RTNL,
> +               .internal_flags = NL80211_FLAG_NEED_NETDEV,
>         },
>         {
>                 .cmd = NL80211_CMD_PROBE_CLIENT,
>                 .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
>                 .doit = nl80211_probe_client,
>                 .flags = GENL_UNS_ADMIN_PERM,
> -               .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
> -                                 NL80211_FLAG_NEED_RTNL,
> +               .internal_flags = NL80211_FLAG_NEED_NETDEV_UP,
>         },
>         {
>                 .cmd = NL80211_CMD_REGISTER_BEACONS,
>                 .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
>                 .doit = nl80211_register_beacons,
>                 .flags = GENL_UNS_ADMIN_PERM,
> -               .internal_flags = NL80211_FLAG_NEED_WIPHY |
> -                                 NL80211_FLAG_NEED_RTNL,
> +               .internal_flags = NL80211_FLAG_NEED_WIPHY,
>         },
>         {
>                 .cmd = NL80211_CMD_SET_NOACK_MAP,
>                 .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
>                 .doit = nl80211_set_noack_map,
>                 .flags = GENL_UNS_ADMIN_PERM,
> -               .internal_flags = NL80211_FLAG_NEED_NETDEV |
> -                                 NL80211_FLAG_NEED_RTNL,
> +               .internal_flags = NL80211_FLAG_NEED_NETDEV,
>         },
>         {
>                 .cmd = NL80211_CMD_START_P2P_DEVICE,
>                 .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
>                 .doit = nl80211_start_p2p_device,
>                 .flags = GENL_UNS_ADMIN_PERM,
> -               .internal_flags = NL80211_FLAG_NEED_WDEV |
> -                                 NL80211_FLAG_NEED_RTNL,
> +               .internal_flags = NL80211_FLAG_NEED_WDEV,
>         },
>         {
>                 .cmd = NL80211_CMD_STOP_P2P_DEVICE,
>                 .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
>                 .doit = nl80211_stop_p2p_device,
>                 .flags = GENL_UNS_ADMIN_PERM,
> -               .internal_flags = NL80211_FLAG_NEED_WDEV_UP |
> -                                 NL80211_FLAG_NEED_RTNL,
> +               .internal_flags = NL80211_FLAG_NEED_WDEV_UP,
>         },
>         {
>                 .cmd = NL80211_CMD_START_NAN,
>                 .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
>                 .doit = nl80211_start_nan,
>                 .flags = GENL_ADMIN_PERM,
> -               .internal_flags = NL80211_FLAG_NEED_WDEV |
> -                                 NL80211_FLAG_NEED_RTNL,
> +               .internal_flags = NL80211_FLAG_NEED_WDEV,
>         },
>         {
>                 .cmd = NL80211_CMD_STOP_NAN,
>                 .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
>                 .doit = nl80211_stop_nan,
>                 .flags = GENL_ADMIN_PERM,
> -               .internal_flags = NL80211_FLAG_NEED_WDEV_UP |
> -                                 NL80211_FLAG_NEED_RTNL,
> +               .internal_flags = NL80211_FLAG_NEED_WDEV_UP,
>         },
>         {
>                 .cmd = NL80211_CMD_ADD_NAN_FUNCTION,
>                 .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
>                 .doit = nl80211_nan_add_func,
>                 .flags = GENL_ADMIN_PERM,
> -               .internal_flags = NL80211_FLAG_NEED_WDEV_UP |
> -                                 NL80211_FLAG_NEED_RTNL,
> +               .internal_flags = NL80211_FLAG_NEED_WDEV_UP,
>         },
>         {
>                 .cmd = NL80211_CMD_DEL_NAN_FUNCTION,
>                 .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
>                 .doit = nl80211_nan_del_func,
>                 .flags = GENL_ADMIN_PERM,
> -               .internal_flags = NL80211_FLAG_NEED_WDEV_UP |
> -                                 NL80211_FLAG_NEED_RTNL,
> +               .internal_flags = NL80211_FLAG_NEED_WDEV_UP,
>         },
>         {
>                 .cmd = NL80211_CMD_CHANGE_NAN_CONFIG,
>                 .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
>                 .doit = nl80211_nan_change_config,
>                 .flags = GENL_ADMIN_PERM,
> -               .internal_flags = NL80211_FLAG_NEED_WDEV_UP |
> -                                 NL80211_FLAG_NEED_RTNL,
> +               .internal_flags = NL80211_FLAG_NEED_WDEV_UP,
>         },
>         {
>                 .cmd = NL80211_CMD_SET_MCAST_RATE,
>                 .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
>                 .doit = nl80211_set_mcast_rate,
>                 .flags = GENL_UNS_ADMIN_PERM,
> -               .internal_flags = NL80211_FLAG_NEED_NETDEV |
> -                                 NL80211_FLAG_NEED_RTNL,
> +               .internal_flags = NL80211_FLAG_NEED_NETDEV,
>         },
>         {
>                 .cmd = NL80211_CMD_SET_MAC_ACL,
>                 .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
>                 .doit = nl80211_set_mac_acl,
>                 .flags = GENL_UNS_ADMIN_PERM,
> -               .internal_flags = NL80211_FLAG_NEED_NETDEV |
> -                                 NL80211_FLAG_NEED_RTNL,
> +               .internal_flags = NL80211_FLAG_NEED_NETDEV,
>         },
>         {
>                 .cmd = NL80211_CMD_RADAR_DETECT,
>                 .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
>                 .doit = nl80211_start_radar_detection,
>                 .flags = GENL_UNS_ADMIN_PERM,
> -               .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
> -                                 NL80211_FLAG_NEED_RTNL,
> +               .internal_flags = NL80211_FLAG_NEED_NETDEV_UP,
>         },
>         {
>                 .cmd = NL80211_CMD_GET_PROTOCOL_FEATURES,
> @@ -14494,47 +14525,41 @@ static const struct genl_ops nl80211_ops[] = {
>                 .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
>                 .doit = nl80211_update_ft_ies,
>                 .flags = GENL_UNS_ADMIN_PERM,
> -               .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
> -                                 NL80211_FLAG_NEED_RTNL,
> +               .internal_flags = NL80211_FLAG_NEED_NETDEV_UP,
>         },
>         {
>                 .cmd = NL80211_CMD_CRIT_PROTOCOL_START,
>                 .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
>                 .doit = nl80211_crit_protocol_start,
>                 .flags = GENL_UNS_ADMIN_PERM,
> -               .internal_flags = NL80211_FLAG_NEED_WDEV_UP |
> -                                 NL80211_FLAG_NEED_RTNL,
> +               .internal_flags = NL80211_FLAG_NEED_WDEV_UP,
>         },
>         {
>                 .cmd = NL80211_CMD_CRIT_PROTOCOL_STOP,
>                 .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
>                 .doit = nl80211_crit_protocol_stop,
>                 .flags = GENL_UNS_ADMIN_PERM,
> -               .internal_flags = NL80211_FLAG_NEED_WDEV_UP |
> -                                 NL80211_FLAG_NEED_RTNL,
> +               .internal_flags = NL80211_FLAG_NEED_WDEV_UP,
>         },
>         {
>                 .cmd = NL80211_CMD_GET_COALESCE,
>                 .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
>                 .doit = nl80211_get_coalesce,
> -               .internal_flags = NL80211_FLAG_NEED_WIPHY |
> -                                 NL80211_FLAG_NEED_RTNL,
> +               .internal_flags = NL80211_FLAG_NEED_WIPHY,
>         },
>         {
>                 .cmd = NL80211_CMD_SET_COALESCE,
>                 .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
>                 .doit = nl80211_set_coalesce,
>                 .flags = GENL_UNS_ADMIN_PERM,
> -               .internal_flags = NL80211_FLAG_NEED_WIPHY |
> -                                 NL80211_FLAG_NEED_RTNL,
> +               .internal_flags = NL80211_FLAG_NEED_WIPHY,
>         },
>         {
>                 .cmd = NL80211_CMD_CHANNEL_SWITCH,
>                 .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
>                 .doit = nl80211_channel_switch,
>                 .flags = GENL_UNS_ADMIN_PERM,
> -               .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
> -                                 NL80211_FLAG_NEED_RTNL,
> +               .internal_flags = NL80211_FLAG_NEED_NETDEV_UP,
>         },
>         {
>                 .cmd = NL80211_CMD_VENDOR,
> @@ -14543,7 +14568,7 @@ static const struct genl_ops nl80211_ops[] = {
>                 .dumpit = nl80211_vendor_cmd_dump,
>                 .flags = GENL_UNS_ADMIN_PERM,
>                 .internal_flags = NL80211_FLAG_NEED_WIPHY |
> -                                 NL80211_FLAG_NEED_RTNL |
> +                                 0 |
>                                   NL80211_FLAG_CLEAR_SKB,
>         },
>         {
> @@ -14551,116 +14576,102 @@ static const struct genl_ops nl80211_ops[] = {
>                 .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
>                 .doit = nl80211_set_qos_map,
>                 .flags = GENL_UNS_ADMIN_PERM,
> -               .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
> -                                 NL80211_FLAG_NEED_RTNL,
> +               .internal_flags = NL80211_FLAG_NEED_NETDEV_UP,
>         },
>         {
>                 .cmd = NL80211_CMD_ADD_TX_TS,
>                 .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
>                 .doit = nl80211_add_tx_ts,
>                 .flags = GENL_UNS_ADMIN_PERM,
> -               .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
> -                                 NL80211_FLAG_NEED_RTNL,
> +               .internal_flags = NL80211_FLAG_NEED_NETDEV_UP,
>         },
>         {
>                 .cmd = NL80211_CMD_DEL_TX_TS,
>                 .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
>                 .doit = nl80211_del_tx_ts,
>                 .flags = GENL_UNS_ADMIN_PERM,
> -               .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
> -                                 NL80211_FLAG_NEED_RTNL,
> +               .internal_flags = NL80211_FLAG_NEED_NETDEV_UP,
>         },
>         {
>                 .cmd = NL80211_CMD_TDLS_CHANNEL_SWITCH,
>                 .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
>                 .doit = nl80211_tdls_channel_switch,
>                 .flags = GENL_UNS_ADMIN_PERM,
> -               .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
> -                                 NL80211_FLAG_NEED_RTNL,
> +               .internal_flags = NL80211_FLAG_NEED_NETDEV_UP,
>         },
>         {
>                 .cmd = NL80211_CMD_TDLS_CANCEL_CHANNEL_SWITCH,
>                 .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
>                 .doit = nl80211_tdls_cancel_channel_switch,
>                 .flags = GENL_UNS_ADMIN_PERM,
> -               .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
> -                                 NL80211_FLAG_NEED_RTNL,
> +               .internal_flags = NL80211_FLAG_NEED_NETDEV_UP,
>         },
>         {
>                 .cmd = NL80211_CMD_SET_MULTICAST_TO_UNICAST,
>                 .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
>                 .doit = nl80211_set_multicast_to_unicast,
>                 .flags = GENL_UNS_ADMIN_PERM,
> -               .internal_flags = NL80211_FLAG_NEED_NETDEV |
> -                                 NL80211_FLAG_NEED_RTNL,
> +               .internal_flags = NL80211_FLAG_NEED_NETDEV,
>         },
>         {
>                 .cmd = NL80211_CMD_SET_PMK,
>                 .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
>                 .doit = nl80211_set_pmk,
>                 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
> -                                 NL80211_FLAG_NEED_RTNL |
> +                                 0 |
>                                   NL80211_FLAG_CLEAR_SKB,
>         },
>         {
>                 .cmd = NL80211_CMD_DEL_PMK,
>                 .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
>                 .doit = nl80211_del_pmk,
> -               .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
> -                                 NL80211_FLAG_NEED_RTNL,
> +               .internal_flags = NL80211_FLAG_NEED_NETDEV_UP,
>         },
>         {
>                 .cmd = NL80211_CMD_EXTERNAL_AUTH,
>                 .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
>                 .doit = nl80211_external_auth,
>                 .flags = GENL_ADMIN_PERM,
> -               .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
> -                                 NL80211_FLAG_NEED_RTNL,
> +               .internal_flags = NL80211_FLAG_NEED_NETDEV_UP,
>         },
>         {
>                 .cmd = NL80211_CMD_CONTROL_PORT_FRAME,
>                 .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
>                 .doit = nl80211_tx_control_port,
>                 .flags = GENL_UNS_ADMIN_PERM,
> -               .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
> -                                 NL80211_FLAG_NEED_RTNL,
> +               .internal_flags = NL80211_FLAG_NEED_NETDEV_UP,
>         },
>         {
>                 .cmd = NL80211_CMD_GET_FTM_RESPONDER_STATS,
>                 .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
>                 .doit = nl80211_get_ftm_responder_stats,
> -               .internal_flags = NL80211_FLAG_NEED_NETDEV |
> -                                 NL80211_FLAG_NEED_RTNL,
> +               .internal_flags = NL80211_FLAG_NEED_NETDEV,
>         },
>         {
>                 .cmd = NL80211_CMD_PEER_MEASUREMENT_START,
>                 .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
>                 .doit = nl80211_pmsr_start,
>                 .flags = GENL_UNS_ADMIN_PERM,
> -               .internal_flags = NL80211_FLAG_NEED_WDEV_UP |
> -                                 NL80211_FLAG_NEED_RTNL,
> +               .internal_flags = NL80211_FLAG_NEED_WDEV_UP,
>         },
>         {
>                 .cmd = NL80211_CMD_NOTIFY_RADAR,
>                 .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
>                 .doit = nl80211_notify_radar_detection,
>                 .flags = GENL_UNS_ADMIN_PERM,
> -               .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
> -                                 NL80211_FLAG_NEED_RTNL,
> +               .internal_flags = NL80211_FLAG_NEED_NETDEV_UP,
>         },
>         {
>                 .cmd = NL80211_CMD_UPDATE_OWE_INFO,
>                 .doit = nl80211_update_owe_info,
>                 .flags = GENL_ADMIN_PERM,
> -               .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
> -                                 NL80211_FLAG_NEED_RTNL,
> +               .internal_flags = NL80211_FLAG_NEED_NETDEV_UP,
>         },
>         {
>                 .cmd = NL80211_CMD_PROBE_MESH_LINK,
>                 .doit = nl80211_probe_mesh_link,
>                 .flags = GENL_UNS_ADMIN_PERM,
> -               .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
> -                                 NL80211_FLAG_NEED_RTNL,
> +               .internal_flags = NL80211_FLAG_NEED_NETDEV_UP,
>         },
>  };
>
> diff --git a/net/wireless/sme.c b/net/wireless/sme.c
> index 7a6c38ddc65a..f6b68685189f 100644
> --- a/net/wireless/sme.c
> +++ b/net/wireless/sme.c
> @@ -67,7 +67,6 @@ static int cfg80211_conn_scan(struct wireless_dev *wdev)
>         struct cfg80211_scan_request *request;
>         int n_channels, err;
>
> -       ASSERT_RTNL();
>         ASSERT_WDEV_LOCK(wdev);
>
>         if (rdev->scan_req || rdev->scan_msg)
> diff --git a/net/wireless/util.c b/net/wireless/util.c
> index 1c39d6a2e850..39587ad2fb48 100644
> --- a/net/wireless/util.c
> +++ b/net/wireless/util.c
> @@ -891,7 +891,7 @@ void cfg80211_process_rdev_events(struct cfg80211_registered_device *rdev)
>  {
>         struct wireless_dev *wdev;
>
> -       ASSERT_RTNL();
> +       lockdep_assert_held(&rdev->mtx);
>
>         list_for_each_entry(wdev, &rdev->wiphy.wdev_list, list)
>                 cfg80211_process_wdev_events(wdev);
> --
> 2.20.1
>

^ permalink raw reply	[flat|nested] 4+ messages in thread

* RE: [EXT] [RFC/RFT] cfg80211: decouple us from the RTNL
  2019-07-31 22:08 [RFC/RFT] cfg80211: decouple us from the RTNL Johannes Berg
  2019-08-01  3:59 ` Sid Hayn
@ 2019-08-05 15:20 ` " Dedy Lansky
  2019-08-05 20:41   ` Johannes Berg
  1 sibling, 1 reply; 4+ messages in thread
From: Dedy Lansky @ 2019-08-05 15:20 UTC (permalink / raw)
  To: Johannes Berg, linux-wireless; +Cc: Florian Westphal, Johannes Berg


From: linux-wireless-owner@vger.kernel.org
<linux-wireless-owner@vger.kernel.org> On Behalf Of Johannes Berg
Sent: Thursday, August 1, 2019 1:09 AM

> Fix that by re-adding a mutex to each wiphy/rdev as we had at
> some point, so we have locking for the wireless_dev lists and
> all the other things in there, and also so that drivers still
> don't have to worry too much about it (they still won't get
> parallel calls for a single device).

Sounds good.

> Then, we can restrict the RTNL to a few cases where we add or
> remove interfaces and really need the added protection. Some
> of the global list management still also uses the RTNL, since
> we need to have it anyway for netdev management.

> TODO:
>  - use wiphy_lock()/wiphy_unlock() in all drivers as the code
>    changed in mac80211 does

I guess this change breaks existing drivers because some drivers assume RTNL
is locked when their cfg callbacks are executed. Is that correct?

Would there be any simple rules for drivers when to use each one of the
locking API: rtnl vs wiphy vs wdev ?

Thanks,
 Dedy.


^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: [EXT] [RFC/RFT] cfg80211: decouple us from the RTNL
  2019-08-05 15:20 ` [EXT] " Dedy Lansky
@ 2019-08-05 20:41   ` Johannes Berg
  0 siblings, 0 replies; 4+ messages in thread
From: Johannes Berg @ 2019-08-05 20:41 UTC (permalink / raw)
  To: Dedy Lansky, linux-wireless; +Cc: Florian Westphal

On Mon, 2019-08-05 at 18:20 +0300, Dedy Lansky wrote:

> > Then, we can restrict the RTNL to a few cases where we add or
> > remove interfaces and really need the added protection. Some
> > of the global list management still also uses the RTNL, since
> > we need to have it anyway for netdev management.
> > TODO:
> >  - use wiphy_lock()/wiphy_unlock() in all drivers as the code
> >    changed in mac80211 does
> 
> I guess this change breaks existing drivers because some drivers assume RTNL
> is locked when their cfg callbacks are executed. Is that correct?

It might, to some extent. Mostly though, it shouldn't really matter to
them since the actual callbacks that manipulate interfaces (add, remove,
set type) and thus require RTNL still hold the RTNL.

> Would there be any simple rules for drivers when to use each one of the
> locking API: rtnl vs wiphy vs wdev ?

I'd say RTNL only if externally required. Wiphy vs. wdev I haven't
really quite made up my mind - I'm contemplating just removing the wdev
lock since for (driver/mac80211) simplicity we'll probably not want
concurrency there anyway? Should be rare (Ben Greear will be the only
one hitting it ...) that you really need concurrency there, and
simplifies things a lot.

johannes


^ permalink raw reply	[flat|nested] 4+ messages in thread

end of thread, back to index

Thread overview: 4+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2019-07-31 22:08 [RFC/RFT] cfg80211: decouple us from the RTNL Johannes Berg
2019-08-01  3:59 ` Sid Hayn
2019-08-05 15:20 ` [EXT] " Dedy Lansky
2019-08-05 20:41   ` Johannes Berg

Linux-Wireless Archive on lore.kernel.org

Archives are clonable:
	git clone --mirror https://lore.kernel.org/linux-wireless/0 linux-wireless/git/0.git

	# If you have public-inbox 1.1+ installed, you may
	# initialize and index your mirror using the following commands:
	public-inbox-init -V2 linux-wireless linux-wireless/ https://lore.kernel.org/linux-wireless \
		linux-wireless@vger.kernel.org linux-wireless@archiver.kernel.org
	public-inbox-index linux-wireless


Newsgroup available over NNTP:
	nntp://nntp.lore.kernel.org/org.kernel.vger.linux-wireless


AGPL code for this site: git clone https://public-inbox.org/ public-inbox