diff options
Diffstat (limited to 'net/mac80211/agg-tx.c')
-rw-r--r-- | net/mac80211/agg-tx.c | 127 |
1 files changed, 79 insertions, 48 deletions
diff --git a/net/mac80211/agg-tx.c b/net/mac80211/agg-tx.c index c8be8eff70d..b7f4f5c1f69 100644 --- a/net/mac80211/agg-tx.c +++ b/net/mac80211/agg-tx.c | |||
@@ -162,6 +162,12 @@ int ___ieee80211_stop_tx_ba_session(struct sta_info *sta, u16 tid, | |||
162 | return -ENOENT; | 162 | return -ENOENT; |
163 | } | 163 | } |
164 | 164 | ||
165 | /* if we're already stopping ignore any new requests to stop */ | ||
166 | if (test_bit(HT_AGG_STATE_STOPPING, &tid_tx->state)) { | ||
167 | spin_unlock_bh(&sta->lock); | ||
168 | return -EALREADY; | ||
169 | } | ||
170 | |||
165 | if (test_bit(HT_AGG_STATE_WANT_START, &tid_tx->state)) { | 171 | if (test_bit(HT_AGG_STATE_WANT_START, &tid_tx->state)) { |
166 | /* not even started yet! */ | 172 | /* not even started yet! */ |
167 | ieee80211_assign_tid_tx(sta, tid, NULL); | 173 | ieee80211_assign_tid_tx(sta, tid, NULL); |
@@ -170,6 +176,8 @@ int ___ieee80211_stop_tx_ba_session(struct sta_info *sta, u16 tid, | |||
170 | return 0; | 176 | return 0; |
171 | } | 177 | } |
172 | 178 | ||
179 | set_bit(HT_AGG_STATE_STOPPING, &tid_tx->state); | ||
180 | |||
173 | spin_unlock_bh(&sta->lock); | 181 | spin_unlock_bh(&sta->lock); |
174 | 182 | ||
175 | #ifdef CONFIG_MAC80211_HT_DEBUG | 183 | #ifdef CONFIG_MAC80211_HT_DEBUG |
@@ -177,8 +185,6 @@ int ___ieee80211_stop_tx_ba_session(struct sta_info *sta, u16 tid, | |||
177 | sta->sta.addr, tid); | 185 | sta->sta.addr, tid); |
178 | #endif /* CONFIG_MAC80211_HT_DEBUG */ | 186 | #endif /* CONFIG_MAC80211_HT_DEBUG */ |
179 | 187 | ||
180 | set_bit(HT_AGG_STATE_STOPPING, &tid_tx->state); | ||
181 | |||
182 | del_timer_sync(&tid_tx->addba_resp_timer); | 188 | del_timer_sync(&tid_tx->addba_resp_timer); |
183 | 189 | ||
184 | /* | 190 | /* |
@@ -188,6 +194,20 @@ int ___ieee80211_stop_tx_ba_session(struct sta_info *sta, u16 tid, | |||
188 | */ | 194 | */ |
189 | clear_bit(HT_AGG_STATE_OPERATIONAL, &tid_tx->state); | 195 | clear_bit(HT_AGG_STATE_OPERATIONAL, &tid_tx->state); |
190 | 196 | ||
197 | /* | ||
198 | * There might be a few packets being processed right now (on | ||
199 | * another CPU) that have already gotten past the aggregation | ||
200 | * check when it was still OPERATIONAL and consequently have | ||
201 | * IEEE80211_TX_CTL_AMPDU set. In that case, this code might | ||
202 | * call into the driver at the same time or even before the | ||
203 | * TX paths calls into it, which could confuse the driver. | ||
204 | * | ||
205 | * Wait for all currently running TX paths to finish before | ||
206 | * telling the driver. New packets will not go through since | ||
207 | * the aggregation session is no longer OPERATIONAL. | ||
208 | */ | ||
209 | synchronize_net(); | ||
210 | |||
191 | tid_tx->stop_initiator = initiator; | 211 | tid_tx->stop_initiator = initiator; |
192 | tid_tx->tx_stop = tx; | 212 | tid_tx->tx_stop = tx; |
193 | 213 | ||
@@ -284,6 +304,38 @@ ieee80211_wake_queue_agg(struct ieee80211_local *local, int tid) | |||
284 | __release(agg_queue); | 304 | __release(agg_queue); |
285 | } | 305 | } |
286 | 306 | ||
307 | /* | ||
308 | * splice packets from the STA's pending to the local pending, | ||
309 | * requires a call to ieee80211_agg_splice_finish later | ||
310 | */ | ||
311 | static void __acquires(agg_queue) | ||
312 | ieee80211_agg_splice_packets(struct ieee80211_local *local, | ||
313 | struct tid_ampdu_tx *tid_tx, u16 tid) | ||
314 | { | ||
315 | int queue = ieee80211_ac_from_tid(tid); | ||
316 | unsigned long flags; | ||
317 | |||
318 | ieee80211_stop_queue_agg(local, tid); | ||
319 | |||
320 | if (WARN(!tid_tx, "TID %d gone but expected when splicing aggregates" | ||
321 | " from the pending queue\n", tid)) | ||
322 | return; | ||
323 | |||
324 | if (!skb_queue_empty(&tid_tx->pending)) { | ||
325 | spin_lock_irqsave(&local->queue_stop_reason_lock, flags); | ||
326 | /* copy over remaining packets */ | ||
327 | skb_queue_splice_tail_init(&tid_tx->pending, | ||
328 | &local->pending[queue]); | ||
329 | spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags); | ||
330 | } | ||
331 | } | ||
332 | |||
333 | static void __releases(agg_queue) | ||
334 | ieee80211_agg_splice_finish(struct ieee80211_local *local, u16 tid) | ||
335 | { | ||
336 | ieee80211_wake_queue_agg(local, tid); | ||
337 | } | ||
338 | |||
287 | void ieee80211_tx_ba_session_handle_start(struct sta_info *sta, int tid) | 339 | void ieee80211_tx_ba_session_handle_start(struct sta_info *sta, int tid) |
288 | { | 340 | { |
289 | struct tid_ampdu_tx *tid_tx; | 341 | struct tid_ampdu_tx *tid_tx; |
@@ -295,19 +347,17 @@ void ieee80211_tx_ba_session_handle_start(struct sta_info *sta, int tid) | |||
295 | tid_tx = rcu_dereference_protected_tid_tx(sta, tid); | 347 | tid_tx = rcu_dereference_protected_tid_tx(sta, tid); |
296 | 348 | ||
297 | /* | 349 | /* |
298 | * While we're asking the driver about the aggregation, | 350 | * Start queuing up packets for this aggregation session. |
299 | * stop the AC queue so that we don't have to worry | 351 | * We're going to release them once the driver is OK with |
300 | * about frames that came in while we were doing that, | 352 | * that. |
301 | * which would require us to put them to the AC pending | ||
302 | * afterwards which just makes the code more complex. | ||
303 | */ | 353 | */ |
304 | ieee80211_stop_queue_agg(local, tid); | ||
305 | |||
306 | clear_bit(HT_AGG_STATE_WANT_START, &tid_tx->state); | 354 | clear_bit(HT_AGG_STATE_WANT_START, &tid_tx->state); |
307 | 355 | ||
308 | /* | 356 | /* |
309 | * make sure no packets are being processed to get | 357 | * Make sure no packets are being processed. This ensures that |
310 | * valid starting sequence number | 358 | * we have a valid starting sequence number and that in-flight |
359 | * packets have been flushed out and no packets for this TID | ||
360 | * will go into the driver during the ampdu_action call. | ||
311 | */ | 361 | */ |
312 | synchronize_net(); | 362 | synchronize_net(); |
313 | 363 | ||
@@ -321,17 +371,15 @@ void ieee80211_tx_ba_session_handle_start(struct sta_info *sta, int tid) | |||
321 | " tid %d\n", tid); | 371 | " tid %d\n", tid); |
322 | #endif | 372 | #endif |
323 | spin_lock_bh(&sta->lock); | 373 | spin_lock_bh(&sta->lock); |
374 | ieee80211_agg_splice_packets(local, tid_tx, tid); | ||
324 | ieee80211_assign_tid_tx(sta, tid, NULL); | 375 | ieee80211_assign_tid_tx(sta, tid, NULL); |
376 | ieee80211_agg_splice_finish(local, tid); | ||
325 | spin_unlock_bh(&sta->lock); | 377 | spin_unlock_bh(&sta->lock); |
326 | 378 | ||
327 | ieee80211_wake_queue_agg(local, tid); | ||
328 | kfree_rcu(tid_tx, rcu_head); | 379 | kfree_rcu(tid_tx, rcu_head); |
329 | return; | 380 | return; |
330 | } | 381 | } |
331 | 382 | ||
332 | /* we can take packets again now */ | ||
333 | ieee80211_wake_queue_agg(local, tid); | ||
334 | |||
335 | /* activate the timer for the recipient's addBA response */ | 383 | /* activate the timer for the recipient's addBA response */ |
336 | mod_timer(&tid_tx->addba_resp_timer, jiffies + ADDBA_RESP_INTERVAL); | 384 | mod_timer(&tid_tx->addba_resp_timer, jiffies + ADDBA_RESP_INTERVAL); |
337 | #ifdef CONFIG_MAC80211_HT_DEBUG | 385 | #ifdef CONFIG_MAC80211_HT_DEBUG |
@@ -451,38 +499,6 @@ int ieee80211_start_tx_ba_session(struct ieee80211_sta *pubsta, u16 tid, | |||
451 | } | 499 | } |
452 | EXPORT_SYMBOL(ieee80211_start_tx_ba_session); | 500 | EXPORT_SYMBOL(ieee80211_start_tx_ba_session); |
453 | 501 | ||
454 | /* | ||
455 | * splice packets from the STA's pending to the local pending, | ||
456 | * requires a call to ieee80211_agg_splice_finish later | ||
457 | */ | ||
458 | static void __acquires(agg_queue) | ||
459 | ieee80211_agg_splice_packets(struct ieee80211_local *local, | ||
460 | struct tid_ampdu_tx *tid_tx, u16 tid) | ||
461 | { | ||
462 | int queue = ieee80211_ac_from_tid(tid); | ||
463 | unsigned long flags; | ||
464 | |||
465 | ieee80211_stop_queue_agg(local, tid); | ||
466 | |||
467 | if (WARN(!tid_tx, "TID %d gone but expected when splicing aggregates" | ||
468 | " from the pending queue\n", tid)) | ||
469 | return; | ||
470 | |||
471 | if (!skb_queue_empty(&tid_tx->pending)) { | ||
472 | spin_lock_irqsave(&local->queue_stop_reason_lock, flags); | ||
473 | /* copy over remaining packets */ | ||
474 | skb_queue_splice_tail_init(&tid_tx->pending, | ||
475 | &local->pending[queue]); | ||
476 | spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags); | ||
477 | } | ||
478 | } | ||
479 | |||
480 | static void __releases(agg_queue) | ||
481 | ieee80211_agg_splice_finish(struct ieee80211_local *local, u16 tid) | ||
482 | { | ||
483 | ieee80211_wake_queue_agg(local, tid); | ||
484 | } | ||
485 | |||
486 | static void ieee80211_agg_tx_operational(struct ieee80211_local *local, | 502 | static void ieee80211_agg_tx_operational(struct ieee80211_local *local, |
487 | struct sta_info *sta, u16 tid) | 503 | struct sta_info *sta, u16 tid) |
488 | { | 504 | { |
@@ -772,12 +788,27 @@ void ieee80211_process_addba_resp(struct ieee80211_local *local, | |||
772 | goto out; | 788 | goto out; |
773 | } | 789 | } |
774 | 790 | ||
775 | del_timer(&tid_tx->addba_resp_timer); | 791 | del_timer_sync(&tid_tx->addba_resp_timer); |
776 | 792 | ||
777 | #ifdef CONFIG_MAC80211_HT_DEBUG | 793 | #ifdef CONFIG_MAC80211_HT_DEBUG |
778 | printk(KERN_DEBUG "switched off addBA timer for tid %d\n", tid); | 794 | printk(KERN_DEBUG "switched off addBA timer for tid %d\n", tid); |
779 | #endif | 795 | #endif |
780 | 796 | ||
797 | /* | ||
798 | * addba_resp_timer may have fired before we got here, and | ||
799 | * caused WANT_STOP to be set. If the stop then was already | ||
800 | * processed further, STOPPING might be set. | ||
801 | */ | ||
802 | if (test_bit(HT_AGG_STATE_WANT_STOP, &tid_tx->state) || | ||
803 | test_bit(HT_AGG_STATE_STOPPING, &tid_tx->state)) { | ||
804 | #ifdef CONFIG_MAC80211_HT_DEBUG | ||
805 | printk(KERN_DEBUG | ||
806 | "got addBA resp for tid %d but we already gave up\n", | ||
807 | tid); | ||
808 | #endif | ||
809 | goto out; | ||
810 | } | ||
811 | |||
781 | if (le16_to_cpu(mgmt->u.action.u.addba_resp.status) | 812 | if (le16_to_cpu(mgmt->u.action.u.addba_resp.status) |
782 | == WLAN_STATUS_SUCCESS) { | 813 | == WLAN_STATUS_SUCCESS) { |
783 | /* | 814 | /* |