aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJohannes Berg <johannes.berg@intel.com>2010-06-10 04:21:41 -0400
committerJohn W. Linville <linville@tuxdriver.com>2010-06-14 15:39:27 -0400
commita6a67db2bc89d2b1ff07e0817f11235c20d2c329 (patch)
tree1096c42ede83af4dbae34387246f91c0d6649baf
parent5d22c89b9bea17a0e48e7534a9b237885e2c0809 (diff)
mac80211: refcount aggregation queue stop
mac80211 currently maintains the ampdu_lock to avoid starting a queue due to one aggregation session while another aggregation session needs the queue stopped. We can do better, however, and instead refcount the queue stops for this particular purpose, thus removing the need for the lock. This will help making ampdu_action able to sleep. Signed-off-by: Johannes Berg <johannes.berg@intel.com> Signed-off-by: John W. Linville <linville@tuxdriver.com>
-rw-r--r--net/mac80211/agg-tx.c77
-rw-r--r--net/mac80211/ieee80211_i.h8
-rw-r--r--net/mac80211/main.c6
3 files changed, 51 insertions, 40 deletions
diff --git a/net/mac80211/agg-tx.c b/net/mac80211/agg-tx.c
index 5a7ef51e3020..7f2042d37904 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 */
234static void __acquires(agg_queue)
235ieee80211_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
246static void __releases(agg_queue)
247ieee80211_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
223int ieee80211_start_tx_ba_session(struct ieee80211_sta *pubsta, u16 tid) 258int 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 */
376static void ieee80211_agg_splice_packets(struct ieee80211_local *local, 400static void __acquires(agg_queue)
377 struct tid_ampdu_tx *tid_tx, 401ieee80211_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
400static void ieee80211_agg_splice_finish(struct ieee80211_local *local, u16 tid) 422static void __releases(agg_queue)
423ieee80211_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 a3ae51308039..8a91b5d83870 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 a2d10d44641e..c2e46e88f3c9 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}
482EXPORT_SYMBOL(ieee80211_alloc_hw); 482EXPORT_SYMBOL(ieee80211_alloc_hw);