diff options
author | David S. Miller <davem@davemloft.net> | 2009-03-27 20:35:07 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2009-03-27 20:35:07 -0400 |
commit | 0870352bc6e0dee485c86a0c99dd60e7089c8917 (patch) | |
tree | 0c6259b663350594bff4f128e12f8bd6d4c769c1 /net/mac80211/agg-tx.c | |
parent | c44a4366649aca4f5b4a51ff71d4c9cde3b7c9da (diff) | |
parent | 8a5117d80fe93de5df5b56480054f7df1fd20755 (diff) |
Merge branch 'master' of git://git.kernel.org/pub/scm/linux/kernel/git/linville/wireless-2.6
Diffstat (limited to 'net/mac80211/agg-tx.c')
-rw-r--r-- | net/mac80211/agg-tx.c | 232 |
1 files changed, 113 insertions, 119 deletions
diff --git a/net/mac80211/agg-tx.c b/net/mac80211/agg-tx.c index 1df116d4d6e7..947aaaad35d2 100644 --- a/net/mac80211/agg-tx.c +++ b/net/mac80211/agg-tx.c | |||
@@ -131,24 +131,6 @@ static int ___ieee80211_stop_tx_ba_session(struct sta_info *sta, u16 tid, | |||
131 | 131 | ||
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) { | ||
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 | /* | ||
146 | * Pretend the driver woke the queue, just in case | ||
147 | * it disabled it before the session was stopped. | ||
148 | */ | ||
149 | ieee80211_wake_queue( | ||
150 | &local->hw, local->hw.queues + sta->tid_to_tx_q[tid]); | ||
151 | } | ||
152 | *state = HT_AGG_STATE_REQ_STOP_BA_MSK | | 134 | *state = HT_AGG_STATE_REQ_STOP_BA_MSK | |
153 | (initiator << HT_AGG_STATE_INITIATOR_SHIFT); | 135 | (initiator << HT_AGG_STATE_INITIATOR_SHIFT); |
154 | 136 | ||
@@ -158,6 +140,10 @@ static int ___ieee80211_stop_tx_ba_session(struct sta_info *sta, u16 tid, | |||
158 | /* HW shall not deny going back to legacy */ | 140 | /* HW shall not deny going back to legacy */ |
159 | if (WARN_ON(ret)) { | 141 | if (WARN_ON(ret)) { |
160 | *state = HT_AGG_STATE_OPERATIONAL; | 142 | *state = HT_AGG_STATE_OPERATIONAL; |
143 | /* | ||
144 | * We may have pending packets get stuck in this case... | ||
145 | * Not bothering with a workaround for now. | ||
146 | */ | ||
161 | } | 147 | } |
162 | 148 | ||
163 | return ret; | 149 | return ret; |
@@ -212,7 +198,7 @@ int ieee80211_start_tx_ba_session(struct ieee80211_hw *hw, u8 *ra, u16 tid) | |||
212 | struct sta_info *sta; | 198 | struct sta_info *sta; |
213 | struct ieee80211_sub_if_data *sdata; | 199 | struct ieee80211_sub_if_data *sdata; |
214 | u8 *state; | 200 | u8 *state; |
215 | int i, qn = -1, ret = 0; | 201 | int ret = 0; |
216 | u16 start_seq_num; | 202 | u16 start_seq_num; |
217 | 203 | ||
218 | if (WARN_ON(!local->ops->ampdu_action)) | 204 | if (WARN_ON(!local->ops->ampdu_action)) |
@@ -226,13 +212,6 @@ int ieee80211_start_tx_ba_session(struct ieee80211_hw *hw, u8 *ra, u16 tid) | |||
226 | ra, tid); | 212 | ra, tid); |
227 | #endif /* CONFIG_MAC80211_HT_DEBUG */ | 213 | #endif /* CONFIG_MAC80211_HT_DEBUG */ |
228 | 214 | ||
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(); | 215 | rcu_read_lock(); |
237 | 216 | ||
238 | sta = sta_info_get(local, ra); | 217 | sta = sta_info_get(local, ra); |
@@ -257,7 +236,17 @@ int ieee80211_start_tx_ba_session(struct ieee80211_hw *hw, u8 *ra, u16 tid) | |||
257 | goto unlock; | 236 | goto unlock; |
258 | } | 237 | } |
259 | 238 | ||
239 | if (test_sta_flags(sta, WLAN_STA_SUSPEND)) { | ||
240 | #ifdef CONFIG_MAC80211_HT_DEBUG | ||
241 | printk(KERN_DEBUG "Suspend in progress. " | ||
242 | "Denying BA session request\n"); | ||
243 | #endif | ||
244 | ret = -EINVAL; | ||
245 | goto unlock; | ||
246 | } | ||
247 | |||
260 | spin_lock_bh(&sta->lock); | 248 | spin_lock_bh(&sta->lock); |
249 | spin_lock(&local->ampdu_lock); | ||
261 | 250 | ||
262 | sdata = sta->sdata; | 251 | sdata = sta->sdata; |
263 | 252 | ||
@@ -278,41 +267,16 @@ int ieee80211_start_tx_ba_session(struct ieee80211_hw *hw, u8 *ra, u16 tid) | |||
278 | goto err_unlock_sta; | 267 | goto err_unlock_sta; |
279 | } | 268 | } |
280 | 269 | ||
281 | if (hw->ampdu_queues) { | 270 | /* |
282 | spin_lock(&local->queue_stop_reason_lock); | 271 | * While we're asking the driver about the aggregation, |
283 | /* reserve a new queue for this session */ | 272 | * stop the AC queue so that we don't have to worry |
284 | for (i = 0; i < local->hw.ampdu_queues; i++) { | 273 | * about frames that came in while we were doing that, |
285 | if (local->ampdu_ac_queue[i] < 0) { | 274 | * which would require us to put them to the AC pending |
286 | qn = i; | 275 | * afterwards which just makes the code more complex. |
287 | local->ampdu_ac_queue[qn] = | 276 | */ |
288 | ieee80211_ac_from_tid(tid); | 277 | ieee80211_stop_queue_by_reason( |
289 | break; | 278 | &local->hw, ieee80211_ac_from_tid(tid), |
290 | } | 279 | IEEE80211_QUEUE_STOP_REASON_AGGREGATION); |
291 | } | ||
292 | spin_unlock(&local->queue_stop_reason_lock); | ||
293 | |||
294 | if (qn < 0) { | ||
295 | #ifdef CONFIG_MAC80211_HT_DEBUG | ||
296 | printk(KERN_DEBUG "BA request denied - " | ||
297 | "queue unavailable for tid %d\n", tid); | ||
298 | #endif /* CONFIG_MAC80211_HT_DEBUG */ | ||
299 | ret = -ENOSPC; | ||
300 | goto err_unlock_sta; | ||
301 | } | ||
302 | |||
303 | /* | ||
304 | * If we successfully allocate the session, we can't have | ||
305 | * anything going on on the queue this TID maps into, so | ||
306 | * stop it for now. This is a "virtual" stop using the same | ||
307 | * mechanism that drivers will use. | ||
308 | * | ||
309 | * XXX: queue up frames for this session in the sta_info | ||
310 | * struct instead to avoid hitting all other STAs. | ||
311 | */ | ||
312 | ieee80211_stop_queue_by_reason( | ||
313 | &local->hw, hw->queues + qn, | ||
314 | IEEE80211_QUEUE_STOP_REASON_AGGREGATION); | ||
315 | } | ||
316 | 280 | ||
317 | /* prepare A-MPDU MLME for Tx aggregation */ | 281 | /* prepare A-MPDU MLME for Tx aggregation */ |
318 | sta->ampdu_mlme.tid_tx[tid] = | 282 | sta->ampdu_mlme.tid_tx[tid] = |
@@ -324,9 +288,11 @@ int ieee80211_start_tx_ba_session(struct ieee80211_hw *hw, u8 *ra, u16 tid) | |||
324 | tid); | 288 | tid); |
325 | #endif | 289 | #endif |
326 | ret = -ENOMEM; | 290 | ret = -ENOMEM; |
327 | goto err_return_queue; | 291 | goto err_wake_queue; |
328 | } | 292 | } |
329 | 293 | ||
294 | skb_queue_head_init(&sta->ampdu_mlme.tid_tx[tid]->pending); | ||
295 | |||
330 | /* Tx timer */ | 296 | /* Tx timer */ |
331 | sta->ampdu_mlme.tid_tx[tid]->addba_resp_timer.function = | 297 | sta->ampdu_mlme.tid_tx[tid]->addba_resp_timer.function = |
332 | sta_addba_resp_timer_expired; | 298 | sta_addba_resp_timer_expired; |
@@ -351,8 +317,13 @@ int ieee80211_start_tx_ba_session(struct ieee80211_hw *hw, u8 *ra, u16 tid) | |||
351 | *state = HT_AGG_STATE_IDLE; | 317 | *state = HT_AGG_STATE_IDLE; |
352 | goto err_free; | 318 | goto err_free; |
353 | } | 319 | } |
354 | sta->tid_to_tx_q[tid] = qn; | ||
355 | 320 | ||
321 | /* Driver vetoed or OKed, but we can take packets again now */ | ||
322 | ieee80211_wake_queue_by_reason( | ||
323 | &local->hw, ieee80211_ac_from_tid(tid), | ||
324 | IEEE80211_QUEUE_STOP_REASON_AGGREGATION); | ||
325 | |||
326 | spin_unlock(&local->ampdu_lock); | ||
356 | spin_unlock_bh(&sta->lock); | 327 | spin_unlock_bh(&sta->lock); |
357 | 328 | ||
358 | /* send an addBA request */ | 329 | /* send an addBA request */ |
@@ -377,17 +348,12 @@ int ieee80211_start_tx_ba_session(struct ieee80211_hw *hw, u8 *ra, u16 tid) | |||
377 | err_free: | 348 | err_free: |
378 | kfree(sta->ampdu_mlme.tid_tx[tid]); | 349 | kfree(sta->ampdu_mlme.tid_tx[tid]); |
379 | sta->ampdu_mlme.tid_tx[tid] = NULL; | 350 | sta->ampdu_mlme.tid_tx[tid] = NULL; |
380 | err_return_queue: | 351 | err_wake_queue: |
381 | if (qn >= 0) { | 352 | ieee80211_wake_queue_by_reason( |
382 | /* We failed, so start queue again right away. */ | 353 | &local->hw, ieee80211_ac_from_tid(tid), |
383 | ieee80211_wake_queue_by_reason(hw, hw->queues + qn, | 354 | IEEE80211_QUEUE_STOP_REASON_AGGREGATION); |
384 | IEEE80211_QUEUE_STOP_REASON_AGGREGATION); | ||
385 | /* give queue back to pool */ | ||
386 | spin_lock(&local->queue_stop_reason_lock); | ||
387 | local->ampdu_ac_queue[qn] = -1; | ||
388 | spin_unlock(&local->queue_stop_reason_lock); | ||
389 | } | ||
390 | err_unlock_sta: | 355 | err_unlock_sta: |
356 | spin_unlock(&local->ampdu_lock); | ||
391 | spin_unlock_bh(&sta->lock); | 357 | spin_unlock_bh(&sta->lock); |
392 | unlock: | 358 | unlock: |
393 | rcu_read_unlock(); | 359 | rcu_read_unlock(); |
@@ -395,6 +361,67 @@ int ieee80211_start_tx_ba_session(struct ieee80211_hw *hw, u8 *ra, u16 tid) | |||
395 | } | 361 | } |
396 | EXPORT_SYMBOL(ieee80211_start_tx_ba_session); | 362 | EXPORT_SYMBOL(ieee80211_start_tx_ba_session); |
397 | 363 | ||
364 | /* | ||
365 | * splice packets from the STA's pending to the local pending, | ||
366 | * requires a call to ieee80211_agg_splice_finish and holding | ||
367 | * local->ampdu_lock across both calls. | ||
368 | */ | ||
369 | static void ieee80211_agg_splice_packets(struct ieee80211_local *local, | ||
370 | struct sta_info *sta, u16 tid) | ||
371 | { | ||
372 | unsigned long flags; | ||
373 | u16 queue = ieee80211_ac_from_tid(tid); | ||
374 | |||
375 | ieee80211_stop_queue_by_reason( | ||
376 | &local->hw, queue, | ||
377 | IEEE80211_QUEUE_STOP_REASON_AGGREGATION); | ||
378 | |||
379 | if (!skb_queue_empty(&sta->ampdu_mlme.tid_tx[tid]->pending)) { | ||
380 | spin_lock_irqsave(&local->queue_stop_reason_lock, flags); | ||
381 | /* mark queue as pending, it is stopped already */ | ||
382 | __set_bit(IEEE80211_QUEUE_STOP_REASON_PENDING, | ||
383 | &local->queue_stop_reasons[queue]); | ||
384 | /* copy over remaining packets */ | ||
385 | skb_queue_splice_tail_init( | ||
386 | &sta->ampdu_mlme.tid_tx[tid]->pending, | ||
387 | &local->pending[queue]); | ||
388 | spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags); | ||
389 | } | ||
390 | } | ||
391 | |||
392 | static void ieee80211_agg_splice_finish(struct ieee80211_local *local, | ||
393 | struct sta_info *sta, u16 tid) | ||
394 | { | ||
395 | u16 queue = ieee80211_ac_from_tid(tid); | ||
396 | |||
397 | ieee80211_wake_queue_by_reason( | ||
398 | &local->hw, queue, | ||
399 | IEEE80211_QUEUE_STOP_REASON_AGGREGATION); | ||
400 | } | ||
401 | |||
402 | /* caller must hold sta->lock */ | ||
403 | static void ieee80211_agg_tx_operational(struct ieee80211_local *local, | ||
404 | struct sta_info *sta, u16 tid) | ||
405 | { | ||
406 | #ifdef CONFIG_MAC80211_HT_DEBUG | ||
407 | printk(KERN_DEBUG "Aggregation is on for tid %d \n", tid); | ||
408 | #endif | ||
409 | |||
410 | spin_lock(&local->ampdu_lock); | ||
411 | ieee80211_agg_splice_packets(local, sta, tid); | ||
412 | /* | ||
413 | * NB: we rely on sta->lock being taken in the TX | ||
414 | * processing here when adding to the pending queue, | ||
415 | * otherwise we could only change the state of the | ||
416 | * session to OPERATIONAL _here_. | ||
417 | */ | ||
418 | ieee80211_agg_splice_finish(local, sta, tid); | ||
419 | spin_unlock(&local->ampdu_lock); | ||
420 | |||
421 | local->ops->ampdu_action(&local->hw, IEEE80211_AMPDU_TX_OPERATIONAL, | ||
422 | &sta->sta, tid, NULL); | ||
423 | } | ||
424 | |||
398 | void ieee80211_start_tx_ba_cb(struct ieee80211_hw *hw, u8 *ra, u16 tid) | 425 | void ieee80211_start_tx_ba_cb(struct ieee80211_hw *hw, u8 *ra, u16 tid) |
399 | { | 426 | { |
400 | struct ieee80211_local *local = hw_to_local(hw); | 427 | struct ieee80211_local *local = hw_to_local(hw); |
@@ -437,20 +464,8 @@ void ieee80211_start_tx_ba_cb(struct ieee80211_hw *hw, u8 *ra, u16 tid) | |||
437 | 464 | ||
438 | *state |= HT_ADDBA_DRV_READY_MSK; | 465 | *state |= HT_ADDBA_DRV_READY_MSK; |
439 | 466 | ||
440 | if (*state == HT_AGG_STATE_OPERATIONAL) { | 467 | if (*state == HT_AGG_STATE_OPERATIONAL) |
441 | #ifdef CONFIG_MAC80211_HT_DEBUG | 468 | ieee80211_agg_tx_operational(local, sta, tid); |
442 | printk(KERN_DEBUG "Aggregation is on for tid %d \n", tid); | ||
443 | #endif | ||
444 | if (hw->ampdu_queues) { | ||
445 | /* | ||
446 | * Wake up this queue, we stopped it earlier, | ||
447 | * this will in turn wake the entire AC. | ||
448 | */ | ||
449 | ieee80211_wake_queue_by_reason(hw, | ||
450 | hw->queues + sta->tid_to_tx_q[tid], | ||
451 | IEEE80211_QUEUE_STOP_REASON_AGGREGATION); | ||
452 | } | ||
453 | } | ||
454 | 469 | ||
455 | out: | 470 | out: |
456 | spin_unlock_bh(&sta->lock); | 471 | spin_unlock_bh(&sta->lock); |
@@ -584,22 +599,19 @@ void ieee80211_stop_tx_ba_cb(struct ieee80211_hw *hw, u8 *ra, u8 tid) | |||
584 | WLAN_BACK_INITIATOR, WLAN_REASON_QSTA_NOT_USE); | 599 | WLAN_BACK_INITIATOR, WLAN_REASON_QSTA_NOT_USE); |
585 | 600 | ||
586 | spin_lock_bh(&sta->lock); | 601 | spin_lock_bh(&sta->lock); |
602 | spin_lock(&local->ampdu_lock); | ||
587 | 603 | ||
588 | if (*state & HT_AGG_STATE_INITIATOR_MSK && | 604 | ieee80211_agg_splice_packets(local, sta, tid); |
589 | hw->ampdu_queues) { | ||
590 | /* | ||
591 | * Wake up this queue, we stopped it earlier, | ||
592 | * this will in turn wake the entire AC. | ||
593 | */ | ||
594 | ieee80211_wake_queue_by_reason(hw, | ||
595 | hw->queues + sta->tid_to_tx_q[tid], | ||
596 | IEEE80211_QUEUE_STOP_REASON_AGGREGATION); | ||
597 | } | ||
598 | 605 | ||
599 | *state = HT_AGG_STATE_IDLE; | 606 | *state = HT_AGG_STATE_IDLE; |
607 | /* from now on packets are no longer put onto sta->pending */ | ||
600 | sta->ampdu_mlme.addba_req_num[tid] = 0; | 608 | sta->ampdu_mlme.addba_req_num[tid] = 0; |
601 | kfree(sta->ampdu_mlme.tid_tx[tid]); | 609 | kfree(sta->ampdu_mlme.tid_tx[tid]); |
602 | sta->ampdu_mlme.tid_tx[tid] = NULL; | 610 | sta->ampdu_mlme.tid_tx[tid] = NULL; |
611 | |||
612 | ieee80211_agg_splice_finish(local, sta, tid); | ||
613 | |||
614 | spin_unlock(&local->ampdu_lock); | ||
603 | spin_unlock_bh(&sta->lock); | 615 | spin_unlock_bh(&sta->lock); |
604 | 616 | ||
605 | rcu_read_unlock(); | 617 | rcu_read_unlock(); |
@@ -637,9 +649,7 @@ void ieee80211_process_addba_resp(struct ieee80211_local *local, | |||
637 | struct ieee80211_mgmt *mgmt, | 649 | struct ieee80211_mgmt *mgmt, |
638 | size_t len) | 650 | size_t len) |
639 | { | 651 | { |
640 | struct ieee80211_hw *hw = &local->hw; | 652 | u16 capab, tid; |
641 | u16 capab; | ||
642 | u16 tid, start_seq_num; | ||
643 | u8 *state; | 653 | u8 *state; |
644 | 654 | ||
645 | capab = le16_to_cpu(mgmt->u.action.u.addba_resp.capab); | 655 | capab = le16_to_cpu(mgmt->u.action.u.addba_resp.capab); |
@@ -673,26 +683,10 @@ void ieee80211_process_addba_resp(struct ieee80211_local *local, | |||
673 | 683 | ||
674 | *state |= HT_ADDBA_RECEIVED_MSK; | 684 | *state |= HT_ADDBA_RECEIVED_MSK; |
675 | 685 | ||
676 | if (hw->ampdu_queues && *state != curstate && | 686 | if (*state != curstate && *state == HT_AGG_STATE_OPERATIONAL) |
677 | *state == HT_AGG_STATE_OPERATIONAL) { | 687 | ieee80211_agg_tx_operational(local, sta, tid); |
678 | /* | ||
679 | * Wake up this queue, we stopped it earlier, | ||
680 | * this will in turn wake the entire AC. | ||
681 | */ | ||
682 | ieee80211_wake_queue_by_reason(hw, | ||
683 | hw->queues + sta->tid_to_tx_q[tid], | ||
684 | IEEE80211_QUEUE_STOP_REASON_AGGREGATION); | ||
685 | } | ||
686 | sta->ampdu_mlme.addba_req_num[tid] = 0; | ||
687 | 688 | ||
688 | if (local->ops->ampdu_action) { | 689 | sta->ampdu_mlme.addba_req_num[tid] = 0; |
689 | (void)local->ops->ampdu_action(hw, | ||
690 | IEEE80211_AMPDU_TX_RESUME, | ||
691 | &sta->sta, tid, &start_seq_num); | ||
692 | } | ||
693 | #ifdef CONFIG_MAC80211_HT_DEBUG | ||
694 | printk(KERN_DEBUG "Resuming TX aggregation for tid %d\n", tid); | ||
695 | #endif /* CONFIG_MAC80211_HT_DEBUG */ | ||
696 | } else { | 690 | } else { |
697 | sta->ampdu_mlme.addba_req_num[tid]++; | 691 | sta->ampdu_mlme.addba_req_num[tid]++; |
698 | ___ieee80211_stop_tx_ba_session(sta, tid, WLAN_BACK_INITIATOR); | 692 | ___ieee80211_stop_tx_ba_session(sta, tid, WLAN_BACK_INITIATOR); |