aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJohannes Berg <johannes@sipsolutions.net>2010-04-06 05:18:47 -0400
committerJohn W. Linville <linville@tuxdriver.com>2010-04-07 14:38:05 -0400
commit098a607091426e79178b9a6c318d993fea131791 (patch)
treed62c9993e49f90ca962cd763b096911d22d7e051
parent618f356b95e37ca0c30b3b513898fda54abd52a6 (diff)
mac80211: clean up/fix aggregation code
The aggregation code has a number of quirks, like inventing an unneeded WLAN_BACK_TIMER value and leaking memory under certain circumstances during station destruction. Fix these issues by using the regular aggregation session teardown code and blocking new aggregation sessions, all before the station is really destructed. As a side effect, this gets rid of the long code block to destroy aggregation safely. Additionally, rename tid_state_rx which can only have the values IDLE and OPERATIONAL to tid_active_rx to make it easier to understand that there is no bitwise stuff going on on the RX side -- the TX side remains because it needs to keep track of the driver and peer states. Signed-off-by: Johannes Berg <johannes@sipsolutions.net> Signed-off-by: John W. Linville <linville@tuxdriver.com>
-rw-r--r--include/linux/ieee80211.h1
-rw-r--r--net/mac80211/agg-rx.c48
-rw-r--r--net/mac80211/debugfs_sta.c10
-rw-r--r--net/mac80211/rx.c5
-rw-r--r--net/mac80211/sta_info.c58
-rw-r--r--net/mac80211/sta_info.h6
6 files changed, 40 insertions, 88 deletions
diff --git a/include/linux/ieee80211.h b/include/linux/ieee80211.h
index 19984958ab7b..e9e03b02cb08 100644
--- a/include/linux/ieee80211.h
+++ b/include/linux/ieee80211.h
@@ -1324,7 +1324,6 @@ enum ieee80211_back_actioncode {
1324enum ieee80211_back_parties { 1324enum ieee80211_back_parties {
1325 WLAN_BACK_RECIPIENT = 0, 1325 WLAN_BACK_RECIPIENT = 0,
1326 WLAN_BACK_INITIATOR = 1, 1326 WLAN_BACK_INITIATOR = 1,
1327 WLAN_BACK_TIMER = 2,
1328}; 1327};
1329 1328
1330/* SA Query action */ 1329/* SA Query action */
diff --git a/net/mac80211/agg-rx.c b/net/mac80211/agg-rx.c
index 7d87f446f030..53233ab50f65 100644
--- a/net/mac80211/agg-rx.c
+++ b/net/mac80211/agg-rx.c
@@ -22,19 +22,20 @@ void __ieee80211_stop_rx_ba_session(struct sta_info *sta, u16 tid,
22 u16 initiator, u16 reason) 22 u16 initiator, u16 reason)
23{ 23{
24 struct ieee80211_local *local = sta->local; 24 struct ieee80211_local *local = sta->local;
25 struct tid_ampdu_rx *tid_rx;
25 int i; 26 int i;
26 27
27 /* check if TID is in operational state */
28 spin_lock_bh(&sta->lock); 28 spin_lock_bh(&sta->lock);
29 if (sta->ampdu_mlme.tid_state_rx[tid] != HT_AGG_STATE_OPERATIONAL) { 29
30 /* check if TID is in operational state */
31 if (!sta->ampdu_mlme.tid_active_rx[tid]) {
30 spin_unlock_bh(&sta->lock); 32 spin_unlock_bh(&sta->lock);
31 return; 33 return;
32 } 34 }
33 35
34 sta->ampdu_mlme.tid_state_rx[tid] = 36 sta->ampdu_mlme.tid_active_rx[tid] = false;
35 HT_AGG_STATE_REQ_STOP_BA_MSK | 37
36 (initiator << HT_AGG_STATE_INITIATOR_SHIFT); 38 tid_rx = sta->ampdu_mlme.tid_rx[tid];
37 spin_unlock_bh(&sta->lock);
38 39
39#ifdef CONFIG_MAC80211_HT_DEBUG 40#ifdef CONFIG_MAC80211_HT_DEBUG
40 printk(KERN_DEBUG "Rx BA session stop requested for %pM tid %u\n", 41 printk(KERN_DEBUG "Rx BA session stop requested for %pM tid %u\n",
@@ -46,37 +47,30 @@ void __ieee80211_stop_rx_ba_session(struct sta_info *sta, u16 tid,
46 printk(KERN_DEBUG "HW problem - can not stop rx " 47 printk(KERN_DEBUG "HW problem - can not stop rx "
47 "aggregation for tid %d\n", tid); 48 "aggregation for tid %d\n", tid);
48 49
49 /* shutdown timer has not expired */
50 if (initiator != WLAN_BACK_TIMER)
51 del_timer_sync(&sta->ampdu_mlme.tid_rx[tid]->session_timer);
52
53 /* check if this is a self generated aggregation halt */ 50 /* check if this is a self generated aggregation halt */
54 if (initiator == WLAN_BACK_RECIPIENT || initiator == WLAN_BACK_TIMER) 51 if (initiator == WLAN_BACK_RECIPIENT)
55 ieee80211_send_delba(sta->sdata, sta->sta.addr, 52 ieee80211_send_delba(sta->sdata, sta->sta.addr,
56 tid, 0, reason); 53 tid, 0, reason);
57 54
58 /* free the reordering buffer */ 55 /* free the reordering buffer */
59 for (i = 0; i < sta->ampdu_mlme.tid_rx[tid]->buf_size; i++) { 56 for (i = 0; i < tid_rx->buf_size; i++) {
60 if (sta->ampdu_mlme.tid_rx[tid]->reorder_buf[i]) { 57 if (tid_rx->reorder_buf[i]) {
61 /* release the reordered frames */ 58 /* release the reordered frames */
62 dev_kfree_skb(sta->ampdu_mlme.tid_rx[tid]->reorder_buf[i]); 59 dev_kfree_skb(tid_rx->reorder_buf[i]);
63 sta->ampdu_mlme.tid_rx[tid]->stored_mpdu_num--; 60 tid_rx->stored_mpdu_num--;
64 sta->ampdu_mlme.tid_rx[tid]->reorder_buf[i] = NULL; 61 tid_rx->reorder_buf[i] = NULL;
65 } 62 }
66 } 63 }
67 64
68 spin_lock_bh(&sta->lock);
69 /* free resources */ 65 /* free resources */
70 kfree(sta->ampdu_mlme.tid_rx[tid]->reorder_buf); 66 kfree(tid_rx->reorder_buf);
71 kfree(sta->ampdu_mlme.tid_rx[tid]->reorder_time); 67 kfree(tid_rx->reorder_time);
72 68 sta->ampdu_mlme.tid_rx[tid] = NULL;
73 if (!sta->ampdu_mlme.tid_rx[tid]->shutdown) {
74 kfree(sta->ampdu_mlme.tid_rx[tid]);
75 sta->ampdu_mlme.tid_rx[tid] = NULL;
76 }
77 69
78 sta->ampdu_mlme.tid_state_rx[tid] = HT_AGG_STATE_IDLE;
79 spin_unlock_bh(&sta->lock); 70 spin_unlock_bh(&sta->lock);
71
72 del_timer_sync(&tid_rx->session_timer);
73 kfree(tid_rx);
80} 74}
81 75
82/* 76/*
@@ -211,7 +205,7 @@ void ieee80211_process_addba_request(struct ieee80211_local *local,
211 /* examine state machine */ 205 /* examine state machine */
212 spin_lock_bh(&sta->lock); 206 spin_lock_bh(&sta->lock);
213 207
214 if (sta->ampdu_mlme.tid_state_rx[tid] != HT_AGG_STATE_IDLE) { 208 if (sta->ampdu_mlme.tid_active_rx[tid]) {
215#ifdef CONFIG_MAC80211_HT_DEBUG 209#ifdef CONFIG_MAC80211_HT_DEBUG
216 if (net_ratelimit()) 210 if (net_ratelimit())
217 printk(KERN_DEBUG "unexpected AddBA Req from " 211 printk(KERN_DEBUG "unexpected AddBA Req from "
@@ -273,7 +267,7 @@ void ieee80211_process_addba_request(struct ieee80211_local *local,
273 } 267 }
274 268
275 /* change state and send addba resp */ 269 /* change state and send addba resp */
276 sta->ampdu_mlme.tid_state_rx[tid] = HT_AGG_STATE_OPERATIONAL; 270 sta->ampdu_mlme.tid_active_rx[tid] = true;
277 tid_agg_rx->dialog_token = dialog_token; 271 tid_agg_rx->dialog_token = dialog_token;
278 tid_agg_rx->ssn = start_seq_num; 272 tid_agg_rx->ssn = start_seq_num;
279 tid_agg_rx->head_seq_num = start_seq_num; 273 tid_agg_rx->head_seq_num = start_seq_num;
diff --git a/net/mac80211/debugfs_sta.c b/net/mac80211/debugfs_sta.c
index 23e720034577..740ff6c5b92c 100644
--- a/net/mac80211/debugfs_sta.c
+++ b/net/mac80211/debugfs_sta.c
@@ -119,7 +119,7 @@ STA_OPS(last_seq_ctrl);
119static ssize_t sta_agg_status_read(struct file *file, char __user *userbuf, 119static ssize_t sta_agg_status_read(struct file *file, char __user *userbuf,
120 size_t count, loff_t *ppos) 120 size_t count, loff_t *ppos)
121{ 121{
122 char buf[64 + STA_TID_NUM * 40], *p = buf; 122 char buf[71 + STA_TID_NUM * 40], *p = buf;
123 int i; 123 int i;
124 struct sta_info *sta = file->private_data; 124 struct sta_info *sta = file->private_data;
125 125
@@ -127,16 +127,16 @@ static ssize_t sta_agg_status_read(struct file *file, char __user *userbuf,
127 p += scnprintf(p, sizeof(buf) + buf - p, "next dialog_token: %#02x\n", 127 p += scnprintf(p, sizeof(buf) + buf - p, "next dialog_token: %#02x\n",
128 sta->ampdu_mlme.dialog_token_allocator + 1); 128 sta->ampdu_mlme.dialog_token_allocator + 1);
129 p += scnprintf(p, sizeof(buf) + buf - p, 129 p += scnprintf(p, sizeof(buf) + buf - p,
130 "TID\t\tRX\tDTKN\tSSN\t\tTX\tDTKN\tSSN\tpending\n"); 130 "TID\t\tRX active\tDTKN\tSSN\t\tTX\tDTKN\tSSN\tpending\n");
131 for (i = 0; i < STA_TID_NUM; i++) { 131 for (i = 0; i < STA_TID_NUM; i++) {
132 p += scnprintf(p, sizeof(buf) + buf - p, "%02d", i); 132 p += scnprintf(p, sizeof(buf) + buf - p, "%02d", i);
133 p += scnprintf(p, sizeof(buf) + buf - p, "\t\t%x", 133 p += scnprintf(p, sizeof(buf) + buf - p, "\t\t%x",
134 sta->ampdu_mlme.tid_state_rx[i]); 134 sta->ampdu_mlme.tid_active_rx[i]);
135 p += scnprintf(p, sizeof(buf) + buf - p, "\t%#.2x", 135 p += scnprintf(p, sizeof(buf) + buf - p, "\t%#.2x",
136 sta->ampdu_mlme.tid_state_rx[i] ? 136 sta->ampdu_mlme.tid_active_rx[i] ?
137 sta->ampdu_mlme.tid_rx[i]->dialog_token : 0); 137 sta->ampdu_mlme.tid_rx[i]->dialog_token : 0);
138 p += scnprintf(p, sizeof(buf) + buf - p, "\t%#.3x", 138 p += scnprintf(p, sizeof(buf) + buf - p, "\t%#.3x",
139 sta->ampdu_mlme.tid_state_rx[i] ? 139 sta->ampdu_mlme.tid_active_rx[i] ?
140 sta->ampdu_mlme.tid_rx[i]->ssn : 0); 140 sta->ampdu_mlme.tid_rx[i]->ssn : 0);
141 141
142 p += scnprintf(p, sizeof(buf) + buf - p, "\t\t%x", 142 p += scnprintf(p, sizeof(buf) + buf - p, "\t\t%x",
diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c
index c02e43b50ac3..62053fa711f3 100644
--- a/net/mac80211/rx.c
+++ b/net/mac80211/rx.c
@@ -720,7 +720,7 @@ static void ieee80211_rx_reorder_ampdu(struct ieee80211_rx_data *rx,
720 720
721 tid = *ieee80211_get_qos_ctl(hdr) & IEEE80211_QOS_CTL_TID_MASK; 721 tid = *ieee80211_get_qos_ctl(hdr) & IEEE80211_QOS_CTL_TID_MASK;
722 722
723 if (sta->ampdu_mlme.tid_state_rx[tid] != HT_AGG_STATE_OPERATIONAL) 723 if (!sta->ampdu_mlme.tid_active_rx[tid])
724 goto dont_reorder; 724 goto dont_reorder;
725 725
726 tid_agg_rx = sta->ampdu_mlme.tid_rx[tid]; 726 tid_agg_rx = sta->ampdu_mlme.tid_rx[tid];
@@ -1805,8 +1805,7 @@ ieee80211_rx_h_ctrl(struct ieee80211_rx_data *rx, struct sk_buff_head *frames)
1805 if (!rx->sta) 1805 if (!rx->sta)
1806 return RX_DROP_MONITOR; 1806 return RX_DROP_MONITOR;
1807 tid = le16_to_cpu(bar->control) >> 12; 1807 tid = le16_to_cpu(bar->control) >> 12;
1808 if (rx->sta->ampdu_mlme.tid_state_rx[tid] 1808 if (!rx->sta->ampdu_mlme.tid_active_rx[tid])
1809 != HT_AGG_STATE_OPERATIONAL)
1810 return RX_DROP_MONITOR; 1809 return RX_DROP_MONITOR;
1811 tid_agg_rx = rx->sta->ampdu_mlme.tid_rx[tid]; 1810 tid_agg_rx = rx->sta->ampdu_mlme.tid_rx[tid];
1812 1811
diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c
index bd11753c1525..5bf044b92dca 100644
--- a/net/mac80211/sta_info.c
+++ b/net/mac80211/sta_info.c
@@ -238,9 +238,6 @@ struct sta_info *sta_info_alloc(struct ieee80211_sub_if_data *sdata,
238 * enable session_timer's data differentiation. refer to 238 * enable session_timer's data differentiation. refer to
239 * sta_rx_agg_session_timer_expired for useage */ 239 * sta_rx_agg_session_timer_expired for useage */
240 sta->timer_to_tid[i] = i; 240 sta->timer_to_tid[i] = i;
241 /* rx */
242 sta->ampdu_mlme.tid_state_rx[i] = HT_AGG_STATE_IDLE;
243 sta->ampdu_mlme.tid_rx[i] = NULL;
244 /* tx */ 241 /* tx */
245 sta->ampdu_mlme.tid_state_tx[i] = HT_AGG_STATE_IDLE; 242 sta->ampdu_mlme.tid_state_tx[i] = HT_AGG_STATE_IDLE;
246 sta->ampdu_mlme.tid_tx[i] = NULL; 243 sta->ampdu_mlme.tid_tx[i] = NULL;
@@ -606,7 +603,7 @@ static int __must_check __sta_info_destroy(struct sta_info *sta)
606 struct ieee80211_sub_if_data *sdata; 603 struct ieee80211_sub_if_data *sdata;
607 struct sk_buff *skb; 604 struct sk_buff *skb;
608 unsigned long flags; 605 unsigned long flags;
609 int ret, i; 606 int ret;
610 607
611 might_sleep(); 608 might_sleep();
612 609
@@ -616,6 +613,15 @@ static int __must_check __sta_info_destroy(struct sta_info *sta)
616 local = sta->local; 613 local = sta->local;
617 sdata = sta->sdata; 614 sdata = sta->sdata;
618 615
616 /*
617 * Before removing the station from the driver and
618 * rate control, it might still start new aggregation
619 * sessions -- block that to make sure the tear-down
620 * will be sufficient.
621 */
622 set_sta_flags(sta, WLAN_STA_BLOCK_BA);
623 ieee80211_sta_tear_down_BA_sessions(sta);
624
619 spin_lock_irqsave(&local->sta_lock, flags); 625 spin_lock_irqsave(&local->sta_lock, flags);
620 ret = sta_info_hash_del(local, sta); 626 ret = sta_info_hash_del(local, sta);
621 /* this might still be the pending list ... which is fine */ 627 /* this might still be the pending list ... which is fine */
@@ -700,50 +706,6 @@ static int __must_check __sta_info_destroy(struct sta_info *sta)
700 while ((skb = skb_dequeue(&sta->tx_filtered)) != NULL) 706 while ((skb = skb_dequeue(&sta->tx_filtered)) != NULL)
701 dev_kfree_skb_any(skb); 707 dev_kfree_skb_any(skb);
702 708
703 for (i = 0; i < STA_TID_NUM; i++) {
704 struct tid_ampdu_rx *tid_rx;
705 struct tid_ampdu_tx *tid_tx;
706
707 spin_lock_bh(&sta->lock);
708 tid_rx = sta->ampdu_mlme.tid_rx[i];
709 /* Make sure timer won't free the tid_rx struct, see below */
710 if (tid_rx)
711 tid_rx->shutdown = true;
712
713 spin_unlock_bh(&sta->lock);
714
715 /*
716 * Outside spinlock - shutdown is true now so that the timer
717 * won't free tid_rx, we have to do that now. Can't let the
718 * timer do it because we have to sync the timer outside the
719 * lock that it takes itself.
720 */
721 if (tid_rx) {
722 del_timer_sync(&tid_rx->session_timer);
723 kfree(tid_rx);
724 }
725
726 /*
727 * No need to do such complications for TX agg sessions, the
728 * path leading to freeing the tid_tx struct goes via a call
729 * from the driver, and thus needs to look up the sta struct
730 * again, which cannot be found when we get here. Hence, we
731 * just need to delete the timer and free the aggregation
732 * info; we won't be telling the peer about it then but that
733 * doesn't matter if we're not talking to it again anyway.
734 */
735 tid_tx = sta->ampdu_mlme.tid_tx[i];
736 if (tid_tx) {
737 del_timer_sync(&tid_tx->addba_resp_timer);
738 /*
739 * STA removed while aggregation session being
740 * started? Bit odd, but purge frames anyway.
741 */
742 skb_queue_purge(&tid_tx->pending);
743 kfree(tid_tx);
744 }
745 }
746
747 __sta_info_free(local, sta); 709 __sta_info_free(local, sta);
748 710
749 return 0; 711 return 0;
diff --git a/net/mac80211/sta_info.h b/net/mac80211/sta_info.h
index 57e81758d6f7..48a5e80957f0 100644
--- a/net/mac80211/sta_info.h
+++ b/net/mac80211/sta_info.h
@@ -36,7 +36,7 @@
36 * frame to this station is transmitted. 36 * frame to this station is transmitted.
37 * @WLAN_STA_MFP: Management frame protection is used with this STA. 37 * @WLAN_STA_MFP: Management frame protection is used with this STA.
38 * @WLAN_STA_BLOCK_BA: Used to deny ADDBA requests (both TX and RX) 38 * @WLAN_STA_BLOCK_BA: Used to deny ADDBA requests (both TX and RX)
39 * during suspend/resume. 39 * during suspend/resume and station removal.
40 * @WLAN_STA_PS_DRIVER: driver requires keeping this station in 40 * @WLAN_STA_PS_DRIVER: driver requires keeping this station in
41 * power-save mode logically to flush frames that might still 41 * power-save mode logically to flush frames that might still
42 * be in the queues 42 * be in the queues
@@ -106,7 +106,6 @@ struct tid_ampdu_tx {
106 * @buf_size: buffer size for incoming A-MPDUs 106 * @buf_size: buffer size for incoming A-MPDUs
107 * @timeout: reset timer value (in TUs). 107 * @timeout: reset timer value (in TUs).
108 * @dialog_token: dialog token for aggregation session 108 * @dialog_token: dialog token for aggregation session
109 * @shutdown: this session is being shut down due to STA removal
110 */ 109 */
111struct tid_ampdu_rx { 110struct tid_ampdu_rx {
112 struct sk_buff **reorder_buf; 111 struct sk_buff **reorder_buf;
@@ -118,7 +117,6 @@ struct tid_ampdu_rx {
118 u16 buf_size; 117 u16 buf_size;
119 u16 timeout; 118 u16 timeout;
120 u8 dialog_token; 119 u8 dialog_token;
121 bool shutdown;
122}; 120};
123 121
124/** 122/**
@@ -156,7 +154,7 @@ enum plink_state {
156 */ 154 */
157struct sta_ampdu_mlme { 155struct sta_ampdu_mlme {
158 /* rx */ 156 /* rx */
159 u8 tid_state_rx[STA_TID_NUM]; 157 bool tid_active_rx[STA_TID_NUM];
160 struct tid_ampdu_rx *tid_rx[STA_TID_NUM]; 158 struct tid_ampdu_rx *tid_rx[STA_TID_NUM];
161 /* tx */ 159 /* tx */
162 u8 tid_state_tx[STA_TID_NUM]; 160 u8 tid_state_tx[STA_TID_NUM];