diff options
-rw-r--r-- | net/mac80211/agg-tx.c | 229 | ||||
-rw-r--r-- | net/mac80211/debugfs_sta.c | 8 | ||||
-rw-r--r-- | net/mac80211/ht.c | 9 | ||||
-rw-r--r-- | net/mac80211/ieee80211_i.h | 2 | ||||
-rw-r--r-- | net/mac80211/rc80211_minstrel_ht.c | 2 | ||||
-rw-r--r-- | net/mac80211/sta_info.c | 12 | ||||
-rw-r--r-- | net/mac80211/sta_info.h | 33 | ||||
-rw-r--r-- | net/mac80211/tx.c | 88 |
8 files changed, 206 insertions, 177 deletions
diff --git a/net/mac80211/agg-tx.c b/net/mac80211/agg-tx.c index c7b7ac40316a..7d8656d51c6b 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; |
diff --git a/net/mac80211/debugfs_sta.c b/net/mac80211/debugfs_sta.c index 95c1ea47ad35..8a74ffb36ad4 100644 --- a/net/mac80211/debugfs_sta.c +++ b/net/mac80211/debugfs_sta.c | |||
@@ -134,15 +134,15 @@ static ssize_t sta_agg_status_read(struct file *file, char __user *userbuf, | |||
134 | sta->ampdu_mlme.tid_rx[i]->ssn : 0); | 134 | sta->ampdu_mlme.tid_rx[i]->ssn : 0); |
135 | 135 | ||
136 | p += scnprintf(p, sizeof(buf) + buf - p, "\t\t%x", | 136 | p += scnprintf(p, sizeof(buf) + buf - p, "\t\t%x", |
137 | sta->ampdu_mlme.tid_state_tx[i]); | 137 | !!sta->ampdu_mlme.tid_tx[i]); |
138 | p += scnprintf(p, sizeof(buf) + buf - p, "\t%#.2x", | 138 | p += scnprintf(p, sizeof(buf) + buf - p, "\t%#.2x", |
139 | sta->ampdu_mlme.tid_state_tx[i] ? | 139 | sta->ampdu_mlme.tid_tx[i] ? |
140 | sta->ampdu_mlme.tid_tx[i]->dialog_token : 0); | 140 | sta->ampdu_mlme.tid_tx[i]->dialog_token : 0); |
141 | p += scnprintf(p, sizeof(buf) + buf - p, "\t%#.3x", | 141 | p += scnprintf(p, sizeof(buf) + buf - p, "\t%#.3x", |
142 | sta->ampdu_mlme.tid_state_tx[i] ? | 142 | sta->ampdu_mlme.tid_tx[i] ? |
143 | sta->ampdu_mlme.tid_tx[i]->ssn : 0); | 143 | sta->ampdu_mlme.tid_tx[i]->ssn : 0); |
144 | p += scnprintf(p, sizeof(buf) + buf - p, "\t%03d", | 144 | p += scnprintf(p, sizeof(buf) + buf - p, "\t%03d", |
145 | sta->ampdu_mlme.tid_state_tx[i] ? | 145 | sta->ampdu_mlme.tid_tx[i] ? |
146 | skb_queue_len(&sta->ampdu_mlme.tid_tx[i]->pending) : 0); | 146 | skb_queue_len(&sta->ampdu_mlme.tid_tx[i]->pending) : 0); |
147 | p += scnprintf(p, sizeof(buf) + buf - p, "\n"); | 147 | p += scnprintf(p, sizeof(buf) + buf - p, "\n"); |
148 | } | 148 | } |
diff --git a/net/mac80211/ht.c b/net/mac80211/ht.c index 2ab106a0a491..1af173ed2d5e 100644 --- a/net/mac80211/ht.c +++ b/net/mac80211/ht.c | |||
@@ -176,13 +176,8 @@ void ieee80211_process_delba(struct ieee80211_sub_if_data *sdata, | |||
176 | 176 | ||
177 | if (initiator == WLAN_BACK_INITIATOR) | 177 | if (initiator == WLAN_BACK_INITIATOR) |
178 | __ieee80211_stop_rx_ba_session(sta, tid, WLAN_BACK_INITIATOR, 0); | 178 | __ieee80211_stop_rx_ba_session(sta, tid, WLAN_BACK_INITIATOR, 0); |
179 | else { /* WLAN_BACK_RECIPIENT */ | 179 | else |
180 | spin_lock_bh(&sta->lock); | 180 | __ieee80211_stop_tx_ba_session(sta, tid, WLAN_BACK_RECIPIENT); |
181 | if (sta->ampdu_mlme.tid_state_tx[tid] & HT_ADDBA_REQUESTED_MSK) | ||
182 | ___ieee80211_stop_tx_ba_session(sta, tid, | ||
183 | WLAN_BACK_RECIPIENT); | ||
184 | spin_unlock_bh(&sta->lock); | ||
185 | } | ||
186 | } | 181 | } |
187 | 182 | ||
188 | int ieee80211_send_smps_action(struct ieee80211_sub_if_data *sdata, | 183 | int ieee80211_send_smps_action(struct ieee80211_sub_if_data *sdata, |
diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index bafe610dcf77..71bdd8b3c3f4 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h | |||
@@ -1119,8 +1119,6 @@ void ieee80211_process_addba_request(struct ieee80211_local *local, | |||
1119 | 1119 | ||
1120 | int __ieee80211_stop_tx_ba_session(struct sta_info *sta, u16 tid, | 1120 | int __ieee80211_stop_tx_ba_session(struct sta_info *sta, u16 tid, |
1121 | enum ieee80211_back_parties initiator); | 1121 | enum ieee80211_back_parties initiator); |
1122 | int ___ieee80211_stop_tx_ba_session(struct sta_info *sta, u16 tid, | ||
1123 | enum ieee80211_back_parties initiator); | ||
1124 | 1122 | ||
1125 | /* Spectrum management */ | 1123 | /* Spectrum management */ |
1126 | void ieee80211_process_measurement_req(struct ieee80211_sub_if_data *sdata, | 1124 | void ieee80211_process_measurement_req(struct ieee80211_sub_if_data *sdata, |
diff --git a/net/mac80211/rc80211_minstrel_ht.c b/net/mac80211/rc80211_minstrel_ht.c index c23f08251da4..7a04951fcb1f 100644 --- a/net/mac80211/rc80211_minstrel_ht.c +++ b/net/mac80211/rc80211_minstrel_ht.c | |||
@@ -365,7 +365,7 @@ minstrel_aggr_check(struct minstrel_priv *mp, struct ieee80211_sta *pubsta, stru | |||
365 | return; | 365 | return; |
366 | 366 | ||
367 | tid = *ieee80211_get_qos_ctl(hdr) & IEEE80211_QOS_CTL_TID_MASK; | 367 | tid = *ieee80211_get_qos_ctl(hdr) & IEEE80211_QOS_CTL_TID_MASK; |
368 | if (likely(sta->ampdu_mlme.tid_state_tx[tid] != HT_AGG_STATE_IDLE)) | 368 | if (likely(sta->ampdu_mlme.tid_tx[tid])) |
369 | return; | 369 | return; |
370 | 370 | ||
371 | ieee80211_start_tx_ba_session(pubsta, tid); | 371 | ieee80211_start_tx_ba_session(pubsta, tid); |
diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c index c426c572d984..06d8e00a2537 100644 --- a/net/mac80211/sta_info.c +++ b/net/mac80211/sta_info.c | |||
@@ -246,14 +246,12 @@ struct sta_info *sta_info_alloc(struct ieee80211_sub_if_data *sdata, | |||
246 | } | 246 | } |
247 | 247 | ||
248 | for (i = 0; i < STA_TID_NUM; i++) { | 248 | for (i = 0; i < STA_TID_NUM; i++) { |
249 | /* timer_to_tid must be initialized with identity mapping to | 249 | /* |
250 | * enable session_timer's data differentiation. refer to | 250 | * timer_to_tid must be initialized with identity mapping |
251 | * sta_rx_agg_session_timer_expired for useage */ | 251 | * to enable session_timer's data differentiation. See |
252 | * sta_rx_agg_session_timer_expired for usage. | ||
253 | */ | ||
252 | sta->timer_to_tid[i] = i; | 254 | sta->timer_to_tid[i] = i; |
253 | /* tx */ | ||
254 | sta->ampdu_mlme.tid_state_tx[i] = HT_AGG_STATE_IDLE; | ||
255 | sta->ampdu_mlme.tid_tx[i] = NULL; | ||
256 | sta->ampdu_mlme.addba_req_num[i] = 0; | ||
257 | } | 255 | } |
258 | skb_queue_head_init(&sta->ps_tx_buf); | 256 | skb_queue_head_init(&sta->ps_tx_buf); |
259 | skb_queue_head_init(&sta->tx_filtered); | 257 | skb_queue_head_init(&sta->tx_filtered); |
diff --git a/net/mac80211/sta_info.h b/net/mac80211/sta_info.h index 16864a6045b4..05278e6288c9 100644 --- a/net/mac80211/sta_info.h +++ b/net/mac80211/sta_info.h | |||
@@ -61,33 +61,40 @@ enum ieee80211_sta_info_flags { | |||
61 | 61 | ||
62 | #define STA_TID_NUM 16 | 62 | #define STA_TID_NUM 16 |
63 | #define ADDBA_RESP_INTERVAL HZ | 63 | #define ADDBA_RESP_INTERVAL HZ |
64 | #define HT_AGG_MAX_RETRIES (0x3) | 64 | #define HT_AGG_MAX_RETRIES 0x3 |
65 | 65 | ||
66 | #define HT_AGG_STATE_INITIATOR_SHIFT (4) | 66 | #define HT_AGG_STATE_DRV_READY 0 |
67 | 67 | #define HT_AGG_STATE_RESPONSE_RECEIVED 1 | |
68 | #define HT_ADDBA_REQUESTED_MSK BIT(0) | 68 | #define HT_AGG_STATE_OPERATIONAL 2 |
69 | #define HT_ADDBA_DRV_READY_MSK BIT(1) | 69 | #define HT_AGG_STATE_STOPPING 3 |
70 | #define HT_ADDBA_RECEIVED_MSK BIT(2) | ||
71 | #define HT_AGG_STATE_REQ_STOP_BA_MSK BIT(3) | ||
72 | #define HT_AGG_STATE_INITIATOR_MSK BIT(HT_AGG_STATE_INITIATOR_SHIFT) | ||
73 | #define HT_AGG_STATE_IDLE (0x0) | ||
74 | #define HT_AGG_STATE_OPERATIONAL (HT_ADDBA_REQUESTED_MSK | \ | ||
75 | HT_ADDBA_DRV_READY_MSK | \ | ||
76 | HT_ADDBA_RECEIVED_MSK) | ||
77 | 70 | ||
78 | /** | 71 | /** |
79 | * struct tid_ampdu_tx - TID aggregation information (Tx). | 72 | * struct tid_ampdu_tx - TID aggregation information (Tx). |
80 | * | 73 | * |
74 | * @rcu_head: rcu head for freeing structure | ||
81 | * @addba_resp_timer: timer for peer's response to addba request | 75 | * @addba_resp_timer: timer for peer's response to addba request |
82 | * @pending: pending frames queue -- use sta's spinlock to protect | 76 | * @pending: pending frames queue -- use sta's spinlock to protect |
83 | * @ssn: Starting Sequence Number expected to be aggregated. | 77 | * @ssn: Starting Sequence Number expected to be aggregated. |
84 | * @dialog_token: dialog token for aggregation session | 78 | * @dialog_token: dialog token for aggregation session |
79 | * @state: session state (see above) | ||
80 | * @stop_initiator: initiator of a session stop | ||
81 | * | ||
82 | * This structure is protected by RCU and the per-station | ||
83 | * spinlock. Assignments to the array holding it must hold | ||
84 | * the spinlock, only the TX path can access it under RCU | ||
85 | * lock-free if, and only if, the state has the flag | ||
86 | * %HT_AGG_STATE_OPERATIONAL set. Otherwise, the TX path | ||
87 | * must also acquire the spinlock and re-check the state, | ||
88 | * see comments in the tx code touching it. | ||
85 | */ | 89 | */ |
86 | struct tid_ampdu_tx { | 90 | struct tid_ampdu_tx { |
91 | struct rcu_head rcu_head; | ||
87 | struct timer_list addba_resp_timer; | 92 | struct timer_list addba_resp_timer; |
88 | struct sk_buff_head pending; | 93 | struct sk_buff_head pending; |
94 | unsigned long state; | ||
89 | u16 ssn; | 95 | u16 ssn; |
90 | u8 dialog_token; | 96 | u8 dialog_token; |
97 | u8 stop_initiator; | ||
91 | }; | 98 | }; |
92 | 99 | ||
93 | /** | 100 | /** |
@@ -129,7 +136,6 @@ struct tid_ampdu_rx { | |||
129 | * struct sta_ampdu_mlme - STA aggregation information. | 136 | * struct sta_ampdu_mlme - STA aggregation information. |
130 | * | 137 | * |
131 | * @tid_rx: aggregation info for Rx per TID -- RCU protected | 138 | * @tid_rx: aggregation info for Rx per TID -- RCU protected |
132 | * @tid_state_tx: TID's state in Tx session state machine. | ||
133 | * @tid_tx: aggregation info for Tx per TID | 139 | * @tid_tx: aggregation info for Tx per TID |
134 | * @addba_req_num: number of times addBA request has been sent. | 140 | * @addba_req_num: number of times addBA request has been sent. |
135 | * @dialog_token_allocator: dialog token enumerator for each new session; | 141 | * @dialog_token_allocator: dialog token enumerator for each new session; |
@@ -138,7 +144,6 @@ struct sta_ampdu_mlme { | |||
138 | /* rx */ | 144 | /* rx */ |
139 | struct tid_ampdu_rx *tid_rx[STA_TID_NUM]; | 145 | struct tid_ampdu_rx *tid_rx[STA_TID_NUM]; |
140 | /* tx */ | 146 | /* tx */ |
141 | u8 tid_state_tx[STA_TID_NUM]; | ||
142 | struct tid_ampdu_tx *tid_tx[STA_TID_NUM]; | 147 | struct tid_ampdu_tx *tid_tx[STA_TID_NUM]; |
143 | u8 addba_req_num[STA_TID_NUM]; | 148 | u8 addba_req_num[STA_TID_NUM]; |
144 | u8 dialog_token_allocator; | 149 | u8 dialog_token_allocator; |
diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c index 680bcb7093db..7bf1f9c9ea34 100644 --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c | |||
@@ -1092,6 +1092,54 @@ static bool __ieee80211_parse_tx_radiotap(struct ieee80211_tx_data *tx, | |||
1092 | return true; | 1092 | return true; |
1093 | } | 1093 | } |
1094 | 1094 | ||
1095 | static bool ieee80211_tx_prep_agg(struct ieee80211_tx_data *tx, | ||
1096 | struct sk_buff *skb, | ||
1097 | struct ieee80211_tx_info *info, | ||
1098 | struct tid_ampdu_tx *tid_tx, | ||
1099 | int tid) | ||
1100 | { | ||
1101 | bool queued = false; | ||
1102 | |||
1103 | if (test_bit(HT_AGG_STATE_OPERATIONAL, &tid_tx->state)) { | ||
1104 | info->flags |= IEEE80211_TX_CTL_AMPDU; | ||
1105 | } else { | ||
1106 | spin_lock(&tx->sta->lock); | ||
1107 | /* | ||
1108 | * Need to re-check now, because we may get here | ||
1109 | * | ||
1110 | * 1) in the window during which the setup is actually | ||
1111 | * already done, but not marked yet because not all | ||
1112 | * packets are spliced over to the driver pending | ||
1113 | * queue yet -- if this happened we acquire the lock | ||
1114 | * either before or after the splice happens, but | ||
1115 | * need to recheck which of these cases happened. | ||
1116 | * | ||
1117 | * 2) during session teardown, if the OPERATIONAL bit | ||
1118 | * was cleared due to the teardown but the pointer | ||
1119 | * hasn't been assigned NULL yet (or we loaded it | ||
1120 | * before it was assigned) -- in this case it may | ||
1121 | * now be NULL which means we should just let the | ||
1122 | * packet pass through because splicing the frames | ||
1123 | * back is already done. | ||
1124 | */ | ||
1125 | tid_tx = tx->sta->ampdu_mlme.tid_tx[tid]; | ||
1126 | |||
1127 | if (!tid_tx) { | ||
1128 | /* do nothing, let packet pass through */ | ||
1129 | } else if (test_bit(HT_AGG_STATE_OPERATIONAL, &tid_tx->state)) { | ||
1130 | info->flags |= IEEE80211_TX_CTL_AMPDU; | ||
1131 | } else { | ||
1132 | queued = true; | ||
1133 | info->control.vif = &tx->sdata->vif; | ||
1134 | info->flags |= IEEE80211_TX_INTFL_NEED_TXPROCESSING; | ||
1135 | __skb_queue_tail(&tid_tx->pending, skb); | ||
1136 | } | ||
1137 | spin_unlock(&tx->sta->lock); | ||
1138 | } | ||
1139 | |||
1140 | return queued; | ||
1141 | } | ||
1142 | |||
1095 | /* | 1143 | /* |
1096 | * initialises @tx | 1144 | * initialises @tx |
1097 | */ | 1145 | */ |
@@ -1104,8 +1152,7 @@ ieee80211_tx_prepare(struct ieee80211_sub_if_data *sdata, | |||
1104 | struct ieee80211_hdr *hdr; | 1152 | struct ieee80211_hdr *hdr; |
1105 | struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); | 1153 | struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); |
1106 | int hdrlen, tid; | 1154 | int hdrlen, tid; |
1107 | u8 *qc, *state; | 1155 | u8 *qc; |
1108 | bool queued = false; | ||
1109 | 1156 | ||
1110 | memset(tx, 0, sizeof(*tx)); | 1157 | memset(tx, 0, sizeof(*tx)); |
1111 | tx->skb = skb; | 1158 | tx->skb = skb; |
@@ -1157,35 +1204,16 @@ ieee80211_tx_prepare(struct ieee80211_sub_if_data *sdata, | |||
1157 | qc = ieee80211_get_qos_ctl(hdr); | 1204 | qc = ieee80211_get_qos_ctl(hdr); |
1158 | tid = *qc & IEEE80211_QOS_CTL_TID_MASK; | 1205 | tid = *qc & IEEE80211_QOS_CTL_TID_MASK; |
1159 | 1206 | ||
1160 | spin_lock(&tx->sta->lock); | 1207 | tid_tx = rcu_dereference(tx->sta->ampdu_mlme.tid_tx[tid]); |
1161 | /* | 1208 | if (tid_tx) { |
1162 | * XXX: This spinlock could be fairly expensive, but see the | 1209 | bool queued; |
1163 | * comment in agg-tx.c:ieee80211_agg_tx_operational(). | ||
1164 | * One way to solve this would be to do something RCU-like | ||
1165 | * for managing the tid_tx struct and using atomic bitops | ||
1166 | * for the actual state -- by introducing an actual | ||
1167 | * 'operational' bit that would be possible. It would | ||
1168 | * require changing ieee80211_agg_tx_operational() to | ||
1169 | * set that bit, and changing the way tid_tx is managed | ||
1170 | * everywhere, including races between that bit and | ||
1171 | * tid_tx going away (tid_tx being added can be easily | ||
1172 | * committed to memory before the 'operational' bit). | ||
1173 | */ | ||
1174 | tid_tx = tx->sta->ampdu_mlme.tid_tx[tid]; | ||
1175 | state = &tx->sta->ampdu_mlme.tid_state_tx[tid]; | ||
1176 | if (*state == HT_AGG_STATE_OPERATIONAL) { | ||
1177 | info->flags |= IEEE80211_TX_CTL_AMPDU; | ||
1178 | } else if (*state != HT_AGG_STATE_IDLE) { | ||
1179 | /* in progress */ | ||
1180 | queued = true; | ||
1181 | info->control.vif = &sdata->vif; | ||
1182 | info->flags |= IEEE80211_TX_INTFL_NEED_TXPROCESSING; | ||
1183 | __skb_queue_tail(&tid_tx->pending, skb); | ||
1184 | } | ||
1185 | spin_unlock(&tx->sta->lock); | ||
1186 | 1210 | ||
1187 | if (unlikely(queued)) | 1211 | queued = ieee80211_tx_prep_agg(tx, skb, info, |
1188 | return TX_QUEUED; | 1212 | tid_tx, tid); |
1213 | |||
1214 | if (unlikely(queued)) | ||
1215 | return TX_QUEUED; | ||
1216 | } | ||
1189 | } | 1217 | } |
1190 | 1218 | ||
1191 | if (is_multicast_ether_addr(hdr->addr1)) { | 1219 | if (is_multicast_ether_addr(hdr->addr1)) { |