diff mbox series

[v2] wifi: cfg80211: skip regulatory checks when the channel is punctured

Message ID 20241125051624.29085-1-quic_kkavita@quicinc.com (mailing list archive)
State New
Delegated to: Johannes Berg
Headers show
Series [v2] wifi: cfg80211: skip regulatory checks when the channel is punctured | expand

Commit Message

Kavita Kavita Nov. 25, 2024, 5:16 a.m. UTC
The kernel performs several regulatory checks for AP mode in
nl80211/cfg80211. These checks include radar detection,
verification of whether the sub-channel is disabled, and
an examination to determine if the channel is a DFS channel
(both DFS usable and DFS available). These checks are
performed across a frequency range, examining each sub-channel.

However, these checks are also performed on frequencies that
have been punctured, which should not be examined as they are
not in use.

This leads to the issue where the AP stops because one of
the 20 MHz sub-channels is disabled or radar detected on
the channel, even when the sub-channel is punctured.

To address this issue, add a condition check wherever
regulatory checks exist for AP mode in nl80211/cfg80211.
This check identifies punctured channels and, upon finding
them, skips the regulatory checks for those channels.

Co-developed-by: Manaswini Paluri <quic_mpaluri@quicinc.com>
Signed-off-by: Manaswini Paluri <quic_mpaluri@quicinc.com>
Signed-off-by: Kavita Kavita <quic_kkavita@quicinc.com>
---
Changes in v2:
- Added macro "for_each_subchan" for traversing non punctured channels.
- Skip setting dfs state for punctured channels.
- for center_freq2, pass 0 as punctured bitmap.
---
diff mbox series

Patch

