[RFC] [PATCH] wifi: ath12k: wait for chan update in reg_notifier

Mihai Moldovan ionic at ionic.de
Mon Dec 9 13:47:33 PST 2024


Currently, setting a new regdomain is asynchronous in ath12k, in the
sense that the reg_notifier function does not wait for the actual
regdomain change to complete, including handling the channel list
update.

This causes issues with user space programs like hostapd, which listen
on a netlink socket, set a new country code, see a reg change event and
assume that the reg change actually completed within the driver as well.

Unfortunately, this has almost never been the case in my testing, which
lead to failures when trying to use channels that are disallowed in the
old regdomain, but allowed in the new regdomain.

Easy reproducer:
iw reg set CN
hostapd hostapd.conf # should contain e.g., channel=100 and country_code=DE
=> fails to start

Be nice to user space and wait for ath12k_regd_update to complete prior
to exiting the reg_notifier, which is triggered by receiving the channel
list update event from the FW.

Signed-off-by: Mihai Moldovan <ionic at ionic.de>
Tested-on: WCN7850 hw2.0 PCI WLAN.HMT.1.0.c5-00481-QCAHMTSWPL_V1.0_V2.0_SILICONZ-3
---
 drivers/net/wireless/ath/ath12k/core.c |  2 ++
 drivers/net/wireless/ath/ath12k/core.h |  3 +++
 drivers/net/wireless/ath/ath12k/mac.c  |  3 +++
 drivers/net/wireless/ath/ath12k/reg.c  | 11 +++++++++++
 4 files changed, 19 insertions(+)

diff --git a/drivers/net/wireless/ath/ath12k/core.c b/drivers/net/wireless/ath/ath12k/core.c
index 07c6cdd9fcd5..9a226484062f 100644
--- a/drivers/net/wireless/ath/ath12k/core.c
+++ b/drivers/net/wireless/ath/ath12k/core.c
@@ -1073,6 +1073,8 @@ static void ath12k_core_pre_reconfigure_recovery(struct ath12k_base *ab)
 
 		ieee80211_stop_queues(ah->hw);
 
+		complete(&ah->completed_regd_update);
+
 		for (j = 0; j < ah->num_radio; j++) {
 			ar = &ah->radio[j];
 
diff --git a/drivers/net/wireless/ath/ath12k/core.h b/drivers/net/wireless/ath/ath12k/core.h
index 0be274f99c99..6507eee32056 100644
--- a/drivers/net/wireless/ath/ath12k/core.h
+++ b/drivers/net/wireless/ath/ath12k/core.h
@@ -360,6 +360,7 @@ struct ath12k_vif_iter {
 #define ATH12K_RX_RATE_TABLE_NUM 320
 
 #define ATH12K_SCAN_TIMEOUT_HZ (20 * HZ)
+#define ATH12K_REGD_UPDATE_TIMEOUT_HZ (60 * HZ)
 
 struct ath12k_rx_peer_rate_stats {
 	u64 ht_mcs_count[HAL_RX_MAX_MCS_HT + 1];
@@ -742,6 +743,8 @@ struct ath12k_hw {
 
 	DECLARE_BITMAP(free_ml_peer_id_map, ATH12K_MAX_MLO_PEERS);
 
+	struct completion completed_regd_update;
+
 	/* protected by wiphy_lock() */
 	struct list_head ml_peers;
 
diff --git a/drivers/net/wireless/ath/ath12k/mac.c b/drivers/net/wireless/ath/ath12k/mac.c
index 4cbe8dbdcba9..b76135b4b164 100644
--- a/drivers/net/wireless/ath/ath12k/mac.c
+++ b/drivers/net/wireless/ath/ath12k/mac.c
@@ -10649,6 +10649,8 @@ static void ath12k_mac_hw_unregister(struct ath12k_hw *ah)
 
 	ieee80211_unregister_hw(hw);
 
+	reinit_completion(&ah->completed_regd_update);
+
 	for_each_ar(ah, ar, i)
 		ath12k_mac_cleanup_unregister(ar);
 
@@ -10861,6 +10863,7 @@ static int ath12k_mac_hw_register(struct ath12k_hw *ah)
 	wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_PUNCT);
 
 	ath12k_reg_init(hw);
+	init_completion(&ah->completed_regd_update);
 
 	if (!is_raw_mode) {
 		hw->netdev_features = NETIF_F_HW_CSUM;
diff --git a/drivers/net/wireless/ath/ath12k/reg.c b/drivers/net/wireless/ath/ath12k/reg.c
index 7d2d887c5dbe..046ad0eadf7e 100644
--- a/drivers/net/wireless/ath/ath12k/reg.c
+++ b/drivers/net/wireless/ath/ath12k/reg.c
@@ -52,6 +52,7 @@ ath12k_reg_notifier(struct wiphy *wiphy, struct regulatory_request *request)
 	struct ath12k_hw *ah = ath12k_hw_to_ah(hw);
 	struct ath12k *ar = ath12k_ah_to_ar(ah, 0);
 	int ret, i;
+	unsigned long left = 0;
 
 	ath12k_dbg(ar->ab, ATH12K_DBG_REG,
 		   "Regulatory Notification received for %s\n", wiphy_name(wiphy));
@@ -125,6 +126,15 @@ ath12k_reg_notifier(struct wiphy *wiphy, struct regulatory_request *request)
 
 		ar->regdom_set_by_user = true;
 	}
+
+	left = wait_for_completion_timeout(&ah->completed_regd_update,
+					   ATH12K_REGD_UPDATE_TIMEOUT_HZ);
+	if (!left) {
+		ath12k_dbg(ar->ab, ATH12K_DBG_REG,
+			   "failed to receive regd update complete: timed out\n");
+	}
+	ath12k_dbg(ar->ab, ATH12K_DBG_REG,
+		   "regd update wait left time %ld\n", left);
 }
 
 int ath12k_reg_update_chan_list(struct ath12k *ar, bool wait)
@@ -343,6 +353,7 @@ int ath12k_regd_update(struct ath12k *ar, bool init)
 		goto skip;
 
 	ah->regd_updated = true;
+	complete(&ah->completed_regd_update);
 skip:
 	return 0;
 err:
-- 
2.45.2




More information about the ath12k mailing list