Linux wireless drivers development
 help / color / mirror / Atom feed
* Re: pull-request: mac80211 2019-07-31
From: David Miller @ 2019-07-31 15:51 UTC (permalink / raw)
  To: johannes; +Cc: netdev, linux-wireless
In-Reply-To: <20190731124933.19420-1-johannes@sipsolutions.net>

From: Johannes Berg <johannes@sipsolutions.net>
Date: Wed, 31 Jul 2019 14:49:32 +0200

> We have few fixes, most importantly probably the NETIF_F_LLTX revert,
> we thought we were now more layered like VLAN or such since we do all
> of the queue control internally, but it caused problems, evidently not.
> 
> Please pull and let me know if there's any problem.

Pulled, thanks Johannes.

^ permalink raw reply

* Re: pull-request: mac80211-next 2019-07-31
From: David Miller @ 2019-07-31 16:00 UTC (permalink / raw)
  To: johannes; +Cc: netdev, linux-wireless
In-Reply-To: <20190731155057.23035-1-johannes@sipsolutions.net>

From: Johannes Berg <johannes@sipsolutions.net>
Date: Wed, 31 Jul 2019 17:50:56 +0200

> There's a fair number of changes here, so I thought I'd get them out.
> I've included two Intel driver cleanups because Luca is on vacation,
> I'm covering for him, and doing it all in one tree let me merge all
> of the patches at once (including mac80211 that depends on that);
> Kalle is aware.
> 
> Also, though this isn't very interesting yet, I've started looking at
> weaning the wireless subsystem off the RTNL for all operations, as it
> can cause significant lock contention, especially with slow USB devices.
> The real patches for that are some way off, but one preparation here is
> to use generic netlink's parallel_ops=true, to avoid trading one place
> with contention for another in the future, and to avoid adding more
> genl_family_attrbuf() usage (since that's no longer possible with the
> parallel_ops setting).
> 
> Please pull and let me know if there's any problem.

Pulled, thanks Johannes.

I'm sure people like Florian Westphal will also appreciate your RTNL
elimination work...

^ permalink raw reply

* [PATCH] staging: wilc1000: merge drv_handle and operation_mode wids
From: Adham.Abozaeid @ 2019-07-31 20:58 UTC (permalink / raw)
  To: linux-wireless; +Cc: devel, gregkh, johannes, Ajay.Kathat, Adham.Abozaeid

From: Adham Abozaeid <adham.abozaeid@microchip.com>

wilc_set_wfi_drv_handler and wilc_set_operation_mode sends the same
parameters to the FW, so it's better to combine them together.

Kept wilc_set_wfi_drv_handler implementation since it sends all the
required parameters, and renamed it to wilc_set_operation_mode to be
more descriptive.

Signed-off-by: Adham Abozaeid <adham.abozaeid@microchip.com>
---
 drivers/staging/wilc1000/wilc_hif.c           | 29 ++-----------------
 drivers/staging/wilc1000/wilc_hif.h           |  5 ++--
 drivers/staging/wilc1000/wilc_netdev.c        |  6 ++--
 .../staging/wilc1000/wilc_wfi_cfgoperations.c | 24 +++++++--------
 drivers/staging/wilc1000/wilc_wlan_if.h       |  3 +-
 5 files changed, 20 insertions(+), 47 deletions(-)

diff --git a/drivers/staging/wilc1000/wilc_hif.c b/drivers/staging/wilc1000/wilc_hif.c
index f8646ea3b0c4..ca252a43cd8c 100644
--- a/drivers/staging/wilc1000/wilc_hif.c
+++ b/drivers/staging/wilc1000/wilc_hif.c
@@ -1409,18 +1409,15 @@ int wilc_set_mac_chnl_num(struct wilc_vif *vif, u8 channel)
 	return result;
 }
 
