diff options
Diffstat (limited to 'net/mac80211/agg-tx.c')
-rw-r--r-- | net/mac80211/agg-tx.c | 136 |
1 files changed, 84 insertions, 52 deletions
diff --git a/net/mac80211/agg-tx.c b/net/mac80211/agg-tx.c index fd718e2b29f7..64b839bfbf17 100644 --- a/net/mac80211/agg-tx.c +++ b/net/mac80211/agg-tx.c | |||
@@ -132,16 +132,6 @@ static int ___ieee80211_stop_tx_ba_session(struct sta_info *sta, u16 tid, | |||
132 | state = &sta->ampdu_mlme.tid_state_tx[tid]; | 132 | state = &sta->ampdu_mlme.tid_state_tx[tid]; |
133 | 133 | ||
134 | if (local->hw.ampdu_queues) { | 134 | if (local->hw.ampdu_queues) { |
135 | if (initiator) { | ||
136 | /* | ||
137 | * Stop the AC queue to avoid issues where we send | ||
138 | * unaggregated frames already before the delba. | ||
139 | */ | ||
140 | ieee80211_stop_queue_by_reason(&local->hw, | ||
141 | local->hw.queues + sta->tid_to_tx_q[tid], | ||
142 | IEEE80211_QUEUE_STOP_REASON_AGGREGATION); | ||
143 | } | ||
144 | |||
145 | /* | 135 | /* |
146 | * Pretend the driver woke the queue, just in case | 136 | * Pretend the driver woke the queue, just in case |
147 | * it disabled it before the session was stopped. | 137 | * it disabled it before the session was stopped. |
@@ -158,6 +148,10 @@ static int ___ieee80211_stop_tx_ba_session(struct sta_info *sta, u16 tid, | |||
158 | /* HW shall not deny going back to legacy */ | 148 | /* HW shall not deny going back to legacy */ |
159 | if (WARN_ON(ret)) { | 149 | if (WARN_ON(ret)) { |
160 | *state = HT_AGG_STATE_OPERATIONAL; | 150 | *state = HT_AGG_STATE_OPERATIONAL; |
151 | /* | ||
152 | * We may have pending packets get stuck in this case... | ||
153 | * Not bothering with a workaround for now. | ||
154 | */ | ||
161 | } | 155 | } |
162 | 156 | ||
163 | return ret; | 157 | return ret; |
@@ -226,13 +220,6 @@ int ieee80211_start_tx_ba_session(struct ieee80211_hw *hw, u8 *ra, u16 tid) | |||
226 | ra, tid); | 220 | ra, tid); |
227 | #endif /* CONFIG_MAC80211_HT_DEBUG */ | 221 | #endif /* CONFIG_MAC80211_HT_DEBUG */ |
228 | 222 | ||
229 | if (hw->ampdu_queues && ieee80211_ac_from_tid(tid) == 0) { | ||
230 | #ifdef CONFIG_MAC80211_HT_DEBUG | ||
231 | printk(KERN_DEBUG "rejecting on voice AC\n"); | ||
232 | #endif | ||
233 | return -EINVAL; | ||
234 | } | ||
235 | |||
236 | rcu_read_lock(); | 223 | rcu_read_lock(); |
237 | 224 | ||
238 | sta = sta_info_get(local, ra); | 225 | sta = sta_info_get(local, ra); |
@@ -267,6 +254,7 @@ int ieee80211_start_tx_ba_session(struct ieee80211_hw *hw, u8 *ra, u16 tid) | |||
267 | } | 254 | } |
268 | 255 | ||
269 | spin_lock_bh(&sta->lock); | 256 | spin_lock_bh(&sta->lock); |
257 | spin_lock(&local->ampdu_lock); | ||
270 | 258 | ||
271 | sdata = sta->sdata; | 259 | sdata = sta->sdata; |
272 | 260 | ||
@@ -308,21 +296,19 @@ int ieee80211_start_tx_ba_session(struct ieee80211_hw *hw, u8 *ra, u16 tid) | |||
308 | ret = -ENOSPC; | 296 | ret = -ENOSPC; |
309 | goto err_unlock_sta; | 297 | goto err_unlock_sta; |
310 | } | 298 | } |
311 | |||
312 | /* | ||
313 | * If we successfully allocate the session, we can't have | ||
314 | * anything going on on the queue this TID maps into, so | ||
315 | * stop it for now. This is a "virtual" stop using the same | ||
316 | * mechanism that drivers will use. | ||
317 | * | ||
318 | * XXX: queue up frames for this session in the sta_info | ||
319 | * struct instead to avoid hitting all other STAs. | ||
320 | */ | ||
321 | ieee80211_stop_queue_by_reason( | ||
322 | &local->hw, hw->queues + qn, | ||
323 | IEEE80211_QUEUE_STOP_REASON_AGGREGATION); | ||
324 | } | 299 | } |
325 | 300 | ||
301 | /* | ||
302 | * While we're asking the driver about the aggregation, | ||
303 | * stop the AC queue so that we don't have to worry | ||
304 | * about frames that came in while we were doing that, | ||
305 | * which would require us to put them to the AC pending | ||
306 | * afterwards which just makes the code more complex. | ||
307 | */ | ||
308 | ieee80211_stop_queue_by_reason( | ||
309 | &local->hw, ieee80211_ac_from_tid(tid), | ||
310 | IEEE80211_QUEUE_STOP_REASON_AGGREGATION); | ||
311 | |||
326 | /* prepare A-MPDU MLME for Tx aggregation */ | 312 | /* prepare A-MPDU MLME for Tx aggregation */ |
327 | sta->ampdu_mlme.tid_tx[tid] = | 313 | sta->ampdu_mlme.tid_tx[tid] = |
328 | kmalloc(sizeof(struct tid_ampdu_tx), GFP_ATOMIC); | 314 | kmalloc(sizeof(struct tid_ampdu_tx), GFP_ATOMIC); |
@@ -336,6 +322,8 @@ int ieee80211_start_tx_ba_session(struct ieee80211_hw *hw, u8 *ra, u16 tid) | |||
336 | goto err_return_queue; | 322 | goto err_return_queue; |
337 | } | 323 | } |
338 | 324 | ||
325 | skb_queue_head_init(&sta->ampdu_mlme.tid_tx[tid]->pending); | ||
326 | |||
339 | /* Tx timer */ | 327 | /* Tx timer */ |
340 | sta->ampdu_mlme.tid_tx[tid]->addba_resp_timer.function = | 328 | sta->ampdu_mlme.tid_tx[tid]->addba_resp_timer.function = |
341 | sta_addba_resp_timer_expired; | 329 | sta_addba_resp_timer_expired; |
@@ -362,6 +350,12 @@ int ieee80211_start_tx_ba_session(struct ieee80211_hw *hw, u8 *ra, u16 tid) | |||
362 | } | 350 | } |
363 | sta->tid_to_tx_q[tid] = qn; | 351 | sta->tid_to_tx_q[tid] = qn; |
364 | 352 | ||
353 | /* Driver vetoed or OKed, but we can take packets again now */ | ||
354 | ieee80211_wake_queue_by_reason( | ||
355 | &local->hw, ieee80211_ac_from_tid(tid), | ||
356 | IEEE80211_QUEUE_STOP_REASON_AGGREGATION); | ||
357 | |||
358 | spin_unlock(&local->ampdu_lock); | ||
365 | spin_unlock_bh(&sta->lock); | 359 | spin_unlock_bh(&sta->lock); |
366 | 360 | ||
367 | /* send an addBA request */ | 361 | /* send an addBA request */ |
@@ -388,15 +382,16 @@ int ieee80211_start_tx_ba_session(struct ieee80211_hw *hw, u8 *ra, u16 tid) | |||
388 | sta->ampdu_mlme.tid_tx[tid] = NULL; | 382 | sta->ampdu_mlme.tid_tx[tid] = NULL; |
389 | err_return_queue: | 383 | err_return_queue: |
390 | if (qn >= 0) { | 384 | if (qn >= 0) { |
391 | /* We failed, so start queue again right away. */ | ||
392 | ieee80211_wake_queue_by_reason(hw, hw->queues + qn, | ||
393 | IEEE80211_QUEUE_STOP_REASON_AGGREGATION); | ||
394 | /* give queue back to pool */ | 385 | /* give queue back to pool */ |
395 | spin_lock(&local->queue_stop_reason_lock); | 386 | spin_lock(&local->queue_stop_reason_lock); |
396 | local->ampdu_ac_queue[qn] = -1; | 387 | local->ampdu_ac_queue[qn] = -1; |
397 | spin_unlock(&local->queue_stop_reason_lock); | 388 | spin_unlock(&local->queue_stop_reason_lock); |
398 | } | 389 | } |
390 | ieee80211_wake_queue_by_reason( | ||
391 | &local->hw, ieee80211_ac_from_tid(tid), | ||
392 | IEEE80211_QUEUE_STOP_REASON_AGGREGATION); | ||
399 | err_unlock_sta: | 393 | err_unlock_sta: |
394 | spin_unlock(&local->ampdu_lock); | ||
400 | spin_unlock_bh(&sta->lock); | 395 | spin_unlock_bh(&sta->lock); |
401 | unlock: | 396 | unlock: |
402 | rcu_read_unlock(); | 397 | rcu_read_unlock(); |
@@ -404,6 +399,45 @@ int ieee80211_start_tx_ba_session(struct ieee80211_hw *hw, u8 *ra, u16 tid) | |||
404 | } | 399 | } |
405 | EXPORT_SYMBOL(ieee80211_start_tx_ba_session); | 400 | EXPORT_SYMBOL(ieee80211_start_tx_ba_session); |
406 | 401 | ||
402 | /* | ||
403 | * splice packets from the STA's pending to the local pending, | ||
404 | * requires a call to ieee80211_agg_splice_finish and holding | ||
405 | * local->ampdu_lock across both calls. | ||
406 | */ | ||
407 | static void ieee80211_agg_splice_packets(struct ieee80211_local *local, | ||
408 | struct sta_info *sta, u16 tid) | ||
409 | { | ||
410 | unsigned long flags; | ||
411 | u16 queue = ieee80211_ac_from_tid(tid); | ||
412 | |||
413 | ieee80211_stop_queue_by_reason( | ||
414 | &local->hw, queue, | ||
415 | IEEE80211_QUEUE_STOP_REASON_AGGREGATION); | ||
416 | |||
417 | if (!skb_queue_empty(&sta->ampdu_mlme.tid_tx[tid]->pending)) { | ||
418 | spin_lock_irqsave(&local->queue_stop_reason_lock, flags); | ||
419 | /* mark queue as pending, it is stopped already */ | ||
420 | __set_bit(IEEE80211_QUEUE_STOP_REASON_PENDING, | ||
421 | &local->queue_stop_reasons[queue]); | ||
422 | /* copy over remaining packets */ | ||
423 | skb_queue_splice_tail_init( | ||
424 | &sta->ampdu_mlme.tid_tx[tid]->pending, | ||
425 | &local->pending[queue]); | ||
426 | spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags); | ||
427 | } | ||
428 | } | ||
429 | |||
430 | static void ieee80211_agg_splice_finish(struct ieee80211_local *local, | ||
431 | struct sta_info *sta, u16 tid) | ||
432 | { | ||
433 | u16 queue = ieee80211_ac_from_tid(tid); | ||
434 | |||
435 | ieee80211_wake_queue_by_reason( | ||
436 | &local->hw, queue, | ||
437 | IEEE80211_QUEUE_STOP_REASON_AGGREGATION); | ||
438 | } | ||
439 | |||
440 | /* caller must hold sta->lock */ | ||
407 | static void ieee80211_agg_tx_operational(struct ieee80211_local *local, | 441 | static void ieee80211_agg_tx_operational(struct ieee80211_local *local, |
408 | struct sta_info *sta, u16 tid) | 442 | struct sta_info *sta, u16 tid) |
409 | { | 443 | { |
@@ -411,15 +445,16 @@ static void ieee80211_agg_tx_operational(struct ieee80211_local *local, | |||
411 | printk(KERN_DEBUG "Aggregation is on for tid %d \n", tid); | 445 | printk(KERN_DEBUG "Aggregation is on for tid %d \n", tid); |
412 | #endif | 446 | #endif |
413 | 447 | ||
414 | if (local->hw.ampdu_queues) { | 448 | spin_lock(&local->ampdu_lock); |
415 | /* | 449 | ieee80211_agg_splice_packets(local, sta, tid); |
416 | * Wake up the A-MPDU queue, we stopped it earlier, | 450 | /* |
417 | * this will in turn wake the entire AC. | 451 | * NB: we rely on sta->lock being taken in the TX |
418 | */ | 452 | * processing here when adding to the pending queue, |
419 | ieee80211_wake_queue_by_reason(&local->hw, | 453 | * otherwise we could only change the state of the |
420 | local->hw.queues + sta->tid_to_tx_q[tid], | 454 | * session to OPERATIONAL _here_. |
421 | IEEE80211_QUEUE_STOP_REASON_AGGREGATION); | 455 | */ |
422 | } | 456 | ieee80211_agg_splice_finish(local, sta, tid); |
457 | spin_unlock(&local->ampdu_lock); | ||
423 | 458 | ||
424 | local->ops->ampdu_action(&local->hw, IEEE80211_AMPDU_TX_OPERATIONAL, | 459 | local->ops->ampdu_action(&local->hw, IEEE80211_AMPDU_TX_OPERATIONAL, |
425 | &sta->sta, tid, NULL); | 460 | &sta->sta, tid, NULL); |
@@ -602,22 +637,19 @@ void ieee80211_stop_tx_ba_cb(struct ieee80211_hw *hw, u8 *ra, u8 tid) | |||
602 | WLAN_BACK_INITIATOR, WLAN_REASON_QSTA_NOT_USE); | 637 | WLAN_BACK_INITIATOR, WLAN_REASON_QSTA_NOT_USE); |
603 | 638 | ||
604 | spin_lock_bh(&sta->lock); | 639 | spin_lock_bh(&sta->lock); |
640 | spin_lock(&local->ampdu_lock); | ||
605 | 641 | ||
606 | if (*state & HT_AGG_STATE_INITIATOR_MSK && | 642 | ieee80211_agg_splice_packets(local, sta, tid); |
607 | hw->ampdu_queues) { | ||
608 | /* | ||
609 | * Wake up this queue, we stopped it earlier, | ||
610 | * this will in turn wake the entire AC. | ||
611 | */ | ||
612 | ieee80211_wake_queue_by_reason(hw, | ||
613 | hw->queues + sta->tid_to_tx_q[tid], | ||
614 | IEEE80211_QUEUE_STOP_REASON_AGGREGATION); | ||
615 | } | ||
616 | 643 | ||
617 | *state = HT_AGG_STATE_IDLE; | 644 | *state = HT_AGG_STATE_IDLE; |
645 | /* from now on packets are no longer put onto sta->pending */ | ||
618 | sta->ampdu_mlme.addba_req_num[tid] = 0; | 646 | sta->ampdu_mlme.addba_req_num[tid] = 0; |
619 | kfree(sta->ampdu_mlme.tid_tx[tid]); | 647 | kfree(sta->ampdu_mlme.tid_tx[tid]); |
620 | sta->ampdu_mlme.tid_tx[tid] = NULL; | 648 | sta->ampdu_mlme.tid_tx[tid] = NULL; |
649 | |||
650 | ieee80211_agg_splice_finish(local, sta, tid); | ||
651 | |||
652 | spin_unlock(&local->ampdu_lock); | ||
621 | spin_unlock_bh(&sta->lock); | 653 | spin_unlock_bh(&sta->lock); |
622 | 654 | ||
623 | rcu_read_unlock(); | 655 | rcu_read_unlock(); |