aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-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 /*