aboutsummaryrefslogtreecommitdiffstats
path: root/net/mac80211
diff options
context:
space:
mode:
authorJohannes Berg <johannes.berg@intel.com>2010-11-16 14:50:28 -0500
committerJohn W. Linville <linville@tuxdriver.com>2010-11-17 16:19:33 -0500
commit50a9432daeece6fc1309bef1dc0a7b8fde8204cb (patch)
tree11b8bdf724dd9951391ea7f963e6539ca86ea4b6 /net/mac80211
parent4bce22b9b84032c77c7e038b07b24fcc706dfc10 (diff)
mac80211: fix powersaving clients races
The code to handle powersaving stations has a race: when the powersave flag is lifted from a station, we could transmit a packet that is being processed for TX at the same time right away, even if there are other frames queued for it. This would cause frame reordering. To fix this, lift the flag only under the appropriate lock that blocks TX. Additionally, the code to allow drivers to block a station while frames for it are on the HW queue is never re-enabled the station, so traffic would get stuck indefinitely. Fix this by clearing the flag for this appropriately. Finally, as an optimisation, don't do anything if the driver unblocks an already unblocked station. Signed-off-by: Johannes Berg <johannes.berg@intel.com> Signed-off-by: John W. Linville <linville@tuxdriver.com>
Diffstat (limited to 'net/mac80211')
-rw-r--r--net/mac80211/ieee80211_i.h3
-rw-r--r--net/mac80211/rx.c2
-rw-r--r--net/mac80211/sta_info.c17
-rw-r--r--net/mac80211/util.c14
4 files changed, 29 insertions, 7 deletions
diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h
index 59a1d38212fd..3598abf21844 100644
--- a/net/mac80211/ieee80211_i.h
+++ b/net/mac80211/ieee80211_i.h
@@ -1278,6 +1278,9 @@ void ieee80211_add_pending_skb(struct ieee80211_local *local,
1278 struct sk_buff *skb); 1278 struct sk_buff *skb);
1279int ieee80211_add_pending_skbs(struct ieee80211_local *local, 1279int ieee80211_add_pending_skbs(struct ieee80211_local *local,
1280 struct sk_buff_head *skbs); 1280 struct sk_buff_head *skbs);
1281int ieee80211_add_pending_skbs_fn(struct ieee80211_local *local,
1282 struct sk_buff_head *skbs,
1283 void (*fn)(void *data), void *data);
1281 1284
1282void ieee80211_send_auth(struct ieee80211_sub_if_data *sdata, 1285void ieee80211_send_auth(struct ieee80211_sub_if_data *sdata,
1283 u16 transaction, u16 auth_alg, 1286 u16 transaction, u16 auth_alg,
diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c
index 902b03ee8f60..d2fcd22ab06d 100644
--- a/net/mac80211/rx.c
+++ b/net/mac80211/rx.c
@@ -1102,8 +1102,6 @@ static void ap_sta_ps_end(struct sta_info *sta)
1102 1102
1103 atomic_dec(&sdata->bss->num_sta_ps); 1103 atomic_dec(&sdata->bss->num_sta_ps);
1104 1104
1105 clear_sta_flags(sta, WLAN_STA_PS_STA);
1106
1107#ifdef CONFIG_MAC80211_VERBOSE_PS_DEBUG 1105#ifdef CONFIG_MAC80211_VERBOSE_PS_DEBUG
1108 printk(KERN_DEBUG "%s: STA %pM aid %d exits power save mode\n", 1106 printk(KERN_DEBUG "%s: STA %pM aid %d exits power save mode\n",
1109 sdata->name, sta->sta.addr, sta->sta.aid); 1107 sdata->name, sta->sta.addr, sta->sta.aid);
diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c
index 6d8f897d8763..eff58571fd7e 100644
--- a/net/mac80211/sta_info.c
+++ b/net/mac80211/sta_info.c
@@ -199,8 +199,11 @@ static void sta_unblock(struct work_struct *wk)
199 199
200 if (!test_sta_flags(sta, WLAN_STA_PS_STA)) 200 if (!test_sta_flags(sta, WLAN_STA_PS_STA))
201 ieee80211_sta_ps_deliver_wakeup(sta); 201 ieee80211_sta_ps_deliver_wakeup(sta);
202 else if (test_and_clear_sta_flags(sta, WLAN_STA_PSPOLL)) 202 else if (test_and_clear_sta_flags(sta, WLAN_STA_PSPOLL)) {
203 clear_sta_flags(sta, WLAN_STA_PS_DRIVER);
203 ieee80211_sta_ps_deliver_poll_response(sta); 204 ieee80211_sta_ps_deliver_poll_response(sta);
205 } else
206 clear_sta_flags(sta, WLAN_STA_PS_DRIVER);
204} 207}
205 208
206static int sta_prepare_rate_control(struct ieee80211_local *local, 209static int sta_prepare_rate_control(struct ieee80211_local *local,
@@ -880,6 +883,13 @@ struct ieee80211_sta *ieee80211_find_sta(struct ieee80211_vif *vif,
880} 883}
881EXPORT_SYMBOL(ieee80211_find_sta); 884EXPORT_SYMBOL(ieee80211_find_sta);
882 885
886static void clear_sta_ps_flags(void *_sta)
887{
888 struct sta_info *sta = _sta;
889
890 clear_sta_flags(sta, WLAN_STA_PS_DRIVER | WLAN_STA_PS_STA);
891}
892
883/* powersave support code */ 893/* powersave support code */
884void ieee80211_sta_ps_deliver_wakeup(struct sta_info *sta) 894void ieee80211_sta_ps_deliver_wakeup(struct sta_info *sta)
885{ 895{
@@ -894,7 +904,8 @@ void ieee80211_sta_ps_deliver_wakeup(struct sta_info *sta)
894 904
895 /* Send all buffered frames to the station */ 905 /* Send all buffered frames to the station */
896 sent = ieee80211_add_pending_skbs(local, &sta->tx_filtered); 906 sent = ieee80211_add_pending_skbs(local, &sta->tx_filtered);
897 buffered = ieee80211_add_pending_skbs(local, &sta->ps_tx_buf); 907 buffered = ieee80211_add_pending_skbs_fn(local, &sta->ps_tx_buf,
908 clear_sta_ps_flags, sta);
898 sent += buffered; 909 sent += buffered;
899 local->total_ps_buffered -= buffered; 910 local->total_ps_buffered -= buffered;
900 911
@@ -973,7 +984,7 @@ void ieee80211_sta_block_awake(struct ieee80211_hw *hw,
973 984
974 if (block) 985 if (block)
975 set_sta_flags(sta, WLAN_STA_PS_DRIVER); 986 set_sta_flags(sta, WLAN_STA_PS_DRIVER);
976 else 987 else if (test_sta_flags(sta, WLAN_STA_PS_DRIVER))
977 ieee80211_queue_work(hw, &sta->drv_unblock_wk); 988 ieee80211_queue_work(hw, &sta->drv_unblock_wk);
978} 989}
979EXPORT_SYMBOL(ieee80211_sta_block_awake); 990EXPORT_SYMBOL(ieee80211_sta_block_awake);
diff --git a/net/mac80211/util.c b/net/mac80211/util.c
index 68d0518254dd..e497476174ce 100644
--- a/net/mac80211/util.c
+++ b/net/mac80211/util.c
@@ -368,8 +368,9 @@ void ieee80211_add_pending_skb(struct ieee80211_local *local,
368 spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags); 368 spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags);
369} 369}
370 370
371int ieee80211_add_pending_skbs(struct ieee80211_local *local, 371int ieee80211_add_pending_skbs_fn(struct ieee80211_local *local,
372 struct sk_buff_head *skbs) 372 struct sk_buff_head *skbs,
373 void (*fn)(void *data), void *data)
373{ 374{
374 struct ieee80211_hw *hw = &local->hw; 375 struct ieee80211_hw *hw = &local->hw;
375 struct sk_buff *skb; 376 struct sk_buff *skb;
@@ -394,6 +395,9 @@ int ieee80211_add_pending_skbs(struct ieee80211_local *local,
394 __skb_queue_tail(&local->pending[queue], skb); 395 __skb_queue_tail(&local->pending[queue], skb);
395 } 396 }
396 397
398 if (fn)
399 fn(data);
400
397 for (i = 0; i < hw->queues; i++) 401 for (i = 0; i < hw->queues; i++)
398 __ieee80211_wake_queue(hw, i, 402 __ieee80211_wake_queue(hw, i,
399 IEEE80211_QUEUE_STOP_REASON_SKB_ADD); 403 IEEE80211_QUEUE_STOP_REASON_SKB_ADD);
@@ -402,6 +406,12 @@ int ieee80211_add_pending_skbs(struct ieee80211_local *local,
402 return ret; 406 return ret;
403} 407}
404 408
409int ieee80211_add_pending_skbs(struct ieee80211_local *local,
410 struct sk_buff_head *skbs)
411{
412 return ieee80211_add_pending_skbs_fn(local, skbs, NULL, NULL);
413}
414
405void ieee80211_stop_queues_by_reason(struct ieee80211_hw *hw, 415void ieee80211_stop_queues_by_reason(struct ieee80211_hw *hw,
406 enum queue_stop_reason reason) 416 enum queue_stop_reason reason)
407{ 417{