diff options
Diffstat (limited to 'net/mac80211/agg-tx.c')
-rw-r--r-- | net/mac80211/agg-tx.c | 229 |
1 files changed, 117 insertions, 112 deletions
diff --git a/net/mac80211/agg-tx.c b/net/mac80211/agg-tx.c index c7b7ac40316..7d8656d51c6 100644 --- a/net/mac80211/agg-tx.c +++ b/net/mac80211/agg-tx.c | |||
@@ -125,25 +125,42 @@ void ieee80211_send_bar(struct ieee80211_sub_if_data *sdata, u8 *ra, u16 tid, u1 | |||
125 | ieee80211_tx_skb(sdata, skb); | 125 | ieee80211_tx_skb(sdata, skb); |
126 | } | 126 | } |
127 | 127 | ||
128 | int ___ieee80211_stop_tx_ba_session(struct sta_info *sta, u16 tid, | 128 | static void kfree_tid_tx(struct rcu_head *rcu_head) |
129 | enum ieee80211_back_parties initiator) | 129 | { |
130 | struct tid_ampdu_tx *tid_tx = | ||
131 | container_of(rcu_head, struct tid_ampdu_tx, rcu_head); | ||
132 | |||
133 | kfree(tid_tx); | ||
134 | } | ||
135 | |||
136 | static int ___ieee80211_stop_tx_ba_session( | ||
137 | struct sta_info *sta, u16 tid, | ||
138 | enum ieee80211_back_parties initiator) | ||
130 | { | 139 | { |
131 | struct ieee80211_local *local = sta->local; | 140 | struct ieee80211_local *local = sta->local; |
141 | struct tid_ampdu_tx *tid_tx = sta->ampdu_mlme.tid_tx[tid]; | ||
132 | int ret; | 142 | int ret; |
133 | u8 *state; | 143 | |
144 | lockdep_assert_held(&sta->lock); | ||
145 | |||
146 | if (WARN_ON(!tid_tx)) | ||
147 | return -ENOENT; | ||
134 | 148 | ||
135 | #ifdef CONFIG_MAC80211_HT_DEBUG | 149 | #ifdef CONFIG_MAC80211_HT_DEBUG |
136 | printk(KERN_DEBUG "Tx BA session stop requested for %pM tid %u\n", | 150 | printk(KERN_DEBUG "Tx BA session stop requested for %pM tid %u\n", |
137 | sta->sta.addr, tid); | 151 | sta->sta.addr, tid); |
138 | #endif /* CONFIG_MAC80211_HT_DEBUG */ | 152 | #endif /* CONFIG_MAC80211_HT_DEBUG */ |
139 | 153 | ||
140 | state = &sta->ampdu_mlme.tid_state_tx[tid]; | 154 | set_bit(HT_AGG_STATE_STOPPING, &tid_tx->state); |
141 | 155 | ||
142 | if (*state == HT_AGG_STATE_OPERATIONAL) | 156 | /* |
143 | sta->ampdu_mlme.addba_req_num[tid] = 0; | 157 | * After this packets are no longer handed right through |
158 | * to the driver but are put onto tid_tx->pending instead, | ||
159 | * with locking to ensure proper access. | ||
160 | */ | ||
161 | clear_bit(HT_AGG_STATE_OPERATIONAL, &tid_tx->state); | ||
144 | 162 | ||
145 | *state = HT_AGG_STATE_REQ_STOP_BA_MSK | | 163 | tid_tx->stop_initiator = initiator; |
146 | (initiator << HT_AGG_STATE_INITIATOR_SHIFT); | ||
147 | 164 | ||
148 | ret = drv_ampdu_action(local, sta->sdata, | 165 | ret = drv_ampdu_action(local, sta->sdata, |
149 | IEEE80211_AMPDU_TX_STOP, | 166 | IEEE80211_AMPDU_TX_STOP, |
@@ -174,15 +191,13 @@ static void sta_addba_resp_timer_expired(unsigned long data) | |||
174 | u16 tid = *(u8 *)data; | 191 | u16 tid = *(u8 *)data; |
175 | struct sta_info *sta = container_of((void *)data, | 192 | struct sta_info *sta = container_of((void *)data, |
176 | struct sta_info, timer_to_tid[tid]); | 193 | struct sta_info, timer_to_tid[tid]); |
177 | u8 *state; | 194 | struct tid_ampdu_tx *tid_tx; |
178 | |||
179 | state = &sta->ampdu_mlme.tid_state_tx[tid]; | ||
180 | 195 | ||
181 | /* check if the TID waits for addBA response */ | 196 | /* check if the TID waits for addBA response */ |
182 | spin_lock_bh(&sta->lock); | 197 | spin_lock_bh(&sta->lock); |
183 | if ((*state & (HT_ADDBA_REQUESTED_MSK | HT_ADDBA_RECEIVED_MSK | | 198 | tid_tx = sta->ampdu_mlme.tid_tx[tid]; |
184 | HT_AGG_STATE_REQ_STOP_BA_MSK)) != | 199 | if (!tid_tx || |
185 | HT_ADDBA_REQUESTED_MSK) { | 200 | test_bit(HT_AGG_STATE_RESPONSE_RECEIVED, &tid_tx->state)) { |
186 | spin_unlock_bh(&sta->lock); | 201 | spin_unlock_bh(&sta->lock); |
187 | #ifdef CONFIG_MAC80211_HT_DEBUG | 202 | #ifdef CONFIG_MAC80211_HT_DEBUG |
188 | printk(KERN_DEBUG "timer expired on tid %d but we are not " | 203 | printk(KERN_DEBUG "timer expired on tid %d but we are not " |
@@ -210,7 +225,7 @@ int ieee80211_start_tx_ba_session(struct ieee80211_sta *pubsta, u16 tid) | |||
210 | struct sta_info *sta = container_of(pubsta, struct sta_info, sta); | 225 | struct sta_info *sta = container_of(pubsta, struct sta_info, sta); |
211 | struct ieee80211_sub_if_data *sdata = sta->sdata; | 226 | struct ieee80211_sub_if_data *sdata = sta->sdata; |
212 | struct ieee80211_local *local = sdata->local; | 227 | struct ieee80211_local *local = sdata->local; |
213 | u8 *state; | 228 | struct tid_ampdu_tx *tid_tx; |
214 | int ret = 0; | 229 | int ret = 0; |
215 | u16 start_seq_num; | 230 | u16 start_seq_num; |
216 | 231 | ||
@@ -256,9 +271,9 @@ int ieee80211_start_tx_ba_session(struct ieee80211_sta *pubsta, u16 tid) | |||
256 | goto err_unlock_sta; | 271 | goto err_unlock_sta; |
257 | } | 272 | } |
258 | 273 | ||
259 | state = &sta->ampdu_mlme.tid_state_tx[tid]; | 274 | tid_tx = sta->ampdu_mlme.tid_tx[tid]; |
260 | /* check if the TID is not in aggregation flow already */ | 275 | /* check if the TID is not in aggregation flow already */ |
261 | if (*state != HT_AGG_STATE_IDLE) { | 276 | if (tid_tx) { |
262 | #ifdef CONFIG_MAC80211_HT_DEBUG | 277 | #ifdef CONFIG_MAC80211_HT_DEBUG |
263 | printk(KERN_DEBUG "BA request denied - session is not " | 278 | printk(KERN_DEBUG "BA request denied - session is not " |
264 | "idle on tid %u\n", tid); | 279 | "idle on tid %u\n", tid); |
@@ -279,9 +294,8 @@ int ieee80211_start_tx_ba_session(struct ieee80211_sta *pubsta, u16 tid) | |||
279 | IEEE80211_QUEUE_STOP_REASON_AGGREGATION); | 294 | IEEE80211_QUEUE_STOP_REASON_AGGREGATION); |
280 | 295 | ||
281 | /* prepare A-MPDU MLME for Tx aggregation */ | 296 | /* prepare A-MPDU MLME for Tx aggregation */ |
282 | sta->ampdu_mlme.tid_tx[tid] = | 297 | tid_tx = kzalloc(sizeof(struct tid_ampdu_tx), GFP_ATOMIC); |
283 | kmalloc(sizeof(struct tid_ampdu_tx), GFP_ATOMIC); | 298 | if (!tid_tx) { |
284 | if (!sta->ampdu_mlme.tid_tx[tid]) { | ||
285 | #ifdef CONFIG_MAC80211_HT_DEBUG | 299 | #ifdef CONFIG_MAC80211_HT_DEBUG |
286 | if (net_ratelimit()) | 300 | if (net_ratelimit()) |
287 | printk(KERN_ERR "allocate tx mlme to tid %d failed\n", | 301 | printk(KERN_ERR "allocate tx mlme to tid %d failed\n", |
@@ -291,33 +305,27 @@ int ieee80211_start_tx_ba_session(struct ieee80211_sta *pubsta, u16 tid) | |||
291 | goto err_wake_queue; | 305 | goto err_wake_queue; |
292 | } | 306 | } |
293 | 307 | ||
294 | skb_queue_head_init(&sta->ampdu_mlme.tid_tx[tid]->pending); | 308 | skb_queue_head_init(&tid_tx->pending); |
295 | 309 | ||
296 | /* Tx timer */ | 310 | /* Tx timer */ |
297 | sta->ampdu_mlme.tid_tx[tid]->addba_resp_timer.function = | 311 | tid_tx->addba_resp_timer.function = sta_addba_resp_timer_expired; |
298 | sta_addba_resp_timer_expired; | 312 | tid_tx->addba_resp_timer.data = (unsigned long)&sta->timer_to_tid[tid]; |
299 | sta->ampdu_mlme.tid_tx[tid]->addba_resp_timer.data = | 313 | init_timer(&tid_tx->addba_resp_timer); |
300 | (unsigned long)&sta->timer_to_tid[tid]; | ||
301 | init_timer(&sta->ampdu_mlme.tid_tx[tid]->addba_resp_timer); | ||
302 | |||
303 | /* Ok, the Addba frame hasn't been sent yet, but if the driver calls the | ||
304 | * call back right away, it must see that the flow has begun */ | ||
305 | *state |= HT_ADDBA_REQUESTED_MSK; | ||
306 | 314 | ||
307 | start_seq_num = sta->tid_seq[tid] >> 4; | 315 | start_seq_num = sta->tid_seq[tid] >> 4; |
308 | 316 | ||
309 | ret = drv_ampdu_action(local, sdata, IEEE80211_AMPDU_TX_START, | 317 | ret = drv_ampdu_action(local, sdata, IEEE80211_AMPDU_TX_START, |
310 | pubsta, tid, &start_seq_num); | 318 | pubsta, tid, &start_seq_num); |
311 | |||
312 | if (ret) { | 319 | if (ret) { |
313 | #ifdef CONFIG_MAC80211_HT_DEBUG | 320 | #ifdef CONFIG_MAC80211_HT_DEBUG |
314 | printk(KERN_DEBUG "BA request denied - HW unavailable for" | 321 | printk(KERN_DEBUG "BA request denied - HW unavailable for" |
315 | " tid %d\n", tid); | 322 | " tid %d\n", tid); |
316 | #endif /* CONFIG_MAC80211_HT_DEBUG */ | 323 | #endif /* CONFIG_MAC80211_HT_DEBUG */ |
317 | *state = HT_AGG_STATE_IDLE; | ||
318 | goto err_free; | 324 | goto err_free; |
319 | } | 325 | } |
320 | 326 | ||
327 | rcu_assign_pointer(sta->ampdu_mlme.tid_tx[tid], tid_tx); | ||
328 | |||
321 | /* Driver vetoed or OKed, but we can take packets again now */ | 329 | /* Driver vetoed or OKed, but we can take packets again now */ |
322 | ieee80211_wake_queue_by_reason( | 330 | ieee80211_wake_queue_by_reason( |
323 | &local->hw, ieee80211_ac_from_tid(tid), | 331 | &local->hw, ieee80211_ac_from_tid(tid), |
@@ -325,32 +333,30 @@ int ieee80211_start_tx_ba_session(struct ieee80211_sta *pubsta, u16 tid) | |||
325 | 333 | ||
326 | spin_unlock(&local->ampdu_lock); | 334 | spin_unlock(&local->ampdu_lock); |
327 | 335 | ||
336 | /* activate the timer for the recipient's addBA response */ | ||
337 | tid_tx->addba_resp_timer.expires = jiffies + ADDBA_RESP_INTERVAL; | ||
338 | add_timer(&tid_tx->addba_resp_timer); | ||
339 | #ifdef CONFIG_MAC80211_HT_DEBUG | ||
340 | printk(KERN_DEBUG "activated addBA response timer on tid %d\n", tid); | ||
341 | #endif | ||
342 | |||
328 | /* prepare tid data */ | 343 | /* prepare tid data */ |
329 | sta->ampdu_mlme.dialog_token_allocator++; | 344 | sta->ampdu_mlme.dialog_token_allocator++; |
330 | sta->ampdu_mlme.tid_tx[tid]->dialog_token = | 345 | tid_tx->dialog_token = sta->ampdu_mlme.dialog_token_allocator; |
331 | sta->ampdu_mlme.dialog_token_allocator; | 346 | tid_tx->ssn = start_seq_num; |
332 | sta->ampdu_mlme.tid_tx[tid]->ssn = start_seq_num; | 347 | |
348 | sta->ampdu_mlme.addba_req_num[tid]++; | ||
333 | 349 | ||
334 | spin_unlock_bh(&sta->lock); | 350 | spin_unlock_bh(&sta->lock); |
335 | 351 | ||
336 | /* send AddBA request */ | 352 | /* send AddBA request */ |
337 | ieee80211_send_addba_request(sdata, pubsta->addr, tid, | 353 | ieee80211_send_addba_request(sdata, pubsta->addr, tid, |
338 | sta->ampdu_mlme.tid_tx[tid]->dialog_token, | 354 | tid_tx->dialog_token, tid_tx->ssn, |
339 | sta->ampdu_mlme.tid_tx[tid]->ssn, | ||
340 | 0x40, 5000); | 355 | 0x40, 5000); |
341 | sta->ampdu_mlme.addba_req_num[tid]++; | ||
342 | /* activate the timer for the recipient's addBA response */ | ||
343 | sta->ampdu_mlme.tid_tx[tid]->addba_resp_timer.expires = | ||
344 | jiffies + ADDBA_RESP_INTERVAL; | ||
345 | add_timer(&sta->ampdu_mlme.tid_tx[tid]->addba_resp_timer); | ||
346 | #ifdef CONFIG_MAC80211_HT_DEBUG | ||
347 | printk(KERN_DEBUG "activated addBA response timer on tid %d\n", tid); | ||
348 | #endif | ||
349 | return 0; | 356 | return 0; |
350 | 357 | ||
351 | err_free: | 358 | err_free: |
352 | kfree(sta->ampdu_mlme.tid_tx[tid]); | 359 | kfree(tid_tx); |
353 | sta->ampdu_mlme.tid_tx[tid] = NULL; | ||
354 | err_wake_queue: | 360 | err_wake_queue: |
355 | ieee80211_wake_queue_by_reason( | 361 | ieee80211_wake_queue_by_reason( |
356 | &local->hw, ieee80211_ac_from_tid(tid), | 362 | &local->hw, ieee80211_ac_from_tid(tid), |
@@ -368,7 +374,8 @@ EXPORT_SYMBOL(ieee80211_start_tx_ba_session); | |||
368 | * local->ampdu_lock across both calls. | 374 | * local->ampdu_lock across both calls. |
369 | */ | 375 | */ |
370 | static void ieee80211_agg_splice_packets(struct ieee80211_local *local, | 376 | static void ieee80211_agg_splice_packets(struct ieee80211_local *local, |
371 | struct sta_info *sta, u16 tid) | 377 | struct tid_ampdu_tx *tid_tx, |
378 | u16 tid) | ||
372 | { | 379 | { |
373 | unsigned long flags; | 380 | unsigned long flags; |
374 | u16 queue = ieee80211_ac_from_tid(tid); | 381 | u16 queue = ieee80211_ac_from_tid(tid); |
@@ -377,31 +384,23 @@ static void ieee80211_agg_splice_packets(struct ieee80211_local *local, | |||
377 | &local->hw, queue, | 384 | &local->hw, queue, |
378 | IEEE80211_QUEUE_STOP_REASON_AGGREGATION); | 385 | IEEE80211_QUEUE_STOP_REASON_AGGREGATION); |
379 | 386 | ||
380 | if (!(sta->ampdu_mlme.tid_state_tx[tid] & HT_ADDBA_REQUESTED_MSK)) | 387 | if (WARN(!tid_tx, "TID %d gone but expected when splicing aggregates" |
381 | return; | 388 | " from the pending queue\n", tid)) |
382 | |||
383 | if (WARN(!sta->ampdu_mlme.tid_tx[tid], | ||
384 | "TID %d gone but expected when splicing aggregates from" | ||
385 | "the pending queue\n", tid)) | ||
386 | return; | 389 | return; |
387 | 390 | ||
388 | if (!skb_queue_empty(&sta->ampdu_mlme.tid_tx[tid]->pending)) { | 391 | if (!skb_queue_empty(&tid_tx->pending)) { |
389 | spin_lock_irqsave(&local->queue_stop_reason_lock, flags); | 392 | spin_lock_irqsave(&local->queue_stop_reason_lock, flags); |
390 | /* copy over remaining packets */ | 393 | /* copy over remaining packets */ |
391 | skb_queue_splice_tail_init( | 394 | skb_queue_splice_tail_init(&tid_tx->pending, |
392 | &sta->ampdu_mlme.tid_tx[tid]->pending, | 395 | &local->pending[queue]); |
393 | &local->pending[queue]); | ||
394 | spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags); | 396 | spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags); |
395 | } | 397 | } |
396 | } | 398 | } |
397 | 399 | ||
398 | static void ieee80211_agg_splice_finish(struct ieee80211_local *local, | 400 | static void ieee80211_agg_splice_finish(struct ieee80211_local *local, u16 tid) |
399 | struct sta_info *sta, u16 tid) | ||
400 | { | 401 | { |
401 | u16 queue = ieee80211_ac_from_tid(tid); | ||
402 | |||
403 | ieee80211_wake_queue_by_reason( | 402 | ieee80211_wake_queue_by_reason( |
404 | &local->hw, queue, | 403 | &local->hw, ieee80211_ac_from_tid(tid), |
405 | IEEE80211_QUEUE_STOP_REASON_AGGREGATION); | 404 | IEEE80211_QUEUE_STOP_REASON_AGGREGATION); |
406 | } | 405 | } |
407 | 406 | ||
@@ -409,19 +408,21 @@ static void ieee80211_agg_splice_finish(struct ieee80211_local *local, | |||
409 | static void ieee80211_agg_tx_operational(struct ieee80211_local *local, | 408 | static void ieee80211_agg_tx_operational(struct ieee80211_local *local, |
410 | struct sta_info *sta, u16 tid) | 409 | struct sta_info *sta, u16 tid) |
411 | { | 410 | { |
411 | lockdep_assert_held(&sta->lock); | ||
412 | |||
412 | #ifdef CONFIG_MAC80211_HT_DEBUG | 413 | #ifdef CONFIG_MAC80211_HT_DEBUG |
413 | printk(KERN_DEBUG "Aggregation is on for tid %d\n", tid); | 414 | printk(KERN_DEBUG "Aggregation is on for tid %d\n", tid); |
414 | #endif | 415 | #endif |
415 | 416 | ||
416 | spin_lock(&local->ampdu_lock); | 417 | spin_lock(&local->ampdu_lock); |
417 | ieee80211_agg_splice_packets(local, sta, tid); | 418 | ieee80211_agg_splice_packets(local, sta->ampdu_mlme.tid_tx[tid], tid); |
418 | /* | 419 | /* |
419 | * NB: we rely on sta->lock being taken in the TX | 420 | * Now mark as operational. This will be visible |
420 | * processing here when adding to the pending queue, | 421 | * in the TX path, and lets it go lock-free in |
421 | * otherwise we could only change the state of the | 422 | * the common case. |
422 | * session to OPERATIONAL _here_. | ||
423 | */ | 423 | */ |
424 | ieee80211_agg_splice_finish(local, sta, tid); | 424 | set_bit(HT_AGG_STATE_OPERATIONAL, &sta->ampdu_mlme.tid_tx[tid]->state); |
425 | ieee80211_agg_splice_finish(local, tid); | ||
425 | spin_unlock(&local->ampdu_lock); | 426 | spin_unlock(&local->ampdu_lock); |
426 | 427 | ||
427 | drv_ampdu_action(local, sta->sdata, | 428 | drv_ampdu_action(local, sta->sdata, |
@@ -434,7 +435,7 @@ void ieee80211_start_tx_ba_cb(struct ieee80211_vif *vif, u8 *ra, u16 tid) | |||
434 | struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif); | 435 | struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif); |
435 | struct ieee80211_local *local = sdata->local; | 436 | struct ieee80211_local *local = sdata->local; |
436 | struct sta_info *sta; | 437 | struct sta_info *sta; |
437 | u8 *state; | 438 | struct tid_ampdu_tx *tid_tx; |
438 | 439 | ||
439 | trace_api_start_tx_ba_cb(sdata, ra, tid); | 440 | trace_api_start_tx_ba_cb(sdata, ra, tid); |
440 | 441 | ||
@@ -456,25 +457,22 @@ void ieee80211_start_tx_ba_cb(struct ieee80211_vif *vif, u8 *ra, u16 tid) | |||
456 | return; | 457 | return; |
457 | } | 458 | } |
458 | 459 | ||
459 | state = &sta->ampdu_mlme.tid_state_tx[tid]; | ||
460 | spin_lock_bh(&sta->lock); | 460 | spin_lock_bh(&sta->lock); |
461 | tid_tx = sta->ampdu_mlme.tid_tx[tid]; | ||
461 | 462 | ||
462 | if (WARN_ON(!(*state & HT_ADDBA_REQUESTED_MSK))) { | 463 | if (WARN_ON(!tid_tx)) { |
463 | #ifdef CONFIG_MAC80211_HT_DEBUG | 464 | #ifdef CONFIG_MAC80211_HT_DEBUG |
464 | printk(KERN_DEBUG "addBA was not requested yet, state is %d\n", | 465 | printk(KERN_DEBUG "addBA was not requested!\n"); |
465 | *state); | ||
466 | #endif | 466 | #endif |
467 | spin_unlock_bh(&sta->lock); | 467 | spin_unlock_bh(&sta->lock); |
468 | rcu_read_unlock(); | 468 | rcu_read_unlock(); |
469 | return; | 469 | return; |
470 | } | 470 | } |
471 | 471 | ||
472 | if (WARN_ON(*state & HT_ADDBA_DRV_READY_MSK)) | 472 | if (WARN_ON(test_and_set_bit(HT_AGG_STATE_DRV_READY, &tid_tx->state))) |
473 | goto out; | 473 | goto out; |
474 | 474 | ||
475 | *state |= HT_ADDBA_DRV_READY_MSK; | 475 | if (test_bit(HT_AGG_STATE_RESPONSE_RECEIVED, &tid_tx->state)) |
476 | |||
477 | if (*state == HT_AGG_STATE_OPERATIONAL) | ||
478 | ieee80211_agg_tx_operational(local, sta, tid); | 476 | ieee80211_agg_tx_operational(local, sta, tid); |
479 | 477 | ||
480 | out: | 478 | out: |
@@ -512,14 +510,14 @@ EXPORT_SYMBOL(ieee80211_start_tx_ba_cb_irqsafe); | |||
512 | int __ieee80211_stop_tx_ba_session(struct sta_info *sta, u16 tid, | 510 | int __ieee80211_stop_tx_ba_session(struct sta_info *sta, u16 tid, |
513 | enum ieee80211_back_parties initiator) | 511 | enum ieee80211_back_parties initiator) |
514 | { | 512 | { |
515 | u8 *state; | 513 | struct tid_ampdu_tx *tid_tx; |
516 | int ret; | 514 | int ret; |
517 | 515 | ||
518 | /* check if the TID is in aggregation */ | ||
519 | state = &sta->ampdu_mlme.tid_state_tx[tid]; | ||
520 | spin_lock_bh(&sta->lock); | 516 | spin_lock_bh(&sta->lock); |
517 | tid_tx = sta->ampdu_mlme.tid_tx[tid]; | ||
521 | 518 | ||
522 | if (*state != HT_AGG_STATE_OPERATIONAL) { | 519 | /* check if the TID is in aggregation */ |
520 | if (!tid_tx || !test_bit(HT_AGG_STATE_OPERATIONAL, &tid_tx->state)) { | ||
523 | ret = -ENOENT; | 521 | ret = -ENOENT; |
524 | goto unlock; | 522 | goto unlock; |
525 | } | 523 | } |
@@ -554,7 +552,7 @@ void ieee80211_stop_tx_ba_cb(struct ieee80211_vif *vif, u8 *ra, u8 tid) | |||
554 | struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif); | 552 | struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif); |
555 | struct ieee80211_local *local = sdata->local; | 553 | struct ieee80211_local *local = sdata->local; |
556 | struct sta_info *sta; | 554 | struct sta_info *sta; |
557 | u8 *state; | 555 | struct tid_ampdu_tx *tid_tx; |
558 | 556 | ||
559 | trace_api_stop_tx_ba_cb(sdata, ra, tid); | 557 | trace_api_stop_tx_ba_cb(sdata, ra, tid); |
560 | 558 | ||
@@ -580,39 +578,45 @@ void ieee80211_stop_tx_ba_cb(struct ieee80211_vif *vif, u8 *ra, u8 tid) | |||
580 | rcu_read_unlock(); | 578 | rcu_read_unlock(); |
581 | return; | 579 | return; |
582 | } | 580 | } |
583 | state = &sta->ampdu_mlme.tid_state_tx[tid]; | ||
584 | 581 | ||
585 | /* NOTE: no need to use sta->lock in this state check, as | 582 | spin_lock_bh(&sta->lock); |
586 | * ieee80211_stop_tx_ba_session will let only one stop call to | 583 | tid_tx = sta->ampdu_mlme.tid_tx[tid]; |
587 | * pass through per sta/tid | 584 | |
588 | */ | 585 | if (!tid_tx || !test_bit(HT_AGG_STATE_STOPPING, &tid_tx->state)) { |
589 | if ((*state & HT_AGG_STATE_REQ_STOP_BA_MSK) == 0) { | ||
590 | #ifdef CONFIG_MAC80211_HT_DEBUG | 586 | #ifdef CONFIG_MAC80211_HT_DEBUG |
591 | printk(KERN_DEBUG "unexpected callback to A-MPDU stop\n"); | 587 | printk(KERN_DEBUG "unexpected callback to A-MPDU stop\n"); |
592 | #endif | 588 | #endif |
589 | spin_unlock_bh(&sta->lock); | ||
593 | rcu_read_unlock(); | 590 | rcu_read_unlock(); |
594 | return; | 591 | return; |
595 | } | 592 | } |
596 | 593 | ||
597 | if (*state & HT_AGG_STATE_INITIATOR_MSK) | 594 | if (tid_tx->stop_initiator == WLAN_BACK_INITIATOR) |
598 | ieee80211_send_delba(sta->sdata, ra, tid, | 595 | ieee80211_send_delba(sta->sdata, ra, tid, |
599 | WLAN_BACK_INITIATOR, WLAN_REASON_QSTA_NOT_USE); | 596 | WLAN_BACK_INITIATOR, WLAN_REASON_QSTA_NOT_USE); |
600 | 597 | ||
601 | spin_lock_bh(&sta->lock); | 598 | /* |
602 | spin_lock(&local->ampdu_lock); | 599 | * When we get here, the TX path will not be lockless any more wrt. |
600 | * aggregation, since the OPERATIONAL bit has long been cleared. | ||
601 | * Thus it will block on getting the lock, if it occurs. So if we | ||
602 | * stop the queue now, we will not get any more packets, and any | ||
603 | * that might be being processed will wait for us here, thereby | ||
604 | * guaranteeing that no packets go to the tid_tx pending queue any | ||
605 | * more. | ||
606 | */ | ||
603 | 607 | ||
604 | ieee80211_agg_splice_packets(local, sta, tid); | 608 | spin_lock(&local->ampdu_lock); |
609 | ieee80211_agg_splice_packets(local, tid_tx, tid); | ||
605 | 610 | ||
606 | *state = HT_AGG_STATE_IDLE; | 611 | /* future packets must not find the tid_tx struct any more */ |
607 | /* from now on packets are no longer put onto sta->pending */ | 612 | rcu_assign_pointer(sta->ampdu_mlme.tid_tx[tid], NULL); |
608 | kfree(sta->ampdu_mlme.tid_tx[tid]); | ||
609 | sta->ampdu_mlme.tid_tx[tid] = NULL; | ||
610 | 613 | ||
611 | ieee80211_agg_splice_finish(local, sta, tid); | 614 | ieee80211_agg_splice_finish(local, tid); |
612 | 615 | ||
616 | call_rcu(&tid_tx->rcu_head, kfree_tid_tx); | ||
613 | spin_unlock(&local->ampdu_lock); | 617 | spin_unlock(&local->ampdu_lock); |
614 | spin_unlock_bh(&sta->lock); | ||
615 | 618 | ||
619 | spin_unlock_bh(&sta->lock); | ||
616 | rcu_read_unlock(); | 620 | rcu_read_unlock(); |
617 | } | 621 | } |
618 | EXPORT_SYMBOL(ieee80211_stop_tx_ba_cb); | 622 | EXPORT_SYMBOL(ieee80211_stop_tx_ba_cb); |
@@ -649,40 +653,41 @@ void ieee80211_process_addba_resp(struct ieee80211_local *local, | |||
649 | struct ieee80211_mgmt *mgmt, | 653 | struct ieee80211_mgmt *mgmt, |
650 | size_t len) | 654 | size_t len) |
651 | { | 655 | { |
656 | struct tid_ampdu_tx *tid_tx; | ||
652 | u16 capab, tid; | 657 | u16 capab, tid; |
653 | u8 *state; | ||
654 | 658 | ||
655 | capab = le16_to_cpu(mgmt->u.action.u.addba_resp.capab); | 659 | capab = le16_to_cpu(mgmt->u.action.u.addba_resp.capab); |
656 | tid = (capab & IEEE80211_ADDBA_PARAM_TID_MASK) >> 2; | 660 | tid = (capab & IEEE80211_ADDBA_PARAM_TID_MASK) >> 2; |
657 | 661 | ||
658 | state = &sta->ampdu_mlme.tid_state_tx[tid]; | ||
659 | |||
660 | spin_lock_bh(&sta->lock); | 662 | spin_lock_bh(&sta->lock); |
661 | 663 | ||
662 | if (!(*state & HT_ADDBA_REQUESTED_MSK)) | 664 | tid_tx = sta->ampdu_mlme.tid_tx[tid]; |
665 | |||
666 | if (!tid_tx) | ||
663 | goto out; | 667 | goto out; |
664 | 668 | ||
665 | if (mgmt->u.action.u.addba_resp.dialog_token != | 669 | if (mgmt->u.action.u.addba_resp.dialog_token != tid_tx->dialog_token) { |
666 | sta->ampdu_mlme.tid_tx[tid]->dialog_token) { | ||
667 | #ifdef CONFIG_MAC80211_HT_DEBUG | 670 | #ifdef CONFIG_MAC80211_HT_DEBUG |
668 | printk(KERN_DEBUG "wrong addBA response token, tid %d\n", tid); | 671 | printk(KERN_DEBUG "wrong addBA response token, tid %d\n", tid); |
669 | #endif /* CONFIG_MAC80211_HT_DEBUG */ | 672 | #endif |
670 | goto out; | 673 | goto out; |
671 | } | 674 | } |
672 | 675 | ||
673 | del_timer(&sta->ampdu_mlme.tid_tx[tid]->addba_resp_timer); | 676 | del_timer(&tid_tx->addba_resp_timer); |
674 | 677 | ||
675 | #ifdef CONFIG_MAC80211_HT_DEBUG | 678 | #ifdef CONFIG_MAC80211_HT_DEBUG |
676 | printk(KERN_DEBUG "switched off addBA timer for tid %d\n", tid); | 679 | printk(KERN_DEBUG "switched off addBA timer for tid %d\n", tid); |
677 | #endif /* CONFIG_MAC80211_HT_DEBUG */ | 680 | #endif |
678 | 681 | ||
679 | if (le16_to_cpu(mgmt->u.action.u.addba_resp.status) | 682 | if (le16_to_cpu(mgmt->u.action.u.addba_resp.status) |
680 | == WLAN_STATUS_SUCCESS) { | 683 | == WLAN_STATUS_SUCCESS) { |
681 | u8 curstate = *state; | 684 | if (test_and_set_bit(HT_AGG_STATE_RESPONSE_RECEIVED, |
682 | 685 | &tid_tx->state)) { | |
683 | *state |= HT_ADDBA_RECEIVED_MSK; | 686 | /* ignore duplicate response */ |
687 | goto out; | ||
688 | } | ||
684 | 689 | ||
685 | if (*state != curstate && *state == HT_AGG_STATE_OPERATIONAL) | 690 | if (test_bit(HT_AGG_STATE_DRV_READY, &tid_tx->state)) |
686 | ieee80211_agg_tx_operational(local, sta, tid); | 691 | ieee80211_agg_tx_operational(local, sta, tid); |
687 | 692 | ||
688 | sta->ampdu_mlme.addba_req_num[tid] = 0; | 693 | sta->ampdu_mlme.addba_req_num[tid] = 0; |