]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
wifi: mac80211: fix channel evacuation logic
authorMiri Korenblit <miriam.rachel.korenblit@intel.com>
Wed, 27 May 2026 19:51:45 +0000 (22:51 +0300)
committerJohannes Berg <johannes.berg@intel.com>
Thu, 28 May 2026 07:50:23 +0000 (09:50 +0200)
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>
net/mac80211/chan.c
net/mac80211/ieee80211_i.h
net/mac80211/nan.c

index c64a991319549b1d0e1dcb50da326467d75546ef..23d46cd5713717efe65d852b70d42394ce33af4d 100644 (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;
index fcdb6fdd4d75ea48f0450c6ab1c8776f0380c3b1..18a1017104325c03b2dcef5bdb8039c2e7073699 100644 (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)
index 44b6e298d94ddd1b5939341ad72fc1bebf6e687d..1800bb96dd294fd0df210ad868ab7bfe183b087a 100644 (file)
@@ -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)