diff options
author | Johannes Berg <johannes.berg@intel.com> | 2010-11-29 05:09:16 -0500 |
---|---|---|
committer | John W. Linville <linville@tuxdriver.com> | 2010-11-29 15:30:30 -0500 |
commit | dd318575ff0aae91ac4cbcc5b60c184e59267212 (patch) | |
tree | 140a0104b99b8edef7b961b4de9182e092782cf9 /net | |
parent | 8b7f8532d15631776ce8bec2bbbc58f6aad738d1 (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.c | 8 | ||||
-rw-r--r-- | net/mac80211/debugfs_sta.c | 29 | ||||
-rw-r--r-- | net/mac80211/rx.c | 17 | ||||
-rw-r--r-- | net/mac80211/sta_info.h | 29 |
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 | */ |
2522 | void ieee80211_release_reorder_timeout(struct sta_info *sta, int tid) | 2531 | void 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 | */ |
92 | struct tid_ampdu_tx { | 93 | struct 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 | */ |
128 | struct tid_ampdu_rx { | 127 | struct tid_ampdu_rx { |
129 | struct rcu_head rcu_head; | 128 | struct rcu_head rcu_head; |