aboutsummaryrefslogtreecommitdiffstats
path: root/net
diff options
context:
space:
mode:
authorJohannes Berg <johannes.berg@intel.com>2010-11-29 05:09:16 -0500
committerJohn W. Linville <linville@tuxdriver.com>2010-11-29 15:30:30 -0500
commitdd318575ff0aae91ac4cbcc5b60c184e59267212 (patch)
tree140a0104b99b8edef7b961b4de9182e092782cf9 /net
parent8b7f8532d15631776ce8bec2bbbc58f6aad738d1 (diff)
mac80211: fix RX aggregation locking
The RX aggregation locking documentation was wrong, which led Christian to also code the timer timeout handling for it somewhat wrongly. Fix the documentation, the two places that need to hold the reorder lock across accesses to the structure, and the debugfs code that should just use RCU. Also, remove acquiring the sta->lock across reorder timeouts since it isn't necessary, and change a few places to GFP_KERNEL because the code path here doesn't need atomic allocations as I noticed when reviewing all this. Signed-off-by: Johannes Berg <johannes.berg@intel.com> Acked-by: Christian Lamparter <chunkeey@googlemail.com> Signed-off-by: John W. Linville <linville@tuxdriver.com>
Diffstat (limited to 'net')
-rw-r--r--net/mac80211/agg-rx.c8
-rw-r--r--net/mac80211/debugfs_sta.c29
-rw-r--r--net/mac80211/rx.c17
-rw-r--r--net/mac80211/sta_info.h29
4 files changed, 45 insertions, 38 deletions
diff --git a/net/mac80211/agg-rx.c b/net/mac80211/agg-rx.c
index 720b7a84af59..f138b195d657 100644
--- a/net/mac80211/agg-rx.c
+++ b/net/mac80211/agg-rx.c
@@ -129,9 +129,7 @@ static void sta_rx_agg_reorder_timer_expired(unsigned long data)
129 timer_to_tid[0]); 129 timer_to_tid[0]);
130 130
131 rcu_read_lock(); 131 rcu_read_lock();
132 spin_lock(&sta->lock);
133 ieee80211_release_reorder_timeout(sta, *ptid); 132 ieee80211_release_reorder_timeout(sta, *ptid);
134 spin_unlock(&sta->lock);
135 rcu_read_unlock(); 133 rcu_read_unlock();
136} 134}
137 135
@@ -256,7 +254,7 @@ void ieee80211_process_addba_request(struct ieee80211_local *local,
256 } 254 }
257 255
258 /* prepare A-MPDU MLME for Rx aggregation */ 256 /* prepare A-MPDU MLME for Rx aggregation */
259 tid_agg_rx = kmalloc(sizeof(struct tid_ampdu_rx), GFP_ATOMIC); 257 tid_agg_rx = kmalloc(sizeof(struct tid_ampdu_rx), GFP_KERNEL);
260 if (!tid_agg_rx) { 258 if (!tid_agg_rx) {
261#ifdef CONFIG_MAC80211_HT_DEBUG 259#ifdef CONFIG_MAC80211_HT_DEBUG
262 if (net_ratelimit()) 260 if (net_ratelimit())
@@ -280,9 +278,9 @@ void ieee80211_process_addba_request(struct ieee80211_local *local,
280 278
281 /* prepare reordering buffer */ 279 /* prepare reordering buffer */
282 tid_agg_rx->reorder_buf = 280 tid_agg_rx->reorder_buf =
283 kcalloc(buf_size, sizeof(struct sk_buff *), GFP_ATOMIC); 281 kcalloc(buf_size, sizeof(struct sk_buff *), GFP_KERNEL);
284 tid_agg_rx->reorder_time = 282 tid_agg_rx->reorder_time =
285 kcalloc(buf_size, sizeof(unsigned long), GFP_ATOMIC); 283 kcalloc(buf_size, sizeof(unsigned long), GFP_KERNEL);
286 if (!tid_agg_rx->reorder_buf || !tid_agg_rx->reorder_time) { 284 if (!tid_agg_rx->reorder_buf || !tid_agg_rx->reorder_time) {
287#ifdef CONFIG_MAC80211_HT_DEBUG 285#ifdef CONFIG_MAC80211_HT_DEBUG
288 if (net_ratelimit()) 286 if (net_ratelimit())
diff --git a/net/mac80211/debugfs_sta.c b/net/mac80211/debugfs_sta.c
index f0fce37f4069..8bb5af85f469 100644
--- a/net/mac80211/debugfs_sta.c
+++ b/net/mac80211/debugfs_sta.c
@@ -112,34 +112,35 @@ static ssize_t sta_agg_status_read(struct file *file, char __user *userbuf,
112 char buf[71 + STA_TID_NUM * 40], *p = buf; 112 char buf[71 + STA_TID_NUM * 40], *p = buf;
113 int i; 113 int i;
114 struct sta_info *sta = file->private_data; 114 struct sta_info *sta = file->private_data;
115 struct tid_ampdu_rx *tid_rx;
116 struct tid_ampdu_tx *tid_tx;
117
118 rcu_read_lock();
115 119
116 spin_lock_bh(&sta->lock);
117 p += scnprintf(p, sizeof(buf) + buf - p, "next dialog_token: %#02x\n", 120 p += scnprintf(p, sizeof(buf) + buf - p, "next dialog_token: %#02x\n",
118 sta->ampdu_mlme.dialog_token_allocator + 1); 121 sta->ampdu_mlme.dialog_token_allocator + 1);
119 p += scnprintf(p, sizeof(buf) + buf - p, 122 p += scnprintf(p, sizeof(buf) + buf - p,
120 "TID\t\tRX active\tDTKN\tSSN\t\tTX\tDTKN\tpending\n"); 123 "TID\t\tRX active\tDTKN\tSSN\t\tTX\tDTKN\tpending\n");
124
121 for (i = 0; i < STA_TID_NUM; i++) { 125 for (i = 0; i < STA_TID_NUM; i++) {
126 tid_rx = rcu_dereference(sta->ampdu_mlme.tid_rx[i]);
127 tid_tx = rcu_dereference(sta->ampdu_mlme.tid_tx[i]);
128
122 p += scnprintf(p, sizeof(buf) + buf - p, "%02d", i); 129 p += scnprintf(p, sizeof(buf) + buf - p, "%02d", i);
123 p += scnprintf(p, sizeof(buf) + buf - p, "\t\t%x", 130 p += scnprintf(p, sizeof(buf) + buf - p, "\t\t%x", !!tid_rx);
124 !!sta->ampdu_mlme.tid_rx[i]);
125 p += scnprintf(p, sizeof(buf) + buf - p, "\t%#.2x", 131 p += scnprintf(p, sizeof(buf) + buf - p, "\t%#.2x",
126 sta->ampdu_mlme.tid_rx[i] ? 132 tid_rx ? tid_rx->dialog_token : 0);
127 sta->ampdu_mlme.tid_rx[i]->dialog_token : 0);
128 p += scnprintf(p, sizeof(buf) + buf - p, "\t%#.3x", 133 p += scnprintf(p, sizeof(buf) + buf - p, "\t%#.3x",
129 sta->ampdu_mlme.tid_rx[i] ? 134 tid_rx ? tid_rx->ssn : 0);
130 sta->ampdu_mlme.tid_rx[i]->ssn : 0);
131 135
132 p += scnprintf(p, sizeof(buf) + buf - p, "\t\t%x", 136 p += scnprintf(p, sizeof(buf) + buf - p, "\t\t%x", !!tid_tx);
133 !!sta->ampdu_mlme.tid_tx[i]);
134 p += scnprintf(p, sizeof(buf) + buf - p, "\t%#.2x", 137 p += scnprintf(p, sizeof(buf) + buf - p, "\t%#.2x",
135 sta->ampdu_mlme.tid_tx[i] ? 138 tid_tx ? tid_tx->dialog_token : 0);
136 sta->ampdu_mlme.tid_tx[i]->dialog_token : 0);
137 p += scnprintf(p, sizeof(buf) + buf - p, "\t%03d", 139 p += scnprintf(p, sizeof(buf) + buf - p, "\t%03d",
138 sta->ampdu_mlme.tid_tx[i] ? 140 tid_tx ? skb_queue_len(&tid_tx->pending) : 0);
139 skb_queue_len(&sta->ampdu_mlme.tid_tx[i]->pending) : 0);
140 p += scnprintf(p, sizeof(buf) + buf - p, "\n"); 141 p += scnprintf(p, sizeof(buf) + buf - p, "\n");
141 } 142 }
142 spin_unlock_bh(&sta->lock); 143 rcu_read_unlock();
143 144
144 return simple_read_from_buffer(userbuf, count, ppos, buf, p - buf); 145 return simple_read_from_buffer(userbuf, count, ppos, buf, p - buf);
145} 146}
diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c
index d2fcd22ab06d..fdeabb19943c 100644
--- a/net/mac80211/rx.c
+++ b/net/mac80211/rx.c
@@ -538,6 +538,8 @@ static void ieee80211_release_reorder_frame(struct ieee80211_hw *hw,
538{ 538{
539 struct sk_buff *skb = tid_agg_rx->reorder_buf[index]; 539 struct sk_buff *skb = tid_agg_rx->reorder_buf[index];
540 540
541 lockdep_assert_held(&tid_agg_rx->reorder_lock);
542
541 if (!skb) 543 if (!skb)
542 goto no_frame; 544 goto no_frame;
543 545
@@ -557,6 +559,8 @@ static void ieee80211_release_reorder_frames(struct ieee80211_hw *hw,
557{ 559{
558 int index; 560 int index;
559 561
562 lockdep_assert_held(&tid_agg_rx->reorder_lock);
563
560 while (seq_less(tid_agg_rx->head_seq_num, head_seq_num)) { 564 while (seq_less(tid_agg_rx->head_seq_num, head_seq_num)) {
561 index = seq_sub(tid_agg_rx->head_seq_num, tid_agg_rx->ssn) % 565 index = seq_sub(tid_agg_rx->head_seq_num, tid_agg_rx->ssn) %
562 tid_agg_rx->buf_size; 566 tid_agg_rx->buf_size;
@@ -581,6 +585,8 @@ static void ieee80211_sta_reorder_release(struct ieee80211_hw *hw,
581{ 585{
582 int index, j; 586 int index, j;
583 587
588 lockdep_assert_held(&tid_agg_rx->reorder_lock);
589
584 /* release the buffer until next missing frame */ 590 /* release the buffer until next missing frame */
585 index = seq_sub(tid_agg_rx->head_seq_num, tid_agg_rx->ssn) % 591 index = seq_sub(tid_agg_rx->head_seq_num, tid_agg_rx->ssn) %
586 tid_agg_rx->buf_size; 592 tid_agg_rx->buf_size;
@@ -683,10 +689,11 @@ static bool ieee80211_sta_manage_reorder_buf(struct ieee80211_hw *hw,
683 int index; 689 int index;
684 bool ret = true; 690 bool ret = true;
685 691
692 spin_lock(&tid_agg_rx->reorder_lock);
693
686 buf_size = tid_agg_rx->buf_size; 694 buf_size = tid_agg_rx->buf_size;
687 head_seq_num = tid_agg_rx->head_seq_num; 695 head_seq_num = tid_agg_rx->head_seq_num;
688 696
689 spin_lock(&tid_agg_rx->reorder_lock);
690 /* frame with out of date sequence number */ 697 /* frame with out of date sequence number */
691 if (seq_less(mpdu_seq_num, head_seq_num)) { 698 if (seq_less(mpdu_seq_num, head_seq_num)) {
692 dev_kfree_skb(skb); 699 dev_kfree_skb(skb);
@@ -1921,9 +1928,12 @@ ieee80211_rx_h_ctrl(struct ieee80211_rx_data *rx, struct sk_buff_head *frames)
1921 mod_timer(&tid_agg_rx->session_timer, 1928 mod_timer(&tid_agg_rx->session_timer,
1922 TU_TO_EXP_TIME(tid_agg_rx->timeout)); 1929 TU_TO_EXP_TIME(tid_agg_rx->timeout));
1923 1930
1931 spin_lock(&tid_agg_rx->reorder_lock);
1924 /* release stored frames up to start of BAR */ 1932 /* release stored frames up to start of BAR */
1925 ieee80211_release_reorder_frames(hw, tid_agg_rx, start_seq_num, 1933 ieee80211_release_reorder_frames(hw, tid_agg_rx, start_seq_num,
1926 frames); 1934 frames);
1935 spin_unlock(&tid_agg_rx->reorder_lock);
1936
1927 kfree_skb(skb); 1937 kfree_skb(skb);
1928 return RX_QUEUED; 1938 return RX_QUEUED;
1929 } 1939 }
@@ -2515,9 +2525,8 @@ static void ieee80211_invoke_rx_handlers(struct ieee80211_rx_data *rx)
2515} 2525}
2516 2526
2517/* 2527/*
2518 * This function makes calls into the RX path. Therefore the 2528 * This function makes calls into the RX path, therefore
2519 * caller must hold the sta_info->lock and everything has to 2529 * it has to be invoked under RCU read lock.
2520 * be under rcu_read_lock protection as well.
2521 */ 2530 */
2522void ieee80211_release_reorder_timeout(struct sta_info *sta, int tid) 2531void ieee80211_release_reorder_timeout(struct sta_info *sta, int tid)
2523{ 2532{
diff --git a/net/mac80211/sta_info.h b/net/mac80211/sta_info.h
index b562d9b6a702..05f11302443b 100644
--- a/net/mac80211/sta_info.h
+++ b/net/mac80211/sta_info.h
@@ -81,13 +81,14 @@ enum ieee80211_sta_info_flags {
81 * @stop_initiator: initiator of a session stop 81 * @stop_initiator: initiator of a session stop
82 * @tx_stop: TX DelBA frame when stopping 82 * @tx_stop: TX DelBA frame when stopping
83 * 83 *
84 * This structure is protected by RCU and the per-station 84 * This structure's lifetime is managed by RCU, assignments to
85 * spinlock. Assignments to the array holding it must hold 85 * the array holding it must hold the aggregation mutex.
86 * the spinlock, only the TX path can access it under RCU 86 *
87 * lock-free if, and only if, the state has the flag 87 * The TX path can access it under RCU lock-free if, and
88 * %HT_AGG_STATE_OPERATIONAL set. Otherwise, the TX path 88 * only if, the state has the flag %HT_AGG_STATE_OPERATIONAL
89 * must also acquire the spinlock and re-check the state, 89 * set. Otherwise, the TX path must also acquire the spinlock
90 * see comments in the tx code touching it. 90 * and re-check the state, see comments in the tx code
91 * touching it.
91 */ 92 */
92struct tid_ampdu_tx { 93struct tid_ampdu_tx {
93 struct rcu_head rcu_head; 94 struct rcu_head rcu_head;
@@ -115,15 +116,13 @@ struct tid_ampdu_tx {
115 * @rcu_head: RCU head used for freeing this struct 116 * @rcu_head: RCU head used for freeing this struct
116 * @reorder_lock: serializes access to reorder buffer, see below. 117 * @reorder_lock: serializes access to reorder buffer, see below.
117 * 118 *
118 * This structure is protected by RCU and the per-station 119 * This structure's lifetime is managed by RCU, assignments to
119 * spinlock. Assignments to the array holding it must hold 120 * the array holding it must hold the aggregation mutex.
120 * the spinlock.
121 * 121 *
122 * The @reorder_lock is used to protect the variables and 122 * The @reorder_lock is used to protect the members of this
123 * arrays such as @reorder_buf, @reorder_time, @head_seq_num, 123 * struct, except for @timeout, @buf_size and @dialog_token,
124 * @stored_mpdu_num and @reorder_time from being corrupted by 124 * which are constant across the lifetime of the struct (the
125 * concurrent access of the RX path and the expired frame 125 * dialog token being used only for debugging).
126 * release timer.
127 */ 126 */
128struct tid_ampdu_rx { 127struct tid_ampdu_rx {
129 struct rcu_head rcu_head; 128 struct rcu_head rcu_head;