From: Miri Korenblit Date: Wed, 27 May 2026 19:51:45 +0000 (+0300) Subject: wifi: mac80211: fix channel evacuation logic X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=f1a389fcc3f13a00c1202b0f63e073a2054184cc;p=thirdparty%2Flinux.git 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 Link: https://patch.msgid.link/20260527224745.5f7b10505145.I1712bc64f9eec9c99f05994208ad124624a29f1c@changeid Signed-off-by: Johannes Berg --- diff --git a/net/mac80211/chan.c b/net/mac80211/chan.c index c64a991319549..23d46cd571371 100644 --- a/net/mac80211/chan.c +++ b/net/mac80211/chan.c @@ -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; diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index fcdb6fdd4d75e..18a1017104325 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -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) diff --git a/net/mac80211/nan.c b/net/mac80211/nan.c index 44b6e298d94dd..1800bb96dd294 100644 --- a/net/mac80211/nan.c +++ b/net/mac80211/nan.c @@ -712,7 +712,7 @@ out: 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)