}
if (n_assigned != n_reserved) {
- if (n_ready == n_reserved) {
- wiphy_info(local->hw.wiphy,
- "channel context reservation cannot be finalized because some interfaces aren't switching\n");
- err = -EBUSY;
- goto err;
- }
+ if (n_ready != n_reserved)
+ return -EAGAIN;
- return -EAGAIN;
+ if (n_assigned == n_reserved + 1 &&
+ ieee80211_nan_try_evacuate(&local->hw,
+ &ctx->replace_ctx->conf))
+ goto use_reserved;
+
+ wiphy_info(local->hw.wiphy,
+ "channel context reservation cannot be finalized because some interfaces aren't switching\n");
+ err = -EBUSY;
+ goto err;
}
+use_reserved:
ctx->conf.radar_enabled = false;
for_each_chanctx_user_reserved(local, ctx, &iter) {
if (ieee80211_link_has_in_place_reservation(iter.link) &&
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;
sched_idx_to_chan[i] = chan;
ieee80211_nan_init_channel(chan,
&sched->nan_channels[i]);
+ }
+ /* Also a pre-existing channel might have been ULWed, so no chanctx */
+ if (!chan->chanctx_conf) {
ret = ieee80211_nan_use_chanctx(sdata, chan, false);
if (ret) {
memset(chan, 0, sizeof(*chan));
ieee80211_nan_free_peer_sched(to_free);
return ret;
}
+
+static void
+ieee80211_nan_evacuate_channel(struct ieee80211_sub_if_data *sdata,
+ struct ieee80211_nan_channel *nan_channel)
+{
+ struct ieee80211_chanctx_conf *conf;
+ struct ieee80211_chanctx *ctx;
+
+ lockdep_assert_wiphy(sdata->local->hw.wiphy);
+
+ if (WARN_ON(!nan_channel || !nan_channel->chanreq.oper.chan))
+ return;
+
+ conf = nan_channel->chanctx_conf;
+ if (WARN_ON(!conf))
+ return;
+
+ nan_channel->chanctx_conf = NULL;
+
+ /* Update all peer channels that reference this chanctx */
+ ieee80211_nan_update_peer_channels(sdata, conf);
+
+ drv_vif_cfg_changed(sdata->local, sdata, BSS_CHANGED_NAN_LOCAL_SCHED);
+
+ cfg80211_nan_channel_evac(&sdata->wdev, &nan_channel->chanreq.oper,
+ GFP_KERNEL);
+
+ /* Update NDI carrier states */
+ ieee80211_nan_update_all_ndi_carriers(sdata->local);
+
+ /* Clean up the channel context if no longer used */
+ ctx = container_of(conf, struct ieee80211_chanctx, conf);
+
+ if (ieee80211_chanctx_num_assigned(sdata->local, ctx) > 0) {
+ ieee80211_recalc_chanctx_chantype(sdata->local, ctx);
+ ieee80211_recalc_smps_chanctx(sdata->local, ctx);
+ ieee80211_recalc_chanctx_min_def(sdata->local, ctx);
+ }
+
+ if (ieee80211_chanctx_refcount(sdata->local, ctx) == 0)
+ ieee80211_free_chanctx(sdata->local, ctx, false);
+}
+
+bool ieee80211_nan_try_evacuate(struct ieee80211_hw *hw,
+ struct ieee80211_chanctx_conf *conf)
+{
+ struct ieee80211_sub_if_data *sdata = NULL, *tmp;
+ struct ieee80211_local *local = hw_to_local(hw);
+ struct ieee80211_nan_channel *evac_chan = NULL;
+ struct ieee80211_nan_sched_cfg *sched_cfg;
+ struct ieee80211_chanctx *ctx = NULL;
+ int min_slot_count = INT_MAX;
+ int usable_channels = 0;
+
+ lockdep_assert_wiphy(local->hw.wiphy);
+
+ if (conf)
+ ctx = container_of(conf, struct ieee80211_chanctx, conf);
+
+ /* Find the NAN interface - there can only be one */
+ list_for_each_entry(tmp, &local->interfaces, list) {
+ if (ieee80211_sdata_running(tmp) &&
+ tmp->vif.type == NL80211_IFTYPE_NAN) {
+ sdata = tmp;
+ break;
+ }
+ }
+
+ if (!sdata)
+ return false;
+
+ sched_cfg = &sdata->vif.cfg.nan_sched;
+
+ /* Find the channel to evacuate and count usable channels */
+ for (int i = 0; i < IEEE80211_NAN_MAX_CHANNELS; i++) {
+ struct ieee80211_nan_channel *chan =
+ &sched_cfg->channels[i];
+ struct ieee80211_chanctx *chan_ctx;
+ int slot_count = 0;
+
+ if (!chan->chanreq.oper.chan || !chan->chanctx_conf)
+ continue;
+
+ usable_channels++;
+
+ chan_ctx = container_of(chan->chanctx_conf,
+ struct ieee80211_chanctx, conf);
+
+ /* If ctx specified, only consider that specific chanctx */
+ if (ctx) {
+ if (chan_ctx == ctx)
+ evac_chan = chan;
+ continue;
+ }
+
+ /* Can only evacuate channels whose chanctx is NAN-only */
+ if (ieee80211_chanctx_refcount(local, chan_ctx) > 1)
+ continue;
+
+ /* Count how many time slots use this channel */
+ for (int s = 0; s < CFG80211_NAN_SCHED_NUM_TIME_SLOTS; s++)
+ if (sched_cfg->schedule[s] == chan)
+ slot_count++;
+
+ if (slot_count < min_slot_count) {
+ min_slot_count = slot_count;
+ evac_chan = chan;
+ }
+ }
+
+ /* No suitable NAN channel found */
+ if (!evac_chan)
+ return false;
+
+ /* NAN needs at least one remaining usable channel after evacuation */
+ if (usable_channels < 2)
+ return false;
+
+ ieee80211_nan_evacuate_channel(sdata, evac_chan);
+
+ return true;
+}
+EXPORT_SYMBOL(ieee80211_nan_try_evacuate);