aboutsummaryrefslogtreecommitdiffstats
path: root/net/mac80211/cfg.c
diff options
context:
space:
mode:
authorLuciano Coelho <luciano.coelho@intel.com>2014-01-13 12:43:00 -0500
committerJohannes Berg <johannes.berg@intel.com>2014-02-04 15:48:06 -0500
commit66e01cf99e0a9d0cbff21b0288c049654d5acf3e (patch)
tree260f0faa7f500757b98049ace42b7d34ab002a77 /net/mac80211/cfg.c
parentb58e81e96a81c80886011ad87cdbe73585dec4f7 (diff)
mac80211: only set CSA beacon when at least one beacon must be transmitted
A beacon should never have a Channel Switch Announcement information element with a count of 0, because a count of 1 means switch just before the next beacon. So, if a count of 0 was valid in a beacon, it would have been transmitted in the next channel already, which is useless. A CSA count equal to zero is only meaningful in action frames or probe_responses. Fix the ieee80211_csa_is_complete() and ieee80211_update_csa() functions accordingly. With a CSA count of 0, we won't transmit any CSA beacons, because the switch will happen before the next TBTT. To avoid extra work and potential confusion in the drivers, complete the CSA immediately, instead of waiting for the driver to call ieee80211_csa_finish(). To keep things simpler, we also switch immediately when the CSA count is 1, while in theory we should delay the switch until just before the next TBTT. Additionally, move the ieee80211_csa_finish() function to cfg.c, where it makes more sense. Tested-by: Simon Wunderlich <sw@simonwunderlich.de> Acked-by: Simon Wunderlich <sw@simonwunderlich.de> Signed-off-by: Luciano Coelho <luciano.coelho@intel.com> Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Diffstat (limited to 'net/mac80211/cfg.c')
-rw-r--r--net/mac80211/cfg.c115
1 files changed, 85 insertions, 30 deletions
diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c
index f111f8df4e65..032081c4cc65 100644
--- a/net/mac80211/cfg.c
+++ b/net/mac80211/cfg.c
@@ -2988,28 +2988,26 @@ cfg80211_beacon_dup(struct cfg80211_beacon_data *beacon)
2988 return new_beacon; 2988 return new_beacon;
2989} 2989}
2990 2990
2991void ieee80211_csa_finalize_work(struct work_struct *work) 2991void ieee80211_csa_finish(struct ieee80211_vif *vif)
2992{ 2992{
2993 struct ieee80211_sub_if_data *sdata = 2993 struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif);
2994 container_of(work, struct ieee80211_sub_if_data,
2995 csa_finalize_work);
2996 struct ieee80211_local *local = sdata->local;
2997 int err, changed = 0;
2998 2994
2999 sdata_lock(sdata); 2995 ieee80211_queue_work(&sdata->local->hw,
3000 /* AP might have been stopped while waiting for the lock. */ 2996 &sdata->csa_finalize_work);
3001 if (!sdata->vif.csa_active) 2997}
3002 goto unlock; 2998EXPORT_SYMBOL(ieee80211_csa_finish);
3003 2999
3004 if (!ieee80211_sdata_running(sdata)) 3000static void ieee80211_csa_finalize(struct ieee80211_sub_if_data *sdata)
3005 goto unlock; 3001{
3002 struct ieee80211_local *local = sdata->local;
3003 int err, changed = 0;
3006 3004
3007 sdata->radar_required = sdata->csa_radar_required; 3005 sdata->radar_required = sdata->csa_radar_required;
3008 mutex_lock(&local->mtx); 3006 mutex_lock(&local->mtx);
3009 err = ieee80211_vif_change_channel(sdata, &changed); 3007 err = ieee80211_vif_change_channel(sdata, &changed);
3010 mutex_unlock(&local->mtx); 3008 mutex_unlock(&local->mtx);
3011 if (WARN_ON(err < 0)) 3009 if (WARN_ON(err < 0))
3012 goto unlock; 3010 return;
3013 3011
3014 if (!local->use_chanctx) { 3012 if (!local->use_chanctx) {
3015 local->_oper_chandef = sdata->csa_chandef; 3013 local->_oper_chandef = sdata->csa_chandef;
@@ -3023,7 +3021,7 @@ void ieee80211_csa_finalize_work(struct work_struct *work)
3023 case NL80211_IFTYPE_AP: 3021 case NL80211_IFTYPE_AP:
3024 err = ieee80211_assign_beacon(sdata, sdata->u.ap.next_beacon); 3022 err = ieee80211_assign_beacon(sdata, sdata->u.ap.next_beacon);
3025 if (err < 0) 3023 if (err < 0)
3026 goto unlock; 3024 return;
3027 3025
3028 changed |= err; 3026 changed |= err;
3029 kfree(sdata->u.ap.next_beacon); 3027 kfree(sdata->u.ap.next_beacon);
@@ -3038,12 +3036,12 @@ void ieee80211_csa_finalize_work(struct work_struct *work)
3038 case NL80211_IFTYPE_MESH_POINT: 3036 case NL80211_IFTYPE_MESH_POINT:
3039 err = ieee80211_mesh_finish_csa(sdata); 3037 err = ieee80211_mesh_finish_csa(sdata);
3040 if (err < 0) 3038 if (err < 0)
3041 goto unlock; 3039 return;
3042 break; 3040 break;
3043#endif 3041#endif
3044 default: 3042 default:
3045 WARN_ON(1); 3043 WARN_ON(1);
3046 goto unlock; 3044 return;
3047 } 3045 }
3048 3046
3049 ieee80211_wake_queues_by_reason(&sdata->local->hw, 3047 ieee80211_wake_queues_by_reason(&sdata->local->hw,
@@ -3051,6 +3049,23 @@ void ieee80211_csa_finalize_work(struct work_struct *work)
3051 IEEE80211_QUEUE_STOP_REASON_CSA); 3049 IEEE80211_QUEUE_STOP_REASON_CSA);
3052 3050
3053 cfg80211_ch_switch_notify(sdata->dev, &sdata->csa_chandef); 3051 cfg80211_ch_switch_notify(sdata->dev, &sdata->csa_chandef);
3052}
3053
3054void ieee80211_csa_finalize_work(struct work_struct *work)
3055{
3056 struct ieee80211_sub_if_data *sdata =
3057 container_of(work, struct ieee80211_sub_if_data,
3058 csa_finalize_work);
3059
3060 sdata_lock(sdata);
3061 /* AP might have been stopped while waiting for the lock. */
3062 if (!sdata->vif.csa_active)
3063 goto unlock;
3064
3065 if (!ieee80211_sdata_running(sdata))
3066 goto unlock;
3067
3068 ieee80211_csa_finalize(sdata);
3054 3069
3055unlock: 3070unlock:
3056 sdata_unlock(sdata); 3071 sdata_unlock(sdata);
@@ -3064,7 +3079,7 @@ int ieee80211_channel_switch(struct wiphy *wiphy, struct net_device *dev,
3064 struct ieee80211_chanctx_conf *chanctx_conf; 3079 struct ieee80211_chanctx_conf *chanctx_conf;
3065 struct ieee80211_chanctx *chanctx; 3080 struct ieee80211_chanctx *chanctx;
3066 struct ieee80211_if_mesh __maybe_unused *ifmsh; 3081 struct ieee80211_if_mesh __maybe_unused *ifmsh;
3067 int err, num_chanctx; 3082 int err, num_chanctx, changed = 0;
3068 3083
3069 lockdep_assert_held(&sdata->wdev.mtx); 3084 lockdep_assert_held(&sdata->wdev.mtx);
3070 3085
@@ -3105,19 +3120,40 @@ int ieee80211_channel_switch(struct wiphy *wiphy, struct net_device *dev,
3105 3120
3106 switch (sdata->vif.type) { 3121 switch (sdata->vif.type) {
3107 case NL80211_IFTYPE_AP: 3122 case NL80211_IFTYPE_AP:
3108 sdata->csa_counter_offset_beacon =
3109 params->counter_offset_beacon;
3110 sdata->csa_counter_offset_presp = params->counter_offset_presp;
3111 sdata->u.ap.next_beacon = 3123 sdata->u.ap.next_beacon =
3112 cfg80211_beacon_dup(&params->beacon_after); 3124 cfg80211_beacon_dup(&params->beacon_after);
3113 if (!sdata->u.ap.next_beacon) 3125 if (!sdata->u.ap.next_beacon)
3114 return -ENOMEM; 3126 return -ENOMEM;
3115 3127
3128 /*
3129 * With a count of 0, we don't have to wait for any
3130 * TBTT before switching, so complete the CSA
3131 * immediately. In theory, with a count == 1 we
3132 * should delay the switch until just before the next
3133 * TBTT, but that would complicate things so we switch
3134 * immediately too. If we would delay the switch
3135 * until the next TBTT, we would have to set the probe
3136 * response here.
3137 *
3138 * TODO: A channel switch with count <= 1 without
3139 * sending a CSA action frame is kind of useless,
3140 * because the clients won't know we're changing
3141 * channels. The action frame must be implemented
3142 * either here or in the userspace.
3143 */
3144 if (params->count <= 1)
3145 break;
3146
3147 sdata->csa_counter_offset_beacon =
3148 params->counter_offset_beacon;
3149 sdata->csa_counter_offset_presp = params->counter_offset_presp;
3116 err = ieee80211_assign_beacon(sdata, &params->beacon_csa); 3150 err = ieee80211_assign_beacon(sdata, &params->beacon_csa);
3117 if (err < 0) { 3151 if (err < 0) {
3118 kfree(sdata->u.ap.next_beacon); 3152 kfree(sdata->u.ap.next_beacon);
3119 return err; 3153 return err;
3120 } 3154 }
3155 changed |= err;
3156
3121 break; 3157 break;
3122 case NL80211_IFTYPE_ADHOC: 3158 case NL80211_IFTYPE_ADHOC:
3123 if (!sdata->vif.bss_conf.ibss_joined) 3159 if (!sdata->vif.bss_conf.ibss_joined)
@@ -3145,9 +3181,16 @@ int ieee80211_channel_switch(struct wiphy *wiphy, struct net_device *dev,
3145 params->chandef.chan->band) 3181 params->chandef.chan->band)
3146 return -EINVAL; 3182 return -EINVAL;
3147 3183
3148 err = ieee80211_ibss_csa_beacon(sdata, params); 3184 /* see comments in the NL80211_IFTYPE_AP block */
3149 if (err < 0) 3185 if (params->count > 1) {
3150 return err; 3186 err = ieee80211_ibss_csa_beacon(sdata, params);
3187 if (err < 0)
3188 return err;
3189 changed |= err;
3190 }
3191
3192 ieee80211_send_action_csa(sdata, params);
3193
3151 break; 3194 break;
3152#ifdef CONFIG_MAC80211_MESH 3195#ifdef CONFIG_MAC80211_MESH
3153 case NL80211_IFTYPE_MESH_POINT: 3196 case NL80211_IFTYPE_MESH_POINT:
@@ -3172,12 +3215,19 @@ int ieee80211_channel_switch(struct wiphy *wiphy, struct net_device *dev,
3172 if (ifmsh->csa_role == IEEE80211_MESH_CSA_ROLE_NONE) 3215 if (ifmsh->csa_role == IEEE80211_MESH_CSA_ROLE_NONE)
3173 ifmsh->csa_role = IEEE80211_MESH_CSA_ROLE_INIT; 3216 ifmsh->csa_role = IEEE80211_MESH_CSA_ROLE_INIT;
3174 3217
3175 err = ieee80211_mesh_csa_beacon(sdata, params, 3218 /* see comments in the NL80211_IFTYPE_AP block */
3176 (ifmsh->csa_role == IEEE80211_MESH_CSA_ROLE_INIT)); 3219 if (params->count > 1) {
3177 if (err < 0) { 3220 err = ieee80211_mesh_csa_beacon(sdata, params);
3178 ifmsh->csa_role = IEEE80211_MESH_CSA_ROLE_NONE; 3221 if (err < 0) {
3179 return err; 3222 ifmsh->csa_role = IEEE80211_MESH_CSA_ROLE_NONE;
3223 return err;
3224 }
3225 changed |= err;
3180 } 3226 }
3227
3228 if (ifmsh->csa_role == IEEE80211_MESH_CSA_ROLE_INIT)
3229 ieee80211_send_action_csa(sdata, params);
3230
3181 break; 3231 break;
3182#endif 3232#endif
3183 default: 3233 default:
@@ -3194,8 +3244,13 @@ int ieee80211_channel_switch(struct wiphy *wiphy, struct net_device *dev,
3194 sdata->csa_chandef = params->chandef; 3244 sdata->csa_chandef = params->chandef;
3195 sdata->vif.csa_active = true; 3245 sdata->vif.csa_active = true;
3196 3246
3197 ieee80211_bss_info_change_notify(sdata, err); 3247 if (changed) {
3198 drv_channel_switch_beacon(sdata, &params->chandef); 3248 ieee80211_bss_info_change_notify(sdata, changed);
3249 drv_channel_switch_beacon(sdata, &params->chandef);
3250 } else {
3251 /* if the beacon didn't change, we can finalize immediately */
3252 ieee80211_csa_finalize(sdata);
3253 }
3199 3254
3200 return 0; 3255 return 0;
3201} 3256}