aboutsummaryrefslogtreecommitdiffstats
path: root/net/mac80211/status.c
diff options
context:
space:
mode:
authorHelmut Schaa <helmut.schaa@googlemail.com>2011-08-11 10:17:42 -0400
committerJohn W. Linville <linville@tuxdriver.com>2011-08-22 14:45:58 -0400
commite69deded2bc29e6dd176089252a11b1854012c76 (patch)
treef20940462acaa806c859d1e7e28ee67bef1cb41b /net/mac80211/status.c
parentc1407b6cb22245ae8653cfc195530a9b8eb52879 (diff)
mac80211: Tear down BA session on BAR tx failure
As described at [1] some STAs (i.e. Intel 5100 Windows) can end up correctly BlockAcking incoming frames without delivering them to user space if a AMPDU subframe got lost and we don't flush the receipients reorder buffer with a BlockAckReq. This in turn results in stuck connections. According to 802.11n-2009 it is not necessary to send a BAR to flush the recepients RX reorder buffer but we still do that to be polite. However, assume the following frame exchange: AP -> STA, AMPDU (failed) AP -> STA, BAR (failed) The client in question then ends up in the same situation and won't deliver frames to userspace anymore since we weren't able to flush its reorder buffer. This is not a hypothetical situation but I was able to observe this exact behavior during a stress test between a rt2800pci AP and a Intel 5100 Windows client. In order to work around this issue just tear down the BA session as soon as a BAR failed to be TX'ed. [1] http://comments.gmane.org/gmane.linux.kernel.wireless.general/66867 Signed-off-by: Helmut Schaa <helmut.schaa@googlemail.com> Signed-off-by: John W. Linville <linville@tuxdriver.com>
Diffstat (limited to 'net/mac80211/status.c')
-rw-r--r--net/mac80211/status.c18
1 files changed, 18 insertions, 0 deletions
diff --git a/net/mac80211/status.c b/net/mac80211/status.c
index a89cca3491b4..e51bd2a1a073 100644
--- a/net/mac80211/status.c
+++ b/net/mac80211/status.c
@@ -187,6 +187,8 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb)
187 int rates_idx = -1; 187 int rates_idx = -1;
188 bool send_to_cooked; 188 bool send_to_cooked;
189 bool acked; 189 bool acked;
190 struct ieee80211_bar *bar;
191 u16 tid;
190 192
191 for (i = 0; i < IEEE80211_TX_MAX_RATES; i++) { 193 for (i = 0; i < IEEE80211_TX_MAX_RATES; i++) {
192 if (info->status.rates[i].idx < 0) { 194 if (info->status.rates[i].idx < 0) {
@@ -243,6 +245,22 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb)
243 tid, ssn); 245 tid, ssn);
244 } 246 }
245 247
248 if (!acked && ieee80211_is_back_req(fc)) {
249 /*
250 * BAR failed, let's tear down the BA session as a
251 * last resort as some STAs (Intel 5100 on Windows)
252 * can get stuck when the BA window isn't flushed
253 * correctly.
254 */
255 bar = (struct ieee80211_bar *) skb->data;
256 if (!(bar->control & IEEE80211_BAR_CTRL_MULTI_TID)) {
257 tid = (bar->control &
258 IEEE80211_BAR_CTRL_TID_INFO_MASK) >>
259 IEEE80211_BAR_CTRL_TID_INFO_SHIFT;
260 ieee80211_stop_tx_ba_session(&sta->sta, tid);
261 }
262 }
263
246 if (info->flags & IEEE80211_TX_STAT_TX_FILTERED) { 264 if (info->flags & IEEE80211_TX_STAT_TX_FILTERED) {
247 ieee80211_handle_filtered_frame(local, sta, skb); 265 ieee80211_handle_filtered_frame(local, sta, skb);
248 rcu_read_unlock(); 266 rcu_read_unlock();