aboutsummaryrefslogtreecommitdiffstats
path: root/net/mac80211/agg-tx.c
diff options
context:
space:
mode:
authorJohannes Berg <johannes.berg@intel.com>2011-05-13 07:35:40 -0400
committerJohn W. Linville <linville@tuxdriver.com>2011-05-16 14:10:40 -0400
commitec034b208dc8aa5dc73ec46c3f27e34c5efbf113 (patch)
tree56140fc3a4115441822b5cc2aa9de5224bcb8654 /net/mac80211/agg-tx.c
parent7527a782e187d1214a5b3dc2897ce441033bb4ef (diff)
mac80211: fix TX a-MPDU locking
During my quest to make mac80211 not have any RCU warnings from sparse, I came across the a-MPDU code again and it wasn't quite clear why it isn't racy. So instead of assigning the tid_tx array with just the spinlock held in ieee80211_start_tx_ba_session use a separate temporary array protected only by the spinlock and protect all assignments to the "live" array by both the spinlock and the mutex so that other code is easily verified to be correct. Due to pointer assignment atomicity I don't think this is a real issue, but I'm not sure, especially on Alpha the current code might be problematic. Signed-off-by: Johannes Berg <johannes.berg@intel.com> Signed-off-by: John W. Linville <linville@tuxdriver.com>
Diffstat (limited to 'net/mac80211/agg-tx.c')
-rw-r--r--net/mac80211/agg-tx.c23
1 files changed, 17 insertions, 6 deletions
diff --git a/net/mac80211/agg-tx.c b/net/mac80211/agg-tx.c
index 63d852cb4ca2..f614ee602089 100644
--- a/net/mac80211/agg-tx.c
+++ b/net/mac80211/agg-tx.c
@@ -136,6 +136,14 @@ void ieee80211_send_bar(struct ieee80211_sub_if_data *sdata, u8 *ra, u16 tid, u1
136 ieee80211_tx_skb(sdata, skb); 136 ieee80211_tx_skb(sdata, skb);
137} 137}
138 138
139void ieee80211_assign_tid_tx(struct sta_info *sta, int tid,
140 struct tid_ampdu_tx *tid_tx)
141{
142 lockdep_assert_held(&sta->ampdu_mlme.mtx);
143 lockdep_assert_held(&sta->lock);
144 rcu_assign_pointer(sta->ampdu_mlme.tid_tx[tid], tid_tx);
145}
146
139static void kfree_tid_tx(struct rcu_head *rcu_head) 147static void kfree_tid_tx(struct rcu_head *rcu_head)
140{ 148{
141 struct tid_ampdu_tx *tid_tx = 149 struct tid_ampdu_tx *tid_tx =
@@ -161,7 +169,7 @@ int ___ieee80211_stop_tx_ba_session(struct sta_info *sta, u16 tid,
161 169
162 if (test_bit(HT_AGG_STATE_WANT_START, &tid_tx->state)) { 170 if (test_bit(HT_AGG_STATE_WANT_START, &tid_tx->state)) {
163 /* not even started yet! */ 171 /* not even started yet! */
164 rcu_assign_pointer(sta->ampdu_mlme.tid_tx[tid], NULL); 172 ieee80211_assign_tid_tx(sta, tid, NULL);
165 spin_unlock_bh(&sta->lock); 173 spin_unlock_bh(&sta->lock);
166 call_rcu(&tid_tx->rcu_head, kfree_tid_tx); 174 call_rcu(&tid_tx->rcu_head, kfree_tid_tx);
167 return 0; 175 return 0;
@@ -318,7 +326,7 @@ void ieee80211_tx_ba_session_handle_start(struct sta_info *sta, int tid)
318 " tid %d\n", tid); 326 " tid %d\n", tid);
319#endif 327#endif
320 spin_lock_bh(&sta->lock); 328 spin_lock_bh(&sta->lock);
321 rcu_assign_pointer(sta->ampdu_mlme.tid_tx[tid], NULL); 329 ieee80211_assign_tid_tx(sta, tid, NULL);
322 spin_unlock_bh(&sta->lock); 330 spin_unlock_bh(&sta->lock);
323 331
324 ieee80211_wake_queue_agg(local, tid); 332 ieee80211_wake_queue_agg(local, tid);
@@ -398,7 +406,7 @@ int ieee80211_start_tx_ba_session(struct ieee80211_sta *pubsta, u16 tid,
398 406
399 tid_tx = sta->ampdu_mlme.tid_tx[tid]; 407 tid_tx = sta->ampdu_mlme.tid_tx[tid];
400 /* check if the TID is not in aggregation flow already */ 408 /* check if the TID is not in aggregation flow already */
401 if (tid_tx) { 409 if (tid_tx || sta->ampdu_mlme.tid_start_tx[tid]) {
402#ifdef CONFIG_MAC80211_HT_DEBUG 410#ifdef CONFIG_MAC80211_HT_DEBUG
403 printk(KERN_DEBUG "BA request denied - session is not " 411 printk(KERN_DEBUG "BA request denied - session is not "
404 "idle on tid %u\n", tid); 412 "idle on tid %u\n", tid);
@@ -433,8 +441,11 @@ int ieee80211_start_tx_ba_session(struct ieee80211_sta *pubsta, u16 tid,
433 sta->ampdu_mlme.dialog_token_allocator++; 441 sta->ampdu_mlme.dialog_token_allocator++;
434 tid_tx->dialog_token = sta->ampdu_mlme.dialog_token_allocator; 442 tid_tx->dialog_token = sta->ampdu_mlme.dialog_token_allocator;
435 443
436 /* finally, assign it to the array */ 444 /*
437 rcu_assign_pointer(sta->ampdu_mlme.tid_tx[tid], tid_tx); 445 * Finally, assign it to the start array; the work item will
446 * collect it and move it to the normal array.
447 */
448 sta->ampdu_mlme.tid_start_tx[tid] = tid_tx;
438 449
439 ieee80211_queue_work(&local->hw, &sta->ampdu_mlme.work); 450 ieee80211_queue_work(&local->hw, &sta->ampdu_mlme.work);
440 451
@@ -697,7 +708,7 @@ void ieee80211_stop_tx_ba_cb(struct ieee80211_vif *vif, u8 *ra, u8 tid)
697 ieee80211_agg_splice_packets(local, tid_tx, tid); 708 ieee80211_agg_splice_packets(local, tid_tx, tid);
698 709
699 /* future packets must not find the tid_tx struct any more */ 710 /* future packets must not find the tid_tx struct any more */
700 rcu_assign_pointer(sta->ampdu_mlme.tid_tx[tid], NULL); 711 ieee80211_assign_tid_tx(sta, tid, NULL);
701 712
702 ieee80211_agg_splice_finish(local, tid); 713 ieee80211_agg_splice_finish(local, tid);
703 714