summaryrefslogtreecommitdiffstats
path: root/net/mac80211/chan.c
diff options
context:
space:
mode:
authorLuciano Coelho <luciano.coelho@intel.com>2014-02-27 13:33:47 +0100
committerJohannes Berg <johannes.berg@intel.com>2014-04-09 10:55:56 +0200
commit5d52ee81101943c507f45c76368026935f6bb75a (patch)
treec048e3cf544e0e87448d4974a9b41e7f435a098a /net/mac80211/chan.c
parentmac80211: implement chanctx reservation (diff)
downloadlinux-5d52ee81101943c507f45c76368026935f6bb75a.tar.xz
linux-5d52ee81101943c507f45c76368026935f6bb75a.zip
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>
Diffstat (limited to 'net/mac80211/chan.c')
-rw-r--r--net/mac80211/chan.c79
1 files changed, 61 insertions, 18 deletions
diff --git a/net/mac80211/chan.c b/net/mac80211/chan.c
index baad75610a6a..57b8ab1bc5c1 100644
--- a/net/mac80211/chan.c
+++ b/net/mac80211/chan.c
@@ -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 *
ieee80211_find_chanctx(struct ieee80211_local *local,
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) {
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;
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 */
new_ctx = ieee80211_find_chanctx(local, chandef, mode);
if (!new_ctx) {
- /* create a new context */
- new_ctx = ieee80211_new_chanctx(local, chandef, mode);
- if (IS_ERR(new_ctx)) {
- ret = PTR_ERR(new_ctx);
- goto out;
+ if (curr_ctx->refcount == 1 &&
+ (local->hw.flags & IEEE80211_HW_CHANGE_RUNNING_CHANCTX)) {
+ /* if we're the only users of the chanctx and
+ * the driver supports changing a running
+ * 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;
- /* unref our reservation before assigning */
+ /* unref our reservation */
ctx->refcount--;
sdata->reserved_chanctx = NULL;
- 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 (old_ctx == ctx) {
+ /* This is our own context, just change it */
+ ret = __ieee80211_vif_change_channel(sdata, old_ctx,
+ &tmp_changed);
+ if (ret)
+ goto out;
+ } else {
+ 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)
- __ieee80211_vif_copy_chanctx_to_vlans(sdata, false);
+ if (sdata->vif.type == NL80211_IFTYPE_AP)
+ __ieee80211_vif_copy_chanctx_to_vlans(sdata, false);
+ }
*changed = tmp_changed;