From: Felix Fietkau Date: Thu, 22 May 2025 07:47:04 +0000 (+0200) Subject: mac80211: allow scanning operating on DFS channels X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=91d33d156349796c88f9e4fafd4de2581b2a2d8b;p=thirdparty%2Fopenwrt.git mac80211: allow scanning operating on DFS channels Only for multi-radio configurations where the scan would not affect the radio running DFS detection Signed-off-by: Felix Fietkau --- diff --git a/package/kernel/mac80211/patches/subsys/350-mac80211-allow-scanning-while-on-radar-channel.patch b/package/kernel/mac80211/patches/subsys/350-mac80211-allow-scanning-while-on-radar-channel.patch new file mode 100644 index 00000000000..d469955a900 --- /dev/null +++ b/package/kernel/mac80211/patches/subsys/350-mac80211-allow-scanning-while-on-radar-channel.patch @@ -0,0 +1,372 @@ +--- a/net/mac80211/cfg.c ++++ b/net/mac80211/cfg.c +@@ -3473,6 +3473,27 @@ static int ieee80211_set_bitrate_mask(st + return 0; + } + ++bool ieee80211_scanning_busy(struct ieee80211_local *local, ++ struct cfg80211_chan_def *chandef) ++{ ++ struct cfg80211_scan_request *scan_req; ++ struct wiphy *wiphy = local->hw.wiphy; ++ u32 mask; ++ ++ if (list_empty(&local->roc_list) && !local->scanning) ++ return false; ++ ++ if (!wiphy->n_radio) ++ return true; ++ ++ mask = ieee80211_offchannel_radio_mask(local); ++ scan_req = wiphy_dereference(wiphy, local->scan_req); ++ if (scan_req) ++ mask |= ieee80211_scan_req_radio_mask(local, scan_req); ++ ++ return mask & ieee80211_chandef_radio_mask(local, chandef); ++} ++ + static int ieee80211_start_radar_detection(struct wiphy *wiphy, + struct net_device *dev, + struct cfg80211_chan_def *chandef, +@@ -3486,7 +3507,7 @@ static int ieee80211_start_radar_detecti + + lockdep_assert_wiphy(local->hw.wiphy); + +- if (!list_empty(&local->roc_list) || local->scanning) ++ if (ieee80211_scanning_busy(local, chandef)) + return -EBUSY; + + link_data = sdata_dereference(sdata->link[link_id], sdata); +@@ -3978,7 +3999,7 @@ __ieee80211_channel_switch(struct wiphy + + lockdep_assert_wiphy(local->hw.wiphy); + +- if (!list_empty(&local->roc_list) || local->scanning) ++ if (ieee80211_scanning_busy(local, ¶ms->chandef)) + return -EBUSY; + + if (sdata->wdev.links[link_id].cac_started) +--- a/net/mac80211/chan.c ++++ b/net/mac80211/chan.c +@@ -629,14 +629,24 @@ ieee80211_find_chanctx(struct ieee80211_ + return NULL; + } + +-bool ieee80211_is_radar_required(struct ieee80211_local *local) ++bool ieee80211_is_radar_required(struct ieee80211_local *local, u32 radio_mask) + { ++ struct ieee80211_chanctx_conf *conf; + struct ieee80211_link_data *link; + + lockdep_assert_wiphy(local->hw.wiphy); + + for_each_sdata_link(local, link) { +- if (link->radar_required) ++ if (!link->radar_required) ++ continue; ++ if (!local->hw.wiphy->n_radio) ++ return true; ++ ++ conf = wiphy_dereference(local->hw.wiphy, link->conf->chanctx_conf); ++ if (!conf) ++ continue; ++ ++ if (conf->radio_idx >= 0 && (radio_mask & BIT(conf->radio_idx))) + return true; + } + +--- a/net/mac80211/ieee80211_i.h ++++ b/net/mac80211/ieee80211_i.h +@@ -1947,6 +1947,12 @@ int ieee80211_mesh_finish_csa(struct iee + u64 *changed); + + /* scan/BSS handling */ ++u32 ieee80211_scan_req_radio_mask(struct ieee80211_local *local, ++ struct cfg80211_scan_request *req); ++bool ieee80211_scanning_busy(struct ieee80211_local *local, ++ struct cfg80211_chan_def *chandef); ++u32 ieee80211_can_leave_ch(struct ieee80211_sub_if_data *sdata, ++ u32 radio_mask); + void ieee80211_scan_work(struct wiphy *wiphy, struct wiphy_work *work); + int ieee80211_request_ibss_scan(struct ieee80211_sub_if_data *sdata, + const u8 *ssid, u8 ssid_len, +@@ -1985,6 +1991,7 @@ void ieee80211_sched_scan_stopped_work(s + /* off-channel/mgmt-tx */ + void ieee80211_offchannel_stop_vifs(struct ieee80211_local *local); + void ieee80211_offchannel_return(struct ieee80211_local *local); ++u32 ieee80211_offchannel_radio_mask(struct ieee80211_local *local); + void ieee80211_roc_setup(struct ieee80211_local *local); + void ieee80211_start_next_roc(struct ieee80211_local *local); + void ieee80211_reconfig_roc(struct ieee80211_local *local); +@@ -2629,6 +2636,8 @@ bool ieee80211_chandef_s1g_oper(const st + struct cfg80211_chan_def *chandef); + void ieee80211_chandef_downgrade(struct cfg80211_chan_def *chandef, + struct ieee80211_conn_settings *conn); ++u32 ieee80211_chandef_radio_mask(struct ieee80211_local *local, ++ struct cfg80211_chan_def *chandef); + static inline void + ieee80211_chanreq_downgrade(struct ieee80211_chan_req *chanreq, + struct ieee80211_conn_settings *conn) +@@ -2685,7 +2694,7 @@ void ieee80211_recalc_chanctx_min_def(st + struct ieee80211_chanctx *ctx, + struct ieee80211_link_data *rsvd_for, + bool check_reserved); +-bool ieee80211_is_radar_required(struct ieee80211_local *local); ++bool ieee80211_is_radar_required(struct ieee80211_local *local, u32 radio_mask); + + void ieee80211_dfs_cac_timer_work(struct wiphy *wiphy, struct wiphy_work *work); + void ieee80211_dfs_cac_cancel(struct ieee80211_local *local, +--- a/net/mac80211/offchannel.c ++++ b/net/mac80211/offchannel.c +@@ -168,6 +168,35 @@ void ieee80211_offchannel_return(struct + false); + } + ++u32 ieee80211_offchannel_radio_mask(struct ieee80211_local *local) ++{ ++ const struct wiphy_radio *radio; ++ struct ieee80211_roc_work *roc; ++ u32 mask = 0; ++ int r; ++ ++ for (r = 0; r < local->hw.wiphy->n_radio; r++) { ++ radio = &local->hw.wiphy->radio[r]; ++ ++ list_for_each_entry(roc, &local->roc_list, list) { ++ struct cfg80211_chan_def chandef = {}; ++ ++ if (!roc->started) ++ continue; ++ ++ cfg80211_chandef_create(&chandef, roc->chan, ++ NL80211_CHAN_NO_HT); ++ if (!cfg80211_radio_chandef_valid(radio, &chandef)) ++ continue; ++ ++ mask |= BIT(r); ++ break; ++ } ++ } ++ ++ return mask; ++} ++ + static void ieee80211_roc_notify_destroy(struct ieee80211_roc_work *roc) + { + /* was never transmitted */ +@@ -566,7 +595,9 @@ static int ieee80211_start_roc_work(stru + enum ieee80211_roc_type type) + { + struct ieee80211_roc_work *roc, *tmp; ++ struct cfg80211_chan_def chandef = {}; + bool queued = false, combine_started = true; ++ u32 radio_mask; + int ret; + + lockdep_assert_wiphy(local->hw.wiphy); +@@ -578,6 +609,12 @@ static int ieee80211_start_roc_work(stru + if (!local->emulate_chanctx && !local->ops->remain_on_channel) + return -EOPNOTSUPP; + ++ cfg80211_chandef_create(&chandef, channel, NL80211_CHAN_NO_HT); ++ radio_mask = ieee80211_chandef_radio_mask(local, &chandef); ++ if (!ieee80211_can_leave_ch(sdata, radio_mask) && ++ !ieee80211_scanning_busy(local, &chandef)) ++ return -EBUSY; ++ + roc = kzalloc(sizeof(*roc), GFP_KERNEL); + if (!roc) + return -ENOMEM; +@@ -613,8 +650,7 @@ static int ieee80211_start_roc_work(stru + } + + /* if there's no need to queue, handle it immediately */ +- if (list_empty(&local->roc_list) && +- !local->scanning && !ieee80211_is_radar_required(local)) { ++ if (list_empty(&local->roc_list) && !local->scanning) { + /* if not HW assist, just queue & schedule work */ + if (!local->ops->remain_on_channel) { + list_add_tail(&roc->list, &local->roc_list); +--- a/net/mac80211/scan.c ++++ b/net/mac80211/scan.c +@@ -571,36 +571,83 @@ static int ieee80211_start_sw_scan(struc + return 0; + } + +-static bool __ieee80211_can_leave_ch(struct ieee80211_sub_if_data *sdata) ++u32 ieee80211_scan_req_radio_mask(struct ieee80211_local *local, ++ struct cfg80211_scan_request *req) ++{ ++ const struct wiphy_radio *radio; ++ u32 mask = 0; ++ int i, r; ++ ++ for (r = 0; r < local->hw.wiphy->n_radio; r++) { ++ radio = &local->hw.wiphy->radio[r]; ++ ++ for (i = 0; i < req->n_channels; i++) { ++ struct cfg80211_chan_def chandef = {}; ++ ++ chandef.chan = req->channels[i]; ++ cfg80211_chandef_create(&chandef, req->channels[i], ++ NL80211_CHAN_NO_HT); ++ if (!cfg80211_radio_chandef_valid(radio, &chandef)) ++ continue; ++ ++ mask |= BIT(r); ++ break; ++ } ++ } ++ ++ return mask; ++} ++ ++u32 ieee80211_can_leave_ch(struct ieee80211_sub_if_data *sdata, ++ u32 radio_mask) + { + struct ieee80211_local *local = sdata->local; + struct ieee80211_sub_if_data *sdata_iter; ++ struct wiphy *wiphy = local->hw.wiphy; ++ struct ieee80211_chanctx_conf *conf; ++ struct ieee80211_link_data *link; + unsigned int link_id; + + lockdep_assert_wiphy(local->hw.wiphy); + +- if (!ieee80211_is_radar_required(local)) ++ if (!ieee80211_is_radar_required(local, radio_mask)) + return true; + + if (!regulatory_pre_cac_allowed(local->hw.wiphy)) + return false; + + list_for_each_entry(sdata_iter, &local->interfaces, list) { +- for_each_valid_link(&sdata_iter->wdev, link_id) +- if (sdata_iter->wdev.links[link_id].cac_started) ++ for_each_valid_link(&sdata_iter->wdev, link_id) { ++ if (!sdata_iter->wdev.links[link_id].cac_started) ++ continue; ++ ++ if (!wiphy->n_radio) + return false; ++ ++ link = sdata_dereference(sdata->link[link_id], sdata); ++ if (!link) ++ continue; ++ ++ conf = wiphy_dereference(wiphy, link->conf->chanctx_conf); ++ if (!conf) ++ continue; ++ ++ if (conf->radio_idx >= 0 && ++ (radio_mask & BIT(conf->radio_idx))) ++ return false; ++ } + } + + return true; + } + + static bool ieee80211_can_scan(struct ieee80211_local *local, +- struct ieee80211_sub_if_data *sdata) ++ struct ieee80211_sub_if_data *sdata, ++ u32 radio_mask) + { +- if (!__ieee80211_can_leave_ch(sdata)) +- return false; +- +- if (!list_empty(&local->roc_list)) ++ if (!list_empty(&local->roc_list) && ++ (!local->hw.wiphy->n_radio || ++ (radio_mask & ieee80211_offchannel_radio_mask(local)))) + return false; + + if (sdata->vif.type == NL80211_IFTYPE_STATION && +@@ -612,15 +659,22 @@ static bool ieee80211_can_scan(struct ie + + void ieee80211_run_deferred_scan(struct ieee80211_local *local) + { ++ struct ieee80211_sub_if_data *sdata; ++ struct cfg80211_scan_request *req; ++ u32 radio_mask; ++ + lockdep_assert_wiphy(local->hw.wiphy); + +- if (!local->scan_req || local->scanning) ++ req = wiphy_dereference(local->hw.wiphy, local->scan_req); ++ if (!req || local->scanning) ++ return; ++ ++ radio_mask = ieee80211_scan_req_radio_mask(local, req); ++ sdata = wiphy_dereference(local->hw.wiphy, local->scan_sdata); ++ if (!ieee80211_can_leave_ch(sdata, radio_mask)) + return; + +- if (!ieee80211_can_scan(local, +- rcu_dereference_protected( +- local->scan_sdata, +- lockdep_is_held(&local->hw.wiphy->mtx)))) ++ if (!ieee80211_can_scan(local, sdata, radio_mask)) + return; + + wiphy_delayed_work_queue(local->hw.wiphy, &local->scan_work, +@@ -703,6 +757,7 @@ static int __ieee80211_start_scan(struct + { + struct ieee80211_local *local = sdata->local; + bool hw_scan = local->ops->hw_scan; ++ u32 radio_mask; + int rc; + + lockdep_assert_wiphy(local->hw.wiphy); +@@ -717,10 +772,11 @@ static int __ieee80211_start_scan(struct + !(sdata->vif.active_links & BIT(req->tsf_report_link_id))) + return -EINVAL; + +- if (!__ieee80211_can_leave_ch(sdata)) ++ radio_mask = ieee80211_scan_req_radio_mask(local, req); ++ if (!ieee80211_can_leave_ch(sdata, radio_mask)) + return -EBUSY; + +- if (!ieee80211_can_scan(local, sdata)) { ++ if (!ieee80211_can_scan(local, sdata, radio_mask)) { + /* wait for the work to finish/time out */ + rcu_assign_pointer(local->scan_req, req); + rcu_assign_pointer(local->scan_sdata, sdata); +--- a/net/mac80211/util.c ++++ b/net/mac80211/util.c +@@ -3644,6 +3644,23 @@ again: + WARN_ON_ONCE(!cfg80211_chandef_valid(c)); + } + ++u32 ieee80211_chandef_radio_mask(struct ieee80211_local *local, ++ struct cfg80211_chan_def *chandef) ++{ ++ struct wiphy *wiphy = local->hw.wiphy; ++ const struct wiphy_radio *radio; ++ u32 mask = 0; ++ int i; ++ ++ for (i = 0; i < wiphy->n_radio; i++) { ++ radio = &wiphy->radio[i]; ++ if (cfg80211_radio_chandef_valid(radio, chandef)) ++ mask |= BIT(i); ++ } ++ ++ return mask; ++} ++ + /* + * Returns true if smps_mode_new is strictly more restrictive than + * smps_mode_old. +--- a/net/wireless/util.c ++++ b/net/wireless/util.c +@@ -2911,6 +2911,9 @@ bool cfg80211_radio_chandef_valid(const + { + u32 freq, width; + ++ if (!cfg80211_chandef_valid(chandef)) ++ return false; ++ + freq = ieee80211_chandef_to_khz(chandef); + width = nl80211_chan_width_to_mhz(chandef->width); + if (!ieee80211_radio_freq_range_valid(radio, freq, width))