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(); |