diff --git a/net/wireless/chan.c b/net/wireless/chan.c
index 40b6375a5de4..bd6b0056ce42 100644
--- a/net/wireless/chan.c
+++ b/net/wireless/chan.c
@@ -583,15 +583,20 @@  cfg80211_chandef_compatible(const struct cfg80211_chan_def *c1,
 EXPORT_SYMBOL(cfg80211_chandef_compatible);
 
 static void cfg80211_set_chans_dfs_state(struct wiphy *wiphy, u32 center_freq,
-					 u32 bandwidth,
+					 u32 bandwidth, u16 punctured,
 					 enum nl80211_dfs_state dfs_state)
 {
 	struct ieee80211_channel *c;
 	u32 freq;
+	int subchan = 0;
 
 	for (freq = center_freq - bandwidth/2 + 10;
 	     freq <= center_freq + bandwidth/2 - 10;
 	     freq += 20) {
+		if (punctured & BIT(subchan))
+			continue;
+		subchan++;
+
 		c = ieee80211_get_channel(wiphy, freq);
 		if (!c || !(c->flags & IEEE80211_CHAN_RADAR))
 			continue;
@@ -615,40 +620,12 @@  void cfg80211_set_dfs_state(struct wiphy *wiphy,
 		return;
 
 	cfg80211_set_chans_dfs_state(wiphy, chandef->center_freq1,
-				     width, dfs_state);
+				     width, chandef->punctured, dfs_state);
 
 	if (!chandef->center_freq2)
 		return;
 	cfg80211_set_chans_dfs_state(wiphy, chandef->center_freq2,
-				     width, dfs_state);
-}
-
-static u32 cfg80211_get_start_freq(u32 center_freq,
-				   u32 bandwidth)
-{
-	u32 start_freq;
-
-	bandwidth = MHZ_TO_KHZ(bandwidth);
-	if (bandwidth <= MHZ_TO_KHZ(20))
-		start_freq = center_freq;
-	else
-		start_freq = center_freq - bandwidth / 2 + MHZ_TO_KHZ(10);
-
-	return start_freq;
-}
-
-static u32 cfg80211_get_end_freq(u32 center_freq,
-				 u32 bandwidth)
-{
-	u32 end_freq;
-
-	bandwidth = MHZ_TO_KHZ(bandwidth);
-	if (bandwidth <= MHZ_TO_KHZ(20))
-		end_freq = center_freq;
-	else
-		end_freq = center_freq + bandwidth / 2 - MHZ_TO_KHZ(10);
-
-	return end_freq;
+				     width, 0, dfs_state);
 }
 
 static bool
@@ -727,24 +704,20 @@  static bool cfg80211_dfs_permissive_chan(struct wiphy *wiphy,
 static int cfg80211_get_chans_dfs_required(struct wiphy *wiphy,
 					    u32 center_freq,
 					    u32 bandwidth,
+					    u16 punctured,
 					    enum nl80211_iftype iftype)
 {
-	struct ieee80211_channel *c;
-	u32 freq, start_freq, end_freq;
-
-	start_freq = cfg80211_get_start_freq(center_freq, bandwidth);
-	end_freq = cfg80211_get_end_freq(center_freq, bandwidth);
+	struct ieee80211_channel *subchan;
 
-	for (freq = start_freq; freq <= end_freq; freq += MHZ_TO_KHZ(20)) {
-		c = ieee80211_get_channel_khz(wiphy, freq);
-		if (!c)
-			return -EINVAL;
-
-		if (c->flags & IEEE80211_CHAN_RADAR &&
-		    !cfg80211_dfs_permissive_chan(wiphy, iftype, c))
+	for_each_subchan(wiphy, center_freq, bandwidth, punctured, subchan) {
+		if ((subchan->flags & IEEE80211_CHAN_RADAR) &&
+		    !cfg80211_dfs_permissive_chan(wiphy, iftype, subchan))
 			return 1;
 	}
 
+	if (subchan && IS_ERR(subchan))
+		return PTR_ERR(subchan);
+
 	return 0;
 }
 
@@ -770,7 +743,8 @@  int cfg80211_chandef_dfs_required(struct wiphy *wiphy,
 
 		ret = cfg80211_get_chans_dfs_required(wiphy,
 					ieee80211_chandef_to_khz(chandef),
-					width, iftype);
+					width, chandef->punctured, iftype);
+
 		if (ret < 0)
 			return ret;
 		else if (ret > 0)
@@ -781,7 +755,8 @@  int cfg80211_chandef_dfs_required(struct wiphy *wiphy,
 
 		ret = cfg80211_get_chans_dfs_required(wiphy,
 					MHZ_TO_KHZ(chandef->center_freq2),
-					width, iftype);
+					width, 0, iftype);
+
 		if (ret < 0)
 			return ret;
 		else if (ret > 0)
@@ -808,38 +783,33 @@  EXPORT_SYMBOL(cfg80211_chandef_dfs_required);
 
 static int cfg80211_get_chans_dfs_usable(struct wiphy *wiphy,
 					 u32 center_freq,
-					 u32 bandwidth)
+					 u32 bandwidth, u16 punctured)
 {
-	struct ieee80211_channel *c;
-	u32 freq, start_freq, end_freq;
+	struct ieee80211_channel *subchan;
 	int count = 0;
 
-	start_freq = cfg80211_get_start_freq(center_freq, bandwidth);
-	end_freq = cfg80211_get_end_freq(center_freq, bandwidth);
-
 	/*
 	 * Check entire range of channels for the bandwidth.
 	 * Check all channels are DFS channels (DFS_USABLE or
 	 * DFS_AVAILABLE). Return number of usable channels
 	 * (require CAC). Allow DFS and non-DFS channel mix.
 	 */
-	for (freq = start_freq; freq <= end_freq; freq += MHZ_TO_KHZ(20)) {
-		c = ieee80211_get_channel_khz(wiphy, freq);
-		if (!c)
-			return -EINVAL;
-
-		if (c->flags & IEEE80211_CHAN_DISABLED)
+	for_each_subchan(wiphy, center_freq, bandwidth, punctured, subchan) {
+		if (subchan->flags & IEEE80211_CHAN_DISABLED)
 			return -EINVAL;
 
-		if (c->flags & IEEE80211_CHAN_RADAR) {
-			if (c->dfs_state == NL80211_DFS_UNAVAILABLE)
+		if (subchan->flags & IEEE80211_CHAN_RADAR) {
+			if (subchan->dfs_state == NL80211_DFS_UNAVAILABLE)
 				return -EINVAL;
 
-			if (c->dfs_state == NL80211_DFS_USABLE)
+			if (subchan->dfs_state == NL80211_DFS_USABLE)
 				count++;
 		}
 	}
 
+	if (subchan && IS_ERR(subchan))
+		return PTR_ERR(subchan);
+
 	return count;
 }
 
@@ -858,7 +828,7 @@  bool cfg80211_chandef_dfs_usable(struct wiphy *wiphy,
 
 	r1 = cfg80211_get_chans_dfs_usable(wiphy,
 					   MHZ_TO_KHZ(chandef->center_freq1),
-					   width);
+					   width, chandef->punctured);
 
 	if (r1 < 0)
 		return false;
@@ -868,7 +838,7 @@  bool cfg80211_chandef_dfs_usable(struct wiphy *wiphy,
 		WARN_ON(!chandef->center_freq2);
 		r2 = cfg80211_get_chans_dfs_usable(wiphy,
 					MHZ_TO_KHZ(chandef->center_freq2),
-					width);
+					width, 0);
 		if (r2 < 0)
 			return false;
 		break;
@@ -1053,37 +1023,32 @@  bool cfg80211_any_wiphy_oper_chan(struct wiphy *wiphy,
 
 static bool cfg80211_get_chans_dfs_available(struct wiphy *wiphy,
 					     u32 center_freq,
-					     u32 bandwidth)
+					     u32 bandwidth, u16 punctured)
 {
-	struct ieee80211_channel *c;
-	u32 freq, start_freq, end_freq;
+	struct ieee80211_channel *subchan;
 	bool dfs_offload;
 
 	dfs_offload = wiphy_ext_feature_isset(wiphy,
 					      NL80211_EXT_FEATURE_DFS_OFFLOAD);
 
-	start_freq = cfg80211_get_start_freq(center_freq, bandwidth);
-	end_freq = cfg80211_get_end_freq(center_freq, bandwidth);
-
 	/*
 	 * Check entire range of channels for the bandwidth.
 	 * If any channel in between is disabled or has not
 	 * had gone through CAC return false
 	 */
-	for (freq = start_freq; freq <= end_freq; freq += MHZ_TO_KHZ(20)) {
-		c = ieee80211_get_channel_khz(wiphy, freq);
-		if (!c)
+	for_each_subchan(wiphy, center_freq, bandwidth, punctured, subchan) {
+		if (subchan->flags & IEEE80211_CHAN_DISABLED)
 			return false;
 
-		if (c->flags & IEEE80211_CHAN_DISABLED)
-			return false;
-
-		if ((c->flags & IEEE80211_CHAN_RADAR) &&
-		    (c->dfs_state != NL80211_DFS_AVAILABLE) &&
-		    !(c->dfs_state == NL80211_DFS_USABLE && dfs_offload))
+		if ((subchan->flags & IEEE80211_CHAN_RADAR) &&
+		    subchan->dfs_state != NL80211_DFS_AVAILABLE &&
+		    !(subchan->dfs_state == NL80211_DFS_USABLE && dfs_offload))
 			return false;
 	}
 
+	if (subchan && IS_ERR(subchan))
+		return false;
+
 	return true;
 }
 
@@ -1102,7 +1067,7 @@  static bool cfg80211_chandef_dfs_available(struct wiphy *wiphy,
 
 	r = cfg80211_get_chans_dfs_available(wiphy,
 					     MHZ_TO_KHZ(chandef->center_freq1),
-					     width);
+					     width, chandef->punctured);
 
 	/* If any of channels unavailable for cf1 just return */
 	if (!r)
@@ -1113,7 +1078,7 @@  static bool cfg80211_chandef_dfs_available(struct wiphy *wiphy,
 		WARN_ON(!chandef->center_freq2);
 		r = cfg80211_get_chans_dfs_available(wiphy,
 					MHZ_TO_KHZ(chandef->center_freq2),
-					width);
+					width, 0);
 		break;
 	default:
 		WARN_ON(chandef->center_freq2);
@@ -1125,30 +1090,25 @@  static bool cfg80211_chandef_dfs_available(struct wiphy *wiphy,
 
 static unsigned int cfg80211_get_chans_dfs_cac_time(struct wiphy *wiphy,
 						    u32 center_freq,
-						    u32 bandwidth)
+						    u32 bandwidth, u16 punctured)
 {
-	struct ieee80211_channel *c;
-	u32 start_freq, end_freq, freq;
+	struct ieee80211_channel *subchan;
 	unsigned int dfs_cac_ms = 0;
 
-	start_freq = cfg80211_get_start_freq(center_freq, bandwidth);
-	end_freq = cfg80211_get_end_freq(center_freq, bandwidth);
-
-	for (freq = start_freq; freq <= end_freq; freq += MHZ_TO_KHZ(20)) {
-		c = ieee80211_get_channel_khz(wiphy, freq);
-		if (!c)
-			return 0;
-
-		if (c->flags & IEEE80211_CHAN_DISABLED)
+	for_each_subchan(wiphy, center_freq, bandwidth, punctured, subchan) {
+		if (subchan->flags & IEEE80211_CHAN_DISABLED)
 			return 0;
 
-		if (!(c->flags & IEEE80211_CHAN_RADAR))
+		if (!(subchan->flags & IEEE80211_CHAN_RADAR))
 			continue;
 
-		if (c->dfs_cac_ms > dfs_cac_ms)
-			dfs_cac_ms = c->dfs_cac_ms;
+		if (subchan->dfs_cac_ms > dfs_cac_ms)
+			dfs_cac_ms = subchan->dfs_cac_ms;
 	}
 
+	if (subchan && IS_ERR(subchan))
+		return 0;
+
 	return dfs_cac_ms;
 }
 
@@ -1168,14 +1128,14 @@  cfg80211_chandef_dfs_cac_time(struct wiphy *wiphy,
 
 	t1 = cfg80211_get_chans_dfs_cac_time(wiphy,
 					     MHZ_TO_KHZ(chandef->center_freq1),
-					     width);
+					     width, chandef->punctured);
 
 	if (!chandef->center_freq2)
 		return t1;
 
 	t2 = cfg80211_get_chans_dfs_cac_time(wiphy,
 					     MHZ_TO_KHZ(chandef->center_freq2),
-					     width);
+					     width, 0);
 
 	return max(t1, t2);
 }
@@ -1183,25 +1143,21 @@  EXPORT_SYMBOL(cfg80211_chandef_dfs_cac_time);
 
 static bool cfg80211_secondary_chans_ok(struct wiphy *wiphy,
 					u32 center_freq, u32 bandwidth,
-					u32 prohibited_flags,
+					u16 punctured, u32 prohibited_flags,
 					u32 permitting_flags)
 {
-	struct ieee80211_channel *c;
-	u32 freq, start_freq, end_freq;
-
-	start_freq = cfg80211_get_start_freq(center_freq, bandwidth);
-	end_freq = cfg80211_get_end_freq(center_freq, bandwidth);
+	struct ieee80211_channel *subchan;
 
-	for (freq = start_freq; freq <= end_freq; freq += MHZ_TO_KHZ(20)) {
-		c = ieee80211_get_channel_khz(wiphy, freq);
-		if (!c)
-			return false;
-		if (c->flags & permitting_flags)
+	for_each_subchan(wiphy, center_freq, bandwidth, punctured, subchan) {
+		if (subchan->flags & permitting_flags)
 			continue;
-		if (c->flags & prohibited_flags)
+		if (subchan->flags & prohibited_flags)
 			return false;
 	}
 
+	if (subchan && IS_ERR(subchan))
+		return false;
+
 	return true;
 }
 
@@ -1423,7 +1379,8 @@  bool _cfg80211_chandef_usable(struct wiphy *wiphy,
 
 	if (!cfg80211_secondary_chans_ok(wiphy,
 					 ieee80211_chandef_to_khz(chandef),
-					 width, prohibited_flags,
+					 width, chandef->punctured,
+					 prohibited_flags,
 					 permitting_flags))
 		return false;
 
@@ -1431,7 +1388,7 @@  bool _cfg80211_chandef_usable(struct wiphy *wiphy,
 		return true;
 	return cfg80211_secondary_chans_ok(wiphy,
 					   MHZ_TO_KHZ(chandef->center_freq2),
-					   width, prohibited_flags,
+					   width, 0, prohibited_flags,
 					   permitting_flags);
 }
 
diff --git a/net/wireless/core.h b/net/wireless/core.h
index 4c45f994a8c0..f4583e0494d7 100644
--- a/net/wireless/core.h
+++ b/net/wireless/core.h
@@ -505,6 +505,18 @@  bool _cfg80211_chandef_usable(struct wiphy *wiphy,
 			      u32 prohibited_flags,
 			      u32 permitting_flags);
 
+#define for_each_subchan(wiphy, center_freq, bandwidth, punctured,	\
+			       subchan)						\
+	for (subchan = ieee80211_next_subchan(wiphy, center_freq, bandwidth,	\
+					      punctured, NULL);			\
+	     subchan && !IS_ERR(subchan);					\
+	     subchan = ieee80211_next_subchan(wiphy, center_freq, bandwidth,	\
+					      punctured, subchan))
+
+struct ieee80211_channel *
+ieee80211_next_subchan(struct wiphy *wiphy, u32 center_freq, u32 bandwidth,
+		       u16 punctured, struct ieee80211_channel *subchan);
+
 static inline unsigned int elapsed_jiffies_msecs(unsigned long start)
 {
 	unsigned long end = jiffies;
diff --git a/net/wireless/util.c b/net/wireless/util.c
index 040d62051eb9..2e2f362b7548 100644
--- a/net/wireless/util.c
+++ b/net/wireless/util.c
@@ -184,6 +184,70 @@  struct ieee80211_channel *ieee80211_get_channel_khz(struct wiphy *wiphy,
 }
 EXPORT_SYMBOL(ieee80211_get_channel_khz);
 
+static u32 cfg80211_get_start_freq(u32 center_freq,
+				   u32 bandwidth)
+{
+	u32 start_freq;
+
+	bandwidth = MHZ_TO_KHZ(bandwidth);
+	if (bandwidth <= MHZ_TO_KHZ(20))
+		start_freq = center_freq;
+	else
+		start_freq = center_freq - bandwidth / 2 + MHZ_TO_KHZ(10);
+
+	return start_freq;
+}
+
+static u32 cfg80211_get_end_freq(u32 center_freq,
+				 u32 bandwidth)
+{
+	u32 end_freq;
+
+	bandwidth = MHZ_TO_KHZ(bandwidth);
+	if (bandwidth <= MHZ_TO_KHZ(20))
+		end_freq = center_freq;
+	else
+		end_freq = center_freq + bandwidth / 2 - MHZ_TO_KHZ(10);
+
+	return end_freq;
+}
+
+struct ieee80211_channel *ieee80211_next_subchan(struct wiphy *wiphy,
+						 u32 center_freq, u32 bandwidth,
+						 u16 punctured,
+						 struct ieee80211_channel *subchan)
+{
+	struct ieee80211_channel *next_subchan;
+	u32 freq, start_freq, end_freq;
+	int pos;
+
+	start_freq = cfg80211_get_start_freq(center_freq, bandwidth);
+	end_freq = cfg80211_get_end_freq(center_freq, bandwidth);
+
+	if (!subchan) {
+		freq = start_freq;
+	} else {
+		freq = ieee80211_channel_to_khz(subchan);
+		freq += MHZ_TO_KHZ(20);
+	}
+
+	while (freq <= end_freq) {
+		next_subchan = ieee80211_get_channel_khz(wiphy, freq);
+
+		if (!next_subchan)
+			return ERR_PTR(-EINVAL);
+
+		pos = (freq - start_freq) / MHZ_TO_KHZ(20);
+
+		if (!(punctured & BIT(pos)))
+			return next_subchan;
+
+		freq += MHZ_TO_KHZ(20);
+	}
+
+	return NULL;
+}
+
 static void set_mandatory_flags_band(struct ieee80211_supported_band *sband)
 {
 	int i, want;