mac80211: allow reservation of a running chanctx

With single-channel drivers, we need to be able to change a running
chanctx if we want to use chanctx reservation.  Not all drivers may be
able to do this, so add a flag that indicates support for it.

Changing a running chanctx can also be used as an optimization in
multi-channel drivers when the context needs to be reserved for future
usage.

Introduce IEEE80211_CHANCTX_RESERVED chanctx mode to mark a channel as
reserved so nobody else can use it (since we know it's going to
change).  In the future, we may allow several vifs to use the same
reservation as long as they plan to use the chanctx on the same
future channel.

Signed-off-by: Luciano Coelho <luciano.coelho@intel.com>
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
This commit is contained in:
Luciano Coelho 2014-02-27 14:33:47 +02:00 committed by Johannes Berg
parent 11335a550b
commit 5d52ee8110
2 changed files with 68 additions and 18 deletions

View File

@ -1555,6 +1555,12 @@ struct ieee80211_tx_control {
* for a single active channel while using channel contexts. When support * for a single active channel while using channel contexts. When support
* is not enabled the default action is to disconnect when getting the * is not enabled the default action is to disconnect when getting the
* CSA frame. * CSA frame.
*
* @IEEE80211_HW_CHANGE_RUNNING_CHANCTX: The hardware can change a
* channel context on-the-fly. This is needed for channel switch
* on single-channel hardware. It can also be used as an
* optimization in certain channel switch cases with
* multi-channel.
*/ */
enum ieee80211_hw_flags { enum ieee80211_hw_flags {
IEEE80211_HW_HAS_RATE_CONTROL = 1<<0, IEEE80211_HW_HAS_RATE_CONTROL = 1<<0,
@ -1586,6 +1592,7 @@ enum ieee80211_hw_flags {
IEEE80211_HW_TIMING_BEACON_ONLY = 1<<26, IEEE80211_HW_TIMING_BEACON_ONLY = 1<<26,
IEEE80211_HW_SUPPORTS_HT_CCK_RATES = 1<<27, IEEE80211_HW_SUPPORTS_HT_CCK_RATES = 1<<27,
IEEE80211_HW_CHANCTX_STA_CSA = 1<<28, IEEE80211_HW_CHANCTX_STA_CSA = 1<<28,
IEEE80211_HW_CHANGE_RUNNING_CHANCTX = 1<<29,
}; };
/** /**

View File

@ -168,6 +168,27 @@ static void ieee80211_change_chanctx(struct ieee80211_local *local,
} }
} }
static bool ieee80211_chanctx_is_reserved(struct ieee80211_local *local,
struct ieee80211_chanctx *ctx)
{
struct ieee80211_sub_if_data *sdata;
bool ret = false;
lockdep_assert_held(&local->chanctx_mtx);
rcu_read_lock();
list_for_each_entry_rcu(sdata, &local->interfaces, list) {
if (!ieee80211_sdata_running(sdata))
continue;
if (sdata->reserved_chanctx == ctx) {
ret = true;
break;
}
}
rcu_read_unlock();
return ret;
}
static struct ieee80211_chanctx * static struct ieee80211_chanctx *
ieee80211_find_chanctx(struct ieee80211_local *local, ieee80211_find_chanctx(struct ieee80211_local *local,
const struct cfg80211_chan_def *chandef, const struct cfg80211_chan_def *chandef,
@ -183,7 +204,12 @@ ieee80211_find_chanctx(struct ieee80211_local *local,
list_for_each_entry(ctx, &local->chanctx_list, list) { list_for_each_entry(ctx, &local->chanctx_list, list) {
const struct cfg80211_chan_def *compat; const struct cfg80211_chan_def *compat;
if (ctx->mode == IEEE80211_CHANCTX_EXCLUSIVE) /* We don't support chanctx reservation for multiple
* vifs yet, so don't allow reserved chanctxs to be
* reused.
*/
if ((ctx->mode == IEEE80211_CHANCTX_EXCLUSIVE) ||
ieee80211_chanctx_is_reserved(local, ctx))
continue; continue;
compat = cfg80211_chandef_compatible(&ctx->conf.def, chandef); compat = cfg80211_chandef_compatible(&ctx->conf.def, chandef);
@ -718,11 +744,20 @@ int ieee80211_vif_reserve_chanctx(struct ieee80211_sub_if_data *sdata,
/* try to find another context with the chandef we want */ /* try to find another context with the chandef we want */
new_ctx = ieee80211_find_chanctx(local, chandef, mode); new_ctx = ieee80211_find_chanctx(local, chandef, mode);
if (!new_ctx) { if (!new_ctx) {
/* create a new context */ if (curr_ctx->refcount == 1 &&
new_ctx = ieee80211_new_chanctx(local, chandef, mode); (local->hw.flags & IEEE80211_HW_CHANGE_RUNNING_CHANCTX)) {
if (IS_ERR(new_ctx)) { /* if we're the only users of the chanctx and
ret = PTR_ERR(new_ctx); * the driver supports changing a running
goto out; * context, reserve our current context
*/
new_ctx = curr_ctx;
} else {
/* create a new context and reserve it */
new_ctx = ieee80211_new_chanctx(local, chandef, mode);
if (IS_ERR(new_ctx)) {
ret = PTR_ERR(new_ctx);
goto out;
}
} }
} }
@ -770,22 +805,30 @@ int ieee80211_vif_use_reserved_context(struct ieee80211_sub_if_data *sdata,
sdata->vif.bss_conf.chandef = sdata->reserved_chandef; sdata->vif.bss_conf.chandef = sdata->reserved_chandef;
/* unref our reservation before assigning */ /* unref our reservation */
ctx->refcount--; ctx->refcount--;
sdata->reserved_chanctx = NULL; sdata->reserved_chanctx = NULL;
ret = ieee80211_assign_vif_chanctx(sdata, ctx); if (old_ctx == ctx) {
if (old_ctx->refcount == 0) /* This is our own context, just change it */
ieee80211_free_chanctx(local, old_ctx); ret = __ieee80211_vif_change_channel(sdata, old_ctx,
if (ret) { &tmp_changed);
/* if assign fails refcount stays the same */ if (ret)
if (ctx->refcount == 0) goto out;
ieee80211_free_chanctx(local, ctx); } else {
goto out; ret = ieee80211_assign_vif_chanctx(sdata, ctx);
} if (old_ctx->refcount == 0)
ieee80211_free_chanctx(local, old_ctx);
if (ret) {
/* if assign fails refcount stays the same */
if (ctx->refcount == 0)
ieee80211_free_chanctx(local, ctx);
goto out;
}
if (sdata->vif.type == NL80211_IFTYPE_AP) if (sdata->vif.type == NL80211_IFTYPE_AP)
__ieee80211_vif_copy_chanctx_to_vlans(sdata, false); __ieee80211_vif_copy_chanctx_to_vlans(sdata, false);
}
*changed = tmp_changed; *changed = tmp_changed;