]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
wifi: mac80211: Allow scan on a radio while operating on DFS on another radio
authorRaj Kumar Bhagat <quic_rajkbhag@quicinc.com>
Tue, 27 May 2025 08:41:45 +0000 (14:11 +0530)
committerJohannes Berg <johannes.berg@intel.com>
Fri, 20 Jun 2025 08:44:05 +0000 (10:44 +0200)
Currently, in multi-radio wiphy cases, if one radio is operating on a DFS
channel, -EBUSY is returned even when a scan is requested on a different
radio. Because of this, an MLD AP with one radio (link) on a DFS channel
and Automatic Channel Selection (ACS) on another radio (link) cannot be
brought up.

In multi-radio wiphy cases, multiple radios are grouped under a single
wiphy. Hence, if a radio is operating on a DFS channel and a scan is
requested on a different radio of the same wiphy, the scan can be allowed
simultaneously without impacting the DFS operations.

Add logic to check the underlying radio used for the requested scan. If the
radio on which DFS is already running is not being used, allow the scan
operation; otherwise, return -EBUSY.

Signed-off-by: Raj Kumar Bhagat <quic_rajkbhag@quicinc.com>
Link: https://patch.msgid.link/20250527-mlo-dfs-acs-v2-3-92c2f37c81d9@quicinc.com
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
net/mac80211/chan.c
net/mac80211/ieee80211_i.h
net/mac80211/offchannel.c
net/mac80211/scan.c

index 3aaf5abf1acc13008a0472672c826d495c80407c..94c83f58e23d0666af881e5e0c981cca17231bfd 100644 (file)
@@ -644,15 +644,39 @@ ieee80211_find_chanctx(struct ieee80211_local *local,
        return NULL;
 }
 