-int wilc_set_wfi_drv_handler(struct wilc_vif *vif, int index, u8 mode,
-			     u8 ifc_id)
+int wilc_set_operation_mode(struct wilc_vif *vif, int index, u8 mode,
+			    u8 ifc_id)
 {
 	struct wid wid;
-	struct host_if_drv *hif_drv = vif->hif_drv;
 	int result;
 	struct wilc_drv_handler drv;
 
-	if (!hif_drv)
-		return -EFAULT;
 
-	wid.id = WID_SET_DRV_HANDLER;
+	wid.id = WID_SET_OPERATION_MODE;
 	wid.type = WID_STR;
 	wid.size = sizeof(drv);
 	wid.val = (u8 *)&drv;
@@ -1435,26 +1432,6 @@ int wilc_set_wfi_drv_handler(struct wilc_vif *vif, int index, u8 mode,
 	return result;
 }
 
-int wilc_set_operation_mode(struct wilc_vif *vif, u32 mode)
-{
-	struct wid wid;
-	struct wilc_op_mode op_mode;
-	int result;
-
-	wid.id = WID_SET_OPERATION_MODE;
-	wid.type = WID_INT;
-	wid.size = sizeof(op_mode);
-	wid.val = (u8 *)&op_mode;
-
-	op_mode.mode = cpu_to_le32(mode);
-
-	result = wilc_send_config_pkt(vif, WILC_SET_CFG, &wid, 1);
-	if (result)
-		netdev_err(vif->ndev, "Failed to set operation mode\n");
-
-	return result;
-}
-
 s32 wilc_get_inactive_time(struct wilc_vif *vif, const u8 *mac, u32 *out_val)
 {
 	struct wid wid;
diff --git a/drivers/staging/wilc1000/wilc_hif.h b/drivers/staging/wilc1000/wilc_hif.h
index be1d2497cde9..3bc305151651 100644
--- a/drivers/staging/wilc1000/wilc_hif.h
+++ b/drivers/staging/wilc1000/wilc_hif.h
@@ -219,9 +219,8 @@ int wilc_remain_on_channel(struct wilc_vif *vif, u64 cookie,
 			   void *user_arg);
 int wilc_listen_state_expired(struct wilc_vif *vif, u64 cookie);
 void wilc_frame_register(struct wilc_vif *vif, u16 frame_type, bool reg);
-int wilc_set_wfi_drv_handler(struct wilc_vif *vif, int index, u8 mode,
-			     u8 ifc_id);
-int wilc_set_operation_mode(struct wilc_vif *vif, u32 mode);
+int wilc_set_operation_mode(struct wilc_vif *vif, int index, u8 mode,
+			    u8 ifc_id);
 int wilc_get_statistics(struct wilc_vif *vif, struct rf_info *stats);
 void wilc_resolve_disconnect_aberration(struct wilc_vif *vif);
 int wilc_get_vif_idx(struct wilc_vif *vif);
diff --git a/drivers/staging/wilc1000/wilc_netdev.c b/drivers/staging/wilc1000/wilc_netdev.c
index 68129a0ba55f..bd848fdf7f8a 100644
--- a/drivers/staging/wilc1000/wilc_netdev.c
+++ b/drivers/staging/wilc1000/wilc_netdev.c
@@ -626,10 +626,8 @@ static int wilc_mac_open(struct net_device *ndev)
 		return ret;
 	}
 
-	wilc_set_wfi_drv_handler(vif, wilc_get_vif_idx(vif), vif->iftype,
-				 vif->idx);
-	wilc_set_operation_mode(vif, vif->iftype);
-
+	wilc_set_operation_mode(vif, wilc_get_vif_idx(vif),
+				 vif->iftype, vif->idx);
 	wilc_get_mac_address(vif, mac_add);
 	netdev_dbg(ndev, "Mac address: %pM\n", mac_add);
 	ether_addr_copy(ndev->dev_addr, mac_add);
diff --git a/drivers/staging/wilc1000/wilc_wfi_cfgoperations.c b/drivers/staging/wilc1000/wilc_wfi_cfgoperations.c
index e2d9efed36b0..cf661e8ab1c8 100644
--- a/drivers/staging/wilc1000/wilc_wfi_cfgoperations.c
+++ b/drivers/staging/wilc1000/wilc_wfi_cfgoperations.c
@@ -1419,7 +1419,8 @@ static int change_virtual_intf(struct wiphy *wiphy, struct net_device *dev,
 		if (vif->iftype == WILC_AP_MODE || vif->iftype == WILC_GO_MODE)
 			wilc_wfi_deinit_mon_interface(wl, true);
 		vif->iftype = WILC_STATION_MODE;
-		wilc_set_operation_mode(vif, WILC_STATION_MODE);
+		wilc_set_operation_mode(vif, wilc_get_vif_idx(vif),
+					 WILC_STATION_MODE, vif->idx);
 
 		memset(priv->assoc_stainfo.sta_associated_bss, 0,
 		       WILC_MAX_NUM_STA * ETH_ALEN);
@@ -1431,8 +1432,8 @@ static int change_virtual_intf(struct wiphy *wiphy, struct net_device *dev,
 		priv->wdev.iftype = type;
 		vif->monitor_flag = 0;
 		vif->iftype = WILC_CLIENT_MODE;
-		wilc_set_operation_mode(vif, WILC_STATION_MODE);
-
+		wilc_set_operation_mode(vif, wilc_get_vif_idx(vif),
+					 WILC_STATION_MODE, vif->idx);
 		break;
 
 	case NL80211_IFTYPE_AP:
@@ -1440,18 +1441,17 @@ static int change_virtual_intf(struct wiphy *wiphy, struct net_device *dev,
 		priv->wdev.iftype = type;
 		vif->iftype = WILC_AP_MODE;
 
-		if (wl->initialized) {
-			wilc_set_wfi_drv_handler(vif, wilc_get_vif_idx(vif),
-						 0, vif->idx);
-			wilc_set_operation_mode(vif, WILC_AP_MODE);
-		}
+		if (wl->initialized)
+			wilc_set_operation_mode(vif, wilc_get_vif_idx(vif),
+						 WILC_AP_MODE, vif->idx);
 		break;
 
 	case NL80211_IFTYPE_P2P_GO:
-		wilc_set_operation_mode(vif, WILC_AP_MODE);
 		dev->ieee80211_ptr->iftype = type;
 		priv->wdev.iftype = type;
 		vif->iftype = WILC_GO_MODE;
+		wilc_set_operation_mode(vif, wilc_get_vif_idx(vif),
+					WILC_AP_MODE, vif->idx);
 		break;
 
 	default:
@@ -1659,15 +1659,15 @@ static int del_virtual_intf(struct wiphy *wiphy, struct wireless_dev *wdev)
 	vif->monitor_flag = 0;
 
 	mutex_lock(&wl->vif_mutex);
-	wilc_set_wfi_drv_handler(vif, 0, 0, 0);
-	for (i = vif->idx; i < wl->vif_num ; i++) {
+	wilc_set_operation_mode(vif, 0, 0, 0);
+	for (i = vif->idx; i < wl->vif_num; i++) {
 		if ((i + 1) >= wl->vif_num) {
 			wl->vif[i] = NULL;
 		} else {
 			vif = wl->vif[i + 1];
 			vif->idx = i;
 			wl->vif[i] = vif;
-			wilc_set_wfi_drv_handler(vif, wilc_get_vif_idx(vif),
+			wilc_set_operation_mode(vif, wilc_get_vif_idx(vif),
 						 vif->iftype, vif->idx);
 		}
 	}
diff --git a/drivers/staging/wilc1000/wilc_wlan_if.h b/drivers/staging/wilc1000/wilc_wlan_if.h
index b89d0e0f04cc..70eac586f80c 100644
--- a/drivers/staging/wilc1000/wilc_wlan_if.h
+++ b/drivers/staging/wilc1000/wilc_wlan_if.h
@@ -724,7 +724,6 @@ enum {
 	/* NMAC Integer WID list */
 	/* Custom Integer WID list */
 	WID_GET_INACTIVE_TIME		= 0x2084,
-	WID_SET_OPERATION_MODE		= 0X2086,
 	/* EMAC String WID list */
 	WID_SSID			= 0x3000,
 	WID_FIRMWARE_VERSION		= 0x3001,
@@ -755,9 +754,9 @@ enum {
 	WID_MODEL_NAME			= 0x3027, /*Added for CAPI tool */
 	WID_MODEL_NUM			= 0x3028, /*Added for CAPI tool */
 	WID_DEVICE_NAME			= 0x3029, /*Added for CAPI tool */
-	WID_SET_DRV_HANDLER		= 0x3079,
 
 	/* NMAC String WID list */
+	WID_SET_OPERATION_MODE		= 0x3079,
 	WID_11N_P_ACTION_REQ		= 0x3080,
 	WID_HUT_TEST_ID			= 0x3081,
 	WID_PMKID_INFO			= 0x3082,
-- 
2.17.1


^ permalink raw reply related

* [PATCH] staging: wilc1000: remove unused function
From: Adham.Abozaeid @ 2019-07-31 21:01 UTC (permalink / raw)
  To: linux-wireless; +Cc: devel, gregkh, johannes, Ajay.Kathat, Adham.Abozaeid

From: Adham Abozaeid <adham.abozaeid@microchip.com>

function wilc_resolve_disconnect_aberration isn't referenced, so
removing it

Signed-off-by: Adham Abozaeid <adham.abozaeid@microchip.com>
---
 drivers/staging/wilc1000/wilc_hif.c | 9 ---------
 drivers/staging/wilc1000/wilc_hif.h | 1 -
 2 files changed, 10 deletions(-)

diff --git a/drivers/staging/wilc1000/wilc_hif.c b/drivers/staging/wilc1000/wilc_hif.c
index ca252a43cd8c..4d809804c784 100644
--- a/drivers/staging/wilc1000/wilc_hif.c
+++ b/drivers/staging/wilc1000/wilc_hif.c
@@ -798,15 +798,6 @@ int wilc_disconnect(struct wilc_vif *vif)
 	return 0;
 }
 
-void wilc_resolve_disconnect_aberration(struct wilc_vif *vif)
-{
-	if (!vif->hif_drv)
-		return;
-	if (vif->hif_drv->hif_state == HOST_IF_WAITING_CONN_RESP ||
-	    vif->hif_drv->hif_state == HOST_IF_CONNECTING)
-		wilc_disconnect(vif);
-}
-
 int wilc_get_statistics(struct wilc_vif *vif, struct rf_info *stats)
 {
 	struct wid wid_list[5];
diff --git a/drivers/staging/wilc1000/wilc_hif.h b/drivers/staging/wilc1000/wilc_hif.h
index 3bc305151651..ac5fe57f872b 100644
--- a/drivers/staging/wilc1000/wilc_hif.h
+++ b/drivers/staging/wilc1000/wilc_hif.h
@@ -222,7 +222,6 @@ void wilc_frame_register(struct wilc_vif *vif, u16 frame_type, bool reg);
 int wilc_set_operation_mode(struct wilc_vif *vif, int index, u8 mode,
 			    u8 ifc_id);
 int wilc_get_statistics(struct wilc_vif *vif, struct rf_info *stats);
-void wilc_resolve_disconnect_aberration(struct wilc_vif *vif);
 int wilc_get_vif_idx(struct wilc_vif *vif);
 int wilc_set_tx_power(struct wilc_vif *vif, u8 tx_power);
 int wilc_get_tx_power(struct wilc_vif *vif, u8 *tx_power);
-- 
2.17.1


^ permalink raw reply related

* [RFC/RFT] cfg80211: decouple us from the RTNL
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 related

* Re: [PATCH -next] iwlwifi: dbg: work around clang bug by marking debug strings static
From: Michael Ellerman @ 2019-08-01  1:21 UTC (permalink / raw)
  To: Nick Desaulniers, kvalo, Luca Coelho
  Cc: Nick Desaulniers, Arnd Bergmann, Nathan Chancellor, Johannes Berg,
	Emmanuel Grumbach, Intel Linux Wireless, David S. Miller,
	Shahar S Matityahu, Sara Sharon, linux-wireless, netdev,
	linux-kernel, clang-built-linux
In-Reply-To: <20190712001708.170259-1-ndesaulniers@google.com>

Nick Desaulniers <ndesaulniers@google.com> writes:
> Commit r353569 in prerelease Clang-9 is producing a linkage failure:
>
> ld: drivers/net/wireless/intel/iwlwifi/fw/dbg.o:
> in function `_iwl_fw_dbg_apply_point':
> dbg.c:(.text+0x827a): undefined reference to `__compiletime_assert_2387'

This breakage is also seen in older GCC versions (v4.6.3 at least):

  drivers/net/wireless/intel/iwlwifi/fw/dbg.c: In function 'iwl_fw_dbg_info_apply.isra.10':
  drivers/net/wireless/intel/iwlwifi/fw/dbg.c:2445:3: error: call to '__compiletime_assert_2446' declared with attribute error: BUILD_BUG_ON failed: err_str[sizeof(err_str) - 2] != '\n'
  drivers/net/wireless/intel/iwlwifi/fw/dbg.c:2451:3: error: call to '__compiletime_assert_2452' declared with attribute error: BUILD_BUG_ON failed: err_str[sizeof(err_str) - 2] != '\n'
  drivers/net/wireless/intel/iwlwifi/fw/dbg.c: In function '_iwl_fw_dbg_apply_point':
  drivers/net/wireless/intel/iwlwifi/fw/dbg.c:2789:5: error: call to '__compiletime_assert_2790' declared with attribute error: BUILD_BUG_ON failed: invalid_ap_str[sizeof(invalid_ap_str) - 2] != '\n'
  drivers/net/wireless/intel/iwlwifi/fw/dbg.c:2800:5: error: call to '__compiletime_assert_2801' declared with attribute error: BUILD_BUG_ON failed: invalid_ap_str[sizeof(invalid_ap_str) - 2] != '\n'
 
  http://kisskb.ellerman.id.au/kisskb/buildresult/13902155/


Luca, you said this was already fixed in your internal tree, and the fix
would appear soon in next, but I don't see anything in linux-next?

cheers

^ permalink raw reply

* Re: [RFC/RFT] cfg80211: decouple us from the RTNL
From: Sid Hayn @ 2019-08-01  3:59 UTC (permalink / raw)
  To: Johannes Berg; +Cc: linux-wireless, Florian Westphal, Johannes Berg
In-Reply-To: <20190731220848.1045-1-johannes@sipsolutions.net>

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

* [PATCH] iwlwifi: dbg_ini: fix compile time assert build errors
From: Johannes Berg @ 2019-08-01  7:04 UTC (permalink / raw)
  To: linux-wireless; +Cc: Mauro Rossi

From: Mauro Rossi <issor.oruam@gmail.com>

This patch fixes and preserves existing code style, and readability,
for IWL_ERR() and IWL_WARN() macros invocations recently added in dbg.c

Fixes the following build errors with Android build system:

/home/utente/pie-x86_kernel/kernel/drivers/net/wireless/intel/iwlwifi/fw/dbg.c: In function '_iwl_fw_dbg_apply_point':
/home/utente/pie-x86_kernel/kernel/drivers/net/wireless/intel/iwlwifi/fw/dbg.c:2445:3:
error: call to '__compiletime_assert_2446' declared with attribute error: BUILD_BUG_ON failed: err_str[sizeof(err_str) - 2] != '\n'
/home/utente/pie-x86_kernel/kernel/drivers/net/wireless/intel/iwlwifi/fw/dbg.c:2451:3:
error: call to '__compiletime_assert_2452' declared with attribute error: BUILD_BUG_ON failed: err_str[sizeof(err_str) - 2] != '\n'
...
/home/utente/pie-x86_kernel/kernel/drivers/net/wireless/intel/iwlwifi/fw/dbg.c:2789:5:
error: call to '__compiletime_assert_2790' declared with attribute error: BUILD_BUG_ON failed: invalid_ap_str[sizeof(invalid_ap_str) - 2] != '\n'
/home/utente/pie-x86_kernel/kernel/drivers/net/wireless/intel/iwlwifi/fw/dbg.c:2800:5:
error: call to '__compiletime_assert_2801' declared with attribute error: BUILD_BUG_ON failed: invalid_ap_str[sizeof(invalid_ap_str) - 2] != '\n'

Fixes: 427ab6385cf3 ("iwlwifi: dbg_ini: enforce apply point early on buffer allocation tlv")
Fixes: 57d88b116175 ("iwlwifi: dbg_ini: support debug info TLV")
Signed-off-by: Mauro Rossi <issor.oruam@gmail.com>
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
---
 drivers/net/wireless/intel/iwlwifi/fw/dbg.c | 22 +++++++++++----------
 1 file changed, 12 insertions(+), 10 deletions(-)

diff --git a/drivers/net/wireless/intel/iwlwifi/fw/dbg.c b/drivers/net/wireless/intel/iwlwifi/fw/dbg.c
index e411ac98290d..4d81776f576d 100644
--- a/drivers/net/wireless/intel/iwlwifi/fw/dbg.c
+++ b/drivers/net/wireless/intel/iwlwifi/fw/dbg.c
@@ -2438,17 +2438,19 @@ static void iwl_fw_dbg_info_apply(struct iwl_fw_runtime *fwrt,
 {
 	u32 img_name_len = le32_to_cpu(dbg_info->img_name_len);
 	u32 dbg_cfg_name_len = le32_to_cpu(dbg_info->dbg_cfg_name_len);
-	const char err_str[] =
-		"WRT: ext=%d. Invalid %s name length %d, expected %d\n";
 
 	if (img_name_len != IWL_FW_INI_MAX_IMG_NAME_LEN) {
-		IWL_WARN(fwrt, err_str, ext, "image", img_name_len,
+		IWL_WARN(fwrt,
+			 "WRT: ext=%d. Invalid image name length %d, expected %d\n",
+			 ext, img_name_len,
 			 IWL_FW_INI_MAX_IMG_NAME_LEN);
 		return;
 	}
 
 	if (dbg_cfg_name_len != IWL_FW_INI_MAX_DBG_CFG_NAME_LEN) {
-		IWL_WARN(fwrt, err_str, ext, "debug cfg", dbg_cfg_name_len,
+		IWL_WARN(fwrt,
+			 "WRT: ext=%d. Invalid debug cfg name length %d, expected %d\n",
+			 ext, dbg_cfg_name_len,
 			 IWL_FW_INI_MAX_DBG_CFG_NAME_LEN);
 		return;
 	}
@@ -2775,8 +2777,6 @@ static void _iwl_fw_dbg_apply_point(struct iwl_fw_runtime *fwrt,
 		struct iwl_ucode_tlv *tlv = iter;
 		void *ini_tlv = (void *)tlv->data;
 		u32 type = le32_to_cpu(tlv->type);
-		const char invalid_ap_str[] =
-			"WRT: ext=%d. Invalid apply point %d for %s\n";
 
 		switch (type) {
 		case IWL_UCODE_TLV_TYPE_DEBUG_INFO:
@@ -2786,8 +2786,9 @@ static void _iwl_fw_dbg_apply_point(struct iwl_fw_runtime *fwrt,
 			struct iwl_fw_ini_allocation_data *buf_alloc = ini_tlv;
 
 			if (pnt != IWL_FW_INI_APPLY_EARLY) {
-				IWL_ERR(fwrt, invalid_ap_str, ext, pnt,
-					"buffer allocation");
+				IWL_ERR(fwrt,
+					"WRT: ext=%d. Invalid apply point %d for buffer allocation\n",
+					ext, pnt);
 				goto next;
 			}
 
@@ -2797,8 +2798,9 @@ static void _iwl_fw_dbg_apply_point(struct iwl_fw_runtime *fwrt,
 		}
 		case IWL_UCODE_TLV_TYPE_HCMD:
 			if (pnt < IWL_FW_INI_APPLY_AFTER_ALIVE) {
-				IWL_ERR(fwrt, invalid_ap_str, ext, pnt,
-					"host command");
+				IWL_ERR(fwrt,
+					"WRT: ext=%d. Invalid apply point %d for host command\n",
+					ext, pnt);
 				goto next;
 			}
 			iwl_fw_dbg_send_hcmd(fwrt, tlv, ext);
-- 
2.20.1


^ permalink raw reply related

* Re: [PATCH -next] iwlwifi: dbg: work around clang bug by marking debug strings static
From: Johannes Berg @ 2019-08-01  7:11 UTC (permalink / raw)
  To: Michael Ellerman, Nick Desaulniers, kvalo, Luca Coelho
  Cc: Arnd Bergmann, Nathan Chancellor, Emmanuel Grumbach,
	Intel Linux Wireless, David S. Miller, Shahar S Matityahu,
	Sara Sharon, linux-wireless, netdev, linux-kernel,
	clang-built-linux
In-Reply-To: <874l31r88y.fsf@concordia.ellerman.id.au>


> Luca, you said this was already fixed in your internal tree, and the fix
> would appear soon in next, but I don't see anything in linux-next?

Luca is still on vacation, but I just sent out a version of the patch we
had applied internally.

Also turns out it wasn't actually _fixed_, just _moved_, so those
internal patches wouldn't have helped anyway.

johannes


^ permalink raw reply

* [RFCv1 1/2] nl80211: Support >4096 byte NEW_WIPHY event nlmsg
From: Denis Kenzior @ 2019-08-01  7:14 UTC (permalink / raw)
  To: johannes, linux-wireless; +Cc: Denis Kenzior

For historical reasons, NEW_WIPHY messages generated by dumps or
GET_WIPHY commands were limited to 4096 bytes due to userspace tools
using limited buffers.  Once the sizes NEW_WIPHY messages exceeded these
sizes, split dumps were introduced.  All any non-legacy data was added
only to messages using split-dumps (including filtered dumps).

When unsolicited NEW_WIPHY events were introduced they inherited the
4096 byte limitation.  These messages thus do not contain any non-legacy
wiphy dump data.  This means that userspace still needs to re-dump the
information from the kernel after receiving such NEW_WIPHY event since
some of the information is missing.  Thus it is desirable to relax such
restrictions for these messages and include the non-legacy data in these
events.

It should be safe to assume that any users of these new unsolicited
NEW_WIPHY events are non-legacy clients, which can use a
larger receive buffer for netlink messages.  Since older, legacy clients
did not utilize NEW_WIPHY events (they did not exist), it is assumed
that even if the client receives such a message (even if truncated), no
harm would result and backwards-compatibility would be kept.
---
 net/wireless/nl80211.c | 49 ++++++++++++++++++++++++++++++++++--------
 1 file changed, 40 insertions(+), 9 deletions(-)

diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
index 1a107f29016b..6774072e836f 100644
--- a/net/wireless/nl80211.c
+++ b/net/wireless/nl80211.c
@@ -1839,6 +1839,7 @@ struct nl80211_dump_wiphy_state {
 	long start;
 	long split_start, band_start, chan_start, capa_start;
 	bool split;
+	bool large_message;
 };
 
 static int nl80211_send_wiphy(struct cfg80211_registered_device *rdev,
@@ -2168,12 +2169,23 @@ static int nl80211_send_wiphy(struct cfg80211_registered_device *rdev,
 		 * helps ensure that newly added capabilities don't break
 		 * older tools by overrunning their buffers.
 		 *
+		 * For unsolicited NEW_WIPHY notifications, it is assumed
+		 * that the client can handle larger messages.  Unsolicited
+		 * NEW_WIPHY notifications were added relatively recently
+		 * and it is not expected that older tools with limited
+		 * buffers would utilize these messages anyway.  E.g. even
+		 * if the message is truncated, it would not have been
+		 * used regardless.
+		 *
 		 * We still increment split_start so that in the split
 		 * case we'll continue with more data in the next round,
-		 * but break unconditionally so unsplit data stops here.
+		 * but break unless large_messages are requested, so
+		 * legacy unsplit data stops here.
 		 */
 		state->split_start++;
-		break;
+		if (state->split || !state->large_message)
+			break;
+		/* Fall through */
 	case 9:
 		if (rdev->wiphy.extended_capabilities &&
 		    (nla_put(msg, NL80211_ATTR_EXT_CAPA,
@@ -2215,7 +2227,9 @@ static int nl80211_send_wiphy(struct cfg80211_registered_device *rdev,
 		}
 
 		state->split_start++;
-		break;
+		if (state->split)
+			break;
+		/* Fall through */
 	case 10:
 		if (nl80211_send_coalesce(msg, rdev))
 			goto nla_put_failure;
@@ -2231,7 +2245,9 @@ static int nl80211_send_wiphy(struct cfg80211_registered_device *rdev,
 			goto nla_put_failure;
 
 		state->split_start++;
-		break;
+		if (state->split)
+			break;
+		/* Fall through */
 	case 11:
 		if (rdev->wiphy.n_vendor_commands) {
 			const struct nl80211_vendor_cmd_info *info;
@@ -2267,7 +2283,9 @@ static int nl80211_send_wiphy(struct cfg80211_registered_device *rdev,
 			nla_nest_end(msg, nested);
 		}
 		state->split_start++;
-		break;
+		if (state->split)
+			break;
+		/* Fall through */
 	case 12:
 		if (rdev->wiphy.flags & WIPHY_FLAG_HAS_CHANNEL_SWITCH &&
 		    nla_put_u8(msg, NL80211_ATTR_MAX_CSA_COUNTERS,
@@ -2309,7 +2327,9 @@ static int nl80211_send_wiphy(struct cfg80211_registered_device *rdev,
 		}
 
 		state->split_start++;
-		break;
+		if (state->split)
+			break;
+		/* Fall through */
 	case 13:
 		if (rdev->wiphy.num_iftype_ext_capab &&
 		    rdev->wiphy.iftype_ext_capab) {
@@ -2377,13 +2397,17 @@ static int nl80211_send_wiphy(struct cfg80211_registered_device *rdev,
 		}
 
 		state->split_start++;
-		break;
+		if (state->split)
+			break;
+		/* Fall through */
 	case 14:
 		if (nl80211_send_pmsr_capa(rdev, msg))
 			goto nla_put_failure;
 
 		state->split_start++;
-		break;
+		if (state->split)
+			break;
+		/* Fall through */
 	case 15:
 		if (rdev->wiphy.akm_suites &&
 		    nla_put(msg, NL80211_ATTR_AKM_SUITES,
@@ -14687,12 +14711,19 @@ void nl80211_notify_wiphy(struct cfg80211_registered_device *rdev,
 			  enum nl80211_commands cmd)
 {
 	struct sk_buff *msg;
+	size_t alloc_size;
 	struct nl80211_dump_wiphy_state state = {};
 
 	WARN_ON(cmd != NL80211_CMD_NEW_WIPHY &&
 		cmd != NL80211_CMD_DEL_WIPHY);
 
-	msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
+	if (cmd == NL80211_CMD_NEW_WIPHY) {
+		state.large_message = true;
+		alloc_size = 8192UL;
+	} else
+		alloc_size = NLMSG_DEFAULT_SIZE;
+
+	msg = nlmsg_new(alloc_size, GFP_KERNEL);
 	if (!msg)
 		return;
 
-- 
2.21.0


^ permalink raw reply related

* [RFCv1 2/2] nl80211: Don't split-dump for clients with large buffers
From: Denis Kenzior @ 2019-08-01  7:14 UTC (permalink / raw)
  To: johannes, linux-wireless; +Cc: Denis Kenzior
In-Reply-To: <20190801071455.4974-1-denkenz@gmail.com>

---
 net/wireless/nl80211.c | 17 +++++++++++++++++
 1 file changed, 17 insertions(+)

diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
index 6774072e836f..e1707cfd9076 100644
--- a/net/wireless/nl80211.c
+++ b/net/wireless/nl80211.c
@@ -2496,6 +2496,22 @@ static int nl80211_dump_wiphy(struct sk_buff *skb, struct netlink_callback *cb)
 			rtnl_unlock();
 			return ret;
 		}
+
+		/*
+		 * auto-detect support for large buffer sizes: af_netlink
+		 * will allocate skbufs larger than 4096 in cases where
+		 * it detects that the client receive buffer (given to
+		 * recvmsg) is bigger.  In such cases we can assume that
+		 * performing split dumps is wasteful since the client
+		 * can likely safely consume the entire un-split wiphy
+		 * message in one go without the extra message header
+		 * overhead.
+		 */
+		if (skb_tailroom(skb) > 4096) {
+			state->large_message = true;
+			state->split = false;
+		}
+
 		cb->args[0] = (long)state;
 	}
 
@@ -2529,6 +2545,7 @@ static int nl80211_dump_wiphy(struct sk_buff *skb, struct netlink_callback *cb)
 				 * We can then retry with the larger buffer.
 				 */
 				if ((ret == -ENOBUFS || ret == -EMSGSIZE) &&
+				    !state->large_message &&
 				    !skb->len && !state->split &&
 				    cb->min_dump_alloc < 4096) {
 					cb->min_dump_alloc = 4096;
-- 
2.21.0


^ permalink raw reply related

* [PATCH] mac80211: fix possible sta leak
From: Johannes Berg @ 2019-08-01  7:30 UTC (permalink / raw)
  To: linux-wireless; +Cc: Johannes Berg

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

If TDLS station addition is rejected, the sta memory is leaked.
Avoid this by moving the check before the allocation.

Fixes: 7ed5285396c2 ("mac80211: don't initiate TDLS connection if station is not associated to AP")
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
---
 net/mac80211/cfg.c | 9 +++++----
 1 file changed, 5 insertions(+), 4 deletions(-)

diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c
index 4d458067d80d..111c400199ec 100644
--- a/net/mac80211/cfg.c
+++ b/net/mac80211/cfg.c
@@ -1546,6 +1546,11 @@ static int ieee80211_add_station(struct wiphy *wiphy, struct net_device *dev,
 	if (is_multicast_ether_addr(mac))
 		return -EINVAL;
 
+	if (params->sta_flags_set & BIT(NL80211_STA_FLAG_TDLS_PEER) &&
+	    sdata->vif.type == NL80211_IFTYPE_STATION &&
+	    !sdata->u.mgd.associated)
+		return -EINVAL;
+
 	sta = sta_info_alloc(sdata, mac, GFP_KERNEL);
 	if (!sta)
 		return -ENOMEM;
@@ -1553,10 +1558,6 @@ static int ieee80211_add_station(struct wiphy *wiphy, struct net_device *dev,
 	if (params->sta_flags_set & BIT(NL80211_STA_FLAG_TDLS_PEER))
 		sta->sta.tdls = true;
 
-	if (sta->sta.tdls && sdata->vif.type == NL80211_IFTYPE_STATION &&
-	    !sdata->u.mgd.associated)
-		return -EINVAL;
-
 	err = sta_apply_parameters(local, sta, params);
 	if (err) {
 		sta_info_free(local, sta);
-- 
2.20.1


^ permalink raw reply related

* [PATCH 0/2] mt76x02: tidy up beacon shared code
From: Lorenzo Bianconi @ 2019-08-01  8:26 UTC (permalink / raw)
  To: nbd; +Cc: lorenzo.bianconi, sgruszka, linux-wireless

Do not copy beacon skb in mt76x02_mac_set_beacon_enable for usb devices
since it will be done in tbtt tasklet. Moreover tidy up a bit beacon
shared code

Lorenzo Bianconi (2):
  mt76: mt76x02: introduce mt76x02_pre_tbtt_enable and
    mt76x02_beacon_enable macros
  mt76: mt76x02: do not copy beacon skb in mt76x02_mac_set_beacon_enable

 .../net/wireless/mediatek/mt76/mt76x0/main.c  |  4 +-
 drivers/net/wireless/mediatek/mt76/mt76x02.h  |  5 ++
 .../wireless/mediatek/mt76/mt76x02_beacon.c   | 66 ++++++++-----------
 .../net/wireless/mediatek/mt76/mt76x02_mac.h  |  2 +-
 .../wireless/mediatek/mt76/mt76x2/usb_main.c  |  4 +-
 5 files changed, 37 insertions(+), 44 deletions(-)

-- 
2.21.0


^ permalink raw reply

* [PATCH 1/2] mt76: mt76x02: introduce mt76x02_pre_tbtt_enable and mt76x02_beacon_enable macros
From: Lorenzo Bianconi @ 2019-08-01  8:26 UTC (permalink / raw)
  To: nbd; +Cc: lorenzo.bianconi, sgruszka, linux-wireless
In-Reply-To: <cover.1564647482.git.lorenzo@kernel.org>

Improve code readability introducing mt76x02_pre_tbtt_enable and
mt76x02_beacon_enable utility macros

Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
---
 drivers/net/wireless/mediatek/mt76/mt76x0/main.c     | 4 ++--
 drivers/net/wireless/mediatek/mt76/mt76x02.h         | 5 +++++
 drivers/net/wireless/mediatek/mt76/mt76x02_beacon.c  | 7 +++----
 drivers/net/wireless/mediatek/mt76/mt76x2/usb_main.c | 4 ++--
 4 files changed, 12 insertions(+), 8 deletions(-)

diff --git a/drivers/net/wireless/mediatek/mt76/mt76x0/main.c b/drivers/net/wireless/mediatek/mt76/mt76x0/main.c
index d7bf7bc15e52..3bc665643e51 100644
--- a/drivers/net/wireless/mediatek/mt76/mt76x0/main.c
+++ b/drivers/net/wireless/mediatek/mt76/mt76x0/main.c
@@ -14,7 +14,7 @@ mt76x0_set_channel(struct mt76x02_dev *dev, struct cfg80211_chan_def *chandef)
 	int ret;
 
 	cancel_delayed_work_sync(&dev->cal_work);
-	dev->beacon_ops->pre_tbtt_enable(dev, false);
+	mt76x02_pre_tbtt_enable(dev, false);
 	if (mt76_is_mmio(dev))
 		tasklet_disable(&dev->dfs_pd.dfs_tasklet);
 
@@ -31,7 +31,7 @@ mt76x0_set_channel(struct mt76x02_dev *dev, struct cfg80211_chan_def *chandef)
 		mt76x02_dfs_init_params(dev);
 		tasklet_enable(&dev->dfs_pd.dfs_tasklet);
 	}
-	dev->beacon_ops->pre_tbtt_enable(dev, true);
+	mt76x02_pre_tbtt_enable(dev, true);
 
 	mt76_txq_schedule_all(&dev->mt76);
 
diff --git a/drivers/net/wireless/mediatek/mt76/mt76x02.h b/drivers/net/wireless/mediatek/mt76/mt76x02.h
index f7fd53a1738a..0d562d8e4be0 100644
--- a/drivers/net/wireless/mediatek/mt76/mt76x02.h
+++ b/drivers/net/wireless/mediatek/mt76/mt76x02.h
@@ -75,6 +75,11 @@ struct mt76x02_beacon_ops {
 	void (*beacon_enable) (struct mt76x02_dev *, bool);
 };
 
+#define mt76x02_beacon_enable(dev, enable)	\
+	(dev)->beacon_ops->beacon_enable((dev), (enable))
+#define mt76x02_pre_tbtt_enable(dev, enable)	\
+	(dev)->beacon_ops->pre_tbtt_enable((dev), (enable))
+
 struct mt76x02_dev {
 	struct mt76_dev mt76; /* must be first */
 
diff --git a/drivers/net/wireless/mediatek/mt76/mt76x02_beacon.c b/drivers/net/wireless/mediatek/mt76/mt76x02_beacon.c
index d61c686e08de..089aab74ae99 100644
--- a/drivers/net/wireless/mediatek/mt76/mt76x02_beacon.c
+++ b/drivers/net/wireless/mediatek/mt76/mt76x02_beacon.c
@@ -141,8 +141,7 @@ __mt76x02_mac_set_beacon_enable(struct mt76x02_dev *dev, u8 vif_idx,
 	      MT_BEACON_TIME_CFG_TBTT_EN |
 	      MT_BEACON_TIME_CFG_TIMER_EN;
 	mt76_rmw(dev, MT_BEACON_TIME_CFG, reg, reg * en);
-
-	dev->beacon_ops->beacon_enable(dev, en);
+	mt76x02_beacon_enable(dev, en);
 }
 
 void mt76x02_mac_set_beacon_enable(struct mt76x02_dev *dev,
@@ -151,7 +150,7 @@ void mt76x02_mac_set_beacon_enable(struct mt76x02_dev *dev,
 	u8 vif_idx = ((struct mt76x02_vif *)vif->drv_priv)->idx;
 	struct sk_buff *skb = NULL;
 
-	dev->beacon_ops->pre_tbtt_enable(dev, false);
+	mt76x02_pre_tbtt_enable(dev, false);
 
 	if (mt76_is_usb(dev))
 		skb = ieee80211_beacon_get(mt76_hw(dev), vif);
@@ -161,7 +160,7 @@ void mt76x02_mac_set_beacon_enable(struct mt76x02_dev *dev,
 
 	__mt76x02_mac_set_beacon_enable(dev, vif_idx, val, skb);
 
-	dev->beacon_ops->pre_tbtt_enable(dev, true);
+	mt76x02_pre_tbtt_enable(dev, true);
 }
 
 void
diff --git a/drivers/net/wireless/mediatek/mt76/mt76x2/usb_main.c b/drivers/net/wireless/mediatek/mt76/mt76x2/usb_main.c
index e4dfc3bea3c5..7e9e44b96671 100644
--- a/drivers/net/wireless/mediatek/mt76/mt76x2/usb_main.c
+++ b/drivers/net/wireless/mediatek/mt76/mt76x2/usb_main.c
@@ -48,7 +48,7 @@ mt76x2u_set_channel(struct mt76x02_dev *dev,
 	int err;
 
 	cancel_delayed_work_sync(&dev->cal_work);
-	dev->beacon_ops->pre_tbtt_enable(dev, false);
+	mt76x02_pre_tbtt_enable(dev, false);
 
 	mutex_lock(&dev->mt76.mutex);
 	set_bit(MT76_RESET, &dev->mt76.state);
@@ -64,7 +64,7 @@ mt76x2u_set_channel(struct mt76x02_dev *dev,
 	clear_bit(MT76_RESET, &dev->mt76.state);
 	mutex_unlock(&dev->mt76.mutex);
 
-	dev->beacon_ops->pre_tbtt_enable(dev, true);
+	mt76x02_pre_tbtt_enable(dev, true);
 	mt76_txq_schedule_all(&dev->mt76);
 
 	return err;
-- 
2.21.0


^ permalink raw reply related

* [PATCH 2/2] mt76: mt76x02: do not copy beacon skb in mt76x02_mac_set_beacon_enable
From: Lorenzo Bianconi @ 2019-08-01  8:26 UTC (permalink / raw)
  To: nbd; +Cc: lorenzo.bianconi, sgruszka, linux-wireless
In-Reply-To: <cover.1564647482.git.lorenzo@kernel.org>

Do not copy beacon skb in mt76x02_mac_set_beacon_enable for usb devices
since it will be done in mt76x02_update_beacon_iter. Moreover squash
mt76x02_mac_set_beacon_enable and __mt76x02_mac_set_beacon_enable since
the latter is run just by mt76x02_mac_set_beacon_enable

Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
---
 .../wireless/mediatek/mt76/mt76x02_beacon.c   | 61 ++++++++-----------
 .../net/wireless/mediatek/mt76/mt76x02_mac.h  |  2 +-
 2 files changed, 26 insertions(+), 37 deletions(-)

diff --git a/drivers/net/wireless/mediatek/mt76/mt76x02_beacon.c b/drivers/net/wireless/mediatek/mt76/mt76x02_beacon.c
index 089aab74ae99..ae2565071137 100644
--- a/drivers/net/wireless/mediatek/mt76/mt76x02_beacon.c
+++ b/drivers/net/wireless/mediatek/mt76/mt76x02_beacon.c
@@ -115,51 +115,40 @@ int mt76x02_mac_set_beacon(struct mt76x02_dev *dev, u8 vif_idx,
 }
 EXPORT_SYMBOL_GPL(mt76x02_mac_set_beacon);
 
-static void
-__mt76x02_mac_set_beacon_enable(struct mt76x02_dev *dev, u8 vif_idx,
-				bool val, struct sk_buff *skb)
-{
-	u8 old_mask = dev->mt76.beacon_mask;
-	bool en;
-	u32 reg;
-
-	if (val) {
-		dev->mt76.beacon_mask |= BIT(vif_idx);
-		if (skb)
-			mt76x02_mac_set_beacon(dev, vif_idx, skb);
-	} else {
-		dev->mt76.beacon_mask &= ~BIT(vif_idx);
-		mt76x02_mac_set_beacon(dev, vif_idx, NULL);
-	}
-
-	if (!!old_mask == !!dev->mt76.beacon_mask)
-		return;
-
-	en = dev->mt76.beacon_mask;
-
-	reg = MT_BEACON_TIME_CFG_BEACON_TX |
-	      MT_BEACON_TIME_CFG_TBTT_EN |
-	      MT_BEACON_TIME_CFG_TIMER_EN;
-	mt76_rmw(dev, MT_BEACON_TIME_CFG, reg, reg * en);
-	mt76x02_beacon_enable(dev, en);
-}
-
 void mt76x02_mac_set_beacon_enable(struct mt76x02_dev *dev,
-				   struct ieee80211_vif *vif, bool val)
+				   struct ieee80211_vif *vif, bool enable)
 {
-	u8 vif_idx = ((struct mt76x02_vif *)vif->drv_priv)->idx;
-	struct sk_buff *skb = NULL;
+	struct mt76x02_vif *mvif = (struct mt76x02_vif *)vif->drv_priv;
+	u8 old_mask = dev->mt76.beacon_mask;
 
 	mt76x02_pre_tbtt_enable(dev, false);
 
-	if (mt76_is_usb(dev))
-		skb = ieee80211_beacon_get(mt76_hw(dev), vif);
-
 	if (!dev->mt76.beacon_mask)
 		dev->tbtt_count = 0;
 
-	__mt76x02_mac_set_beacon_enable(dev, vif_idx, val, skb);
+	if (enable) {
+		dev->mt76.beacon_mask |= BIT(mvif->idx);
+	} else {
+		dev->mt76.beacon_mask &= ~BIT(mvif->idx);
+		mt76x02_mac_set_beacon(dev, mvif->idx, NULL);
+	}
 
+	if (!!old_mask == !!dev->mt76.beacon_mask)
+		goto out;
+
+	if (dev->mt76.beacon_mask)
+		mt76_set(dev, MT_BEACON_TIME_CFG,
+			 MT_BEACON_TIME_CFG_BEACON_TX |
+			 MT_BEACON_TIME_CFG_TBTT_EN |
+			 MT_BEACON_TIME_CFG_TIMER_EN);
+	else
+		mt76_clear(dev, MT_BEACON_TIME_CFG,
+			   MT_BEACON_TIME_CFG_BEACON_TX |
+			   MT_BEACON_TIME_CFG_TBTT_EN |
+			   MT_BEACON_TIME_CFG_TIMER_EN);
+	mt76x02_beacon_enable(dev, !!dev->mt76.beacon_mask);
+
+out:
 	mt76x02_pre_tbtt_enable(dev, true);
 }
 
diff --git a/drivers/net/wireless/mediatek/mt76/mt76x02_mac.h b/drivers/net/wireless/mediatek/mt76/mt76x02_mac.h
index cb39da79527a..4a84db7e8522 100644
--- a/drivers/net/wireless/mediatek/mt76/mt76x02_mac.h
+++ b/drivers/net/wireless/mediatek/mt76/mt76x02_mac.h
@@ -207,7 +207,7 @@ void mt76x02_mac_set_bssid(struct mt76x02_dev *dev, u8 idx, const u8 *addr);
 int mt76x02_mac_set_beacon(struct mt76x02_dev *dev, u8 vif_idx,
 			   struct sk_buff *skb);
 void mt76x02_mac_set_beacon_enable(struct mt76x02_dev *dev,
-				   struct ieee80211_vif *vif, bool val);
+				   struct ieee80211_vif *vif, bool enable);
 
 void mt76x02_edcca_init(struct mt76x02_dev *dev);
 #endif
-- 
2.21.0


^ permalink raw reply related

* Re: [RFCv1 2/2] nl80211: Don't split-dump for clients with large buffers
From: Johannes Berg @ 2019-08-01  9:13 UTC (permalink / raw)
  To: Denis Kenzior, linux-wireless
In-Reply-To: <20190801071455.4974-2-denkenz@gmail.com>

On Thu, 2019-08-01 at 02:14 -0500, Denis Kenzior wrote:

> +		/*
> +		 * auto-detect support for large buffer sizes: af_netlink
> +		 * will allocate skbufs larger than 4096 in cases where
> +		 * it detects that the client receive buffer (given to
> +		 * recvmsg) is bigger.  In such cases we can assume that
> +		 * performing split dumps is wasteful since the client
> +		 * can likely safely consume the entire un-split wiphy
> +		 * message in one go without the extra message header
> +		 * overhead.
> +		 */
> +		if (skb_tailroom(skb) > 4096) {
> +			state->large_message = true;
> +			state->split = false;
> +		}

Hmm. That's kinda a neat idea, but I don't think it's a good idea. Have
you checked how long the message is now?

Since we *did* in fact hit the previous limit, and have added a *lot* of
things since then (this was years ago, after all), I wouldn't be
surprised if we're reasonably close to the new limit you propose even
now already.

Also, keep in mind that there are some devices that just have an
*enormous* amount of channels, and that's only going to increase (right
now with 6/7 GHz, etc.)

So in general, given all the variable things we have here, all this
buffer size estimation doesn't seem very robust to me. You could have
any number of variable things in a message:
 * channel list - which we alleviated somewhat by having a separate
   channel dump, so not all data is included here (which I guess you'll
   complain about next :P)
 * nl80211_send_mgmt_stypes() things are also a bit variable, and we
   keep adding interface types etc., and some devices may support lots
   of frames (there's an upper bound, but it's not that small)
 * interface combinations - only getting more complex with more complex
   devices and more concurrency use cases
 * vendor commands have no real limit
 * I'm sure measurement use cases will only increases
 * and generally of course we keep adding to everything


Also, I don't really buy the *need* for this since you're just removing
a few kernel/user roundtrips here when new devices are discovered, a
rare event. The parsing isn't really any more complicated for the
userspace side.


Regarding the other patch, I think most of the above also applies there.
I can sort of see how you think it's *nice* to have all the data right
there, but I really don't see why you're so hung up about having to
request the full information ... And I really don't want to see this hit
the wall again in the future, in some weird scenarios with devices that
have lots of <any of the above information>.


> It should be safe to assume that any users of these new unsolicited
> NEW_WIPHY events are non-legacy clients, which can use a
> larger receive buffer for netlink messages.  Since older, legacy clients
> did not utilize NEW_WIPHY events (they did not exist), it is assumed
> that even if the client receives such a message (even if truncated), no
> harm would result and backwards-compatibility would be kept.

Interesting idea, but no, in general you cannot assume that. Older
clients might have added support for NEW_WIPHY without fixing the split
dumps first ...

Also, you mention in the code that messages are truncated, but I'm
pretty sure they're just dropped, not truncated.

And finally, I also see no reason to send out many KB of data for what
might in the end (e.g. in iw) just be a debug message.


But really I think the thing that kills this proposal is the fact that
it reintroduces a message size limit (even if higher now) that we're
somewhat likely to hit in the future.

johannes


^ permalink raw reply

* RE: [PATCH] rtw88: pci: enable MSI interrupt
From: Tony Chuang @ 2019-08-01  9:21 UTC (permalink / raw)
  To: Brian Norris
  Cc: kvalo@codeaurora.org, linux-wireless@vger.kernel.org,
	jano.vesely@gmail.com
In-Reply-To: <20190730195703.GA224792@google.com>

> Subject: Re: [PATCH] rtw88: pci: enable MSI interrupt
> 
> Hi,
> 
> On Tue, Jul 30, 2019 at 07:50:14PM +0800, yhchuang@realtek.com wrote:
> > From: Yu-Yen Ting <steventing@realtek.com>
> >
> > MSI interrupt should be enabled on certain platform.
> >
> > Add a module parameter disable_msi to disable MSI interrupt,
> > driver will then use legacy interrupt instead.
> > And the interrupt mode is not able to change at run-time, so
> > the module parameter is read only.
> 
> Well, if we unbind/rebind the device, probe() will pick up the new
> value. e.g.:
> 
>   echo '0000:01:00.0' > /sys/bus/pci/drivers/rtw_pci/unbind
>   echo '0000:01:00.0' > /sys/bus/pci/drivers/rtw_pci/bind
> 
> So is it really necessary to mark read-only? I think there's a general
> understanding that module parameters are not always "immediately
> effective."


If there's a general understanding of not always effective immediately,
I think I can change the file mode to 0644.


> 
> > Tested-by: Ján Veselý <jano.vesely@gmail.com>
> > Signed-off-by: Yu-Yen Ting <steventing@realtek.com>
> > Signed-off-by: Yan-Hsuan Chuang <yhchuang@realtek.com>
> > ---
> >  drivers/net/wireless/realtek/rtw88/pci.c | 51
> ++++++++++++++++++++++++++++++--
> >  drivers/net/wireless/realtek/rtw88/pci.h |  1 +
> >  2 files changed, 49 insertions(+), 3 deletions(-)
> >
> > diff --git a/drivers/net/wireless/realtek/rtw88/pci.c
> b/drivers/net/wireless/realtek/rtw88/pci.c
> > index 23dd06a..25410f6 100644
> > --- a/drivers/net/wireless/realtek/rtw88/pci.c
> > +++ b/drivers/net/wireless/realtek/rtw88/pci.c
> > @@ -874,6 +878,7 @@ static irqreturn_t rtw_pci_interrupt_handler(int irq,
> void *dev)
> >  	if (!rtwpci->irq_enabled)
> >  		goto out;
> >
> > +	rtw_pci_disable_interrupt(rtwdev, rtwpci);
> 
> Why exactly do you have to mask interrupts during the ISR? Is there a
> race in rtw_pci_irq_recognized() or something?


I think there is a race between SW and HW, if we do not stop the
IRQ first, write 1 clear will make the interrupt to be lost.


> 
> >  	rtw_pci_irq_recognized(rtwdev, rtwpci, irq_status);
> >
> >  	if (irq_status[0] & IMR_MGNTDOK)
> 
> ...
> 
> 
> Otherwise, looks fine:
> 
> Reviewed-by: Brian Norris <briannorris@chromium.org>
> 


Yan-Hsuan

^ permalink raw reply

* Re: [RFCv1 2/2] nl80211: Don't split-dump for clients with large buffers
From: Marcel Holtmann @ 2019-08-01 10:36 UTC (permalink / raw)
  To: Johannes Berg; +Cc: Denis Kenzior, linux-wireless
In-Reply-To: <fec97dd04fe0b91a2d0d046839344f62cd8e127a.camel@sipsolutions.net>

Hi Johannes,

>> +		/*
>> +		 * auto-detect support for large buffer sizes: af_netlink
>> +		 * will allocate skbufs larger than 4096 in cases where
>> +		 * it detects that the client receive buffer (given to
>> +		 * recvmsg) is bigger.  In such cases we can assume that
>> +		 * performing split dumps is wasteful since the client
>> +		 * can likely safely consume the entire un-split wiphy
>> +		 * message in one go without the extra message header
>> +		 * overhead.
>> +		 */
>> +		if (skb_tailroom(skb) > 4096) {
>> +			state->large_message = true;
>> +			state->split = false;
>> +		}
> 
> Hmm. That's kinda a neat idea, but I don't think it's a good idea. Have
> you checked how long the message is now?
> 
> Since we *did* in fact hit the previous limit, and have added a *lot* of
> things since then (this was years ago, after all), I wouldn't be
> surprised if we're reasonably close to the new limit you propose even
> now already.
> 
> Also, keep in mind that there are some devices that just have an
> *enormous* amount of channels, and that's only going to increase (right
> now with 6/7 GHz, etc.)
> 
> So in general, given all the variable things we have here, all this
> buffer size estimation doesn't seem very robust to me. You could have
> any number of variable things in a message:
> * channel list - which we alleviated somewhat by having a separate
>   channel dump, so not all data is included here (which I guess you'll
>   complain about next :P)
> * nl80211_send_mgmt_stypes() things are also a bit variable, and we
>   keep adding interface types etc., and some devices may support lots
>   of frames (there's an upper bound, but it's not that small)
> * interface combinations - only getting more complex with more complex
>   devices and more concurrency use cases
> * vendor commands have no real limit
> * I'm sure measurement use cases will only increases
> * and generally of course we keep adding to everything
> 
> 
> Also, I don't really buy the *need* for this since you're just removing
> a few kernel/user roundtrips here when new devices are discovered, a
> rare event. The parsing isn't really any more complicated for the
> userspace side.

that is an argument that is coming to bite you. Forcing multiple roundtrips or even collecting multiple split message for some ancient legacy client behavior is just silly. If clients provide larger buffers, we should start using them.

I have proven a long time ago that round-trips are causing delays and creating visible user experience issues. Look up my DHCP presentation from either LinuxCon or PlumbersConf. One round-trip leads to another and at some point you end up with seconds wasted because you want to sit here and ignore efforts in improving the situation.

> Regarding the other patch, I think most of the above also applies there.
> I can sort of see how you think it's *nice* to have all the data right
> there, but I really don't see why you're so hung up about having to
> request the full information ... And I really don't want to see this hit
> the wall again in the future, in some weird scenarios with devices that
> have lots of <any of the above information>.
> 
> 
>> It should be safe to assume that any users of these new unsolicited
>> NEW_WIPHY events are non-legacy clients, which can use a
>> larger receive buffer for netlink messages.  Since older, legacy clients
>> did not utilize NEW_WIPHY events (they did not exist), it is assumed
>> that even if the client receives such a message (even if truncated), no
>> harm would result and backwards-compatibility would be kept.
> 
> Interesting idea, but no, in general you cannot assume that. Older
> clients might have added support for NEW_WIPHY without fixing the split
> dumps first ...
> 
> Also, you mention in the code that messages are truncated, but I'm
> pretty sure they're just dropped, not truncated.
> 
> And finally, I also see no reason to send out many KB of data for what
> might in the end (e.g. in iw) just be a debug message.

Actually iw is just a dev tool. It should not be run in production and so that is not an argument. Any proper client that cares about your WiFi connections will want this information.

> But really I think the thing that kills this proposal is the fact that
> it reintroduces a message size limit (even if higher now) that we're
> somewhat likely to hit in the future.

Maybe we need to accept that current nl80211 API is broken and start over. Or we should at least start deprecating commands and replacing them with new ones that are doing a better job for clients that actually behave properly.

Regards

Marcel


^ permalink raw reply

* Re: [RFCv1 2/2] nl80211: Don't split-dump for clients with large buffers
From: Johannes Berg @ 2019-08-01 11:04 UTC (permalink / raw)
  To: Marcel Holtmann; +Cc: Denis Kenzior, linux-wireless
In-Reply-To: <29DA5CC8-9CAF-4F9A-933C-ED3D6B25FA4E@holtmann.org>

Hi Marcel,

> > Also, I don't really buy the *need* for this since you're just removing
> > a few kernel/user roundtrips here when new devices are discovered, a
> > rare event. The parsing isn't really any more complicated for the
> > userspace side.
> 
> that is an argument that is coming to bite you. Forcing multiple
> roundtrips or even collecting multiple split message for some ancient
> legacy client behavior is just silly. If clients provide larger
> buffers, we should start using them.

I'm not arguing legacy/old client behaviour.

> I have proven a long time ago that round-trips are causing delays and
> creating visible user experience issues. Look up my DHCP presentation
> from either LinuxCon or PlumbersConf. One round-trip leads to another
> and at some point you end up with seconds wasted because you want to
> sit here and ignore efforts in improving the situation.

Comparing network roundtrips to local kernel access isn't exactly a very
good comparison.

> > And finally, I also see no reason to send out many KB of data for what
> > might in the end (e.g. in iw) just be a debug message.
> 
> Actually iw is just a dev tool. It should not be run in production and
> so that is not an argument. Any proper client that cares about your
> WiFi connections will want this information.

Again, this isn't an argument. I said wpa_s is an example. Any other
number of tools works, even wpa_s. Heck, probably even iwd, when
configured to not care about some devices (unless you can't even make it
ignore devices, which I'd consider a deficiency in its own right).

> > But really I think the thing that kills this proposal is the fact that
> > it reintroduces a message size limit (even if higher now) that we're
> > somewhat likely to hit in the future.
> 
> Maybe we need to accept that current nl80211 API is broken and start
> over. Or we should at least start deprecating commands and replacing
> them with new ones that are doing a better job for clients that
> actually behave properly.

I know you love throwing things away and rewriting them, but you're not
going to solve the problem.

I suggest you re-read my email and actually reply to it, rather than
throwing out bullet points.

Frankly, I'm tired of having a discussion where all you do is accuse me
of not caring about the problem, but then you don't even respond to any
arguments.

johannes


^ permalink raw reply

* Re: [PATCH v5 1/2] nl80211: Add support for EDMG channels
From: Alexei Lazar @ 2019-08-01 13:15 UTC (permalink / raw)
  To: Johannes Berg; +Cc: linux-wireless, wil6210
In-Reply-To: <4deeee0289e706d5e594d00d987c233fb8fb25b5.camel@sipsolutions.net>

On 2019-07-26 16:29, Johannes Berg wrote:
> Hi Alexei,
> 
> I'm not exactly sure why, but this breaks practically all connectivity
> on 2.4 and 5 GHz channels (at least in hwsim tests).
> 
> Please check and resubmit.

Sorry for this issue, we have run hwsim test through wpa_supplicant and
catch small issue that caused the tests to fail - fix raised.
Can you please share with us how you run the hwsim, you also used the
wpa_supplicant unit tests or some other tool?

> 
> It'd also be good to reformat the commit log a bit, maybe adding
> paragraphs, it's a bit of a "wall of text".

Done.

> 
> Thanks,
> johannes

-- 
Alexei Lazar
Qualcomm Israel, on behalf of Qualcomm Innovation Center, Inc.
The Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum a
Linux Foundation Collaborative Project

^ permalink raw reply

* [PATCH v6 0/2] Add support for new channels on 60GHz band
From: Alexei Avshalom Lazar @ 2019-08-01 13:16 UTC (permalink / raw)
  To: Johannes Berg; +Cc: Alexei Avshalom Lazar, linux-wireless, wil6210

The following set of patches add support for new channels on
60GHz band and EDMG channels:
Changelog:
V6:
- update commit message
- initialize chandef variable
V5:
- Addressed to comments from the community
- Added enum for bw_config
- Updated nla_policy to NLA_POLICY_RANGE()
V4:
- Addressed to comments from the community
- Updated comments and variable name
V3:
- Addressed to comments from the community
- Added 60G check for verifying some EDMG cases are relevant
- Used the ieee80211_sta_edmg_cap struct as the edmg channel configuration
- Updated nla_policy to NLA_POLICY_RANGE()
V2
- Addressed to comments from the community
- Align to latest Spec release

Alexei Avshalom Lazar (2):
  nl80211: Add support for EDMG channels
  wil6210: Add EDMG channel support

 drivers/net/wireless/ath/wil6210/cfg80211.c  | 205 +++++++++++++++++++++++++--
 drivers/net/wireless/ath/wil6210/txrx_edma.c |   2 +
 drivers/net/wireless/ath/wil6210/txrx_edma.h |   6 +
 drivers/net/wireless/ath/wil6210/wil6210.h   |   8 +-
 drivers/net/wireless/ath/wil6210/wmi.c       |   5 +-
 drivers/net/wireless/ath/wil6210/wmi.h       |  26 +++-
 include/net/cfg80211.h                       |  86 ++++++++++-
 include/uapi/linux/nl80211.h                 |  24 ++++
 net/mac80211/mlme.c                          |   2 +-
 net/mac80211/status.c                        |   2 +-
 net/wireless/chan.c                          | 159 +++++++++++++++++++++
 net/wireless/nl80211.c                       |  37 +++++
 net/wireless/util.c                          |  42 +++++-
 13 files changed, 584 insertions(+), 20 deletions(-)

-- 
2.7.4


^ permalink raw reply

* [PATCH v6 1/2] nl80211: Add support for EDMG channels
From: Alexei Avshalom Lazar @ 2019-08-01 13:16 UTC (permalink / raw)
  To: Johannes Berg; +Cc: Alexei Avshalom Lazar, linux-wireless, wil6210
In-Reply-To: <1564665374-2856-1-git-send-email-ailizaro@codeaurora.org>

802.11ay specification defines Enhanced Directional Multi-Gigabit
(EDMG) STA and AP which allow channel bonding of 2 channels and more.

Introduce new NL attributes that are needed for enabling and
configuring EDMG support.

Two new attributes are used by kernel to publish driver's EDMG
capabilities to the userspace:
NL80211_BAND_ATTR_EDMG_CHANNELS - bitmap field that indicates the 2.16
GHz channel(s) that are supported by the driver.
When this attribute is not set it means driver does not support EDMG.
NL80211_BAND_ATTR_EDMG_BW_CONFIG - represent the channel bandwidth
configurations supported by the driver.

Additional two new attributes are used by the userspace for connect
command and for AP configuration:
NL80211_ATTR_WIPHY_EDMG_CHANNELS
NL80211_ATTR_WIPHY_EDMG_BW_CONFIG

New rate info flag - RATE_INFO_FLAGS_EDMG, can be reported from driver
and used for bitrate calculation that will take into account EDMG
according to the 802.11ay specification.
---
 drivers/net/wireless/ath/wil6210/cfg80211.c |   2 +-
 include/net/cfg80211.h                      |  86 ++++++++++++++-
 include/uapi/linux/nl80211.h                |  24 +++++
 net/mac80211/mlme.c                         |   2 +-
 net/mac80211/status.c                       |   2 +-
 net/wireless/chan.c                         | 159 ++++++++++++++++++++++++++++
 net/wireless/nl80211.c                      |  37 +++++++
 net/wireless/util.c                         |  42 +++++++-
 8 files changed, 346 insertions(+), 8 deletions(-)

diff --git a/drivers/net/wireless/ath/wil6210/cfg80211.c b/drivers/net/wireless/ath/wil6210/cfg80211.c
index 2fb4258..2414f57 100644
--- a/drivers/net/wireless/ath/wil6210/cfg80211.c
+++ b/drivers/net/wireless/ath/wil6210/cfg80211.c
@@ -351,7 +351,7 @@ int wil_cid_fill_sinfo(struct wil6210_vif *vif, int cid,
 			BIT_ULL(NL80211_STA_INFO_RX_DROP_MISC) |
 			BIT_ULL(NL80211_STA_INFO_TX_FAILED);
 
-	sinfo->txrate.flags = RATE_INFO_FLAGS_60G;
+	sinfo->txrate.flags = RATE_INFO_FLAGS_DMG;
 	sinfo->txrate.mcs = le16_to_cpu(reply.evt.bf_mcs);
 	sinfo->rxrate.mcs = stats->last_mcs_rx;
 	sinfo->rx_bytes = stats->rx_bytes;
diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h
index 26e2ad2..616dfcd 100644
--- a/include/net/cfg80211.h
+++ b/include/net/cfg80211.h
@@ -318,6 +318,60 @@ struct ieee80211_sband_iftype_data {
 };
 
 /**
+ * enum ieee80211_edmg_bw_config - allowed channel bandwidth configurations
+ *
+ * @IEEE80211_EDMG_BW_CONFIG_4: 2.16GHz
+ * @IEEE80211_EDMG_BW_CONFIG_5: 2.16GHz and 4.32GHz
+ * @IEEE80211_EDMG_BW_CONFIG_6: 2.16GHz, 4.32GHz and 6.48GHz
+ * @IEEE80211_EDMG_BW_CONFIG_7: 2.16GHz, 4.32GHz, 6.48GHz and 8.64GHz
+ * @IEEE80211_EDMG_BW_CONFIG_8: 2.16GHz and 2.16GHz + 2.16GHz
+ * @IEEE80211_EDMG_BW_CONFIG_9: 2.16GHz, 4.32GHz and 2.16GHz + 2.16GHz
+ * @IEEE80211_EDMG_BW_CONFIG_10: 2.16GHz, 4.32GHz, 6.48GHz and 2.16GHz+2.16GHz
+ * @IEEE80211_EDMG_BW_CONFIG_11: 2.16GHz, 4.32GHz, 6.48GHz, 8.64GHz and
+ *	2.16GHz+2.16GHz
+ * @IEEE80211_EDMG_BW_CONFIG_12: 2.16GHz, 2.16GHz + 2.16GHz and
+ *	4.32GHz + 4.32GHz
+ * @IEEE80211_EDMG_BW_CONFIG_13: 2.16GHz, 4.32GHz, 2.16GHz + 2.16GHz and
+ *	4.32GHz + 4.32GHz
+ * @IEEE80211_EDMG_BW_CONFIG_14: 2.16GHz, 4.32GHz, 6.48GHz, 2.16GHz + 2.16GHz
+ *	and 4.32GHz + 4.32GHz
+ * @IEEE80211_EDMG_BW_CONFIG_15: 2.16GHz, 4.32GHz, 6.48GHz, 8.64GHz,
+ *	2.16GHz + 2.16GHz and 4.32GHz + 4.32GHz
+ */
+enum ieee80211_edmg_bw_config {
+	IEEE80211_EDMG_BW_CONFIG_4	= 4,
+	IEEE80211_EDMG_BW_CONFIG_5	= 5,
+	IEEE80211_EDMG_BW_CONFIG_6	= 6,
+	IEEE80211_EDMG_BW_CONFIG_7	= 7,
+	IEEE80211_EDMG_BW_CONFIG_8	= 8,
+	IEEE80211_EDMG_BW_CONFIG_9	= 9,
+	IEEE80211_EDMG_BW_CONFIG_10	= 10,
+	IEEE80211_EDMG_BW_CONFIG_11	= 11,
+	IEEE80211_EDMG_BW_CONFIG_12	= 12,
+	IEEE80211_EDMG_BW_CONFIG_13	= 13,
+	IEEE80211_EDMG_BW_CONFIG_14	= 14,
+	IEEE80211_EDMG_BW_CONFIG_15	= 15,
+};
+
+/**
+ * struct ieee80211_edmg - EDMG configuration
+ *
+ * This structure describes most essential parameters needed
+ * to describe 802.11ay EDMG configuration
+ *
+ * @channels: bitmap that indicates the 2.16 GHz channel(s)
+ *	that are allowed to be used for transmissions.
+ *	Bit 0 indicates channel 1, bit 1 indicates channel 2, etc.
+ *	Set to 0 indicate EDMG not supported.
+ * @bw_config: Channel BW Configuration subfield encodes
+ *	the allowed channel bandwidth configurations
+ */
+struct ieee80211_edmg {
+	u8 channels;
+	enum ieee80211_edmg_bw_config bw_config;
+};
+
+/**
  * struct ieee80211_supported_band - frequency band definition
  *
  * This structure describes a frequency band a wiphy
@@ -333,6 +387,7 @@ struct ieee80211_sband_iftype_data {
  * @n_bitrates: Number of bitrates in @bitrates
  * @ht_cap: HT capabilities in this band
  * @vht_cap: VHT capabilities in this band
+ * @edmg_cap: EDMG capabilities in this band
  * @n_iftype_data: number of iftype data entries
  * @iftype_data: interface type data entries.  Note that the bits in
  *	@types_mask inside this structure cannot overlap (i.e. only
@@ -347,6 +402,7 @@ struct ieee80211_supported_band {
 	int n_bitrates;
 	struct ieee80211_sta_ht_cap ht_cap;
 	struct ieee80211_sta_vht_cap vht_cap;
+	struct ieee80211_edmg edmg_cap;
 	u16 n_iftype_data;
 	const struct ieee80211_sband_iftype_data *iftype_data;
 };
@@ -514,12 +570,17 @@ struct key_params {
  * @center_freq1: center frequency of first segment
  * @center_freq2: center frequency of second segment
  *	(only with 80+80 MHz)
+ * @edmg: define the EDMG channels configuration.
+ *	If edmg is requested (i.e. the .channels member is non-zero),
+ *	chan will define the primary channel and all other
+ *	parameters are ignored.
  */
 struct cfg80211_chan_def {
 	struct ieee80211_channel *chan;
 	enum nl80211_chan_width width;
 	u32 center_freq1;
 	u32 center_freq2;
+	struct ieee80211_edmg edmg;
 };
 
 /**
@@ -578,6 +639,19 @@ cfg80211_chandef_identical(const struct cfg80211_chan_def *chandef1,
 }
 
 /**
+ * cfg80211_chandef_is_edmg - check if chandef represents an EDMG channel
+ *
+ * @chandef: the channel definition
+ *
+ * Return: %true if EDMG defined, %false otherwise.
+ */
+static inline bool
+cfg80211_chandef_is_edmg(const struct cfg80211_chan_def *chandef)
+{
+	return chandef->edmg.channels || chandef->edmg.bw_config;
+}
+
+/**
  * cfg80211_chandef_compatible - check if two channel definitions are compatible
  * @chandef1: first channel definition
  * @chandef2: second channel definition
@@ -1162,15 +1236,17 @@ int cfg80211_check_station_change(struct wiphy *wiphy,
  * @RATE_INFO_FLAGS_MCS: mcs field filled with HT MCS
  * @RATE_INFO_FLAGS_VHT_MCS: mcs field filled with VHT MCS
  * @RATE_INFO_FLAGS_SHORT_GI: 400ns guard interval
- * @RATE_INFO_FLAGS_60G: 60GHz MCS
+ * @RATE_INFO_FLAGS_DMG: 60GHz MCS
  * @RATE_INFO_FLAGS_HE_MCS: HE MCS information
+ * @RATE_INFO_FLAGS_EDMG: 60GHz MCS in EDMG mode
  */
 enum rate_info_flags {
 	RATE_INFO_FLAGS_MCS			= BIT(0),
 	RATE_INFO_FLAGS_VHT_MCS			= BIT(1),
 	RATE_INFO_FLAGS_SHORT_GI		= BIT(2),
-	RATE_INFO_FLAGS_60G			= BIT(3),
+	RATE_INFO_FLAGS_DMG			= BIT(3),
 	RATE_INFO_FLAGS_HE_MCS			= BIT(4),
+	RATE_INFO_FLAGS_EDMG			= BIT(5),
 };
 
 /**
@@ -1210,6 +1286,7 @@ enum rate_info_bw {
  * @he_dcm: HE DCM value
  * @he_ru_alloc: HE RU allocation (from &enum nl80211_he_ru_alloc,
  *	only valid if bw is %RATE_INFO_BW_HE_RU)
+ * @n_bonded_ch: In case of EDMG the number of bonded channels (1-4)
  */
 struct rate_info {
 	u8 flags;
@@ -1220,6 +1297,7 @@ struct rate_info {
 	u8 he_gi;
 	u8 he_dcm;
 	u8 he_ru_alloc;
+	u8 n_bonded_ch;
 };
 
 /**
@@ -2421,6 +2499,9 @@ struct cfg80211_bss_selection {
  * @fils_erp_rrk_len: Length of @fils_erp_rrk in octets.
  * @want_1x: indicates user-space supports and wants to use 802.1X driver
  *	offload of 4-way handshake.
+ * @edmg: define the EDMG channels.
+ *	This may specify multiple channels and bonding options for the driver
+ *	to choose from, based on BSS configuration.
  */
 struct cfg80211_connect_params {
 	struct ieee80211_channel *channel;
@@ -2454,6 +2535,7 @@ struct cfg80211_connect_params {
 	const u8 *fils_erp_rrk;
 	size_t fils_erp_rrk_len;
 	bool want_1x;
+	struct ieee80211_edmg edmg;
 };
 
 /**
diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h
index c45587c..cfe8dfe 100644
--- a/include/uapi/linux/nl80211.h
+++ b/include/uapi/linux/nl80211.h
@@ -52,6 +52,11 @@
 #define NL80211_MULTICAST_GROUP_NAN		"nan"
 #define NL80211_MULTICAST_GROUP_TESTMODE	"testmode"
 
+#define NL80211_EDMG_BW_CONFIG_MIN	4
+#define NL80211_EDMG_BW_CONFIG_MAX	15
+#define NL80211_EDMG_CHANNELS_MIN	1
+#define NL80211_EDMG_CHANNELS_MAX	0x3c /* 0b00111100 */
+
 /**
  * DOC: Station handling
  *
@@ -2358,6 +2363,13 @@ enum nl80211_commands {
  *
  * @NL80211_ATTR_TWT_RESPONDER: Enable target wait time responder support.
  *
+ * @NL80211_ATTR_WIPHY_EDMG_CHANNELS: bitmap that indicates the 2.16 GHz
+ *	channel(s) that are allowed to be used for EDMG transmissions.
+ *	Defined by IEEE P802.11ay/D4.0 section 9.4.2.251.
+ * @NL80211_ATTR_WIPHY_EDMG_BW_CONFIG: Channel BW Configuration subfield encodes
+ *	the allowed channel bandwidth configurations.
+ *	Defined by IEEE P802.11ay/D4.0 section 9.4.2.251, Table 13.
+ *
  * @NUM_NL80211_ATTR: total number of nl80211_attrs available
  * @NL80211_ATTR_MAX: highest attribute number currently defined
  * @__NL80211_ATTR_AFTER_LAST: internal use
@@ -2815,6 +2827,9 @@ enum nl80211_attrs {
 
 	NL80211_ATTR_TWT_RESPONDER,
 
+	NL80211_ATTR_WIPHY_EDMG_CHANNELS,
+	NL80211_ATTR_WIPHY_EDMG_BW_CONFIG,
+
 	/* add attributes here, update the policy in nl80211.c */
 
 	__NL80211_ATTR_AFTER_LAST,
@@ -3423,6 +3438,12 @@ enum nl80211_band_iftype_attr {
  * @NL80211_BAND_ATTR_VHT_CAPA: VHT capabilities, as in the HT information IE
  * @NL80211_BAND_ATTR_IFTYPE_DATA: nested array attribute, with each entry using
  *	attributes from &enum nl80211_band_iftype_attr
+ * @NL80211_BAND_ATTR_EDMG_CHANNELS: bitmap that indicates the 2.16 GHz
+ *	channel(s) that are allowed to be used for EDMG transmissions.
+ *	Defined by IEEE P802.11ay/D4.0 section 9.4.2.251.
+ * @NL80211_BAND_ATTR_EDMG_BW_CONFIG: Channel BW Configuration subfield encodes
+ *	the allowed channel bandwidth configurations.
+ *	Defined by IEEE P802.11ay/D4.0 section 9.4.2.251, Table 13.
  * @NL80211_BAND_ATTR_MAX: highest band attribute currently defined
  * @__NL80211_BAND_ATTR_AFTER_LAST: internal use
  */
@@ -3440,6 +3461,9 @@ enum nl80211_band_attr {
 	NL80211_BAND_ATTR_VHT_CAPA,
 	NL80211_BAND_ATTR_IFTYPE_DATA,
 
+	NL80211_BAND_ATTR_EDMG_CHANNELS,
+	NL80211_BAND_ATTR_EDMG_BW_CONFIG,
+
 	/* keep last */
 	__NL80211_BAND_ATTR_AFTER_LAST,
 	NL80211_BAND_ATTR_MAX = __NL80211_BAND_ATTR_AFTER_LAST - 1
diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c
index a99ad032..54e847a 100644
--- a/net/mac80211/mlme.c
+++ b/net/mac80211/mlme.c
@@ -158,10 +158,10 @@ ieee80211_determine_chantype(struct ieee80211_sub_if_data *sdata,
 	memcpy(&sta_ht_cap, &sband->ht_cap, sizeof(sta_ht_cap));
 	ieee80211_apply_htcap_overrides(sdata, &sta_ht_cap);
 
+	memset(chandef, 0, sizeof(struct cfg80211_chan_def));
 	chandef->chan = channel;
 	chandef->width = NL80211_CHAN_WIDTH_20_NOHT;
 	chandef->center_freq1 = channel->center_freq;
-	chandef->center_freq2 = 0;
 
 	if (!ht_oper || !sta_ht_cap.ht_supported) {
 		ret = IEEE80211_STA_DISABLE_HT | IEEE80211_STA_DISABLE_VHT;
diff --git a/net/mac80211/status.c b/net/mac80211/status.c
index f03aa89..87099e1 100644
--- a/net/mac80211/status.c
+++ b/net/mac80211/status.c
@@ -323,7 +323,7 @@ ieee80211_add_tx_radiotap_header(struct ieee80211_local *local,
 
 	if (status && status->rate && !(status->rate->flags &
 					(RATE_INFO_FLAGS_MCS |
-					 RATE_INFO_FLAGS_60G |
+					 RATE_INFO_FLAGS_DMG |
 					 RATE_INFO_FLAGS_VHT_MCS |
 					 RATE_INFO_FLAGS_HE_MCS)))
 		legacy_rate = status->rate->legacy;
diff --git a/net/wireless/chan.c b/net/wireless/chan.c
index 7dc1bbd..d823e5f 100644
--- a/net/wireless/chan.c
+++ b/net/wireless/chan.c
@@ -14,6 +14,11 @@
 #include "core.h"
 #include "rdev-ops.h"
 
+static bool cfg80211_valid_60g_freq(u32 freq)
+{
+	return freq >= 58320 && freq <= 70200;
+}
+
 void cfg80211_chandef_create(struct cfg80211_chan_def *chandef,
 			     struct ieee80211_channel *chan,
 			     enum nl80211_channel_type chan_type)
@@ -23,6 +28,8 @@ void cfg80211_chandef_create(struct cfg80211_chan_def *chandef,
 
 	chandef->chan = chan;
 	chandef->center_freq2 = 0;
+	chandef->edmg.bw_config = 0;
+	chandef->edmg.channels = 0;
 
 	switch (chan_type) {
 	case NL80211_CHAN_NO_HT:
@@ -47,6 +54,91 @@ void cfg80211_chandef_create(struct cfg80211_chan_def *chandef,
 }
 EXPORT_SYMBOL(cfg80211_chandef_create);
 
+static bool cfg80211_edmg_chandef_valid(const struct cfg80211_chan_def *chandef)
+{
+	int max_contiguous = 0;
+	int num_of_enabled = 0;
+	int contiguous = 0;
+	int i;
+
+	if (!chandef->edmg.channels || !chandef->edmg.bw_config)
+		return false;
+
+	if (!cfg80211_valid_60g_freq(chandef->chan->center_freq))
+		return false;
+
+	for (i = 0; i < 6; i++) {
+		if (chandef->edmg.channels & BIT(i)) {
+			contiguous++;
+			num_of_enabled++;
+		} else {
+			contiguous = 0;
+		}
+
+		max_contiguous = max(contiguous, max_contiguous);
+	}
+	/* basic verification of edmg configuration according to
+	 * IEEE P802.11ay/D4.0 section 9.4.2.251
+	 */
+	/* check bw_config against contiguous edmg channels */
+	switch (chandef->edmg.bw_config) {
+	case IEEE80211_EDMG_BW_CONFIG_4:
+	case IEEE80211_EDMG_BW_CONFIG_8:
+	case IEEE80211_EDMG_BW_CONFIG_12:
+		if (max_contiguous < 1)
+			return false;
+		break;
+	case IEEE80211_EDMG_BW_CONFIG_5:
+	case IEEE80211_EDMG_BW_CONFIG_9:
+	case IEEE80211_EDMG_BW_CONFIG_13:
+		if (max_contiguous < 2)
+			return false;
+		break;
+	case IEEE80211_EDMG_BW_CONFIG_6:
+	case IEEE80211_EDMG_BW_CONFIG_10:
+	case IEEE80211_EDMG_BW_CONFIG_14:
+		if (max_contiguous < 3)
+			return false;
+		break;
+	case IEEE80211_EDMG_BW_CONFIG_7:
+	case IEEE80211_EDMG_BW_CONFIG_11:
+	case IEEE80211_EDMG_BW_CONFIG_15:
+		if (max_contiguous < 4)
+			return false;
+		break;
+
+	default:
+		return false;
+	}
+
+	/* check bw_config against aggregated (non contiguous) edmg channels */
+	switch (chandef->edmg.bw_config) {
+	case IEEE80211_EDMG_BW_CONFIG_4:
+	case IEEE80211_EDMG_BW_CONFIG_5:
+	case IEEE80211_EDMG_BW_CONFIG_6:
+	case IEEE80211_EDMG_BW_CONFIG_7:
+		break;
+	case IEEE80211_EDMG_BW_CONFIG_8:
+	case IEEE80211_EDMG_BW_CONFIG_9:
+	case IEEE80211_EDMG_BW_CONFIG_10:
+	case IEEE80211_EDMG_BW_CONFIG_11:
+		if (num_of_enabled < 2)
+			return false;
+		break;
+	case IEEE80211_EDMG_BW_CONFIG_12:
+	case IEEE80211_EDMG_BW_CONFIG_13:
+	case IEEE80211_EDMG_BW_CONFIG_14:
+	case IEEE80211_EDMG_BW_CONFIG_15:
+		if (num_of_enabled < 4 || max_contiguous < 2)
+			return false;
+		break;
+	default:
+		return false;
+	}
+
+	return true;
+}
+
 bool cfg80211_chandef_valid(const struct cfg80211_chan_def *chandef)
 {
 	u32 control_freq;
@@ -112,6 +204,10 @@ bool cfg80211_chandef_valid(const struct cfg80211_chan_def *chandef)
 		return false;
 	}
 
+	if (cfg80211_chandef_is_edmg(chandef) &&
+	    !cfg80211_edmg_chandef_valid(chandef))
+		return false;
+
 	return true;
 }
 EXPORT_SYMBOL(cfg80211_chandef_valid);
@@ -721,12 +817,66 @@ static bool cfg80211_secondary_chans_ok(struct wiphy *wiphy,
 	return true;
 }
 
+/* check if the operating channels are valid and supported */
+static bool cfg80211_edmg_usable(struct wiphy *wiphy, u8 edmg_channels,
+				 enum ieee80211_edmg_bw_config edmg_bw_config,
+				 int primary_channel,
+				 struct ieee80211_edmg *edmg_cap)
+{
+	struct ieee80211_channel *chan;
+	int i, freq;
+	int channels_counter = 0;
+
+	if (!edmg_channels && !edmg_bw_config)
+		return true;
+
+	if ((!edmg_channels && edmg_bw_config) ||
+	    (edmg_channels && !edmg_bw_config))
+		return false;
+
+	if (!(edmg_channels & BIT(primary_channel - 1)))
+		return false;
+
+	/* 60GHz channels 1..6 */
+	for (i = 0; i < 6; i++) {
+		if (!(edmg_channels & BIT(i)))
+			continue;
+
+		if (!(edmg_cap->channels & BIT(i)))
+			return false;
+
+		channels_counter++;
+
+		freq = ieee80211_channel_to_frequency(i + 1,
+						      NL80211_BAND_60GHZ);
+		chan = ieee80211_get_channel(wiphy, freq);
+		if (!chan || chan->flags & IEEE80211_CHAN_DISABLED)
+			return false;
+	}
+
+	/* IEEE802.11 allows max 4 channels */
+	if (channels_counter > 4)
+		return false;
+
+	/* check bw_config is a subset of what driver supports
+	 * (see IEEE P802.11ay/D4.0 section 9.4.2.251, Table 13)
+	 */
+	if ((edmg_bw_config % 4) > (edmg_cap->bw_config % 4))
+		return false;
+
+	if (edmg_bw_config > edmg_cap->bw_config)
+		return false;
+
+	return true;
+}
+
 bool cfg80211_chandef_usable(struct wiphy *wiphy,
 			     const struct cfg80211_chan_def *chandef,
 			     u32 prohibited_flags)
 {
 	struct ieee80211_sta_ht_cap *ht_cap;
 	struct ieee80211_sta_vht_cap *vht_cap;
+	struct ieee80211_edmg *edmg_cap;
 	u32 width, control_freq, cap;
 
 	if (WARN_ON(!cfg80211_chandef_valid(chandef)))
@@ -734,6 +884,15 @@ bool cfg80211_chandef_usable(struct wiphy *wiphy,
 
 	ht_cap = &wiphy->bands[chandef->chan->band]->ht_cap;
 	vht_cap = &wiphy->bands[chandef->chan->band]->vht_cap;
+	edmg_cap = &wiphy->bands[chandef->chan->band]->edmg_cap;
+
+	if (edmg_cap->channels &&
+	    !cfg80211_edmg_usable(wiphy,
+				  chandef->edmg.channels,
+				  chandef->edmg.bw_config,
+				  chandef->chan->hw_value,
+				  edmg_cap))
+		return false;
 
 	control_freq = chandef->chan->center_freq;
 
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
index 65f85fd..11888fa 100644
--- a/net/wireless/nl80211.c
+++ b/net/wireless/nl80211.c
@@ -289,6 +289,13 @@ const struct nla_policy nl80211_policy[NUM_NL80211_ATTR] = {
 
 	[NL80211_ATTR_WIPHY_FREQ] = { .type = NLA_U32 },
 	[NL80211_ATTR_WIPHY_CHANNEL_TYPE] = { .type = NLA_U32 },
+	[NL80211_ATTR_WIPHY_EDMG_CHANNELS] = NLA_POLICY_RANGE(NLA_U8,
+						NL80211_EDMG_CHANNELS_MIN,
+						NL80211_EDMG_CHANNELS_MAX),
+	[NL80211_ATTR_WIPHY_EDMG_BW_CONFIG] = NLA_POLICY_RANGE(NLA_U8,
+						NL80211_EDMG_BW_CONFIG_MIN,
+						NL80211_EDMG_BW_CONFIG_MAX),
+
 	[NL80211_ATTR_CHANNEL_WIDTH] = { .type = NLA_U32 },
 	[NL80211_ATTR_CENTER_FREQ1] = { .type = NLA_U32 },
 	[NL80211_ATTR_CENTER_FREQ2] = { .type = NLA_U32 },
@@ -1555,6 +1562,15 @@ static int nl80211_send_band_rateinfo(struct sk_buff *msg,
 		nla_nest_end(msg, nl_iftype_data);
 	}
 
+	/* add EDMG info */
+	if (sband->edmg_cap.channels &&
+	    (nla_put_u8(msg, NL80211_BAND_ATTR_EDMG_CHANNELS,
+		       sband->edmg_cap.channels) ||
+	    nla_put_u8(msg, NL80211_BAND_ATTR_EDMG_BW_CONFIG,
+		       sband->edmg_cap.bw_config)))
+
+		return -ENOBUFS;
+
 	/* add bitrates */
 	nl_rates = nla_nest_start_noflag(msg, NL80211_BAND_ATTR_RATES);
 	if (!nl_rates)
@@ -2646,6 +2662,18 @@ int nl80211_parse_chandef(struct cfg80211_registered_device *rdev,
 				nla_get_u32(attrs[NL80211_ATTR_CENTER_FREQ2]);
 	}
 
+	if (info->attrs[NL80211_ATTR_WIPHY_EDMG_CHANNELS]) {
+		chandef->edmg.channels =
+		      nla_get_u8(info->attrs[NL80211_ATTR_WIPHY_EDMG_CHANNELS]);
+
+		if (info->attrs[NL80211_ATTR_WIPHY_EDMG_BW_CONFIG])
+			chandef->edmg.bw_config =
+		     nla_get_u8(info->attrs[NL80211_ATTR_WIPHY_EDMG_BW_CONFIG]);
+	} else {
+		chandef->edmg.bw_config = 0;
+		chandef->edmg.channels = 0;
+	}
+
 	if (!cfg80211_chandef_valid(chandef)) {
 		NL_SET_ERR_MSG(extack, "invalid channel definition");
 		return -EINVAL;
@@ -9814,6 +9842,15 @@ static int nl80211_connect(struct sk_buff *skb, struct genl_info *info)
 			return -EINVAL;
 	}
 
+	if (info->attrs[NL80211_ATTR_WIPHY_EDMG_CHANNELS]) {
+		connect.edmg.channels =
+		      nla_get_u8(info->attrs[NL80211_ATTR_WIPHY_EDMG_CHANNELS]);
+
+		if (info->attrs[NL80211_ATTR_WIPHY_EDMG_BW_CONFIG])
+			connect.edmg.bw_config =
+		     nla_get_u8(info->attrs[NL80211_ATTR_WIPHY_EDMG_BW_CONFIG]);
+	}
+
 	if (connect.privacy && info->attrs[NL80211_ATTR_KEYS]) {
 		connkeys = nl80211_parse_connkeys(rdev, info, NULL);
 		if (IS_ERR(connkeys))
diff --git a/net/wireless/util.c b/net/wireless/util.c
index d0e35b7..01b52ef 100644
--- a/net/wireless/util.c
+++ b/net/wireless/util.c
@@ -1034,7 +1034,7 @@ static u32 cfg80211_calculate_bitrate_ht(struct rate_info *rate)
 	return (bitrate + 50000) / 100000;
 }
 
-static u32 cfg80211_calculate_bitrate_60g(struct rate_info *rate)
+static u32 cfg80211_calculate_bitrate_dmg(struct rate_info *rate)
 {
 	static const u32 __mcs2bitrate[] = {
 		/* control PHY */
@@ -1081,6 +1081,40 @@ static u32 cfg80211_calculate_bitrate_60g(struct rate_info *rate)
 	return __mcs2bitrate[rate->mcs];
 }
 
+static u32 cfg80211_calculate_bitrate_edmg(struct rate_info *rate)
+{
+	static const u32 __mcs2bitrate[] = {
+		/* control PHY */
+		[0] =   275,
+		/* SC PHY */
+		[1] =  3850,
+		[2] =  7700,
+		[3] =  9625,
+		[4] = 11550,
+		[5] = 12512, /* 1251.25 mbps */
+		[6] = 13475,
+		[7] = 15400,
+		[8] = 19250,
+		[9] = 23100,
+		[10] = 25025,
+		[11] = 26950,
+		[12] = 30800,
+		[13] = 38500,
+		[14] = 46200,
+		[15] = 50050,
+		[16] = 53900,
+		[17] = 57750,
+		[18] = 69300,
+		[19] = 75075,
+		[20] = 80850,
+	};
+
+	if (WARN_ON_ONCE(rate->mcs >= ARRAY_SIZE(__mcs2bitrate)))
+		return 0;
+
+	return __mcs2bitrate[rate->mcs] * rate->n_bonded_ch;
+}
+
 static u32 cfg80211_calculate_bitrate_vht(struct rate_info *rate)
 {
 	static const u32 base[4][10] = {
@@ -1253,8 +1287,10 @@ u32 cfg80211_calculate_bitrate(struct rate_info *rate)
 {
 	if (rate->flags & RATE_INFO_FLAGS_MCS)
 		return cfg80211_calculate_bitrate_ht(rate);
-	if (rate->flags & RATE_INFO_FLAGS_60G)
-		return cfg80211_calculate_bitrate_60g(rate);
+	if (rate->flags & RATE_INFO_FLAGS_DMG)
+		return cfg80211_calculate_bitrate_dmg(rate);
+	if (rate->flags & RATE_INFO_FLAGS_EDMG)
+		return cfg80211_calculate_bitrate_edmg(rate);
 	if (rate->flags & RATE_INFO_FLAGS_VHT_MCS)
 		return cfg80211_calculate_bitrate_vht(rate);
 	if (rate->flags & RATE_INFO_FLAGS_HE_MCS)
-- 
2.7.4


^ permalink raw reply related

* [PATCH v6 2/2] wil6210: Add EDMG channel support
From: Alexei Avshalom Lazar @ 2019-08-01 13:16 UTC (permalink / raw)
  To: Johannes Berg; +Cc: Alexei Avshalom Lazar, linux-wireless, wil6210
In-Reply-To: <1564665374-2856-1-git-send-email-ailizaro@codeaurora.org>

Add support for Enhanced Directional Multi-Gigabit (EDMG) channels 9-11.
wil6210 reports it's EDMG capabilities (that are also based on FW
capability) to cfg80211 by filling
wiphy->bands[NL80211_BAND_60GHZ]->edmg_cap.
wil6210 handles edmg.channels and edmg.bw_config requested in connect
and start_ap operations.
---
 drivers/net/wireless/ath/wil6210/cfg80211.c  | 205 +++++++++++++++++++++++++--
 drivers/net/wireless/ath/wil6210/txrx_edma.c |   2 +
 drivers/net/wireless/ath/wil6210/txrx_edma.h |   6 +
 drivers/net/wireless/ath/wil6210/wil6210.h   |   8 +-
 drivers/net/wireless/ath/wil6210/wmi.c       |   5 +-
 drivers/net/wireless/ath/wil6210/wmi.h       |  26 +++-
 6 files changed, 239 insertions(+), 13 deletions(-)

diff --git a/drivers/net/wireless/ath/wil6210/cfg80211.c b/drivers/net/wireless/ath/wil6210/cfg80211.c
index 2414f57..5b2fdad 100644
--- a/drivers/net/wireless/ath/wil6210/cfg80211.c
+++ b/drivers/net/wireless/ath/wil6210/cfg80211.c
@@ -25,6 +25,22 @@
 
 #define WIL_MAX_ROC_DURATION_MS 5000
 
+#define WIL_EDMG_CHANNEL_9_SUBCHANNELS	(BIT(0) | BIT(1))
+#define WIL_EDMG_CHANNEL_10_SUBCHANNELS	(BIT(1) | BIT(2))
+#define WIL_EDMG_CHANNEL_11_SUBCHANNELS	(BIT(2) | BIT(3))
+
+/* WIL_EDMG_BW_CONFIGURATION define the allowed channel bandwidth
+ * configurations as defined by IEEE 802.11 section 9.4.2.251, Table 13.
+ * The value 5 allowing CB1 and CB2 of adjacent channels.
+ */
+#define WIL_EDMG_BW_CONFIGURATION 5
+
+/* WIL_EDMG_CHANNELS is a bitmap that indicates the 2.16 GHz channel(s) that
+ * are allowed to be used for EDMG transmissions in the BSS as defined by
+ * IEEE 802.11 section 9.4.2.251.
+ */
+#define WIL_EDMG_CHANNELS (BIT(0) | BIT(1) | BIT(2) | BIT(3))
+
 bool disable_ap_sme;
 module_param(disable_ap_sme, bool, 0444);
 MODULE_PARM_DESC(disable_ap_sme, " let user space handle AP mode SME");
@@ -51,6 +67,39 @@ static struct ieee80211_channel wil_60ghz_channels[] = {
 	CHAN60G(4, 0),
 };
 
+/* Rx channel bonding mode */
+enum wil_rx_cb_mode {
+	WIL_RX_CB_MODE_DMG,
+	WIL_RX_CB_MODE_EDMG,
+	WIL_RX_CB_MODE_WIDE,
+};
+
+static int wil_rx_cb_mode_to_n_bonded(u8 cb_mode)
+{
+	switch (cb_mode) {
+	case WIL_RX_CB_MODE_DMG:
+	case WIL_RX_CB_MODE_EDMG:
+		return 1;
+	case WIL_RX_CB_MODE_WIDE:
+		return 2;
+	default:
+		return 1;
+	}
+}
+
+static int wil_tx_cb_mode_to_n_bonded(u8 cb_mode)
+{
+	switch (cb_mode) {
+	case WMI_TX_MODE_DMG:
+	case WMI_TX_MODE_EDMG_CB1:
+		return 1;
+	case WMI_TX_MODE_EDMG_CB2:
+		return 2;
+	default:
+		return 1;
+	}
+}
+
 static void
 wil_memdup_ie(u8 **pdst, size_t *pdst_len, const u8 *src, size_t src_len)
 {
@@ -82,6 +131,12 @@ void update_supported_bands(struct wil6210_priv *wil)
 
 	wiphy->bands[NL80211_BAND_60GHZ]->n_channels =
 						wil_num_supported_channels(wil);
+
+	if (test_bit(WMI_FW_CAPABILITY_CHANNEL_BONDING, wil->fw_capabilities))
+		wiphy->bands[NL80211_BAND_60GHZ]->edmg_cap.channels =
+							WIL_EDMG_CHANNELS;
+		wiphy->bands[NL80211_BAND_60GHZ]->edmg_cap.bw_config =
+						      WIL_EDMG_BW_CONFIGURATION;
 }
 
 /* Vendor id to be used in vendor specific command and events
@@ -300,6 +355,86 @@ int wil_iftype_nl2wmi(enum nl80211_iftype type)
 	return -EOPNOTSUPP;
 }
 
+int wil_spec2wmi_ch(u8 spec_ch, u8 *wmi_ch)
+{
+	switch (spec_ch) {
+	case 1:
+		*wmi_ch = WMI_CHANNEL_1;
+		break;
+	case 2:
+		*wmi_ch = WMI_CHANNEL_2;
+		break;
+	case 3:
+		*wmi_ch = WMI_CHANNEL_3;
+		break;
+	case 4:
+		*wmi_ch = WMI_CHANNEL_4;
+		break;
+	case 5:
+		*wmi_ch = WMI_CHANNEL_5;
+		break;
+	case 6:
+		*wmi_ch = WMI_CHANNEL_6;
+		break;
+	case 9:
+		*wmi_ch = WMI_CHANNEL_9;
+		break;
+	case 10:
+		*wmi_ch = WMI_CHANNEL_10;
+		break;
+	case 11:
+		*wmi_ch = WMI_CHANNEL_11;
+		break;
+	case 12:
+		*wmi_ch = WMI_CHANNEL_12;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+int wil_wmi2spec_ch(u8 wmi_ch, u8 *spec_ch)
+{
+	switch (wmi_ch) {
+	case WMI_CHANNEL_1:
+		*spec_ch = 1;
+		break;
+	case WMI_CHANNEL_2:
+		*spec_ch = 2;
+		break;
+	case WMI_CHANNEL_3:
+		*spec_ch = 3;
+		break;
+	case WMI_CHANNEL_4:
+		*spec_ch = 4;
+		break;
+	case WMI_CHANNEL_5:
+		*spec_ch = 5;
+		break;
+	case WMI_CHANNEL_6:
+		*spec_ch = 6;
+		break;
+	case WMI_CHANNEL_9:
+		*spec_ch = 9;
+		break;
+	case WMI_CHANNEL_10:
+		*spec_ch = 10;
+		break;
+	case WMI_CHANNEL_11:
+		*spec_ch = 11;
+		break;
+	case WMI_CHANNEL_12:
+		*spec_ch = 12;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
 int wil_cid_fill_sinfo(struct wil6210_vif *vif, int cid,
 		       struct station_info *sinfo)
 {
@@ -314,6 +449,7 @@ int wil_cid_fill_sinfo(struct wil6210_vif *vif, int cid,
 	} __packed reply;
 	struct wil_net_stats *stats = &wil->sta[cid].stats;
 	int rc;
+	u8 txflag = RATE_INFO_FLAGS_DMG;
 
 	memset(&reply, 0, sizeof(reply));
 
@@ -327,7 +463,8 @@ int wil_cid_fill_sinfo(struct wil6210_vif *vif, int cid,
 		    "  MCS %d TSF 0x%016llx\n"
 		    "  BF status 0x%08x RSSI %d SQI %d%%\n"
 		    "  Tx Tpt %d goodput %d Rx goodput %d\n"
-		    "  Sectors(rx:tx) my %d:%d peer %d:%d\n""}\n",
+		    "  Sectors(rx:tx) my %d:%d peer %d:%d\n"
+		    "  Tx mode %d}\n",
 		    cid, vif->mid, le16_to_cpu(reply.evt.bf_mcs),
 		    le64_to_cpu(reply.evt.tsf), reply.evt.status,
 		    reply.evt.rssi,
@@ -338,7 +475,8 @@ int wil_cid_fill_sinfo(struct wil6210_vif *vif, int cid,
 		    le16_to_cpu(reply.evt.my_rx_sector),
 		    le16_to_cpu(reply.evt.my_tx_sector),
 		    le16_to_cpu(reply.evt.other_rx_sector),
-		    le16_to_cpu(reply.evt.other_tx_sector));
+		    le16_to_cpu(reply.evt.other_tx_sector),
+		    reply.evt.tx_mode);
 
 	sinfo->generation = wil->sinfo_gen;
 
@@ -351,9 +489,16 @@ int wil_cid_fill_sinfo(struct wil6210_vif *vif, int cid,
 			BIT_ULL(NL80211_STA_INFO_RX_DROP_MISC) |
 			BIT_ULL(NL80211_STA_INFO_TX_FAILED);
 
-	sinfo->txrate.flags = RATE_INFO_FLAGS_DMG;
+	if (wil->use_enhanced_dma_hw && reply.evt.tx_mode != WMI_TX_MODE_DMG)
+		txflag = RATE_INFO_FLAGS_EDMG;
+
+	sinfo->txrate.flags = txflag;
 	sinfo->txrate.mcs = le16_to_cpu(reply.evt.bf_mcs);
 	sinfo->rxrate.mcs = stats->last_mcs_rx;
+	sinfo->txrate.n_bonded_ch =
+				  wil_tx_cb_mode_to_n_bonded(reply.evt.tx_mode);
+	sinfo->rxrate.n_bonded_ch =
+			     wil_rx_cb_mode_to_n_bonded(stats->last_cb_mode_rx);
 	sinfo->rx_bytes = stats->rx_bytes;
 	sinfo->rx_packets = stats->rx_packets;
 	sinfo->rx_dropped_misc = stats->rx_dropped;
@@ -1022,6 +1167,33 @@ static int wil_ft_connect(struct wiphy *wiphy,
 	return rc;
 }
 
+static int wil_get_wmi_edmg_channel(struct wil6210_priv *wil, u8 edmg_bw_config,
+				    u8 edmg_channels, u8 *wmi_ch)
+{
+	if (!edmg_bw_config) {
+		*wmi_ch = 0;
+		return 0;
+	} else if (edmg_bw_config == WIL_EDMG_BW_CONFIGURATION) {
+		/* convert from edmg channel bitmap into edmg channel number */
+		switch (edmg_channels) {
+		case WIL_EDMG_CHANNEL_9_SUBCHANNELS:
+			return wil_spec2wmi_ch(9, wmi_ch);
+		case WIL_EDMG_CHANNEL_10_SUBCHANNELS:
+			return wil_spec2wmi_ch(10, wmi_ch);
+		case WIL_EDMG_CHANNEL_11_SUBCHANNELS:
+			return wil_spec2wmi_ch(11, wmi_ch);
+		default:
+			wil_err(wil, "Unsupported edmg channel bitmap 0x%x\n",
+				edmg_channels);
+			return -EINVAL;
+		}
+	} else {
+		wil_err(wil, "Unsupported EDMG BW configuration %d\n",
+			edmg_bw_config);
+		return -EINVAL;
+	}
+}
+
 static int wil_cfg80211_connect(struct wiphy *wiphy,
 				struct net_device *ndev,
 				struct cfg80211_connect_params *sme)
@@ -1167,6 +1339,11 @@ static int wil_cfg80211_connect(struct wiphy *wiphy,
 	memcpy(conn.ssid, ssid_eid+2, conn.ssid_len);
 	conn.channel = ch - 1;
 
+	rc = wil_get_wmi_edmg_channel(wil, sme->edmg.bw_config,
+				      sme->edmg.channels, &conn.edmg_channel);
+	if (rc < 0)
+		return rc;
+
 	ether_addr_copy(conn.bssid, bss->bssid);
 	ether_addr_copy(conn.dst_mac, bss->bssid);
 
@@ -1728,7 +1905,7 @@ static int _wil_cfg80211_set_ies(struct wil6210_vif *vif,
 static int _wil_cfg80211_start_ap(struct wiphy *wiphy,
 				  struct net_device *ndev,
 				  const u8 *ssid, size_t ssid_len, u32 privacy,
-				  int bi, u8 chan,
+				  int bi, u8 chan, u8 wmi_edmg_channel,
 				  struct cfg80211_beacon_data *bcon,
 				  u8 hidden_ssid, u32 pbss)
 {
@@ -1791,6 +1968,7 @@ static int _wil_cfg80211_start_ap(struct wiphy *wiphy,
 
 	vif->privacy = privacy;
 	vif->channel = chan;
+	vif->wmi_edmg_channel = wmi_edmg_channel;
 	vif->hidden_ssid = hidden_ssid;
 	vif->pbss = pbss;
 	vif->bi = bi;
@@ -1801,7 +1979,8 @@ static int _wil_cfg80211_start_ap(struct wiphy *wiphy,
 	if (!wil_has_other_active_ifaces(wil, ndev, false, true))
 		wil6210_bus_request(wil, WIL_MAX_BUS_REQUEST_KBPS);
 
-	rc = wmi_pcp_start(vif, bi, wmi_nettype, chan, hidden_ssid, is_go);
+	rc = wmi_pcp_start(vif, bi, wmi_nettype, chan, wmi_edmg_channel,
+			   hidden_ssid, is_go);
 	if (rc)
 		goto err_pcp_start;
 
@@ -1853,7 +2032,8 @@ void wil_cfg80211_ap_recovery(struct wil6210_priv *wil)
 		rc = _wil_cfg80211_start_ap(wiphy, ndev,
 					    vif->ssid, vif->ssid_len,
 					    vif->privacy, vif->bi,
-					    vif->channel, &bcon,
+					    vif->channel,
+					    vif->wmi_edmg_channel, &bcon,
 					    vif->hidden_ssid, vif->pbss);
 		if (rc) {
 			wil_err(wil, "vif %d recovery failed (%d)\n", i, rc);
@@ -1903,7 +2083,8 @@ static int wil_cfg80211_change_beacon(struct wiphy *wiphy,
 		rc = _wil_cfg80211_start_ap(wiphy, ndev, vif->ssid,
 					    vif->ssid_len, privacy,
 					    wdev->beacon_interval,
-					    vif->channel, bcon,
+					    vif->channel,
+					    vif->wmi_edmg_channel, bcon,
 					    vif->hidden_ssid,
 					    vif->pbss);
 	} else {
@@ -1922,10 +2103,17 @@ static int wil_cfg80211_start_ap(struct wiphy *wiphy,
 	struct ieee80211_channel *channel = info->chandef.chan;
 	struct cfg80211_beacon_data *bcon = &info->beacon;
 	struct cfg80211_crypto_settings *crypto = &info->crypto;
+	u8 wmi_edmg_channel;
 	u8 hidden_ssid;
 
 	wil_dbg_misc(wil, "start_ap\n");
 
+	rc = wil_get_wmi_edmg_channel(wil, info->chandef.edmg.bw_config,
+				      info->chandef.edmg.channels,
+				      &wmi_edmg_channel);
+	if (rc < 0)
+		return rc;
+
 	if (!channel) {
 		wil_err(wil, "AP: No channel???\n");
 		return -EINVAL;
@@ -1965,7 +2153,8 @@ static int wil_cfg80211_start_ap(struct wiphy *wiphy,
 	rc = _wil_cfg80211_start_ap(wiphy, ndev,
 				    info->ssid, info->ssid_len, info->privacy,
 				    info->beacon_interval, channel->hw_value,
-				    bcon, hidden_ssid, info->pbss);
+				    wmi_edmg_channel, bcon, hidden_ssid,
+				    info->pbss);
 
 	return rc;
 }
diff --git a/drivers/net/wireless/ath/wil6210/txrx_edma.c b/drivers/net/wireless/ath/wil6210/txrx_edma.c
index 71b7ad4..7df2cb9 100644
--- a/drivers/net/wireless/ath/wil6210/txrx_edma.c
+++ b/drivers/net/wireless/ath/wil6210/txrx_edma.c
@@ -1025,6 +1025,8 @@ static struct sk_buff *wil_sring_reap_rx_edma(struct wil6210_priv *wil,
 			stats->rx_per_mcs[stats->last_mcs_rx]++;
 	}
 
+	stats->last_cb_mode_rx  = wil_rx_status_get_cb_mode(msg);
+
 	if (!wil->use_rx_hw_reordering && !wil->use_compressed_rx_status &&
 	    wil_check_bar(wil, msg, cid, skb, stats) == -EAGAIN) {
 		kfree_skb(skb);
diff --git a/drivers/net/wireless/ath/wil6210/txrx_edma.h b/drivers/net/wireless/ath/wil6210/txrx_edma.h
index e9e6ea9..724d223 100644
--- a/drivers/net/wireless/ath/wil6210/txrx_edma.h
+++ b/drivers/net/wireless/ath/wil6210/txrx_edma.h
@@ -366,6 +366,12 @@ static inline u8 wil_rx_status_get_mcs(void *msg)
 			    16, 21);
 }
 
+static inline u8 wil_rx_status_get_cb_mode(void *msg)
+{
+	return WIL_GET_BITS(((struct wil_rx_status_compressed *)msg)->d1,
+			    22, 23);
+}
+
 static inline u16 wil_rx_status_get_flow_id(void *msg)
 {
 	return WIL_GET_BITS(((struct wil_rx_status_compressed *)msg)->d0,
diff --git a/drivers/net/wireless/ath/wil6210/wil6210.h b/drivers/net/wireless/ath/wil6210/wil6210.h
index 6f456b3..d8c78a0 100644
--- a/drivers/net/wireless/ath/wil6210/wil6210.h
+++ b/drivers/net/wireless/ath/wil6210/wil6210.h
@@ -590,6 +590,7 @@ struct wil_net_stats {
 	unsigned long	rx_amsdu_error; /* eDMA specific */
 	unsigned long	rx_csum_err;
 	u16 last_mcs_rx;
+	u8 last_cb_mode_rx;
 	u64 rx_per_mcs[WIL_MCS_MAX + 1];
 	u32 ft_roams; /* relevant in STA mode */
 };
@@ -850,6 +851,7 @@ struct wil6210_vif {
 	DECLARE_BITMAP(status, wil_vif_status_last);
 	u32 privacy; /* secure connection? */
 	u16 channel; /* relevant in AP mode */
+	u8 wmi_edmg_channel; /* relevant in AP mode */
 	u8 hidden_ssid; /* relevant in AP mode */
 	u32 ap_isolate; /* no intra-BSS communication */
 	bool pbss;
@@ -1335,7 +1337,7 @@ void wil_p2p_wdev_free(struct wil6210_priv *wil);
 
 int wmi_set_mac_address(struct wil6210_priv *wil, void *addr);
 int wmi_pcp_start(struct wil6210_vif *vif, int bi, u8 wmi_nettype, u8 chan,
-		  u8 hidden_ssid, u8 is_go);
+		  u8 edmg_chan, u8 hidden_ssid, u8 is_go);
 int wmi_pcp_stop(struct wil6210_vif *vif);
 int wmi_led_cfg(struct wil6210_priv *wil, bool enable);
 int wmi_abort_scan(struct wil6210_vif *vif);
@@ -1412,6 +1414,10 @@ int wmi_mgmt_tx_ext(struct wil6210_vif *vif, const u8 *buf, size_t len,
 		    u8 channel, u16 duration_ms);
 int wmi_rbufcap_cfg(struct wil6210_priv *wil, bool enable, u16 threshold);
 
+int wil_wmi2spec_ch(u8 wmi_ch, u8 *spec_ch);
+int wil_spec2wmi_ch(u8 spec_ch, u8 *wmi_ch);
+void wil_update_supported_bands(struct wil6210_priv *wil);
+
 int reverse_memcmp(const void *cs, const void *ct, size_t count);
 
 /* WMI for enhanced DMA */
diff --git a/drivers/net/wireless/ath/wil6210/wmi.c b/drivers/net/wireless/ath/wil6210/wmi.c
index 475b1a2..5760d14 100644
--- a/drivers/net/wireless/ath/wil6210/wmi.c
+++ b/drivers/net/wireless/ath/wil6210/wmi.c
@@ -2163,8 +2163,8 @@ int wmi_rbufcap_cfg(struct wil6210_priv *wil, bool enable, u16 threshold)
 	return rc;
 }
 
-int wmi_pcp_start(struct wil6210_vif *vif,
-		  int bi, u8 wmi_nettype, u8 chan, u8 hidden_ssid, u8 is_go)
+int wmi_pcp_start(struct wil6210_vif *vif, int bi, u8 wmi_nettype,
+		  u8 chan, u8 wmi_edmg_chan, u8 hidden_ssid, u8 is_go)
 {
 	struct wil6210_priv *wil = vif_to_wil(vif);
 	int rc;
@@ -2174,6 +2174,7 @@ int wmi_pcp_start(struct wil6210_vif *vif,
 		.network_type = wmi_nettype,
 		.disable_sec_offload = 1,
 		.channel = chan - 1,
+		.edmg_channel = wmi_edmg_chan,
 		.pcp_max_assoc_sta = wil->max_assoc_sta,
 		.hidden_ssid = hidden_ssid,
 		.is_go = is_go,
diff --git a/drivers/net/wireless/ath/wil6210/wmi.h b/drivers/net/wireless/ath/wil6210/wmi.h
index 3e37229..d75022b 100644
--- a/drivers/net/wireless/ath/wil6210/wmi.h
+++ b/drivers/net/wireless/ath/wil6210/wmi.h
@@ -97,6 +97,7 @@ enum wmi_fw_capability {
 	WMI_FW_CAPABILITY_SET_SILENT_RSSI_TABLE		= 13,
 	WMI_FW_CAPABILITY_LO_POWER_CALIB_FROM_OTP	= 14,
 	WMI_FW_CAPABILITY_PNO				= 15,
+	WMI_FW_CAPABILITY_CHANNEL_BONDING		= 17,
 	WMI_FW_CAPABILITY_REF_CLOCK_CONTROL		= 18,
 	WMI_FW_CAPABILITY_AP_SME_OFFLOAD_NONE		= 19,
 	WMI_FW_CAPABILITY_MULTI_VIFS			= 20,
@@ -361,6 +362,19 @@ enum wmi_connect_ctrl_flag_bits {
 
 #define WMI_MAX_SSID_LEN	(32)
 
+enum wmi_channel {
+	WMI_CHANNEL_1	= 0x00,
+	WMI_CHANNEL_2	= 0x01,
+	WMI_CHANNEL_3	= 0x02,
+	WMI_CHANNEL_4	= 0x03,
+	WMI_CHANNEL_5	= 0x04,
+	WMI_CHANNEL_6	= 0x05,
+	WMI_CHANNEL_9	= 0x06,
+	WMI_CHANNEL_10	= 0x07,
+	WMI_CHANNEL_11	= 0x08,
+	WMI_CHANNEL_12	= 0x09,
+};
+
 /* WMI_CONNECT_CMDID */
 struct wmi_connect_cmd {
 	u8 network_type;
@@ -372,8 +386,12 @@ struct wmi_connect_cmd {
 	u8 group_crypto_len;
 	u8 ssid_len;
 	u8 ssid[WMI_MAX_SSID_LEN];
+	/* enum wmi_channel WMI_CHANNEL_1..WMI_CHANNEL_6; for EDMG this is
+	 * the primary channel number
+	 */
 	u8 channel;
-	u8 reserved0;
+	/* enum wmi_channel WMI_CHANNEL_9..WMI_CHANNEL_12 */
+	u8 edmg_channel;
 	u8 bssid[WMI_MAC_LEN];
 	__le32 ctrl_flags;
 	u8 dst_mac[WMI_MAC_LEN];
@@ -2312,8 +2330,12 @@ struct wmi_notify_req_done_event {
 
 /* WMI_CONNECT_EVENTID */
 struct wmi_connect_event {
+	/* enum wmi_channel WMI_CHANNEL_1..WMI_CHANNEL_6; for EDMG this is
+	 * the primary channel number
+	 */
 	u8 channel;
-	u8 reserved0;
+	/* enum wmi_channel WMI_CHANNEL_9..WMI_CHANNEL_12 */
+	u8 edmg_channel;
 	u8 bssid[WMI_MAC_LEN];
 	__le16 listen_interval;
 	__le16 beacon_interval;
-- 
2.7.4


^ permalink raw reply related

* Re: [PATCH v5 1/2] nl80211: Add support for EDMG channels
From: Johannes Berg @ 2019-08-01 13:49 UTC (permalink / raw)
  To: Alexei Lazar; +Cc: linux-wireless, wil6210
In-Reply-To: <8a445e39175f1aaf7cc3eaa6c4474619@codeaurora.org>

On Thu, 2019-08-01 at 16:15 +0300, Alexei Lazar wrote:
> On 2019-07-26 16:29, Johannes Berg wrote:
> > Hi Alexei,
> > 
> > I'm not exactly sure why, but this breaks practically all connectivity
> > on 2.4 and 5 GHz channels (at least in hwsim tests).
> > 
> > Please check and resubmit.
> 
> Sorry for this issue, we have run hwsim test through wpa_supplicant and
> catch small issue that caused the tests to fail - fix raised.
> Can you please share with us how you run the hwsim, you also used the
> wpa_supplicant unit tests or some other tool?

Yes, I keep saying "hwsim tests" when really I mean the hostap hwsim-
based tests...

johannes


^ permalink raw reply

* Re: [PATCH 16/16] iwlwifi: mvm: fix version check for GEO_TX_POWER_LIMIT support
From: Jonas Hahnfeld @ 2019-08-01 14:07 UTC (permalink / raw)
  To: Luca Coelho, kvalo; +Cc: linux-wireless, Luca Coelho
In-Reply-To: <20190720102545.5952-17-luca@coelho.fi>

[-- Attachment #1: Type: text/plain, Size: 2111 bytes --]

Am Samstag, den 20.07.2019, 13:25 +0300 schrieb Luca Coelho:
> From: Luca Coelho <
> luciano.coelho@intel.com
> >
> 
> We erroneously added a check for FW API version 41 before sending
> GEO_TX_POWER_LIMIT, but this was already implemented in version 38.
> Additionally, it was cherry-picked to older versions, namely 17, 26
> and 29, so check for those as well.
> 
> Signed-off-by: Luca Coelho <
> luciano.coelho@intel.com
> >
> ---
>  drivers/net/wireless/intel/iwlwifi/mvm/fw.c | 9 +++++++--
>  1 file changed, 7 insertions(+), 2 deletions(-)
> 
> diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/fw.c b/drivers/net/wireless/intel/iwlwifi/mvm/fw.c
> index a837cf40afde..00c89bcfdf6a 100644
> --- a/drivers/net/wireless/intel/iwlwifi/mvm/fw.c
> +++ b/drivers/net/wireless/intel/iwlwifi/mvm/fw.c
> @@ -886,9 +886,14 @@ static bool iwl_mvm_sar_geo_support(struct iwl_mvm *mvm)
>  	 * The GEO_TX_POWER_LIMIT command is not supported on earlier
>  	 * firmware versions.  Unfortunately, we don't have a TLV API
>  	 * flag to rely on, so rely on the major version which is in
> -	 * the first byte of ucode_ver.
> +	 * the first byte of ucode_ver.  This was implemented
> +	 * initially on version 38 and then backported to 36, 29 and
> +	 * 17.
>  	 */
> -	return IWL_UCODE_SERIAL(mvm->fw->ucode_ver) >= 41;
> +	return IWL_UCODE_SERIAL(mvm->fw->ucode_ver) >= 38 ||
> +	       IWL_UCODE_SERIAL(mvm->fw->ucode_ver) == 36 ||
> +	       IWL_UCODE_SERIAL(mvm->fw->ucode_ver) == 29 ||
> +	       IWL_UCODE_SERIAL(mvm->fw->ucode_ver) == 17;
>  }
>  
>  int iwl_mvm_get_sar_geo_profile(struct iwl_mvm *mvm)

After pulling iwlwifi-fixes-for-kvalo-2019-07-30, I still get firmware
crashes on my "Intel(R) Dual Band Wireless AC 8260" which uses firmware
version 36. I tried the latest ucode in linux-firmware (36.77d01142.0),
but it seems GEO_TX_POWER_LIMIT is not yet implemented there.

Could you let me know which firmware version I need for Linux 5.3? Can
there be a check in iwl_mvm_sar_geo_support() that the backported
support is indeed available?

Thanks,
Jonas

[-- Attachment #2: This is a digitally signed message part --]
[-- Type: application/pgp-signature, Size: 488 bytes --]

^ permalink raw reply


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox