From: Felix Fietkau Date: Sun, 1 Feb 2026 15:38:53 +0000 (+0000) Subject: mac80211: improve patch to allow grace period for DFS X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=8e3de10b4462e54224c83884f1388ba68dae93ef;p=thirdparty%2Fopenwrt.git mac80211: improve patch to allow grace period for DFS Fix corner cases in updates. Improve channel puncturing handling. Fix dealing with CSA. Signed-off-by: Felix Fietkau --- diff --git a/package/kernel/mac80211/patches/ath/404-regd_no_assoc_hints.patch b/package/kernel/mac80211/patches/ath/404-regd_no_assoc_hints.patch index 57c9c7b0d92..a82021af315 100644 --- a/package/kernel/mac80211/patches/ath/404-regd_no_assoc_hints.patch +++ b/package/kernel/mac80211/patches/ath/404-regd_no_assoc_hints.patch @@ -1,6 +1,6 @@ --- a/net/wireless/reg.c +++ b/net/wireless/reg.c -@@ -3335,6 +3335,8 @@ void regulatory_hint_country_ie(struct w +@@ -3336,6 +3336,8 @@ void regulatory_hint_country_ie(struct w enum environment_cap env = ENVIRON_ANY; struct regulatory_request *request = NULL, *lr; @@ -9,7 +9,7 @@ /* IE len must be evenly divisible by 2 */ if (country_ie_len & 0x01) return; -@@ -3584,6 +3586,7 @@ static bool is_wiphy_all_set_reg_flag(en +@@ -3585,6 +3587,7 @@ static bool is_wiphy_all_set_reg_flag(en void regulatory_hint_disconnect(void) { diff --git a/package/kernel/mac80211/patches/subsys/310-cfg80211-allow-grace-period-for-DFS-available-after-.patch b/package/kernel/mac80211/patches/subsys/310-cfg80211-allow-grace-period-for-DFS-available-after-.patch index 01fe5dd7df8..5ada929cc7d 100644 --- a/package/kernel/mac80211/patches/subsys/310-cfg80211-allow-grace-period-for-DFS-available-after-.patch +++ b/package/kernel/mac80211/patches/subsys/310-cfg80211-allow-grace-period-for-DFS-available-after-.patch @@ -3,7 +3,27 @@ Date: Thu, 14 Sep 2023 13:17:16 +0200 Subject: [PATCH] cfg80211: allow grace period for DFS available after beacon shutdown -Fixes reconfiguring an AP on a DFS channel in non-ETSI regdomain +In non-ETSI regulatory domains, DFS channel availability from completed CAC +expires after REG_PRE_CAC_EXPIRY_GRACE_MS (2 seconds) when no longer in use. +This creates a problem when reconfiguring an AP on a DFS channel: stopping +the AP to apply new settings causes the channel to immediately expire to +USABLE state, requiring a full CAC again. + +The root cause is that the grace period timeout was calculated from +dfs_state_entered, which records when CAC completed. For an AP that has +been running for hours, this timestamp is far in the past, causing +immediate expiration. + +Fix this by introducing a new field dfs_state_last_available that tracks +when a DFS channel was last actively used. This timestamp is updated: +- When DFS state transitions to AVAILABLE (CAC completion) +- When an AP stops beaconing on the channel +- When a channel switch moves away from the channel +- When DFS state is copied between wiphys + +The grace period for AVAILABLE->USABLE transitions now uses this new +timestamp, giving a 2-second window to reconfigure the AP without losing +DFS availability. Fixes: b35a51c7dd25 ("cfg80211: Make pre-CAC results valid only for ETSI domain") Signed-off-by: Felix Fietkau @@ -61,55 +81,30 @@ Signed-off-by: Felix Fietkau } } -@@ -990,6 +992,53 @@ bool cfg80211_any_wiphy_oper_chan(struct +@@ -990,6 +992,28 @@ bool cfg80211_any_wiphy_oper_chan(struct return false; } -+static void -+__cfg80211_update_last_available(struct wiphy *wiphy, -+ u32 center_freq, -+ u32 bandwidth) -+{ -+ struct ieee80211_channel *c; -+ u32 freq, start_freq, end_freq; -+ -+ if (bandwidth <= MHZ_TO_KHZ(20)) -+ start_freq = end_freq = center_freq; -+ else { -+ start_freq = center_freq - bandwidth / 2 + MHZ_TO_KHZ(10); -+ end_freq = center_freq + bandwidth / 2 - MHZ_TO_KHZ(10); -+ } -+ -+ /* -+ * 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) -+ return; -+ -+ c->dfs_state_last_available = jiffies; -+ } -+} -+ +void cfg80211_update_last_available(struct wiphy *wiphy, + const struct cfg80211_chan_def *chandef) +{ ++ struct ieee80211_channel *c; + int width; + ++ if (WARN_ON(!cfg80211_chandef_valid(chandef))) ++ return; ++ + width = cfg80211_chandef_get_width(chandef); + if (width < 0) + return; + -+ __cfg80211_update_last_available(wiphy, MHZ_TO_KHZ(chandef->center_freq1), -+ width); -+ if (chandef->width != NL80211_CHAN_WIDTH_80P80) -+ return; ++ for_each_subchan(chandef, freq, cf) { ++ c = ieee80211_get_channel_khz(wiphy, freq); ++ if (!c) ++ return; + -+ __cfg80211_update_last_available(wiphy, MHZ_TO_KHZ(chandef->center_freq2), -+ width); ++ c->dfs_state_last_available = jiffies; ++ } +} + static bool cfg80211_chandef_dfs_available(struct wiphy *wiphy, @@ -151,3 +146,25 @@ Signed-off-by: Felix Fietkau if (time_after_eq(jiffies, timeout)) { c->dfs_state = NL80211_DFS_USABLE; c->dfs_state_entered = jiffies; +--- a/net/wireless/nl80211.c ++++ b/net/wireless/nl80211.c +@@ -20967,6 +20967,9 @@ void cfg80211_ch_switch_notify(struct ne + break; + case NL80211_IFTYPE_AP: + case NL80211_IFTYPE_P2P_GO: ++ if (wdev->links[link_id].ap.chandef.chan) ++ cfg80211_update_last_available(wdev->wiphy, ++ &wdev->links[link_id].ap.chandef); + wdev->links[link_id].ap.chandef = *chandef; + break; + case NL80211_IFTYPE_ADHOC: +--- a/net/wireless/reg.c ++++ b/net/wireless/reg.c +@@ -2954,6 +2954,7 @@ static void reg_copy_dfs_chan_state(stru + dst_chan->dfs_state == NL80211_DFS_USABLE) { + dst_chan->dfs_state = src_chan->dfs_state; + dst_chan->dfs_state_entered = src_chan->dfs_state_entered; ++ dst_chan->dfs_state_last_available = src_chan->dfs_state_last_available; + } + } +