aboutsummaryrefslogtreecommitdiffstats
path: root/net
diff options
context:
space:
mode:
authorJohannes Berg <johannes.berg@intel.com>2010-06-10 04:21:42 -0400
committerJohn W. Linville <linville@tuxdriver.com>2010-06-14 15:39:27 -0400
commit0ab337032a0dfcd5f2527d3306d3deeba5f95b59 (patch)
tree88795edc149fd88574fece0c8f23774ee8a9155c /net
parenta6a67db2bc89d2b1ff07e0817f11235c20d2c329 (diff)
mac80211: make TX aggregation start/stop request async
When the driver or rate control requests starting or stopping an aggregation session, that currently causes a direct callback into the driver, which could potentially cause locking problems. Also, the functions need to be callable from contexts that cannot sleep, and thus will interfere with making the ampdu_action callback sleeping. To address these issues, add a new work item for each station that will process any start or stop requests out of line. Signed-off-by: Johannes Berg <johannes.berg@intel.com> Signed-off-by: John W. Linville <linville@tuxdriver.com>
Diffstat (limited to 'net')
-rw-r--r--net/mac80211/agg-tx.c177
-rw-r--r--net/mac80211/debugfs_sta.c5
-rw-r--r--net/mac80211/ht.c2
-rw-r--r--net/mac80211/ieee80211_i.h1
-rw-r--r--net/mac80211/sta_info.c1
-rw-r--r--net/mac80211/sta_info.h6
-rw-r--r--net/mac80211/tx.c5
7 files changed, 139 insertions, 58 deletions
diff --git a/net/mac80211/agg-tx.c b/net/mac80211/agg-tx.c
index 7f2042d37904..ee8bfe18b488 100644
--- a/net/mac80211/agg-tx.c
+++ b/net/mac80211/agg-tx.c
@@ -146,6 +146,13 @@ static int ___ieee80211_stop_tx_ba_session(
146 if (WARN_ON(!tid_tx)) 146 if (WARN_ON(!tid_tx))
147 return -ENOENT; 147 return -ENOENT;
148 148
149 if (test_bit(HT_AGG_STATE_WANT_START, &tid_tx->state)) {
150 /* not even started yet! */
151 rcu_assign_pointer(sta->ampdu_mlme.tid_tx[tid], NULL);
152 call_rcu(&tid_tx->rcu_head, kfree_tid_tx);
153 return 0;
154 }
155
149#ifdef CONFIG_MAC80211_HT_DEBUG 156#ifdef CONFIG_MAC80211_HT_DEBUG
150 printk(KERN_DEBUG "Tx BA session stop requested for %pM tid %u\n", 157 printk(KERN_DEBUG "Tx BA session stop requested for %pM tid %u\n",
151 sta->sta.addr, tid); 158 sta->sta.addr, tid);
@@ -255,6 +262,94 @@ ieee80211_wake_queue_agg(struct ieee80211_local *local, int tid)
255 __release(agg_queue); 262 __release(agg_queue);
256} 263}
257 264
265static void ieee80211_tx_ba_session_handle_start(struct sta_info *sta, int tid)
266{
267 struct tid_ampdu_tx *tid_tx = sta->ampdu_mlme.tid_tx[tid];
268 struct ieee80211_local *local = sta->local;
269 struct ieee80211_sub_if_data *sdata = sta->sdata;
270 u16 start_seq_num;
271 int ret;
272
273 /*
274 * While we're asking the driver about the aggregation,
275 * stop the AC queue so that we don't have to worry
276 * about frames that came in while we were doing that,
277 * which would require us to put them to the AC pending
278 * afterwards which just makes the code more complex.
279 */
280 ieee80211_stop_queue_agg(local, tid);
281
282 clear_bit(HT_AGG_STATE_WANT_START, &tid_tx->state);
283
284 /*
285 * This might be off by one due to a race that we can't
286 * really prevent here without synchronize_net() which
287 * can't be called now.
288 */
289 start_seq_num = sta->tid_seq[tid] >> 4;
290
291 ret = drv_ampdu_action(local, sdata, IEEE80211_AMPDU_TX_START,
292 &sta->sta, tid, &start_seq_num);
293 if (ret) {
294#ifdef CONFIG_MAC80211_HT_DEBUG
295 printk(KERN_DEBUG "BA request denied - HW unavailable for"
296 " tid %d\n", tid);
297#endif
298 rcu_assign_pointer(sta->ampdu_mlme.tid_tx[tid], NULL);
299 ieee80211_wake_queue_agg(local, tid);
300 call_rcu(&tid_tx->rcu_head, kfree_tid_tx);
301 return;
302 }
303
304 /* we can take packets again now */
305 ieee80211_wake_queue_agg(local, tid);
306
307 /* activate the timer for the recipient's addBA response */
308 mod_timer(&tid_tx->addba_resp_timer, jiffies + ADDBA_RESP_INTERVAL);
309#ifdef CONFIG_MAC80211_HT_DEBUG
310 printk(KERN_DEBUG "activated addBA response timer on tid %d\n", tid);
311#endif
312
313 sta->ampdu_mlme.addba_req_num[tid]++;
314
315 /* send AddBA request */
316 ieee80211_send_addba_request(sdata, sta->sta.addr, tid,
317 tid_tx->dialog_token, start_seq_num,
318 0x40, 5000);
319}
320
321void ieee80211_tx_ba_session_work(struct work_struct *work)
322{
323 struct sta_info *sta =
324 container_of(work, struct sta_info, ampdu_mlme.work);
325 struct tid_ampdu_tx *tid_tx;
326 int tid;
327
328 /*
329 * When this flag is set, new sessions should be
330 * blocked, and existing sessions will be torn
331 * down by the code that set the flag, so this
332 * need not run.
333 */
334 if (test_sta_flags(sta, WLAN_STA_BLOCK_BA))
335 return;
336
337 spin_lock_bh(&sta->lock);
338 for (tid = 0; tid < STA_TID_NUM; tid++) {
339 tid_tx = sta->ampdu_mlme.tid_tx[tid];
340 if (!tid_tx)
341 continue;
342
343 if (test_bit(HT_AGG_STATE_WANT_START, &tid_tx->state))
344 ieee80211_tx_ba_session_handle_start(sta, tid);
345 else if (test_and_clear_bit(HT_AGG_STATE_WANT_STOP,
346 &tid_tx->state))
347 ___ieee80211_stop_tx_ba_session(sta, tid,
348 WLAN_BACK_INITIATOR);
349 }
350 spin_unlock_bh(&sta->lock);
351}
352
258int ieee80211_start_tx_ba_session(struct ieee80211_sta *pubsta, u16 tid) 353int ieee80211_start_tx_ba_session(struct ieee80211_sta *pubsta, u16 tid)
259{ 354{
260 struct sta_info *sta = container_of(pubsta, struct sta_info, sta); 355 struct sta_info *sta = container_of(pubsta, struct sta_info, sta);
@@ -262,7 +357,6 @@ int ieee80211_start_tx_ba_session(struct ieee80211_sta *pubsta, u16 tid)
262 struct ieee80211_local *local = sdata->local; 357 struct ieee80211_local *local = sdata->local;
263 struct tid_ampdu_tx *tid_tx; 358 struct tid_ampdu_tx *tid_tx;
264 int ret = 0; 359 int ret = 0;
265 u16 start_seq_num;
266 360
267 trace_api_start_tx_ba_session(pubsta, tid); 361 trace_api_start_tx_ba_session(pubsta, tid);
268 362
@@ -316,15 +410,6 @@ int ieee80211_start_tx_ba_session(struct ieee80211_sta *pubsta, u16 tid)
316 goto err_unlock_sta; 410 goto err_unlock_sta;
317 } 411 }
318 412
319 /*
320 * While we're asking the driver about the aggregation,
321 * stop the AC queue so that we don't have to worry
322 * about frames that came in while we were doing that,
323 * which would require us to put them to the AC pending
324 * afterwards which just makes the code more complex.
325 */
326 ieee80211_stop_queue_agg(local, tid);
327
328 /* prepare A-MPDU MLME for Tx aggregation */ 413 /* prepare A-MPDU MLME for Tx aggregation */
329 tid_tx = kzalloc(sizeof(struct tid_ampdu_tx), GFP_ATOMIC); 414 tid_tx = kzalloc(sizeof(struct tid_ampdu_tx), GFP_ATOMIC);
330 if (!tid_tx) { 415 if (!tid_tx) {
@@ -334,59 +419,27 @@ int ieee80211_start_tx_ba_session(struct ieee80211_sta *pubsta, u16 tid)
334 tid); 419 tid);
335#endif 420#endif
336 ret = -ENOMEM; 421 ret = -ENOMEM;
337 goto err_wake_queue; 422 goto err_unlock_sta;
338 } 423 }
339 424
340 skb_queue_head_init(&tid_tx->pending); 425 skb_queue_head_init(&tid_tx->pending);
426 __set_bit(HT_AGG_STATE_WANT_START, &tid_tx->state);
341 427
342 /* Tx timer */ 428 /* Tx timer */
343 tid_tx->addba_resp_timer.function = sta_addba_resp_timer_expired; 429 tid_tx->addba_resp_timer.function = sta_addba_resp_timer_expired;
344 tid_tx->addba_resp_timer.data = (unsigned long)&sta->timer_to_tid[tid]; 430 tid_tx->addba_resp_timer.data = (unsigned long)&sta->timer_to_tid[tid];
345 init_timer(&tid_tx->addba_resp_timer); 431 init_timer(&tid_tx->addba_resp_timer);
346 432
347 start_seq_num = sta->tid_seq[tid] >> 4; 433 /* assign a dialog token */
348
349 ret = drv_ampdu_action(local, sdata, IEEE80211_AMPDU_TX_START,
350 pubsta, tid, &start_seq_num);
351 if (ret) {
352#ifdef CONFIG_MAC80211_HT_DEBUG
353 printk(KERN_DEBUG "BA request denied - HW unavailable for"
354 " tid %d\n", tid);
355#endif /* CONFIG_MAC80211_HT_DEBUG */
356 goto err_free;
357 }
358
359 rcu_assign_pointer(sta->ampdu_mlme.tid_tx[tid], tid_tx);
360
361 /* Driver vetoed or OKed, but we can take packets again now */
362 ieee80211_wake_queue_agg(local, tid);
363
364 /* activate the timer for the recipient's addBA response */
365 tid_tx->addba_resp_timer.expires = jiffies + ADDBA_RESP_INTERVAL;
366 add_timer(&tid_tx->addba_resp_timer);
367#ifdef CONFIG_MAC80211_HT_DEBUG
368 printk(KERN_DEBUG "activated addBA response timer on tid %d\n", tid);
369#endif
370
371 /* prepare tid data */
372 sta->ampdu_mlme.dialog_token_allocator++; 434 sta->ampdu_mlme.dialog_token_allocator++;
373 tid_tx->dialog_token = sta->ampdu_mlme.dialog_token_allocator; 435 tid_tx->dialog_token = sta->ampdu_mlme.dialog_token_allocator;
374 tid_tx->ssn = start_seq_num;
375
376 sta->ampdu_mlme.addba_req_num[tid]++;
377 436
378 spin_unlock_bh(&sta->lock); 437 /* finally, assign it to the array */
438 rcu_assign_pointer(sta->ampdu_mlme.tid_tx[tid], tid_tx);
379 439
380 /* send AddBA request */ 440 ieee80211_queue_work(&local->hw, &sta->ampdu_mlme.work);
381 ieee80211_send_addba_request(sdata, pubsta->addr, tid,
382 tid_tx->dialog_token, tid_tx->ssn,
383 0x40, 5000);
384 return 0;
385 441
386 err_free: 442 /* this flow continues off the work */
387 kfree(tid_tx);
388 err_wake_queue:
389 ieee80211_wake_queue_agg(local, tid);
390 err_unlock_sta: 443 err_unlock_sta:
391 spin_unlock_bh(&sta->lock); 444 spin_unlock_bh(&sta->lock);
392 return ret; 445 return ret;
@@ -534,8 +587,7 @@ int __ieee80211_stop_tx_ba_session(struct sta_info *sta, u16 tid,
534 spin_lock_bh(&sta->lock); 587 spin_lock_bh(&sta->lock);
535 tid_tx = sta->ampdu_mlme.tid_tx[tid]; 588 tid_tx = sta->ampdu_mlme.tid_tx[tid];
536 589
537 /* check if the TID is in aggregation */ 590 if (!tid_tx) {
538 if (!tid_tx || !test_bit(HT_AGG_STATE_OPERATIONAL, &tid_tx->state)) {
539 ret = -ENOENT; 591 ret = -ENOENT;
540 goto unlock; 592 goto unlock;
541 } 593 }
@@ -552,6 +604,8 @@ int ieee80211_stop_tx_ba_session(struct ieee80211_sta *pubsta, u16 tid)
552 struct sta_info *sta = container_of(pubsta, struct sta_info, sta); 604 struct sta_info *sta = container_of(pubsta, struct sta_info, sta);
553 struct ieee80211_sub_if_data *sdata = sta->sdata; 605 struct ieee80211_sub_if_data *sdata = sta->sdata;
554 struct ieee80211_local *local = sdata->local; 606 struct ieee80211_local *local = sdata->local;
607 struct tid_ampdu_tx *tid_tx;
608 int ret = 0;
555 609
556 trace_api_stop_tx_ba_session(pubsta, tid); 610 trace_api_stop_tx_ba_session(pubsta, tid);
557 611
@@ -561,7 +615,26 @@ int ieee80211_stop_tx_ba_session(struct ieee80211_sta *pubsta, u16 tid)
561 if (tid >= STA_TID_NUM) 615 if (tid >= STA_TID_NUM)
562 return -EINVAL; 616 return -EINVAL;
563 617
564 return __ieee80211_stop_tx_ba_session(sta, tid, WLAN_BACK_INITIATOR); 618 spin_lock_bh(&sta->lock);
619 tid_tx = sta->ampdu_mlme.tid_tx[tid];
620
621 if (!tid_tx) {
622 ret = -ENOENT;
623 goto unlock;
624 }
625
626 if (test_bit(HT_AGG_STATE_STOPPING, &tid_tx->state)) {
627 /* already in progress stopping it */
628 ret = 0;
629 goto unlock;
630 }
631
632 set_bit(HT_AGG_STATE_WANT_STOP, &tid_tx->state);
633 ieee80211_queue_work(&local->hw, &sta->ampdu_mlme.work);
634
635 unlock:
636 spin_unlock_bh(&sta->lock);
637 return ret;
565} 638}
566EXPORT_SYMBOL(ieee80211_stop_tx_ba_session); 639EXPORT_SYMBOL(ieee80211_stop_tx_ba_session);
567 640
diff --git a/net/mac80211/debugfs_sta.c b/net/mac80211/debugfs_sta.c
index 8a74ffb36ad4..76839d4dfaac 100644
--- a/net/mac80211/debugfs_sta.c
+++ b/net/mac80211/debugfs_sta.c
@@ -121,7 +121,7 @@ static ssize_t sta_agg_status_read(struct file *file, char __user *userbuf,
121 p += scnprintf(p, sizeof(buf) + buf - p, "next dialog_token: %#02x\n", 121 p += scnprintf(p, sizeof(buf) + buf - p, "next dialog_token: %#02x\n",
122 sta->ampdu_mlme.dialog_token_allocator + 1); 122 sta->ampdu_mlme.dialog_token_allocator + 1);
123 p += scnprintf(p, sizeof(buf) + buf - p, 123 p += scnprintf(p, sizeof(buf) + buf - p,
124 "TID\t\tRX active\tDTKN\tSSN\t\tTX\tDTKN\tSSN\tpending\n"); 124 "TID\t\tRX active\tDTKN\tSSN\t\tTX\tDTKN\tpending\n");
125 for (i = 0; i < STA_TID_NUM; i++) { 125 for (i = 0; i < STA_TID_NUM; i++) {
126 p += scnprintf(p, sizeof(buf) + buf - p, "%02d", i); 126 p += scnprintf(p, sizeof(buf) + buf - p, "%02d", i);
127 p += scnprintf(p, sizeof(buf) + buf - p, "\t\t%x", 127 p += scnprintf(p, sizeof(buf) + buf - p, "\t\t%x",
@@ -138,9 +138,6 @@ static ssize_t sta_agg_status_read(struct file *file, char __user *userbuf,
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_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",
142 sta->ampdu_mlme.tid_tx[i] ?
143 sta->ampdu_mlme.tid_tx[i]->ssn : 0);
144 p += scnprintf(p, sizeof(buf) + buf - p, "\t%03d", 141 p += scnprintf(p, sizeof(buf) + buf - p, "\t%03d",
145 sta->ampdu_mlme.tid_tx[i] ? 142 sta->ampdu_mlme.tid_tx[i] ?
146 skb_queue_len(&sta->ampdu_mlme.tid_tx[i]->pending) : 0); 143 skb_queue_len(&sta->ampdu_mlme.tid_tx[i]->pending) : 0);
diff --git a/net/mac80211/ht.c b/net/mac80211/ht.c
index 1af173ed2d5e..4dfba7808a24 100644
--- a/net/mac80211/ht.c
+++ b/net/mac80211/ht.c
@@ -105,6 +105,8 @@ void ieee80211_sta_tear_down_BA_sessions(struct sta_info *sta)
105{ 105{
106 int i; 106 int i;
107 107
108 cancel_work_sync(&sta->ampdu_mlme.work);
109
108 for (i = 0; i < STA_TID_NUM; i++) { 110 for (i = 0; i < STA_TID_NUM; i++) {
109 __ieee80211_stop_tx_ba_session(sta, i, WLAN_BACK_INITIATOR); 111 __ieee80211_stop_tx_ba_session(sta, i, WLAN_BACK_INITIATOR);
110 __ieee80211_stop_rx_ba_session(sta, i, WLAN_BACK_RECIPIENT, 112 __ieee80211_stop_rx_ba_session(sta, i, WLAN_BACK_RECIPIENT,
diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h
index 8a91b5d83870..aec84c36c4c1 100644
--- a/net/mac80211/ieee80211_i.h
+++ b/net/mac80211/ieee80211_i.h
@@ -1115,6 +1115,7 @@ int __ieee80211_stop_tx_ba_session(struct sta_info *sta, u16 tid,
1115 enum ieee80211_back_parties initiator); 1115 enum ieee80211_back_parties initiator);
1116void ieee80211_start_tx_ba_cb(struct ieee80211_vif *vif, u8 *ra, u16 tid); 1116void ieee80211_start_tx_ba_cb(struct ieee80211_vif *vif, u8 *ra, u16 tid);
1117void ieee80211_stop_tx_ba_cb(struct ieee80211_vif *vif, u8 *ra, u8 tid); 1117void ieee80211_stop_tx_ba_cb(struct ieee80211_vif *vif, u8 *ra, u8 tid);
1118void ieee80211_tx_ba_session_work(struct work_struct *work);
1118 1119
1119/* Spectrum management */ 1120/* Spectrum management */
1120void ieee80211_process_measurement_req(struct ieee80211_sub_if_data *sdata, 1121void ieee80211_process_measurement_req(struct ieee80211_sub_if_data *sdata,
diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c
index 06d8e00a2537..8aa8558ba21e 100644
--- a/net/mac80211/sta_info.c
+++ b/net/mac80211/sta_info.c
@@ -235,6 +235,7 @@ struct sta_info *sta_info_alloc(struct ieee80211_sub_if_data *sdata,
235 spin_lock_init(&sta->lock); 235 spin_lock_init(&sta->lock);
236 spin_lock_init(&sta->flaglock); 236 spin_lock_init(&sta->flaglock);
237 INIT_WORK(&sta->drv_unblock_wk, sta_unblock); 237 INIT_WORK(&sta->drv_unblock_wk, sta_unblock);
238 INIT_WORK(&sta->ampdu_mlme.work, ieee80211_tx_ba_session_work);
238 239
239 memcpy(sta->sta.addr, addr, ETH_ALEN); 240 memcpy(sta->sta.addr, addr, ETH_ALEN);
240 sta->local = local; 241 sta->local = local;
diff --git a/net/mac80211/sta_info.h b/net/mac80211/sta_info.h
index 05278e6288c9..040cbb0ac3af 100644
--- a/net/mac80211/sta_info.h
+++ b/net/mac80211/sta_info.h
@@ -67,6 +67,8 @@ enum ieee80211_sta_info_flags {
67#define HT_AGG_STATE_RESPONSE_RECEIVED 1 67#define HT_AGG_STATE_RESPONSE_RECEIVED 1
68#define HT_AGG_STATE_OPERATIONAL 2 68#define HT_AGG_STATE_OPERATIONAL 2
69#define HT_AGG_STATE_STOPPING 3 69#define HT_AGG_STATE_STOPPING 3
70#define HT_AGG_STATE_WANT_START 4
71#define HT_AGG_STATE_WANT_STOP 5
70 72
71/** 73/**
72 * struct tid_ampdu_tx - TID aggregation information (Tx). 74 * struct tid_ampdu_tx - TID aggregation information (Tx).
@@ -74,7 +76,6 @@ enum ieee80211_sta_info_flags {
74 * @rcu_head: rcu head for freeing structure 76 * @rcu_head: rcu head for freeing structure
75 * @addba_resp_timer: timer for peer's response to addba request 77 * @addba_resp_timer: timer for peer's response to addba request
76 * @pending: pending frames queue -- use sta's spinlock to protect 78 * @pending: pending frames queue -- use sta's spinlock to protect
77 * @ssn: Starting Sequence Number expected to be aggregated.
78 * @dialog_token: dialog token for aggregation session 79 * @dialog_token: dialog token for aggregation session
79 * @state: session state (see above) 80 * @state: session state (see above)
80 * @stop_initiator: initiator of a session stop 81 * @stop_initiator: initiator of a session stop
@@ -92,7 +93,6 @@ struct tid_ampdu_tx {
92 struct timer_list addba_resp_timer; 93 struct timer_list addba_resp_timer;
93 struct sk_buff_head pending; 94 struct sk_buff_head pending;
94 unsigned long state; 95 unsigned long state;
95 u16 ssn;
96 u8 dialog_token; 96 u8 dialog_token;
97 u8 stop_initiator; 97 u8 stop_initiator;
98}; 98};
@@ -139,11 +139,13 @@ struct tid_ampdu_rx {
139 * @tid_tx: aggregation info for Tx per TID 139 * @tid_tx: aggregation info for Tx per TID
140 * @addba_req_num: number of times addBA request has been sent. 140 * @addba_req_num: number of times addBA request has been sent.
141 * @dialog_token_allocator: dialog token enumerator for each new session; 141 * @dialog_token_allocator: dialog token enumerator for each new session;
142 * @work: work struct for starting/stopping aggregation
142 */ 143 */
143struct sta_ampdu_mlme { 144struct sta_ampdu_mlme {
144 /* rx */ 145 /* rx */
145 struct tid_ampdu_rx *tid_rx[STA_TID_NUM]; 146 struct tid_ampdu_rx *tid_rx[STA_TID_NUM];
146 /* tx */ 147 /* tx */
148 struct work_struct work;
147 struct tid_ampdu_tx *tid_tx[STA_TID_NUM]; 149 struct tid_ampdu_tx *tid_tx[STA_TID_NUM];
148 u8 addba_req_num[STA_TID_NUM]; 150 u8 addba_req_num[STA_TID_NUM];
149 u8 dialog_token_allocator; 151 u8 dialog_token_allocator;
diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c
index 7bf1f9c9ea34..698d4718b1a4 100644
--- a/net/mac80211/tx.c
+++ b/net/mac80211/tx.c
@@ -1102,6 +1102,11 @@ static bool ieee80211_tx_prep_agg(struct ieee80211_tx_data *tx,
1102 1102
1103 if (test_bit(HT_AGG_STATE_OPERATIONAL, &tid_tx->state)) { 1103 if (test_bit(HT_AGG_STATE_OPERATIONAL, &tid_tx->state)) {
1104 info->flags |= IEEE80211_TX_CTL_AMPDU; 1104 info->flags |= IEEE80211_TX_CTL_AMPDU;
1105 } else if (test_bit(HT_AGG_STATE_WANT_START, &tid_tx->state)) {
1106 /*
1107 * nothing -- this aggregation session is being started
1108 * but that might still fail with the driver
1109 */
1105 } else { 1110 } else {
1106 spin_lock(&tx->sta->lock); 1111 spin_lock(&tx->sta->lock);
1107 /* 1112 /*