aboutsummaryrefslogtreecommitdiffstats
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
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>
-rw-r--r--include/net/mac80211.h10
-rw-r--r--net/mac80211/cfg.c115
-rw-r--r--net/mac80211/ibss.c6
-rw-r--r--net/mac80211/ieee80211_i.h3
-rw-r--r--net/mac80211/mesh.c9
-rw-r--r--net/mac80211/tx.c19
6 files changed, 102 insertions, 60 deletions
diff --git a/include/net/mac80211.h b/include/net/mac80211.h
index f4ab2fb4d50c..df1004be7ba5 100644
--- a/include/net/mac80211.h
+++ b/include/net/mac80211.h
@@ -2750,11 +2750,13 @@ enum ieee80211_roc_type {
2750 * @channel_switch_beacon: Starts a channel switch to a new channel. 2750 * @channel_switch_beacon: Starts a channel switch to a new channel.
2751 * Beacons are modified to include CSA or ECSA IEs before calling this 2751 * Beacons are modified to include CSA or ECSA IEs before calling this
2752 * function. The corresponding count fields in these IEs must be 2752 * function. The corresponding count fields in these IEs must be
2753 * decremented, and when they reach zero the driver must call 2753 * decremented, and when they reach 1 the driver must call
2754 * ieee80211_csa_finish(). Drivers which use ieee80211_beacon_get() 2754 * ieee80211_csa_finish(). Drivers which use ieee80211_beacon_get()
2755 * get the csa counter decremented by mac80211, but must check if it is 2755 * get the csa counter decremented by mac80211, but must check if it is
2756 * zero using ieee80211_csa_is_complete() after the beacon has been 2756 * 1 using ieee80211_csa_is_complete() after the beacon has been
2757 * transmitted and then call ieee80211_csa_finish(). 2757 * transmitted and then call ieee80211_csa_finish().
2758 * If the CSA count starts as zero or 1, this function will not be called,
2759 * since there won't be any time to beacon before the switch anyway.
2758 * 2760 *
2759 * @join_ibss: Join an IBSS (on an IBSS interface); this is called after all 2761 * @join_ibss: Join an IBSS (on an IBSS interface); this is called after all
2760 * information in bss_conf is set up and the beacon can be retrieved. A 2762 * information in bss_conf is set up and the beacon can be retrieved. A
@@ -3452,13 +3454,13 @@ static inline struct sk_buff *ieee80211_beacon_get(struct ieee80211_hw *hw,
3452 * @vif: &struct ieee80211_vif pointer from the add_interface callback. 3454 * @vif: &struct ieee80211_vif pointer from the add_interface callback.
3453 * 3455 *
3454 * After a channel switch announcement was scheduled and the counter in this 3456 * After a channel switch announcement was scheduled and the counter in this
3455 * announcement hit zero, this function must be called by the driver to 3457 * announcement hits 1, this function must be called by the driver to
3456 * notify mac80211 that the channel can be changed. 3458 * notify mac80211 that the channel can be changed.
3457 */ 3459 */
3458void ieee80211_csa_finish(struct ieee80211_vif *vif); 3460void ieee80211_csa_finish(struct ieee80211_vif *vif);
3459 3461
3460/** 3462/**
3461 * ieee80211_csa_is_complete - find out if counters reached zero 3463 * ieee80211_csa_is_complete - find out if counters reached 1
3462 * @vif: &struct ieee80211_vif pointer from the add_interface callback. 3464 * @vif: &struct ieee80211_vif pointer from the add_interface callback.
3463 * 3465 *
3464 * This function returns whether the channel switch counters reached zero. 3466 * This function returns whether the channel switch counters reached zero.
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}
diff --git a/net/mac80211/ibss.c b/net/mac80211/ibss.c
index 771080ec7212..ed7eec3f6ee0 100644
--- a/net/mac80211/ibss.c
+++ b/net/mac80211/ibss.c
@@ -521,12 +521,6 @@ int ieee80211_ibss_csa_beacon(struct ieee80211_sub_if_data *sdata,
521 if (old_presp) 521 if (old_presp)
522 kfree_rcu(old_presp, rcu_head); 522 kfree_rcu(old_presp, rcu_head);
523 523
524 /* it might not send the beacon for a while. send an action frame
525 * immediately to announce the channel switch.
526 */
527 if (csa_settings)
528 ieee80211_send_action_csa(sdata, csa_settings);
529
530 return BSS_CHANGED_BEACON; 524 return BSS_CHANGED_BEACON;
531 out: 525 out:
532 return ret; 526 return ret;
diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h
index 428f5bd874e3..96eb272297e1 100644
--- a/net/mac80211/ieee80211_i.h
+++ b/net/mac80211/ieee80211_i.h
@@ -1412,8 +1412,7 @@ void ieee80211_mesh_work(struct ieee80211_sub_if_data *sdata);
1412void ieee80211_mesh_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata, 1412void ieee80211_mesh_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata,
1413 struct sk_buff *skb); 1413 struct sk_buff *skb);
1414int ieee80211_mesh_csa_beacon(struct ieee80211_sub_if_data *sdata, 1414int ieee80211_mesh_csa_beacon(struct ieee80211_sub_if_data *sdata,
1415 struct cfg80211_csa_settings *csa_settings, 1415 struct cfg80211_csa_settings *csa_settings);
1416 bool csa_action);
1417int ieee80211_mesh_finish_csa(struct ieee80211_sub_if_data *sdata); 1416int ieee80211_mesh_finish_csa(struct ieee80211_sub_if_data *sdata);
1418 1417
1419/* scan/BSS handling */ 1418/* scan/BSS handling */
diff --git a/net/mac80211/mesh.c b/net/mac80211/mesh.c
index b4219937e75e..b02ac3378b13 100644
--- a/net/mac80211/mesh.c
+++ b/net/mac80211/mesh.c
@@ -1066,7 +1066,8 @@ int ieee80211_mesh_finish_csa(struct ieee80211_sub_if_data *sdata)
1066 /* Remove the CSA and MCSP elements from the beacon */ 1066 /* Remove the CSA and MCSP elements from the beacon */
1067 tmp_csa_settings = rcu_dereference(ifmsh->csa); 1067 tmp_csa_settings = rcu_dereference(ifmsh->csa);
1068 rcu_assign_pointer(ifmsh->csa, NULL); 1068 rcu_assign_pointer(ifmsh->csa, NULL);
1069 kfree_rcu(tmp_csa_settings, rcu_head); 1069 if (tmp_csa_settings)
1070 kfree_rcu(tmp_csa_settings, rcu_head);
1070 ret = ieee80211_mesh_rebuild_beacon(sdata); 1071 ret = ieee80211_mesh_rebuild_beacon(sdata);
1071 if (ret) 1072 if (ret)
1072 return -EINVAL; 1073 return -EINVAL;
@@ -1079,8 +1080,7 @@ int ieee80211_mesh_finish_csa(struct ieee80211_sub_if_data *sdata)
1079} 1080}
1080 1081
1081int ieee80211_mesh_csa_beacon(struct ieee80211_sub_if_data *sdata, 1082int ieee80211_mesh_csa_beacon(struct ieee80211_sub_if_data *sdata,
1082 struct cfg80211_csa_settings *csa_settings, 1083 struct cfg80211_csa_settings *csa_settings)
1083 bool csa_action)
1084{ 1084{
1085 struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh; 1085 struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
1086 struct mesh_csa_settings *tmp_csa_settings; 1086 struct mesh_csa_settings *tmp_csa_settings;
@@ -1104,9 +1104,6 @@ int ieee80211_mesh_csa_beacon(struct ieee80211_sub_if_data *sdata,
1104 return ret; 1104 return ret;
1105 } 1105 }
1106 1106
1107 if (csa_action)
1108 ieee80211_send_action_csa(sdata, csa_settings);
1109
1110 return BSS_CHANGED_BEACON; 1107 return BSS_CHANGED_BEACON;
1111} 1108}
1112 1109
diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c
index 27c990bf2320..bb990ecfa655 100644
--- a/net/mac80211/tx.c
+++ b/net/mac80211/tx.c
@@ -2402,15 +2402,6 @@ static int ieee80211_beacon_add_tim(struct ieee80211_sub_if_data *sdata,
2402 return 0; 2402 return 0;
2403} 2403}
2404 2404
2405void ieee80211_csa_finish(struct ieee80211_vif *vif)
2406{
2407 struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif);
2408
2409 ieee80211_queue_work(&sdata->local->hw,
2410 &sdata->csa_finalize_work);
2411}
2412EXPORT_SYMBOL(ieee80211_csa_finish);
2413
2414static void ieee80211_update_csa(struct ieee80211_sub_if_data *sdata, 2405static void ieee80211_update_csa(struct ieee80211_sub_if_data *sdata,
2415 struct beacon_data *beacon) 2406 struct beacon_data *beacon)
2416{ 2407{
@@ -2439,8 +2430,12 @@ static void ieee80211_update_csa(struct ieee80211_sub_if_data *sdata,
2439 if (WARN_ON(counter_offset_beacon >= beacon_data_len)) 2430 if (WARN_ON(counter_offset_beacon >= beacon_data_len))
2440 return; 2431 return;
2441 2432
2442 /* warn if the driver did not check for/react to csa completeness */ 2433 /* Warn if the driver did not check for/react to csa
2443 if (WARN_ON(beacon_data[counter_offset_beacon] == 0)) 2434 * completeness. A beacon with CSA counter set to 0 should
2435 * never occur, because a counter of 1 means switch just
2436 * before the next beacon.
2437 */
2438 if (WARN_ON(beacon_data[counter_offset_beacon] == 1))
2444 return; 2439 return;
2445 2440
2446 beacon_data[counter_offset_beacon]--; 2441 beacon_data[counter_offset_beacon]--;
@@ -2506,7 +2501,7 @@ bool ieee80211_csa_is_complete(struct ieee80211_vif *vif)
2506 if (WARN_ON(counter_beacon > beacon_data_len)) 2501 if (WARN_ON(counter_beacon > beacon_data_len))
2507 goto out; 2502 goto out;
2508 2503
2509 if (beacon_data[counter_beacon] == 0) 2504 if (beacon_data[counter_beacon] == 1)
2510 ret = true; 2505 ret = true;
2511 out: 2506 out:
2512 rcu_read_unlock(); 2507 rcu_read_unlock();