diff options
author | Michal Kazior <michal.kazior@tieto.com> | 2014-06-25 06:35:08 -0400 |
---|---|---|
committer | Johannes Berg <johannes.berg@intel.com> | 2014-06-25 12:06:20 -0400 |
commit | 03078de4f928ffcbe629a914dea8bdf66a9d6a48 (patch) | |
tree | a52ef5c76e459885efe13b13492434777959f670 /net/mac80211/cfg.c | |
parent | 71e6195ed2541d764fb0d7ef39cdb2236817116b (diff) |
mac80211: use chanctx reservation for AP CSA
Channel switch finalization is now 2-step. First
step is when driver calls csa_finish(), the other
is when reservation is actually finalized (which
can be deferred for in-place reservation).
It is now safe to call ieee80211_csa_finish() more
than once.
Signed-off-by: Michal Kazior <michal.kazior@tieto.com>
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Diffstat (limited to 'net/mac80211/cfg.c')
-rw-r--r-- | net/mac80211/cfg.c | 82 |
1 files changed, 55 insertions, 27 deletions
diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index 73c9e1003e25..927b4ea0128b 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c | |||
@@ -2792,17 +2792,35 @@ static int __ieee80211_csa_finalize(struct ieee80211_sub_if_data *sdata) | |||
2792 | 2792 | ||
2793 | sdata_assert_lock(sdata); | 2793 | sdata_assert_lock(sdata); |
2794 | lockdep_assert_held(&local->mtx); | 2794 | lockdep_assert_held(&local->mtx); |
2795 | lockdep_assert_held(&local->chanctx_mtx); | ||
2795 | 2796 | ||
2796 | sdata->radar_required = sdata->csa_radar_required; | 2797 | /* |
2797 | err = ieee80211_vif_change_channel(sdata, &changed); | 2798 | * using reservation isn't immediate as it may be deferred until later |
2798 | if (err < 0) | 2799 | * with multi-vif. once reservation is complete it will re-schedule the |
2799 | return err; | 2800 | * work with no reserved_chanctx so verify chandef to check if it |
2801 | * completed successfully | ||
2802 | */ | ||
2800 | 2803 | ||
2801 | if (!local->use_chanctx) { | 2804 | if (sdata->reserved_chanctx) { |
2802 | local->_oper_chandef = sdata->csa_chandef; | 2805 | /* |
2803 | ieee80211_hw_config(local, 0); | 2806 | * with multi-vif csa driver may call ieee80211_csa_finish() |
2807 | * many times while waiting for other interfaces to use their | ||
2808 | * reservations | ||
2809 | */ | ||
2810 | if (sdata->reserved_ready) | ||
2811 | return 0; | ||
2812 | |||
2813 | err = ieee80211_vif_use_reserved_context(sdata); | ||
2814 | if (err) | ||
2815 | return err; | ||
2816 | |||
2817 | return 0; | ||
2804 | } | 2818 | } |
2805 | 2819 | ||
2820 | if (!cfg80211_chandef_identical(&sdata->vif.bss_conf.chandef, | ||
2821 | &sdata->csa_chandef)) | ||
2822 | return -EINVAL; | ||
2823 | |||
2806 | sdata->vif.csa_active = false; | 2824 | sdata->vif.csa_active = false; |
2807 | 2825 | ||
2808 | err = ieee80211_set_after_csa_beacon(sdata, &changed); | 2826 | err = ieee80211_set_after_csa_beacon(sdata, &changed); |
@@ -2839,6 +2857,7 @@ void ieee80211_csa_finalize_work(struct work_struct *work) | |||
2839 | 2857 | ||
2840 | sdata_lock(sdata); | 2858 | sdata_lock(sdata); |
2841 | mutex_lock(&local->mtx); | 2859 | mutex_lock(&local->mtx); |
2860 | mutex_lock(&local->chanctx_mtx); | ||
2842 | 2861 | ||
2843 | /* AP might have been stopped while waiting for the lock. */ | 2862 | /* AP might have been stopped while waiting for the lock. */ |
2844 | if (!sdata->vif.csa_active) | 2863 | if (!sdata->vif.csa_active) |
@@ -2850,6 +2869,7 @@ void ieee80211_csa_finalize_work(struct work_struct *work) | |||
2850 | ieee80211_csa_finalize(sdata); | 2869 | ieee80211_csa_finalize(sdata); |
2851 | 2870 | ||
2852 | unlock: | 2871 | unlock: |
2872 | mutex_unlock(&local->chanctx_mtx); | ||
2853 | mutex_unlock(&local->mtx); | 2873 | mutex_unlock(&local->mtx); |
2854 | sdata_unlock(sdata); | 2874 | sdata_unlock(sdata); |
2855 | } | 2875 | } |
@@ -2995,7 +3015,7 @@ __ieee80211_channel_switch(struct wiphy *wiphy, struct net_device *dev, | |||
2995 | struct ieee80211_local *local = sdata->local; | 3015 | struct ieee80211_local *local = sdata->local; |
2996 | struct ieee80211_chanctx_conf *conf; | 3016 | struct ieee80211_chanctx_conf *conf; |
2997 | struct ieee80211_chanctx *chanctx; | 3017 | struct ieee80211_chanctx *chanctx; |
2998 | int err, num_chanctx, changed = 0; | 3018 | int err, changed = 0; |
2999 | 3019 | ||
3000 | sdata_assert_lock(sdata); | 3020 | sdata_assert_lock(sdata); |
3001 | lockdep_assert_held(&local->mtx); | 3021 | lockdep_assert_held(&local->mtx); |
@@ -3010,37 +3030,43 @@ __ieee80211_channel_switch(struct wiphy *wiphy, struct net_device *dev, | |||
3010 | &sdata->vif.bss_conf.chandef)) | 3030 | &sdata->vif.bss_conf.chandef)) |
3011 | return -EINVAL; | 3031 | return -EINVAL; |
3012 | 3032 | ||
3033 | /* don't allow another channel switch if one is already active. */ | ||
3034 | if (sdata->vif.csa_active) | ||
3035 | return -EBUSY; | ||
3036 | |||
3013 | mutex_lock(&local->chanctx_mtx); | 3037 | mutex_lock(&local->chanctx_mtx); |
3014 | conf = rcu_dereference_protected(sdata->vif.chanctx_conf, | 3038 | conf = rcu_dereference_protected(sdata->vif.chanctx_conf, |
3015 | lockdep_is_held(&local->chanctx_mtx)); | 3039 | lockdep_is_held(&local->chanctx_mtx)); |
3016 | if (!conf) { | 3040 | if (!conf) { |
3017 | mutex_unlock(&local->chanctx_mtx); | 3041 | err = -EBUSY; |
3018 | return -EBUSY; | 3042 | goto out; |
3019 | } | 3043 | } |
3020 | 3044 | ||
3021 | /* don't handle for multi-VIF cases */ | ||
3022 | chanctx = container_of(conf, struct ieee80211_chanctx, conf); | 3045 | chanctx = container_of(conf, struct ieee80211_chanctx, conf); |
3023 | if (ieee80211_chanctx_refcount(local, chanctx) > 1) { | 3046 | if (!chanctx) { |
3024 | mutex_unlock(&local->chanctx_mtx); | 3047 | err = -EBUSY; |
3025 | return -EBUSY; | 3048 | goto out; |
3026 | } | 3049 | } |
3027 | num_chanctx = 0; | ||
3028 | list_for_each_entry_rcu(chanctx, &local->chanctx_list, list) | ||
3029 | num_chanctx++; | ||
3030 | mutex_unlock(&local->chanctx_mtx); | ||
3031 | 3050 | ||
3032 | if (num_chanctx > 1) | 3051 | err = ieee80211_vif_reserve_chanctx(sdata, ¶ms->chandef, |
3033 | return -EBUSY; | 3052 | chanctx->mode, |
3053 | params->radar_required); | ||
3054 | if (err) | ||
3055 | goto out; | ||
3034 | 3056 | ||
3035 | /* don't allow another channel switch if one is already active. */ | 3057 | /* if reservation is invalid then this will fail */ |
3036 | if (sdata->vif.csa_active) | 3058 | err = ieee80211_check_combinations(sdata, NULL, chanctx->mode, 0); |
3037 | return -EBUSY; | 3059 | if (err) { |
3060 | ieee80211_vif_unreserve_chanctx(sdata); | ||
3061 | goto out; | ||
3062 | } | ||
3038 | 3063 | ||
3039 | err = ieee80211_set_csa_beacon(sdata, params, &changed); | 3064 | err = ieee80211_set_csa_beacon(sdata, params, &changed); |
3040 | if (err) | 3065 | if (err) { |
3041 | return err; | 3066 | ieee80211_vif_unreserve_chanctx(sdata); |
3067 | goto out; | ||
3068 | } | ||
3042 | 3069 | ||
3043 | sdata->csa_radar_required = params->radar_required; | ||
3044 | sdata->csa_chandef = params->chandef; | 3070 | sdata->csa_chandef = params->chandef; |
3045 | sdata->csa_block_tx = params->block_tx; | 3071 | sdata->csa_block_tx = params->block_tx; |
3046 | sdata->vif.csa_active = true; | 3072 | sdata->vif.csa_active = true; |
@@ -3057,7 +3083,9 @@ __ieee80211_channel_switch(struct wiphy *wiphy, struct net_device *dev, | |||
3057 | ieee80211_csa_finalize(sdata); | 3083 | ieee80211_csa_finalize(sdata); |
3058 | } | 3084 | } |
3059 | 3085 | ||
3060 | return 0; | 3086 | out: |
3087 | mutex_unlock(&local->chanctx_mtx); | ||
3088 | return err; | ||
3061 | } | 3089 | } |
3062 | 3090 | ||
3063 | int ieee80211_channel_switch(struct wiphy *wiphy, struct net_device *dev, | 3091 | int ieee80211_channel_switch(struct wiphy *wiphy, struct net_device *dev, |