ath9k: add beacon related stability fixes
[openwrt/openwrt.git] / package / kernel / mac80211 / patches / 317-ath9k-Fix-beacon-configuration-for-addition-removal-.patch
1 From: Benjamin Berg <benjamin.berg@open-mesh.com>
2 Date: Mon, 4 Jul 2016 14:37:25 +0200
3 Subject: [PATCH] ath9k: Fix beacon configuration for addition/removal of
4 interfaces
5
6 This patch fixes some issues with interface reconfiguration. It could
7 for example happen that an AP interface in beacon slot 0 was removed
8 leaving an IBSS station in one of the other slots. When this happens
9 the driver never sends out the beacon as it only tries to send a beacon
10 from slot 0.
11
12 Appart from that the tracking of required changes to the beacon config is
13 relatively complicated and prone to errors.
14
15 The approach taken here is to solve reconfiguration issues is to
16 reconfigure the beacons when any interface changes. This means that
17 the complexity of deciding whether an interface change may modify the
18 beacon configuration is gone. It also means that the beacon config will
19 be reliably updated when an interface is removed.
20
21 The issue that a single non-AP interface might not be in beacon
22 slot 0 and wouldn't be send out is solved by moving it into the
23 first slot. The TSF value in hardware is adjusted accordingly so
24 that the timestamp of the beacons stay consistent.
25
26 Signed-off-by: Benjamin Berg <benjamin.berg@open-mesh.com>
27 ---
28
29 --- a/drivers/net/wireless/ath/ath9k/ath9k.h
30 +++ b/drivers/net/wireless/ath/ath9k/ath9k.h
31 @@ -637,6 +637,8 @@ struct ath9k_vif_iter_data {
32 int nwds; /* number of WDS vifs */
33 int nadhocs; /* number of adhoc vifs */
34 int nocbs; /* number of OCB vifs */
35 + int nbcnvifs; /* number of beaconing vifs */
36 + struct ieee80211_vif *primary_beacon_vif;
37 struct ieee80211_vif *primary_sta;
38 };
39
40 @@ -685,10 +687,11 @@ struct ath_beacon {
41 };
42
43 void ath9k_beacon_tasklet(unsigned long data);
44 -void ath9k_beacon_config(struct ath_softc *sc, struct ieee80211_vif *vif,
45 - u32 changed);
46 +void ath9k_beacon_config(struct ath_softc *sc, struct ieee80211_vif *main_vif,
47 + bool beacons);
48 void ath9k_beacon_assign_slot(struct ath_softc *sc, struct ieee80211_vif *vif);
49 void ath9k_beacon_remove_slot(struct ath_softc *sc, struct ieee80211_vif *vif);
50 +void ath9k_beacon_ensure_primary_slot(struct ath_softc *sc);
51 void ath9k_set_beacon(struct ath_softc *sc);
52 bool ath9k_csa_is_finished(struct ath_softc *sc, struct ieee80211_vif *vif);
53 void ath9k_csa_update(struct ath_softc *sc);
54 --- a/drivers/net/wireless/ath/ath9k/beacon.c
55 +++ b/drivers/net/wireless/ath/ath9k/beacon.c
56 @@ -209,7 +209,6 @@ void ath9k_beacon_assign_slot(struct ath
57 }
58
59 sc->beacon.bslot[avp->av_bslot] = vif;
60 - sc->nbcnvifs++;
61
62 ath_dbg(common, CONFIG, "Added interface at beacon slot: %d\n",
63 avp->av_bslot);
64 @@ -220,15 +219,12 @@ void ath9k_beacon_remove_slot(struct ath
65 struct ath_common *common = ath9k_hw_common(sc->sc_ah);
66 struct ath_vif *avp = (void *)vif->drv_priv;
67 struct ath_buf *bf = avp->av_bcbuf;
68 - struct ath_beacon_config *cur_conf = &sc->cur_chan->beacon;
69
70 ath_dbg(common, CONFIG, "Removing interface at beacon slot: %d\n",
71 avp->av_bslot);
72
73 tasklet_disable(&sc->bcon_tasklet);
74
75 - cur_conf->enable_beacon &= ~BIT(avp->av_bslot);
76 -
77 if (bf && bf->bf_mpdu) {
78 struct sk_buff *skb = bf->bf_mpdu;
79 dma_unmap_single(sc->dev, bf->bf_buf_addr,
80 @@ -240,12 +236,73 @@ void ath9k_beacon_remove_slot(struct ath
81
82 avp->av_bcbuf = NULL;
83 sc->beacon.bslot[avp->av_bslot] = NULL;
84 - sc->nbcnvifs--;
85 list_add_tail(&bf->list, &sc->beacon.bbuf);
86
87 tasklet_enable(&sc->bcon_tasklet);
88 }
89
90 +void ath9k_beacon_ensure_primary_slot(struct ath_softc *sc)
91 +{
92 + struct ath_common *common = ath9k_hw_common(sc->sc_ah);
93 + struct ieee80211_vif *vif;
94 + struct ath_vif *avp;
95 + s64 tsfadjust;
96 + u32 offset;
97 + int first_slot = ATH_BCBUF;
98 + int slot;
99 +
100 + tasklet_disable(&sc->bcon_tasklet);
101 +
102 + /* Find first taken slot. */
103 + for (slot = 0; slot < ATH_BCBUF; slot++) {
104 + if (sc->beacon.bslot[slot]) {
105 + first_slot = slot;
106 + break;
107 + }
108 + }
109 + if (first_slot == 0)
110 + goto out;
111 +
112 + /* Re-enumarate all slots, moving them forward. */
113 + for (slot = 0; slot < ATH_BCBUF; slot++) {
114 + if (slot + first_slot < ATH_BCBUF) {
115 + vif = sc->beacon.bslot[slot + first_slot];
116 + sc->beacon.bslot[slot] = vif;
117 +
118 + if (vif) {
119 + avp = (void *)vif->drv_priv;
120 + avp->av_bslot = slot;
121 + }
122 + } else {
123 + sc->beacon.bslot[slot] = NULL;
124 + }
125 + }
126 +
127 + vif = sc->beacon.bslot[0];
128 + if (WARN_ON(!vif))
129 + goto out;
130 +
131 + /* Get the tsf_adjust value for the new first slot. */
132 + avp = (void *)vif->drv_priv;
133 + tsfadjust = le64_to_cpu(avp->tsf_adjust);
134 +
135 + ath_dbg(common, CONFIG,
136 + "Adjusting global TSF after beacon slot reassignment: %lld\n",
137 + (signed long long)tsfadjust);
138 +
139 + /* Modify TSF as required and update the HW. */
140 + avp->chanctx->tsf_val += tsfadjust;
141 + if (sc->cur_chan == avp->chanctx) {
142 + offset = ath9k_hw_get_tsf_offset(&avp->chanctx->tsf_ts, NULL);
143 + ath9k_hw_settsf64(sc->sc_ah, avp->chanctx->tsf_val + offset);
144 + }
145 +
146 + /* The slots tsf_adjust will be updated by ath9k_beacon_config later. */
147 +
148 +out:
149 + tasklet_enable(&sc->bcon_tasklet);
150 +}
151 +
152 static int ath9k_beacon_choose_slot(struct ath_softc *sc)
153 {
154 struct ath_common *common = ath9k_hw_common(sc->sc_ah);
155 @@ -274,26 +331,33 @@ static int ath9k_beacon_choose_slot(stru
156 return slot;
157 }
158
159 -static void ath9k_set_tsfadjust(struct ath_softc *sc, struct ieee80211_vif *vif)
160 +static void ath9k_set_tsfadjust(struct ath_softc *sc,
161 + struct ath_beacon_config *cur_conf)
162 {
163 struct ath_common *common = ath9k_hw_common(sc->sc_ah);
164 - struct ath_vif *avp = (void *)vif->drv_priv;
165 - struct ath_beacon_config *cur_conf = &avp->chanctx->beacon;
166 s64 tsfadjust;
167 + int slot;
168
169 - if (avp->av_bslot == 0)
170 - return;
171 + for (slot = 0; slot < ATH_BCBUF; slot++) {
172 + struct ath_vif *avp;
173
174 - /* tsf_adjust is added to the TSF value. We send out the beacon late,
175 - * so need to adjust the TSF starting point to be later in time (i.e.
176 - * the theoretical first beacon has a TSF of 0 after correction).
177 - */
178 - tsfadjust = cur_conf->beacon_interval * avp->av_bslot;
179 - tsfadjust = -TU_TO_USEC(tsfadjust) / ATH_BCBUF;
180 - avp->tsf_adjust = cpu_to_le64(tsfadjust);
181 + if (!sc->beacon.bslot[slot])
182 + continue;
183
184 - ath_dbg(common, CONFIG, "tsfadjust is: %lld for bslot: %d\n",
185 - (signed long long)tsfadjust, avp->av_bslot);
186 + avp = (void *)sc->beacon.bslot[slot]->drv_priv;
187 +
188 + /* tsf_adjust is added to the TSF value. We send out the
189 + * beacon late, so need to adjust the TSF starting point to be
190 + * later in time (i.e. the theoretical first beacon has a TSF
191 + * of 0 after correction).
192 + */
193 + tsfadjust = cur_conf->beacon_interval * avp->av_bslot;
194 + tsfadjust = -TU_TO_USEC(tsfadjust) / ATH_BCBUF;
195 + avp->tsf_adjust = cpu_to_le64(tsfadjust);
196 +
197 + ath_dbg(common, CONFIG, "tsfadjust is: %lld for bslot: %d\n",
198 + (signed long long)tsfadjust, avp->av_bslot);
199 + }
200 }
201
202 bool ath9k_csa_is_finished(struct ath_softc *sc, struct ieee80211_vif *vif)
203 @@ -447,20 +511,28 @@ void ath9k_beacon_tasklet(unsigned long
204 * Both nexttbtt and intval have to be in usecs.
205 */
206 static void ath9k_beacon_init(struct ath_softc *sc, u32 nexttbtt,
207 - u32 intval, bool reset_tsf)
208 + u32 intval)
209 {
210 struct ath_hw *ah = sc->sc_ah;
211
212 ath9k_hw_disable_interrupts(ah);
213 - if (reset_tsf)
214 - ath9k_hw_reset_tsf(ah);
215 ath9k_beaconq_config(sc);
216 ath9k_hw_beaconinit(ah, nexttbtt, intval);
217 + ah->imask |= ATH9K_INT_SWBA;
218 sc->beacon.bmisscnt = 0;
219 ath9k_hw_set_interrupts(ah);
220 ath9k_hw_enable_interrupts(ah);
221 }
222
223 +static void ath9k_beacon_stop(struct ath_softc *sc)
224 +{
225 + ath9k_hw_disable_interrupts(sc->sc_ah);
226 + sc->sc_ah->imask &= ~(ATH9K_INT_SWBA | ATH9K_INT_BMISS);
227 + sc->beacon.bmisscnt = 0;
228 + ath9k_hw_set_interrupts(sc->sc_ah);
229 + ath9k_hw_enable_interrupts(sc->sc_ah);
230 +}
231 +
232 /*
233 * For multi-bss ap support beacons are either staggered evenly over N slots or
234 * burst together. For the former arrange for the SWBA to be delivered for each
235 @@ -472,7 +544,7 @@ static void ath9k_beacon_config_ap(struc
236 struct ath_hw *ah = sc->sc_ah;
237
238 ath9k_cmn_beacon_config_ap(ah, conf, ATH_BCBUF);
239 - ath9k_beacon_init(sc, conf->nexttbtt, conf->intval, false);
240 + ath9k_beacon_init(sc, conf->nexttbtt, conf->intval);
241 }
242
243 static void ath9k_beacon_config_sta(struct ath_hw *ah,
244 @@ -501,7 +573,7 @@ static void ath9k_beacon_config_adhoc(st
245
246 ath9k_cmn_beacon_config_adhoc(ah, conf);
247
248 - ath9k_beacon_init(sc, conf->nexttbtt, conf->intval, conf->ibss_creator);
249 + ath9k_beacon_init(sc, conf->nexttbtt, conf->intval);
250
251 /*
252 * Set the global 'beacon has been configured' flag for the
253 @@ -511,44 +583,6 @@ static void ath9k_beacon_config_adhoc(st
254 set_bit(ATH_OP_BEACONS, &common->op_flags);
255 }
256
257 -static bool ath9k_allow_beacon_config(struct ath_softc *sc,
258 - struct ieee80211_vif *vif)
259 -{
260 - struct ath_common *common = ath9k_hw_common(sc->sc_ah);
261 - struct ath_vif *avp = (void *)vif->drv_priv;
262 -
263 - if (ath9k_is_chanctx_enabled()) {
264 - /*
265 - * If the VIF is not present in the current channel context,
266 - * then we can't do the usual opmode checks. Allow the
267 - * beacon config for the VIF to be updated in this case and
268 - * return immediately.
269 - */
270 - if (sc->cur_chan != avp->chanctx)
271 - return true;
272 - }
273 -
274 - if (sc->sc_ah->opmode == NL80211_IFTYPE_AP) {
275 - if (vif->type != NL80211_IFTYPE_AP) {
276 - ath_dbg(common, CONFIG,
277 - "An AP interface is already present !\n");
278 - return false;
279 - }
280 - }
281 -
282 - if (sc->sc_ah->opmode == NL80211_IFTYPE_STATION) {
283 - if ((vif->type == NL80211_IFTYPE_STATION) &&
284 - test_bit(ATH_OP_BEACONS, &common->op_flags) &&
285 - vif != sc->cur_chan->primary_sta) {
286 - ath_dbg(common, CONFIG,
287 - "Beacon already configured for a station interface\n");
288 - return false;
289 - }
290 - }
291 -
292 - return true;
293 -}
294 -
295 static void ath9k_cache_beacon_config(struct ath_softc *sc,
296 struct ath_chanctx *ctx,
297 struct ieee80211_bss_conf *bss_conf)
298 @@ -584,87 +618,79 @@ static void ath9k_cache_beacon_config(st
299 if (cur_conf->dtim_period == 0)
300 cur_conf->dtim_period = 1;
301
302 + ath9k_set_tsfadjust(sc, cur_conf);
303 }
304
305 -void ath9k_beacon_config(struct ath_softc *sc, struct ieee80211_vif *vif,
306 - u32 changed)
307 +void ath9k_beacon_config(struct ath_softc *sc, struct ieee80211_vif *main_vif,
308 + bool beacons)
309 {
310 - struct ieee80211_bss_conf *bss_conf = &vif->bss_conf;
311 - struct ath_hw *ah = sc->sc_ah;
312 - struct ath_common *common = ath9k_hw_common(ah);
313 - struct ath_vif *avp = (void *)vif->drv_priv;
314 - struct ath_chanctx *ctx = avp->chanctx;
315 + struct ath_hw *ah = sc->sc_ah;
316 + struct ath_common *common = ath9k_hw_common(ah);
317 + struct ath_vif *avp;
318 + struct ath_chanctx *ctx;
319 struct ath_beacon_config *cur_conf;
320 unsigned long flags;
321 + bool enabled;
322 bool skip_beacon = false;
323
324 - if (!ctx)
325 + if (!beacons) {
326 + clear_bit(ATH_OP_BEACONS, &common->op_flags);
327 + ath9k_beacon_stop(sc);
328 return;
329 + }
330
331 - cur_conf = &avp->chanctx->beacon;
332 - if (vif->type == NL80211_IFTYPE_AP)
333 - ath9k_set_tsfadjust(sc, vif);
334 -
335 - if (!ath9k_allow_beacon_config(sc, vif))
336 + if (WARN_ON(!main_vif))
337 return;
338
339 - if (vif->type == NL80211_IFTYPE_STATION) {
340 - ath9k_cache_beacon_config(sc, ctx, bss_conf);
341 - if (ctx != sc->cur_chan)
342 - return;
343 + avp = (void *)main_vif->drv_priv;
344 + ctx = avp->chanctx;
345 + cur_conf = &ctx->beacon;
346 + enabled = cur_conf->enable_beacon;
347 + cur_conf->enable_beacon = beacons;
348 +
349 + if (sc->sc_ah->opmode == NL80211_IFTYPE_STATION) {
350 + ath9k_cache_beacon_config(sc, ctx, &main_vif->bss_conf);
351
352 ath9k_set_beacon(sc);
353 set_bit(ATH_OP_BEACONS, &common->op_flags);
354 return;
355 }
356
357 - /*
358 - * Take care of multiple interfaces when
359 - * enabling/disabling SWBA.
360 - */
361 - if (changed & BSS_CHANGED_BEACON_ENABLED) {
362 - bool enabled = cur_conf->enable_beacon;
363 -
364 - if (!bss_conf->enable_beacon) {
365 - cur_conf->enable_beacon &= ~BIT(avp->av_bslot);
366 - } else {
367 - cur_conf->enable_beacon |= BIT(avp->av_bslot);
368 - if (!enabled)
369 - ath9k_cache_beacon_config(sc, ctx, bss_conf);
370 - }
371 - }
372 -
373 - if (ctx != sc->cur_chan)
374 - return;
375 + /* Update the beacon configuration. */
376 + ath9k_cache_beacon_config(sc, ctx, &main_vif->bss_conf);
377
378 /*
379 * Configure the HW beacon registers only when we have a valid
380 * beacon interval.
381 */
382 if (cur_conf->beacon_interval) {
383 - /*
384 - * If we are joining an existing IBSS network, start beaconing
385 - * only after a TSF-sync has taken place. Ensure that this
386 - * happens by setting the appropriate flags.
387 + /* Special case to sync the TSF when joining an existing IBSS.
388 + * This is only done if no AP interface is active.
389 + * Note that mac80211 always resets the TSF when creating a new
390 + * IBSS interface.
391 */
392 - if ((changed & BSS_CHANGED_IBSS) && !bss_conf->ibss_creator &&
393 - bss_conf->enable_beacon) {
394 + if (sc->sc_ah->opmode == NL80211_IFTYPE_ADHOC &&
395 + !enabled && beacons && !main_vif->bss_conf.ibss_creator) {
396 spin_lock_irqsave(&sc->sc_pm_lock, flags);
397 sc->ps_flags |= PS_BEACON_SYNC | PS_WAIT_FOR_BEACON;
398 spin_unlock_irqrestore(&sc->sc_pm_lock, flags);
399 skip_beacon = true;
400 - } else {
401 - ath9k_set_beacon(sc);
402 }
403
404 /*
405 * Do not set the ATH_OP_BEACONS flag for IBSS joiner mode
406 * here, it is done in ath9k_beacon_config_adhoc().
407 */
408 - if (cur_conf->enable_beacon && !skip_beacon)
409 + if (beacons && !skip_beacon) {
410 set_bit(ATH_OP_BEACONS, &common->op_flags);
411 - else
412 + ath9k_set_beacon(sc);
413 + } else {
414 clear_bit(ATH_OP_BEACONS, &common->op_flags);
415 + ath9k_beacon_stop(sc);
416 + }
417 + } else {
418 + clear_bit(ATH_OP_BEACONS, &common->op_flags);
419 + ath9k_beacon_stop(sc);
420 }
421 }
422
423 --- a/drivers/net/wireless/ath/ath9k/common.h
424 +++ b/drivers/net/wireless/ath/ath9k/common.h
425 @@ -50,6 +50,7 @@
426 #define IEEE80211_MS_TO_TU(x) (((x) * 1000) / 1024)
427
428 struct ath_beacon_config {
429 + struct ieee80211_vif *main_vif;
430 int beacon_interval;
431 u16 dtim_period;
432 u16 bmiss_timeout;
433 --- a/drivers/net/wireless/ath/ath9k/main.c
434 +++ b/drivers/net/wireless/ath/ath9k/main.c
435 @@ -910,6 +910,22 @@ static bool ath9k_uses_beacons(int type)
436 }
437 }
438
439 +static void ath9k_vif_iter_set_beacon(struct ath9k_vif_iter_data *iter_data,
440 + struct ieee80211_vif *vif)
441 +{
442 + /* Use the first (configured) interface, but prefering AP interfaces. */
443 + if (!iter_data->primary_beacon_vif) {
444 + iter_data->primary_beacon_vif = vif;
445 + } else {
446 + if (iter_data->primary_beacon_vif->type != NL80211_IFTYPE_AP &&
447 + vif->type == NL80211_IFTYPE_AP)
448 + iter_data->primary_beacon_vif = vif;
449 + }
450 +
451 + iter_data->beacons = true;
452 + iter_data->nbcnvifs += 1;
453 +}
454 +
455 static void ath9k_vif_iter(struct ath9k_vif_iter_data *iter_data,
456 u8 *mac, struct ieee80211_vif *vif)
457 {
458 @@ -931,6 +947,8 @@ static void ath9k_vif_iter(struct ath9k_
459 switch (vif->type) {
460 case NL80211_IFTYPE_AP:
461 iter_data->naps++;
462 + if (vif->bss_conf.enable_beacon)
463 + ath9k_vif_iter_set_beacon(iter_data, vif);
464 break;
465 case NL80211_IFTYPE_STATION:
466 iter_data->nstations++;
467 @@ -943,12 +961,12 @@ static void ath9k_vif_iter(struct ath9k_
468 case NL80211_IFTYPE_ADHOC:
469 iter_data->nadhocs++;
470 if (vif->bss_conf.enable_beacon)
471 - iter_data->beacons = true;
472 + ath9k_vif_iter_set_beacon(iter_data, vif);
473 break;
474 case NL80211_IFTYPE_MESH_POINT:
475 iter_data->nmeshes++;
476 if (vif->bss_conf.enable_beacon)
477 - iter_data->beacons = true;
478 + ath9k_vif_iter_set_beacon(iter_data, vif);
479 break;
480 case NL80211_IFTYPE_WDS:
481 iter_data->nwds++;
482 @@ -1081,7 +1099,6 @@ void ath9k_calculate_summary_state(struc
483 struct ath_hw *ah = sc->sc_ah;
484 struct ath_common *common = ath9k_hw_common(ah);
485 struct ath9k_vif_iter_data iter_data;
486 - struct ath_beacon_config *cur_conf;
487
488 ath_chanctx_check_active(sc, ctx);
489
490 @@ -1103,13 +1120,12 @@ void ath9k_calculate_summary_state(struc
491 ath_hw_setbssidmask(common);
492
493 if (iter_data.naps > 0) {
494 - cur_conf = &ctx->beacon;
495 ath9k_hw_set_tsfadjust(ah, true);
496 ah->opmode = NL80211_IFTYPE_AP;
497 - if (cur_conf->enable_beacon)
498 - iter_data.beacons = true;
499 } else {
500 ath9k_hw_set_tsfadjust(ah, false);
501 + if (iter_data.beacons)
502 + ath9k_beacon_ensure_primary_slot(sc);
503
504 if (iter_data.nmeshes)
505 ah->opmode = NL80211_IFTYPE_MESH_POINT;
506 @@ -1134,7 +1150,6 @@ void ath9k_calculate_summary_state(struc
507 ctx->switch_after_beacon = true;
508 }
509
510 - ah->imask &= ~ATH9K_INT_SWBA;
511 if (ah->opmode == NL80211_IFTYPE_STATION) {
512 bool changed = (iter_data.primary_sta != ctx->primary_sta);
513
514 @@ -1151,16 +1166,12 @@ void ath9k_calculate_summary_state(struc
515 if (ath9k_hw_mci_is_enabled(sc->sc_ah))
516 ath9k_mci_update_wlan_channels(sc, true);
517 }
518 - } else if (iter_data.beacons) {
519 - ah->imask |= ATH9K_INT_SWBA;
520 }
521 + sc->nbcnvifs = iter_data.nbcnvifs;
522 + ath9k_beacon_config(sc, iter_data.primary_beacon_vif,
523 + iter_data.beacons);
524 ath9k_hw_set_interrupts(ah);
525
526 - if (iter_data.beacons)
527 - set_bit(ATH_OP_BEACONS, &common->op_flags);
528 - else
529 - clear_bit(ATH_OP_BEACONS, &common->op_flags);
530 -
531 if (ah->slottime != iter_data.slottime) {
532 ah->slottime = iter_data.slottime;
533 ath9k_hw_init_global_settings(ah);
534 @@ -1777,9 +1788,7 @@ static void ath9k_bss_info_changed(struc
535 if ((changed & BSS_CHANGED_BEACON_ENABLED) ||
536 (changed & BSS_CHANGED_BEACON_INT) ||
537 (changed & BSS_CHANGED_BEACON_INFO)) {
538 - ath9k_beacon_config(sc, vif, changed);
539 - if (changed & BSS_CHANGED_BEACON_ENABLED)
540 - ath9k_calculate_summary_state(sc, avp->chanctx);
541 + ath9k_calculate_summary_state(sc, avp->chanctx);
542 }
543
544 if ((avp->chanctx == sc->cur_chan) &&