aboutsummaryrefslogtreecommitdiffstats
path: root/net
diff options
context:
space:
mode:
authorJohannes Berg <johannes.berg@intel.com>2010-06-10 04:21:38 -0400
committerJohn W. Linville <linville@tuxdriver.com>2010-06-14 15:39:27 -0400
commita87f736d942c86255e3088c606f0e3eab6bbf784 (patch)
tree74d3d63dca9fc982fcb7c165247d6924c021b4ef /net
parentc1475ca99edcc7216ddc45838ab2c3281c14ba22 (diff)
mac80211: use RCU for RX aggregation
Currently we allocate some memory for each RX aggregation session and additionally keep a flag indicating whether or not it is valid. By using RCU to protect the pointer and making sure that the memory is fully set up before it becomes visible to the RX path, we can remove the need for the bool that indicates validity, as well as for locking on the RX path since it is always synchronised against itself, and we can guarantee that all other modifications are done when the structure is not visible to the RX path. The net result is that since we remove locking requirements from the RX path, we can in the future use any kind of lock for the setup and teardown code paths. 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-rx.c70
-rw-r--r--net/mac80211/debugfs_sta.c6
-rw-r--r--net/mac80211/rx.c34
-rw-r--r--net/mac80211/sta_info.h14
4 files changed, 61 insertions, 63 deletions
diff --git a/net/mac80211/agg-rx.c b/net/mac80211/agg-rx.c
index 6bb9a9a94960..bbf36d980232 100644
--- a/net/mac80211/agg-rx.c
+++ b/net/mac80211/agg-rx.c
@@ -19,25 +19,36 @@
19#include "ieee80211_i.h" 19#include "ieee80211_i.h"
20#include "driver-ops.h" 20#include "driver-ops.h"
21 21
22static void ieee80211_free_tid_rx(struct rcu_head *h)
23{
24 struct tid_ampdu_rx *tid_rx =
25 container_of(h, struct tid_ampdu_rx, rcu_head);
26 int i;
27
28 for (i = 0; i < tid_rx->buf_size; i++)
29 dev_kfree_skb(tid_rx->reorder_buf[i]);
30 kfree(tid_rx->reorder_buf);
31 kfree(tid_rx->reorder_time);
32 kfree(tid_rx);
33}
34
22static void ___ieee80211_stop_rx_ba_session(struct sta_info *sta, u16 tid, 35static void ___ieee80211_stop_rx_ba_session(struct sta_info *sta, u16 tid,
23 u16 initiator, u16 reason, 36 u16 initiator, u16 reason,
24 bool from_timer) 37 bool from_timer)
25{ 38{
26 struct ieee80211_local *local = sta->local; 39 struct ieee80211_local *local = sta->local;
27 struct tid_ampdu_rx *tid_rx; 40 struct tid_ampdu_rx *tid_rx;
28 int i;
29 41
30 spin_lock_bh(&sta->lock); 42 spin_lock_bh(&sta->lock);
31 43
32 /* check if TID is in operational state */ 44 tid_rx = sta->ampdu_mlme.tid_rx[tid];
33 if (!sta->ampdu_mlme.tid_active_rx[tid]) { 45
46 if (!tid_rx) {
34 spin_unlock_bh(&sta->lock); 47 spin_unlock_bh(&sta->lock);
35 return; 48 return;
36 } 49 }
37 50
38 sta->ampdu_mlme.tid_active_rx[tid] = false; 51 rcu_assign_pointer(sta->ampdu_mlme.tid_rx[tid], NULL);
39
40 tid_rx = sta->ampdu_mlme.tid_rx[tid];
41 52
42#ifdef CONFIG_MAC80211_HT_DEBUG 53#ifdef CONFIG_MAC80211_HT_DEBUG
43 printk(KERN_DEBUG "Rx BA session stop requested for %pM tid %u\n", 54 printk(KERN_DEBUG "Rx BA session stop requested for %pM tid %u\n",
@@ -54,26 +65,12 @@ static void ___ieee80211_stop_rx_ba_session(struct sta_info *sta, u16 tid,
54 ieee80211_send_delba(sta->sdata, sta->sta.addr, 65 ieee80211_send_delba(sta->sdata, sta->sta.addr,
55 tid, 0, reason); 66 tid, 0, reason);
56 67
57 /* free the reordering buffer */
58 for (i = 0; i < tid_rx->buf_size; i++) {
59 if (tid_rx->reorder_buf[i]) {
60 /* release the reordered frames */
61 dev_kfree_skb(tid_rx->reorder_buf[i]);
62 tid_rx->stored_mpdu_num--;
63 tid_rx->reorder_buf[i] = NULL;
64 }
65 }
66
67 /* free resources */
68 kfree(tid_rx->reorder_buf);
69 kfree(tid_rx->reorder_time);
70 sta->ampdu_mlme.tid_rx[tid] = NULL;
71
72 spin_unlock_bh(&sta->lock); 68 spin_unlock_bh(&sta->lock);
73 69
74 if (!from_timer) 70 if (!from_timer)
75 del_timer_sync(&tid_rx->session_timer); 71 del_timer_sync(&tid_rx->session_timer);
76 kfree(tid_rx); 72
73 call_rcu(&tid_rx->rcu_head, ieee80211_free_tid_rx);
77} 74}
78 75
79void __ieee80211_stop_rx_ba_session(struct sta_info *sta, u16 tid, 76void __ieee80211_stop_rx_ba_session(struct sta_info *sta, u16 tid,
@@ -214,7 +211,7 @@ void ieee80211_process_addba_request(struct ieee80211_local *local,
214 /* examine state machine */ 211 /* examine state machine */
215 spin_lock_bh(&sta->lock); 212 spin_lock_bh(&sta->lock);
216 213
217 if (sta->ampdu_mlme.tid_active_rx[tid]) { 214 if (sta->ampdu_mlme.tid_rx[tid]) {
218#ifdef CONFIG_MAC80211_HT_DEBUG 215#ifdef CONFIG_MAC80211_HT_DEBUG
219 if (net_ratelimit()) 216 if (net_ratelimit())
220 printk(KERN_DEBUG "unexpected AddBA Req from " 217 printk(KERN_DEBUG "unexpected AddBA Req from "
@@ -225,9 +222,8 @@ void ieee80211_process_addba_request(struct ieee80211_local *local,
225 } 222 }
226 223
227 /* prepare A-MPDU MLME for Rx aggregation */ 224 /* prepare A-MPDU MLME for Rx aggregation */
228 sta->ampdu_mlme.tid_rx[tid] = 225 tid_agg_rx = kmalloc(sizeof(struct tid_ampdu_rx), GFP_ATOMIC);
229 kmalloc(sizeof(struct tid_ampdu_rx), GFP_ATOMIC); 226 if (!tid_agg_rx) {
230 if (!sta->ampdu_mlme.tid_rx[tid]) {
231#ifdef CONFIG_MAC80211_HT_DEBUG 227#ifdef CONFIG_MAC80211_HT_DEBUG
232 if (net_ratelimit()) 228 if (net_ratelimit())
233 printk(KERN_ERR "allocate rx mlme to tid %d failed\n", 229 printk(KERN_ERR "allocate rx mlme to tid %d failed\n",
@@ -235,14 +231,11 @@ void ieee80211_process_addba_request(struct ieee80211_local *local,
235#endif 231#endif
236 goto end; 232 goto end;
237 } 233 }
238 /* rx timer */
239 sta->ampdu_mlme.tid_rx[tid]->session_timer.function =
240 sta_rx_agg_session_timer_expired;
241 sta->ampdu_mlme.tid_rx[tid]->session_timer.data =
242 (unsigned long)&sta->timer_to_tid[tid];
243 init_timer(&sta->ampdu_mlme.tid_rx[tid]->session_timer);
244 234
245 tid_agg_rx = sta->ampdu_mlme.tid_rx[tid]; 235 /* rx timer */
236 tid_agg_rx->session_timer.function = sta_rx_agg_session_timer_expired;
237 tid_agg_rx->session_timer.data = (unsigned long)&sta->timer_to_tid[tid];
238 init_timer(&tid_agg_rx->session_timer);
246 239
247 /* prepare reordering buffer */ 240 /* prepare reordering buffer */
248 tid_agg_rx->reorder_buf = 241 tid_agg_rx->reorder_buf =
@@ -257,8 +250,7 @@ void ieee80211_process_addba_request(struct ieee80211_local *local,
257#endif 250#endif
258 kfree(tid_agg_rx->reorder_buf); 251 kfree(tid_agg_rx->reorder_buf);
259 kfree(tid_agg_rx->reorder_time); 252 kfree(tid_agg_rx->reorder_time);
260 kfree(sta->ampdu_mlme.tid_rx[tid]); 253 kfree(tid_agg_rx);
261 sta->ampdu_mlme.tid_rx[tid] = NULL;
262 goto end; 254 goto end;
263 } 255 }
264 256
@@ -270,13 +262,12 @@ void ieee80211_process_addba_request(struct ieee80211_local *local,
270 262
271 if (ret) { 263 if (ret) {
272 kfree(tid_agg_rx->reorder_buf); 264 kfree(tid_agg_rx->reorder_buf);
265 kfree(tid_agg_rx->reorder_time);
273 kfree(tid_agg_rx); 266 kfree(tid_agg_rx);
274 sta->ampdu_mlme.tid_rx[tid] = NULL;
275 goto end; 267 goto end;
276 } 268 }
277 269
278 /* change state and send addba resp */ 270 /* update data */
279 sta->ampdu_mlme.tid_active_rx[tid] = true;
280 tid_agg_rx->dialog_token = dialog_token; 271 tid_agg_rx->dialog_token = dialog_token;
281 tid_agg_rx->ssn = start_seq_num; 272 tid_agg_rx->ssn = start_seq_num;
282 tid_agg_rx->head_seq_num = start_seq_num; 273 tid_agg_rx->head_seq_num = start_seq_num;
@@ -284,6 +275,9 @@ void ieee80211_process_addba_request(struct ieee80211_local *local,
284 tid_agg_rx->timeout = timeout; 275 tid_agg_rx->timeout = timeout;
285 tid_agg_rx->stored_mpdu_num = 0; 276 tid_agg_rx->stored_mpdu_num = 0;
286 status = WLAN_STATUS_SUCCESS; 277 status = WLAN_STATUS_SUCCESS;
278
279 /* activate it for RX */
280 rcu_assign_pointer(sta->ampdu_mlme.tid_rx[tid], tid_agg_rx);
287end: 281end:
288 spin_unlock_bh(&sta->lock); 282 spin_unlock_bh(&sta->lock);
289 283
diff --git a/net/mac80211/debugfs_sta.c b/net/mac80211/debugfs_sta.c
index 576e024715e3..95c1ea47ad35 100644
--- a/net/mac80211/debugfs_sta.c
+++ b/net/mac80211/debugfs_sta.c
@@ -125,12 +125,12 @@ static ssize_t sta_agg_status_read(struct file *file, char __user *userbuf,
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",
128 sta->ampdu_mlme.tid_active_rx[i]); 128 !!sta->ampdu_mlme.tid_rx[i]);
129 p += scnprintf(p, sizeof(buf) + buf - p, "\t%#.2x", 129 p += scnprintf(p, sizeof(buf) + buf - p, "\t%#.2x",
130 sta->ampdu_mlme.tid_active_rx[i] ? 130 sta->ampdu_mlme.tid_rx[i] ?
131 sta->ampdu_mlme.tid_rx[i]->dialog_token : 0); 131 sta->ampdu_mlme.tid_rx[i]->dialog_token : 0);
132 p += scnprintf(p, sizeof(buf) + buf - p, "\t%#.3x", 132 p += scnprintf(p, sizeof(buf) + buf - p, "\t%#.3x",
133 sta->ampdu_mlme.tid_active_rx[i] ? 133 sta->ampdu_mlme.tid_rx[i] ?
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",
diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c
index 84f11733b9fe..ee01daccacbb 100644
--- a/net/mac80211/rx.c
+++ b/net/mac80211/rx.c
@@ -719,16 +719,13 @@ static void ieee80211_rx_reorder_ampdu(struct ieee80211_rx_data *rx,
719 719
720 tid = *ieee80211_get_qos_ctl(hdr) & IEEE80211_QOS_CTL_TID_MASK; 720 tid = *ieee80211_get_qos_ctl(hdr) & IEEE80211_QOS_CTL_TID_MASK;
721 721
722 spin_lock(&sta->lock); 722 tid_agg_rx = rcu_dereference(sta->ampdu_mlme.tid_rx[tid]);
723 723 if (!tid_agg_rx)
724 if (!sta->ampdu_mlme.tid_active_rx[tid]) 724 goto dont_reorder;
725 goto dont_reorder_unlock;
726
727 tid_agg_rx = sta->ampdu_mlme.tid_rx[tid];
728 725
729 /* qos null data frames are excluded */ 726 /* qos null data frames are excluded */
730 if (unlikely(hdr->frame_control & cpu_to_le16(IEEE80211_STYPE_NULLFUNC))) 727 if (unlikely(hdr->frame_control & cpu_to_le16(IEEE80211_STYPE_NULLFUNC)))
731 goto dont_reorder_unlock; 728 goto dont_reorder;
732 729
733 /* new, potentially un-ordered, ampdu frame - process it */ 730 /* new, potentially un-ordered, ampdu frame - process it */
734 731
@@ -740,20 +737,22 @@ static void ieee80211_rx_reorder_ampdu(struct ieee80211_rx_data *rx,
740 /* if this mpdu is fragmented - terminate rx aggregation session */ 737 /* if this mpdu is fragmented - terminate rx aggregation session */
741 sc = le16_to_cpu(hdr->seq_ctrl); 738 sc = le16_to_cpu(hdr->seq_ctrl);
742 if (sc & IEEE80211_SCTL_FRAG) { 739 if (sc & IEEE80211_SCTL_FRAG) {
743 spin_unlock(&sta->lock);
744 skb->pkt_type = IEEE80211_SDATA_QUEUE_TYPE_FRAME; 740 skb->pkt_type = IEEE80211_SDATA_QUEUE_TYPE_FRAME;
745 skb_queue_tail(&rx->sdata->skb_queue, skb); 741 skb_queue_tail(&rx->sdata->skb_queue, skb);
746 ieee80211_queue_work(&local->hw, &rx->sdata->work); 742 ieee80211_queue_work(&local->hw, &rx->sdata->work);
747 return; 743 return;
748 } 744 }
749 745
750 if (ieee80211_sta_manage_reorder_buf(hw, tid_agg_rx, skb, frames)) { 746 /*
751 spin_unlock(&sta->lock); 747 * No locking needed -- we will only ever process one
748 * RX packet at a time, and thus own tid_agg_rx. All
749 * other code manipulating it needs to (and does) make
750 * sure that we cannot get to it any more before doing
751 * anything with it.
752 */
753 if (ieee80211_sta_manage_reorder_buf(hw, tid_agg_rx, skb, frames))
752 return; 754 return;
753 }
754 755
755 dont_reorder_unlock:
756 spin_unlock(&sta->lock);
757 dont_reorder: 756 dont_reorder:
758 __skb_queue_tail(frames, skb); 757 __skb_queue_tail(frames, skb);
759} 758}
@@ -1830,13 +1829,11 @@ ieee80211_rx_h_ctrl(struct ieee80211_rx_data *rx, struct sk_buff_head *frames)
1830 &bar_data, sizeof(bar_data))) 1829 &bar_data, sizeof(bar_data)))
1831 return RX_DROP_MONITOR; 1830 return RX_DROP_MONITOR;
1832 1831
1833 spin_lock(&rx->sta->lock);
1834 tid = le16_to_cpu(bar_data.control) >> 12; 1832 tid = le16_to_cpu(bar_data.control) >> 12;
1835 if (!rx->sta->ampdu_mlme.tid_active_rx[tid]) { 1833
1836 spin_unlock(&rx->sta->lock); 1834 tid_agg_rx = rcu_dereference(rx->sta->ampdu_mlme.tid_rx[tid]);
1835 if (!tid_agg_rx)
1837 return RX_DROP_MONITOR; 1836 return RX_DROP_MONITOR;
1838 }
1839 tid_agg_rx = rx->sta->ampdu_mlme.tid_rx[tid];
1840 1837
1841 start_seq_num = le16_to_cpu(bar_data.start_seq_num) >> 4; 1838 start_seq_num = le16_to_cpu(bar_data.start_seq_num) >> 4;
1842 1839
@@ -1849,7 +1846,6 @@ ieee80211_rx_h_ctrl(struct ieee80211_rx_data *rx, struct sk_buff_head *frames)
1849 ieee80211_release_reorder_frames(hw, tid_agg_rx, start_seq_num, 1846 ieee80211_release_reorder_frames(hw, tid_agg_rx, start_seq_num,
1850 frames); 1847 frames);
1851 kfree_skb(skb); 1848 kfree_skb(skb);
1852 spin_unlock(&rx->sta->lock);
1853 return RX_QUEUED; 1849 return RX_QUEUED;
1854 } 1850 }
1855 1851
diff --git a/net/mac80211/sta_info.h b/net/mac80211/sta_info.h
index 786bbd3103b1..16864a6045b4 100644
--- a/net/mac80211/sta_info.h
+++ b/net/mac80211/sta_info.h
@@ -102,8 +102,18 @@ struct tid_ampdu_tx {
102 * @buf_size: buffer size for incoming A-MPDUs 102 * @buf_size: buffer size for incoming A-MPDUs
103 * @timeout: reset timer value (in TUs). 103 * @timeout: reset timer value (in TUs).
104 * @dialog_token: dialog token for aggregation session 104 * @dialog_token: dialog token for aggregation session
105 * @rcu_head: RCU head used for freeing this struct
106 *
107 * This structure is protected by RCU and the per-station
108 * spinlock. Assignments to the array holding it must hold
109 * the spinlock, only the RX path can access it under RCU
110 * lock-free. The RX path, since it is single-threaded,
111 * can even modify the structure without locking since the
112 * only other modifications to it are done when the struct
113 * can not yet or no longer be found by the RX path.
105 */ 114 */
106struct tid_ampdu_rx { 115struct tid_ampdu_rx {
116 struct rcu_head rcu_head;
107 struct sk_buff **reorder_buf; 117 struct sk_buff **reorder_buf;
108 unsigned long *reorder_time; 118 unsigned long *reorder_time;
109 struct timer_list session_timer; 119 struct timer_list session_timer;
@@ -118,8 +128,7 @@ struct tid_ampdu_rx {
118/** 128/**
119 * struct sta_ampdu_mlme - STA aggregation information. 129 * struct sta_ampdu_mlme - STA aggregation information.
120 * 130 *
121 * @tid_active_rx: TID's state in Rx session state machine. 131 * @tid_rx: aggregation info for Rx per TID -- RCU protected
122 * @tid_rx: aggregation info for Rx per TID
123 * @tid_state_tx: TID's state in Tx session state machine. 132 * @tid_state_tx: TID's state in Tx session state machine.
124 * @tid_tx: aggregation info for Tx per TID 133 * @tid_tx: aggregation info for Tx per TID
125 * @addba_req_num: number of times addBA request has been sent. 134 * @addba_req_num: number of times addBA request has been sent.
@@ -127,7 +136,6 @@ struct tid_ampdu_rx {
127 */ 136 */
128struct sta_ampdu_mlme { 137struct sta_ampdu_mlme {
129 /* rx */ 138 /* rx */
130 bool tid_active_rx[STA_TID_NUM];
131 struct tid_ampdu_rx *tid_rx[STA_TID_NUM]; 139 struct tid_ampdu_rx *tid_rx[STA_TID_NUM];
132 /* tx */ 140 /* tx */
133 u8 tid_state_tx[STA_TID_NUM]; 141 u8 tid_state_tx[STA_TID_NUM];