diff options
-rw-r--r-- | net/mac80211/agg-tx.c | 77 | ||||
-rw-r--r-- | net/mac80211/ieee80211_i.h | 8 | ||||
-rw-r--r-- | net/mac80211/main.c | 6 |
3 files changed, 51 insertions, 40 deletions
diff --git a/net/mac80211/agg-tx.c b/net/mac80211/agg-tx.c index 5a7ef51e302..7f2042d3790 100644 --- a/net/mac80211/agg-tx.c +++ b/net/mac80211/agg-tx.c | |||
@@ -220,6 +220,41 @@ static inline int ieee80211_ac_from_tid(int tid) | |||
220 | return ieee802_1d_to_ac[tid & 7]; | 220 | return ieee802_1d_to_ac[tid & 7]; |
221 | } | 221 | } |
222 | 222 | ||
223 | /* | ||
224 | * When multiple aggregation sessions on multiple stations | ||
225 | * are being created/destroyed simultaneously, we need to | ||
226 | * refcount the global queue stop caused by that in order | ||
227 | * to not get into a situation where one of the aggregation | ||
228 | * setup or teardown re-enables queues before the other is | ||
229 | * ready to handle that. | ||
230 | * | ||
231 | * These two functions take care of this issue by keeping | ||
232 | * a global "agg_queue_stop" refcount. | ||
233 | */ | ||
234 | static void __acquires(agg_queue) | ||
235 | ieee80211_stop_queue_agg(struct ieee80211_local *local, int tid) | ||
236 | { | ||
237 | int queue = ieee80211_ac_from_tid(tid); | ||
238 | |||
239 | if (atomic_inc_return(&local->agg_queue_stop[queue]) == 1) | ||
240 | ieee80211_stop_queue_by_reason( | ||
241 | &local->hw, queue, | ||
242 | IEEE80211_QUEUE_STOP_REASON_AGGREGATION); | ||
243 | __acquire(agg_queue); | ||
244 | } | ||
245 | |||
246 | static void __releases(agg_queue) | ||
247 | ieee80211_wake_queue_agg(struct ieee80211_local *local, int tid) | ||
248 | { | ||
249 | int queue = ieee80211_ac_from_tid(tid); | ||
250 | |||
251 | if (atomic_dec_return(&local->agg_queue_stop[queue]) == 0) | ||
252 | ieee80211_wake_queue_by_reason( | ||
253 | &local->hw, queue, | ||
254 | IEEE80211_QUEUE_STOP_REASON_AGGREGATION); | ||
255 | __release(agg_queue); | ||
256 | } | ||
257 | |||
223 | int ieee80211_start_tx_ba_session(struct ieee80211_sta *pubsta, u16 tid) | 258 | int ieee80211_start_tx_ba_session(struct ieee80211_sta *pubsta, u16 tid) |
224 | { | 259 | { |
225 | struct sta_info *sta = container_of(pubsta, struct sta_info, sta); | 260 | struct sta_info *sta = container_of(pubsta, struct sta_info, sta); |
@@ -263,7 +298,6 @@ int ieee80211_start_tx_ba_session(struct ieee80211_sta *pubsta, u16 tid) | |||
263 | } | 298 | } |
264 | 299 | ||
265 | spin_lock_bh(&sta->lock); | 300 | spin_lock_bh(&sta->lock); |
266 | spin_lock(&local->ampdu_lock); | ||
267 | 301 | ||
268 | /* we have tried too many times, receiver does not want A-MPDU */ | 302 | /* we have tried too many times, receiver does not want A-MPDU */ |
269 | if (sta->ampdu_mlme.addba_req_num[tid] > HT_AGG_MAX_RETRIES) { | 303 | if (sta->ampdu_mlme.addba_req_num[tid] > HT_AGG_MAX_RETRIES) { |
@@ -289,9 +323,7 @@ int ieee80211_start_tx_ba_session(struct ieee80211_sta *pubsta, u16 tid) | |||
289 | * which would require us to put them to the AC pending | 323 | * which would require us to put them to the AC pending |
290 | * afterwards which just makes the code more complex. | 324 | * afterwards which just makes the code more complex. |
291 | */ | 325 | */ |
292 | ieee80211_stop_queue_by_reason( | 326 | ieee80211_stop_queue_agg(local, tid); |
293 | &local->hw, ieee80211_ac_from_tid(tid), | ||
294 | IEEE80211_QUEUE_STOP_REASON_AGGREGATION); | ||
295 | 327 | ||
296 | /* prepare A-MPDU MLME for Tx aggregation */ | 328 | /* prepare A-MPDU MLME for Tx aggregation */ |
297 | tid_tx = kzalloc(sizeof(struct tid_ampdu_tx), GFP_ATOMIC); | 329 | tid_tx = kzalloc(sizeof(struct tid_ampdu_tx), GFP_ATOMIC); |
@@ -327,11 +359,7 @@ int ieee80211_start_tx_ba_session(struct ieee80211_sta *pubsta, u16 tid) | |||
327 | rcu_assign_pointer(sta->ampdu_mlme.tid_tx[tid], tid_tx); | 359 | rcu_assign_pointer(sta->ampdu_mlme.tid_tx[tid], tid_tx); |
328 | 360 | ||
329 | /* Driver vetoed or OKed, but we can take packets again now */ | 361 | /* Driver vetoed or OKed, but we can take packets again now */ |
330 | ieee80211_wake_queue_by_reason( | 362 | ieee80211_wake_queue_agg(local, tid); |
331 | &local->hw, ieee80211_ac_from_tid(tid), | ||
332 | IEEE80211_QUEUE_STOP_REASON_AGGREGATION); | ||
333 | |||
334 | spin_unlock(&local->ampdu_lock); | ||
335 | 363 | ||
336 | /* activate the timer for the recipient's addBA response */ | 364 | /* activate the timer for the recipient's addBA response */ |
337 | tid_tx->addba_resp_timer.expires = jiffies + ADDBA_RESP_INTERVAL; | 365 | tid_tx->addba_resp_timer.expires = jiffies + ADDBA_RESP_INTERVAL; |
@@ -358,11 +386,8 @@ int ieee80211_start_tx_ba_session(struct ieee80211_sta *pubsta, u16 tid) | |||
358 | err_free: | 386 | err_free: |
359 | kfree(tid_tx); | 387 | kfree(tid_tx); |
360 | err_wake_queue: | 388 | err_wake_queue: |
361 | ieee80211_wake_queue_by_reason( | 389 | ieee80211_wake_queue_agg(local, tid); |
362 | &local->hw, ieee80211_ac_from_tid(tid), | ||
363 | IEEE80211_QUEUE_STOP_REASON_AGGREGATION); | ||
364 | err_unlock_sta: | 390 | err_unlock_sta: |
365 | spin_unlock(&local->ampdu_lock); | ||
366 | spin_unlock_bh(&sta->lock); | 391 | spin_unlock_bh(&sta->lock); |
367 | return ret; | 392 | return ret; |
368 | } | 393 | } |
@@ -370,19 +395,16 @@ EXPORT_SYMBOL(ieee80211_start_tx_ba_session); | |||
370 | 395 | ||
371 | /* | 396 | /* |
372 | * splice packets from the STA's pending to the local pending, | 397 | * splice packets from the STA's pending to the local pending, |
373 | * requires a call to ieee80211_agg_splice_finish and holding | 398 | * requires a call to ieee80211_agg_splice_finish later |
374 | * local->ampdu_lock across both calls. | ||
375 | */ | 399 | */ |
376 | static void ieee80211_agg_splice_packets(struct ieee80211_local *local, | 400 | static void __acquires(agg_queue) |
377 | struct tid_ampdu_tx *tid_tx, | 401 | ieee80211_agg_splice_packets(struct ieee80211_local *local, |
378 | u16 tid) | 402 | struct tid_ampdu_tx *tid_tx, u16 tid) |
379 | { | 403 | { |
404 | int queue = ieee80211_ac_from_tid(tid); | ||
380 | unsigned long flags; | 405 | unsigned long flags; |
381 | u16 queue = ieee80211_ac_from_tid(tid); | ||
382 | 406 | ||
383 | ieee80211_stop_queue_by_reason( | 407 | ieee80211_stop_queue_agg(local, tid); |
384 | &local->hw, queue, | ||
385 | IEEE80211_QUEUE_STOP_REASON_AGGREGATION); | ||
386 | 408 | ||
387 | if (WARN(!tid_tx, "TID %d gone but expected when splicing aggregates" | 409 | if (WARN(!tid_tx, "TID %d gone but expected when splicing aggregates" |
388 | " from the pending queue\n", tid)) | 410 | " from the pending queue\n", tid)) |
@@ -397,11 +419,10 @@ static void ieee80211_agg_splice_packets(struct ieee80211_local *local, | |||
397 | } | 419 | } |
398 | } | 420 | } |
399 | 421 | ||
400 | static void ieee80211_agg_splice_finish(struct ieee80211_local *local, u16 tid) | 422 | static void __releases(agg_queue) |
423 | ieee80211_agg_splice_finish(struct ieee80211_local *local, u16 tid) | ||
401 | { | 424 | { |
402 | ieee80211_wake_queue_by_reason( | 425 | ieee80211_wake_queue_agg(local, tid); |
403 | &local->hw, ieee80211_ac_from_tid(tid), | ||
404 | IEEE80211_QUEUE_STOP_REASON_AGGREGATION); | ||
405 | } | 426 | } |
406 | 427 | ||
407 | /* caller must hold sta->lock */ | 428 | /* caller must hold sta->lock */ |
@@ -414,7 +435,6 @@ static void ieee80211_agg_tx_operational(struct ieee80211_local *local, | |||
414 | printk(KERN_DEBUG "Aggregation is on for tid %d\n", tid); | 435 | printk(KERN_DEBUG "Aggregation is on for tid %d\n", tid); |
415 | #endif | 436 | #endif |
416 | 437 | ||
417 | spin_lock(&local->ampdu_lock); | ||
418 | ieee80211_agg_splice_packets(local, sta->ampdu_mlme.tid_tx[tid], tid); | 438 | ieee80211_agg_splice_packets(local, sta->ampdu_mlme.tid_tx[tid], tid); |
419 | /* | 439 | /* |
420 | * Now mark as operational. This will be visible | 440 | * Now mark as operational. This will be visible |
@@ -423,7 +443,6 @@ static void ieee80211_agg_tx_operational(struct ieee80211_local *local, | |||
423 | */ | 443 | */ |
424 | set_bit(HT_AGG_STATE_OPERATIONAL, &sta->ampdu_mlme.tid_tx[tid]->state); | 444 | set_bit(HT_AGG_STATE_OPERATIONAL, &sta->ampdu_mlme.tid_tx[tid]->state); |
425 | ieee80211_agg_splice_finish(local, tid); | 445 | ieee80211_agg_splice_finish(local, tid); |
426 | spin_unlock(&local->ampdu_lock); | ||
427 | 446 | ||
428 | drv_ampdu_action(local, sta->sdata, | 447 | drv_ampdu_action(local, sta->sdata, |
429 | IEEE80211_AMPDU_TX_OPERATIONAL, | 448 | IEEE80211_AMPDU_TX_OPERATIONAL, |
@@ -604,7 +623,6 @@ void ieee80211_stop_tx_ba_cb(struct ieee80211_vif *vif, u8 *ra, u8 tid) | |||
604 | * more. | 623 | * more. |
605 | */ | 624 | */ |
606 | 625 | ||
607 | spin_lock(&local->ampdu_lock); | ||
608 | ieee80211_agg_splice_packets(local, tid_tx, tid); | 626 | ieee80211_agg_splice_packets(local, tid_tx, tid); |
609 | 627 | ||
610 | /* future packets must not find the tid_tx struct any more */ | 628 | /* future packets must not find the tid_tx struct any more */ |
@@ -613,7 +631,6 @@ void ieee80211_stop_tx_ba_cb(struct ieee80211_vif *vif, u8 *ra, u8 tid) | |||
613 | ieee80211_agg_splice_finish(local, tid); | 631 | ieee80211_agg_splice_finish(local, tid); |
614 | 632 | ||
615 | call_rcu(&tid_tx->rcu_head, kfree_tid_tx); | 633 | call_rcu(&tid_tx->rcu_head, kfree_tid_tx); |
616 | spin_unlock(&local->ampdu_lock); | ||
617 | 634 | ||
618 | spin_unlock_bh(&sta->lock); | 635 | spin_unlock_bh(&sta->lock); |
619 | rcu_read_unlock(); | 636 | rcu_read_unlock(); |
diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index a3ae5130803..8a91b5d8387 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h | |||
@@ -723,13 +723,7 @@ struct ieee80211_local { | |||
723 | struct sk_buff_head pending[IEEE80211_MAX_QUEUES]; | 723 | struct sk_buff_head pending[IEEE80211_MAX_QUEUES]; |
724 | struct tasklet_struct tx_pending_tasklet; | 724 | struct tasklet_struct tx_pending_tasklet; |
725 | 725 | ||
726 | /* | 726 | atomic_t agg_queue_stop[IEEE80211_MAX_QUEUES]; |
727 | * This lock is used to prevent concurrent A-MPDU | ||
728 | * session start/stop processing, this thus also | ||
729 | * synchronises the ->ampdu_action() callback to | ||
730 | * drivers and limits it to one at a time. | ||
731 | */ | ||
732 | spinlock_t ampdu_lock; | ||
733 | 727 | ||
734 | /* number of interfaces with corresponding IFF_ flags */ | 728 | /* number of interfaces with corresponding IFF_ flags */ |
735 | atomic_t iff_allmultis, iff_promiscs; | 729 | atomic_t iff_allmultis, iff_promiscs; |
diff --git a/net/mac80211/main.c b/net/mac80211/main.c index a2d10d44641..c2e46e88f3c 100644 --- a/net/mac80211/main.c +++ b/net/mac80211/main.c | |||
@@ -463,8 +463,10 @@ struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len, | |||
463 | 463 | ||
464 | sta_info_init(local); | 464 | sta_info_init(local); |
465 | 465 | ||
466 | for (i = 0; i < IEEE80211_MAX_QUEUES; i++) | 466 | for (i = 0; i < IEEE80211_MAX_QUEUES; i++) { |
467 | skb_queue_head_init(&local->pending[i]); | 467 | skb_queue_head_init(&local->pending[i]); |
468 | atomic_set(&local->agg_queue_stop[i], 0); | ||
469 | } | ||
468 | tasklet_init(&local->tx_pending_tasklet, ieee80211_tx_pending, | 470 | tasklet_init(&local->tx_pending_tasklet, ieee80211_tx_pending, |
469 | (unsigned long)local); | 471 | (unsigned long)local); |
470 | 472 | ||
@@ -475,8 +477,6 @@ struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len, | |||
475 | skb_queue_head_init(&local->skb_queue); | 477 | skb_queue_head_init(&local->skb_queue); |
476 | skb_queue_head_init(&local->skb_queue_unreliable); | 478 | skb_queue_head_init(&local->skb_queue_unreliable); |
477 | 479 | ||
478 | spin_lock_init(&local->ampdu_lock); | ||
479 | |||
480 | return local_to_hw(local); | 480 | return local_to_hw(local); |
481 | } | 481 | } |
482 | EXPORT_SYMBOL(ieee80211_alloc_hw); | 482 | EXPORT_SYMBOL(ieee80211_alloc_hw); |