wifi: mac80211: fix channel evacuation logic

When we try to assign a chanctx to a link, if
ieee80211_find_or_create_chanctx() failed, we try to evacuate a NAN
channel and call it again.

This logic is broken:
In case there are not enough chanctxs we will fail earlier,
when we check ieee80211_check_combinations().

To fix this, do the following in case ieee80211_check_combinations()
failed:
- check if there is a NAN channel that can be evacuated
- make ieee80211_check_combinations() not consider the chanctx of that NAN
  channel, so we pretend that it was already evacuated
- If now ieee80211_check_combinations() is successful, we know that it
  helped, and we can remove that NAN channel for real.

Signed-off-by: Miri Korenblit <miriam.rachel.korenblit@intel.com>
Link: https://patch.msgid.link/20260527224745.5f7b10505145.I1712bc64f9eec9c99f05994208ad124624a29f1c@changeid
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
This commit is contained in:
Miri Korenblit 2026-05-27 22:51:45 +03:00 committed by Johannes Berg
parent a5b9ebd5c3
commit f1a389fcc3
3 changed files with 75 additions and 13 deletions

View File

@ -2216,6 +2216,64 @@ ieee80211_find_or_create_chanctx(struct ieee80211_sub_if_data *sdata,
assign_on_failure, radio_idx);
}
static bool
ieee80211_nan_evac_chanctx_filter(struct ieee80211_chanctx *ctx,
void *filter_data)
{
return ctx == filter_data;
}
static int
ieee80211_try_nan_chan_evacuation(struct ieee80211_local *local,
struct ieee80211_sub_if_data *sdata,
const struct cfg80211_chan_def *chandef,
enum ieee80211_chanctx_mode mode,
u8 radar_detect_width)
{
struct ieee80211_sub_if_data *nan_sdata = ieee80211_find_nan_sdata(local);
struct ieee80211_check_combinations_data comb_data = {
.chandef = chandef,
.chanmode = mode,
.radar_detect = radar_detect_width,
.radio_idx = -1,
.chanctx_filter = ieee80211_nan_evac_chanctx_filter,
};
struct ieee80211_nan_channel *evac_chan;
struct ieee80211_chanctx *evac_ctx;
int ret;
if (!nan_sdata)
return -ENOENT;
/* Find an evacuation candidate... */
evac_chan = ieee80211_nan_find_evac_chan(local, nan_sdata, NULL);
if (!evac_chan || WARN_ON(!evac_chan->chanctx_conf))
return -ENOENT;
evac_ctx = container_of(evac_chan->chanctx_conf,
struct ieee80211_chanctx, conf);
/*
* ... check combinations assuming to-be-evacuated ctx is already
* released
*/
comb_data.filter_data = evac_ctx;
ret = ieee80211_check_combinations_ext(sdata, &comb_data);
if (ret < 0)
return ret;
/* That helped! Let's evacuate the channel */
ieee80211_nan_evacuate_channel(nan_sdata, evac_chan);
/* Re-check, just to be on the safe-side */
ret = ieee80211_check_combinations(sdata, chandef, mode,
radar_detect_width, -1);
/* That shouldn't happen, we checked before! */
WARN_ON(ret);
return ret;
}
int _ieee80211_link_use_channel(struct ieee80211_link_data *link,
const struct ieee80211_chan_req *chanreq,
enum ieee80211_chanctx_mode mode,
@ -2247,23 +2305,21 @@ int _ieee80211_link_use_channel(struct ieee80211_link_data *link,
ret = ieee80211_check_combinations(sdata, &chanreq->oper, mode,
radar_detect_width, -1);
if (ret < 0)
goto out;
if (ret < 0) {
/* Let's check if evacuating a NAN channel will help */
ret = ieee80211_try_nan_chan_evacuation(local, sdata,
&chanreq->oper,
mode,
radar_detect_width);
if (ret < 0)
goto out;
}
if (!local->in_reconfig)
__ieee80211_link_release_channel(link, false);
ctx = ieee80211_find_or_create_chanctx(sdata, chanreq, mode,
assign_on_failure, &reused_ctx);
if (IS_ERR(ctx)) {
/* Try to evacuate a NAN channel to free up a chanctx */
if (ieee80211_nan_try_evacuate(&local->hw, NULL))
ctx = ieee80211_find_or_create_chanctx(sdata, chanreq,
mode,
assign_on_failure,
&reused_ctx);
}
if (IS_ERR(ctx)) {
ret = PTR_ERR(ctx);
goto out;

View File

@ -2059,6 +2059,12 @@ int ieee80211_nan_set_peer_sched(struct ieee80211_sub_if_data *sdata,
struct cfg80211_nan_peer_sched *sched);
void ieee80211_nan_free_peer_sched(struct ieee80211_nan_peer_sched *sched);
void ieee80211_nan_update_ndi_carrier(struct ieee80211_sub_if_data *ndi_sdata);
struct ieee80211_nan_channel *
ieee80211_nan_find_evac_chan(struct ieee80211_local *local,
struct ieee80211_sub_if_data *sdata,
struct ieee80211_chanctx *ctx);
void ieee80211_nan_evacuate_channel(struct ieee80211_sub_if_data *sdata,
struct ieee80211_nan_channel *nan_channel);
static inline struct ieee80211_sub_if_data *
ieee80211_find_nan_sdata(struct ieee80211_local *local)

View File

@ -712,7 +712,7 @@ int ieee80211_nan_set_peer_sched(struct ieee80211_sub_if_data *sdata,
return ret;
}
static void
void
ieee80211_nan_evacuate_channel(struct ieee80211_sub_if_data *sdata,
struct ieee80211_nan_channel *nan_channel)
{
@ -754,7 +754,7 @@ ieee80211_nan_evacuate_channel(struct ieee80211_sub_if_data *sdata,
ieee80211_free_chanctx(sdata->local, ctx, false);
}
static struct ieee80211_nan_channel *
struct ieee80211_nan_channel *
ieee80211_nan_find_evac_chan(struct ieee80211_local *local,
struct ieee80211_sub_if_data *sdata,
struct ieee80211_chanctx *ctx)