aboutsummaryrefslogtreecommitdiffstats
path: root/net/mac80211/sta_info.c
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/sta_info.c
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/sta_info.c')
-rw-r--r--net/mac80211/sta_info.c17
1 files changed, 14 insertions, 3 deletions
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);