aboutsummaryrefslogtreecommitdiffstats
path: root/net/mac80211/agg-tx.c
diff options
context:
space:
mode:
authorJohannes Berg <johannes@sipsolutions.net>2009-02-10 15:25:50 -0500
committerJohn W. Linville <linville@tuxdriver.com>2009-02-13 13:45:41 -0500
commit23e6a7ea5cb1a902d37ab0c783709c178fa834df (patch)
tree0fa4e1f792dbfb0d57426b0e2f3c0cb7a2125402 /net/mac80211/agg-tx.c
parent86ab6c5a6c5204f6c25281b9039330b8f5e9b692 (diff)
mac80211: fix race in TX aggregation
When disabling TX aggregation because it was rejected or from the timer (it was not accepted), there is a window where we first set the state to operation, unlock, and then undo the whole thing. Avoid that by splitting up the stop function. Also get rid of the pointless sta_info indirection in the timer. Signed-off-by: Johannes Berg <johannes@sipsolutions.net> Signed-off-by: John W. Linville <linville@tuxdriver.com>
Diffstat (limited to 'net/mac80211/agg-tx.c')
-rw-r--r--net/mac80211/agg-tx.c95
1 files changed, 48 insertions, 47 deletions
diff --git a/net/mac80211/agg-tx.c b/net/mac80211/agg-tx.c
index 61bb7db04808..a49b76f61da3 100644
--- a/net/mac80211/agg-tx.c
+++ b/net/mac80211/agg-tx.c
@@ -123,6 +123,34 @@ void ieee80211_send_bar(struct ieee80211_sub_if_data *sdata, u8 *ra, u16 tid, u1
123 ieee80211_tx_skb(sdata, skb, 0); 123 ieee80211_tx_skb(sdata, skb, 0);
124} 124}
125 125
126static int __ieee80211_stop_tx_ba_session(struct ieee80211_local *local,
127 struct sta_info *sta, u16 tid,
128 enum ieee80211_back_parties initiator)
129{
130 int ret;
131 u8 *state;
132
133 state = &sta->ampdu_mlme.tid_state_tx[tid];
134
135 if (local->hw.ampdu_queues)
136 ieee80211_stop_queue(&local->hw, sta->tid_to_tx_q[tid]);
137
138 *state = HT_AGG_STATE_REQ_STOP_BA_MSK |
139 (initiator << HT_AGG_STATE_INITIATOR_SHIFT);
140
141 ret = local->ops->ampdu_action(&local->hw, IEEE80211_AMPDU_TX_STOP,
142 &sta->sta, tid, NULL);
143
144 /* HW shall not deny going back to legacy */
145 if (WARN_ON(ret)) {
146 *state = HT_AGG_STATE_OPERATIONAL;
147 if (local->hw.ampdu_queues)
148 ieee80211_wake_queue(&local->hw, sta->tid_to_tx_q[tid]);
149 }
150
151 return ret;
152}
153
126/* 154/*
127 * After sending add Block Ack request we activated a timer until 155 * After sending add Block Ack request we activated a timer until
128 * add Block Ack response will arrive from the recipient. 156 * add Block Ack response will arrive from the recipient.
@@ -135,23 +163,13 @@ static void sta_addba_resp_timer_expired(unsigned long data)
135 * flow in sta_info_create gives the TID as data, while the timer_to_id 163 * flow in sta_info_create gives the TID as data, while the timer_to_id
136 * array gives the sta through container_of */ 164 * array gives the sta through container_of */
137 u16 tid = *(u8 *)data; 165 u16 tid = *(u8 *)data;
138 struct sta_info *temp_sta = container_of((void *)data, 166 struct sta_info *sta = container_of((void *)data,
139 struct sta_info, timer_to_tid[tid]); 167 struct sta_info, timer_to_tid[tid]);
140 168 struct ieee80211_local *local = sta->local;
141 struct ieee80211_local *local = temp_sta->local;
142 struct ieee80211_hw *hw = &local->hw;
143 struct sta_info *sta;
144 u8 *state; 169 u8 *state;
145 170
146 rcu_read_lock();
147
148 sta = sta_info_get(local, temp_sta->sta.addr);
149 if (!sta) {
150 rcu_read_unlock();
151 return;
152 }
153
154 state = &sta->ampdu_mlme.tid_state_tx[tid]; 171 state = &sta->ampdu_mlme.tid_state_tx[tid];
172
155 /* check if the TID waits for addBA response */ 173 /* check if the TID waits for addBA response */
156 spin_lock_bh(&sta->lock); 174 spin_lock_bh(&sta->lock);
157 if (!(*state & HT_ADDBA_REQUESTED_MSK)) { 175 if (!(*state & HT_ADDBA_REQUESTED_MSK)) {
@@ -161,21 +179,15 @@ static void sta_addba_resp_timer_expired(unsigned long data)
161 printk(KERN_DEBUG "timer expired on tid %d but we are not " 179 printk(KERN_DEBUG "timer expired on tid %d but we are not "
162 "expecting addBA response there", tid); 180 "expecting addBA response there", tid);
163#endif 181#endif
164 goto timer_expired_exit; 182 return;
165 } 183 }
166 184
167#ifdef CONFIG_MAC80211_HT_DEBUG 185#ifdef CONFIG_MAC80211_HT_DEBUG
168 printk(KERN_DEBUG "addBA response timer expired on tid %d\n", tid); 186 printk(KERN_DEBUG "addBA response timer expired on tid %d\n", tid);
169#endif 187#endif
170 188
171 /* go through the state check in stop_BA_session */ 189 __ieee80211_stop_tx_ba_session(local, sta, tid, WLAN_BACK_INITIATOR);
172 *state = HT_AGG_STATE_OPERATIONAL;
173 spin_unlock_bh(&sta->lock); 190 spin_unlock_bh(&sta->lock);
174 ieee80211_stop_tx_ba_session(hw, temp_sta->sta.addr, tid,
175 WLAN_BACK_INITIATOR);
176
177timer_expired_exit:
178 rcu_read_unlock();
179} 191}
180 192
181int ieee80211_start_tx_ba_session(struct ieee80211_hw *hw, u8 *ra, u16 tid) 193int ieee80211_start_tx_ba_session(struct ieee80211_hw *hw, u8 *ra, u16 tid)
@@ -187,6 +199,9 @@ int ieee80211_start_tx_ba_session(struct ieee80211_hw *hw, u8 *ra, u16 tid)
187 u8 *state; 199 u8 *state;
188 int ret = 0; 200 int ret = 0;
189 201
202 if (WARN_ON(!local->ops->ampdu_action))
203 return -EINVAL;
204
190 if ((tid >= STA_TID_NUM) || !(hw->flags & IEEE80211_HW_AMPDU_AGGREGATION)) 205 if ((tid >= STA_TID_NUM) || !(hw->flags & IEEE80211_HW_AMPDU_AGGREGATION))
191 return -EINVAL; 206 return -EINVAL;
192 207
@@ -280,9 +295,8 @@ int ieee80211_start_tx_ba_session(struct ieee80211_hw *hw, u8 *ra, u16 tid)
280 /* This is slightly racy because the queue isn't stopped */ 295 /* This is slightly racy because the queue isn't stopped */
281 start_seq_num = sta->tid_seq[tid]; 296 start_seq_num = sta->tid_seq[tid];
282 297
283 if (local->ops->ampdu_action) 298 ret = local->ops->ampdu_action(hw, IEEE80211_AMPDU_TX_START,
284 ret = local->ops->ampdu_action(hw, IEEE80211_AMPDU_TX_START, 299 &sta->sta, tid, &start_seq_num);
285 &sta->sta, tid, &start_seq_num);
286 300
287 if (ret) { 301 if (ret) {
288 /* No need to requeue the packets in the agg queue, since we 302 /* No need to requeue the packets in the agg queue, since we
@@ -423,6 +437,9 @@ int ieee80211_stop_tx_ba_session(struct ieee80211_hw *hw,
423 u8 *state; 437 u8 *state;
424 int ret = 0; 438 int ret = 0;
425 439
440 if (WARN_ON(!local->ops->ampdu_action))
441 return -EINVAL;
442
426 if (tid >= STA_TID_NUM) 443 if (tid >= STA_TID_NUM)
427 return -EINVAL; 444 return -EINVAL;
428 445
@@ -439,7 +456,7 @@ int ieee80211_stop_tx_ba_session(struct ieee80211_hw *hw,
439 456
440 if (*state != HT_AGG_STATE_OPERATIONAL) { 457 if (*state != HT_AGG_STATE_OPERATIONAL) {
441 ret = -ENOENT; 458 ret = -ENOENT;
442 goto stop_BA_exit; 459 goto unlock;
443 } 460 }
444 461
445#ifdef CONFIG_MAC80211_HT_DEBUG 462#ifdef CONFIG_MAC80211_HT_DEBUG
@@ -447,27 +464,13 @@ int ieee80211_stop_tx_ba_session(struct ieee80211_hw *hw,
447 ra, tid); 464 ra, tid);
448#endif /* CONFIG_MAC80211_HT_DEBUG */ 465#endif /* CONFIG_MAC80211_HT_DEBUG */
449 466
450 if (hw->ampdu_queues) 467 ret = __ieee80211_stop_tx_ba_session(local, sta, tid, initiator);
451 ieee80211_stop_queue(hw, sta->tid_to_tx_q[tid]);
452
453 *state = HT_AGG_STATE_REQ_STOP_BA_MSK |
454 (initiator << HT_AGG_STATE_INITIATOR_SHIFT);
455 468
456 if (local->ops->ampdu_action) 469 unlock:
457 ret = local->ops->ampdu_action(hw, IEEE80211_AMPDU_TX_STOP,
458 &sta->sta, tid, NULL);
459
460 /* HW shall not deny going back to legacy */
461 if (WARN_ON(ret)) {
462 *state = HT_AGG_STATE_OPERATIONAL;
463 if (hw->ampdu_queues)
464 ieee80211_wake_queue(hw, sta->tid_to_tx_q[tid]);
465 goto stop_BA_exit;
466 }
467
468stop_BA_exit:
469 spin_unlock_bh(&sta->lock); 470 spin_unlock_bh(&sta->lock);
471
470 rcu_read_unlock(); 472 rcu_read_unlock();
473
471 return ret; 474 return ret;
472} 475}
473EXPORT_SYMBOL(ieee80211_stop_tx_ba_session); 476EXPORT_SYMBOL(ieee80211_stop_tx_ba_session);
@@ -623,10 +626,8 @@ void ieee80211_process_addba_resp(struct ieee80211_local *local,
623 spin_unlock_bh(&sta->lock); 626 spin_unlock_bh(&sta->lock);
624 } else { 627 } else {
625 sta->ampdu_mlme.addba_req_num[tid]++; 628 sta->ampdu_mlme.addba_req_num[tid]++;
626 /* this will allow the state check in stop_BA_session */ 629 __ieee80211_stop_tx_ba_session(local, sta, tid,
627 *state = HT_AGG_STATE_OPERATIONAL; 630 WLAN_BACK_INITIATOR);
628 spin_unlock_bh(&sta->lock); 631 spin_unlock_bh(&sta->lock);
629 ieee80211_stop_tx_ba_session(hw, sta->sta.addr, tid,
630 WLAN_BACK_INITIATOR);
631 } 632 }
632} 633}