-bool ieee80211_is_radar_required(struct ieee80211_local *local)
+bool ieee80211_is_radar_required(struct ieee80211_local *local,
+                                struct cfg80211_scan_request *req)
 {
+       struct wiphy *wiphy = local->hw.wiphy;
        struct ieee80211_link_data *link;
+       struct ieee80211_channel *chan;
+       int radio_idx;
 
        lockdep_assert_wiphy(local->hw.wiphy);
 
+       if (!req)
+               return false;
+
        for_each_sdata_link(local, link) {
-               if (link->radar_required)
-                       return true;
+               if (link->radar_required) {
+                       if (wiphy->n_radio < 2)
+                               return true;
+
+                       chan = link->conf->chanreq.oper.chan;
+                       radio_idx = cfg80211_get_radio_idx_by_chan(wiphy, chan);
+                       /*
+                        * The radio index (radio_idx) is expected to be valid,
+                        * as it's derived from a channel tied to a link. If
+                        * it's invalid (i.e., negative), return true to avoid
+                        * potential issues with radar-sensitive operations.
+                        */
+                       if (radio_idx < 0)
+                               return true;
+
+                       if (ieee80211_is_radio_idx_in_scan_req(wiphy, req,
+                                                              radio_idx))
+                               return true;
+               }
        }
 
        return false;
index 7719d6c307fed5f44c9a5e35b8250c31daa6f523..9b9c7209878b076c5c3c09a78d067cf54d2d95a6 100644 (file)
@@ -2712,7 +2712,8 @@ void ieee80211_recalc_chanctx_min_def(struct ieee80211_local *local,
                                      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,
+                                struct cfg80211_scan_request *req);
 bool ieee80211_is_radio_idx_in_scan_req(struct wiphy *wiphy,
                                        struct cfg80211_scan_request *scan_req,
                                        int radio_idx);
index 2b9abc27462eb5a41f47523d1653c7f27137588d..686d9f6e9b527acaa3500e8d99d5ca513bb1025e 100644 (file)
@@ -567,6 +567,7 @@ static int ieee80211_start_roc_work(struct ieee80211_local *local,
 {
        struct ieee80211_roc_work *roc, *tmp;
        bool queued = false, combine_started = true;
+       struct cfg80211_scan_request *req;
        int ret;
 
        lockdep_assert_wiphy(local->hw.wiphy);
@@ -612,9 +613,11 @@ static int ieee80211_start_roc_work(struct ieee80211_local *local,
                roc->mgmt_tx_cookie = *cookie;
        }
 
+       req = wiphy_dereference(local->hw.wiphy, local->scan_req);
+
        /* if there's no need to queue, handle it immediately */
        if (list_empty(&local->roc_list) &&
-           !local->scanning && !ieee80211_is_radar_required(local)) {
+           !local->scanning && !ieee80211_is_radar_required(local, req)) {
                /* if not HW assist, just queue & schedule work */
                if (!local->ops->remain_on_channel) {
                        list_add_tail(&roc->list, &local->roc_list);
index cd8385ecafd9e95b67c5abdfedeabe340c9fcdfa..9799164a56d9359b1bd7a75d820e1bb1bf4c9992 100644 (file)
@@ -586,7 +586,8 @@ static int ieee80211_start_sw_scan(struct ieee80211_local *local,
        return 0;
 }
 
-static bool __ieee80211_can_leave_ch(struct ieee80211_sub_if_data *sdata)
+static bool __ieee80211_can_leave_ch(struct ieee80211_sub_if_data *sdata,
+                                    struct cfg80211_scan_request *req)
 {
        struct ieee80211_local *local = sdata->local;
        struct ieee80211_sub_if_data *sdata_iter;
@@ -594,7 +595,7 @@ static bool __ieee80211_can_leave_ch(struct ieee80211_sub_if_data *sdata)
 
        lockdep_assert_wiphy(local->hw.wiphy);
 
-       if (!ieee80211_is_radar_required(local))
+       if (!ieee80211_is_radar_required(local, req))
                return true;
 
        if (!regulatory_pre_cac_allowed(local->hw.wiphy))
@@ -610,9 +611,10 @@ static bool __ieee80211_can_leave_ch(struct ieee80211_sub_if_data *sdata)
 }
 
 static bool ieee80211_can_scan(struct ieee80211_local *local,
-                              struct ieee80211_sub_if_data *sdata)
+                              struct ieee80211_sub_if_data *sdata,
+                              struct cfg80211_scan_request *req)
 {
-       if (!__ieee80211_can_leave_ch(sdata))
+       if (!__ieee80211_can_leave_ch(sdata, req))
                return false;
 
        if (!list_empty(&local->roc_list))
@@ -627,15 +629,19 @@ static bool ieee80211_can_scan(struct ieee80211_local *local,
 
 void ieee80211_run_deferred_scan(struct ieee80211_local *local)
 {
+       struct cfg80211_scan_request *req;
+
        lockdep_assert_wiphy(local->hw.wiphy);
 
        if (!local->scan_req || local->scanning)
                return;
 
+       req = wiphy_dereference(local->hw.wiphy, local->scan_req);
        if (!ieee80211_can_scan(local,
                                rcu_dereference_protected(
                                        local->scan_sdata,
-                                       lockdep_is_held(&local->hw.wiphy->mtx))))
+                                       lockdep_is_held(&local->hw.wiphy->mtx)),
+                               req))
                return;
 
        wiphy_delayed_work_queue(local->hw.wiphy, &local->scan_work,
@@ -732,10 +738,10 @@ static int __ieee80211_start_scan(struct ieee80211_sub_if_data *sdata,
            !(sdata->vif.active_links & BIT(req->tsf_report_link_id)))
                return -EINVAL;
 
-       if (!__ieee80211_can_leave_ch(sdata))
+       if (!__ieee80211_can_leave_ch(sdata, req))
                return -EBUSY;
 
-       if (!ieee80211_can_scan(local, sdata)) {
+       if (!ieee80211_can_scan(local, sdata, req)) {
                /* wait for the work to finish/time out */
                rcu_assign_pointer(local->scan_req, req);
                rcu_assign_pointer(local->scan_sdata, sdata);