Commit c9172fae authored by Raj Kumar Bhagat's avatar Raj Kumar Bhagat Committed by Johannes Berg
Browse files

wifi: mac80211: Allow scan on a radio while operating on DFS on another radio



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: default avatarRaj Kumar Bhagat <quic_rajkbhag@quicinc.com>
Link: https://patch.msgid.link/20250527-mlo-dfs-acs-v2-3-92c2f37c81d9@quicinc.com


Signed-off-by: default avatarJohannes Berg <johannes.berg@intel.com>
parent fe8582db
Loading
Loading
Loading
Loading
+27 −3
Original line number Diff line number Diff line
@@ -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)
		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;
+2 −1
Original line number Diff line number Diff line
@@ -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);
+4 −1
Original line number Diff line number Diff line
@@ -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);
+13 −7
Original line number Diff line number Diff line
@@ -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);