diff options
author | Vivek Natarajan <vnatarajan@atheros.com> | 2011-02-23 02:34:32 -0500 |
---|---|---|
committer | John W. Linville <linville@tuxdriver.com> | 2011-02-23 16:25:29 -0500 |
commit | f3e85b9edeaf8ad0446a37a40c873f3f8898c57d (patch) | |
tree | 298ae716ab7215423a4bc1192783c475be769838 /net/mac80211 | |
parent | 05db8c5729fac2788f45bf327d168f2ea397f6a1 (diff) |
mac80211: Fix a race on enabling power save.
There is a race on sending a data frame before the tx completion
of nullfunc frame for enabling power save. As the data quickly
follows the nullfunc frame, the AP thinks that the station is out
of power save and continues to send the frames. Whereas in the
station, the nullfunc ack will be processed after the tx completion
of data frame and mac80211 goes to powersave. Thus the power
save state mismatch between the station and the AP causes some
data loss and some applications fail because of that. This patch
fixes this issue.
Signed-off-by: Vivek Natarajan <vnatarajan@atheros.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
Diffstat (limited to 'net/mac80211')
-rw-r--r-- | net/mac80211/mlme.c | 14 | ||||
-rw-r--r-- | net/mac80211/status.c | 2 |
2 files changed, 13 insertions, 3 deletions
diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 7b3f9df725bd..abb011660803 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c | |||
@@ -738,9 +738,19 @@ void ieee80211_dynamic_ps_enable_work(struct work_struct *work) | |||
738 | return; | 738 | return; |
739 | 739 | ||
740 | if ((local->hw.flags & IEEE80211_HW_PS_NULLFUNC_STACK) && | 740 | if ((local->hw.flags & IEEE80211_HW_PS_NULLFUNC_STACK) && |
741 | (!(ifmgd->flags & IEEE80211_STA_NULLFUNC_ACKED))) | 741 | (!(ifmgd->flags & IEEE80211_STA_NULLFUNC_ACKED))) { |
742 | netif_tx_stop_all_queues(sdata->dev); | ||
743 | /* | ||
744 | * Flush all the frames queued in the driver before | ||
745 | * going to power save | ||
746 | */ | ||
747 | drv_flush(local, false); | ||
742 | ieee80211_send_nullfunc(local, sdata, 1); | 748 | ieee80211_send_nullfunc(local, sdata, 1); |
743 | 749 | ||
750 | /* Flush once again to get the tx status of nullfunc frame */ | ||
751 | drv_flush(local, false); | ||
752 | } | ||
753 | |||
744 | if (!((local->hw.flags & IEEE80211_HW_REPORTS_TX_ACK_STATUS) && | 754 | if (!((local->hw.flags & IEEE80211_HW_REPORTS_TX_ACK_STATUS) && |
745 | (local->hw.flags & IEEE80211_HW_PS_NULLFUNC_STACK)) || | 755 | (local->hw.flags & IEEE80211_HW_PS_NULLFUNC_STACK)) || |
746 | (ifmgd->flags & IEEE80211_STA_NULLFUNC_ACKED)) { | 756 | (ifmgd->flags & IEEE80211_STA_NULLFUNC_ACKED)) { |
@@ -748,6 +758,8 @@ void ieee80211_dynamic_ps_enable_work(struct work_struct *work) | |||
748 | local->hw.conf.flags |= IEEE80211_CONF_PS; | 758 | local->hw.conf.flags |= IEEE80211_CONF_PS; |
749 | ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_PS); | 759 | ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_PS); |
750 | } | 760 | } |
761 | |||
762 | netif_tx_start_all_queues(sdata->dev); | ||
751 | } | 763 | } |
752 | 764 | ||
753 | void ieee80211_dynamic_ps_timer(unsigned long data) | 765 | void ieee80211_dynamic_ps_timer(unsigned long data) |
diff --git a/net/mac80211/status.c b/net/mac80211/status.c index 010a559bd872..865185127f51 100644 --- a/net/mac80211/status.c +++ b/net/mac80211/status.c | |||
@@ -318,8 +318,6 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb) | |||
318 | if (info->flags & IEEE80211_TX_STAT_ACK) { | 318 | if (info->flags & IEEE80211_TX_STAT_ACK) { |
319 | local->ps_sdata->u.mgd.flags |= | 319 | local->ps_sdata->u.mgd.flags |= |
320 | IEEE80211_STA_NULLFUNC_ACKED; | 320 | IEEE80211_STA_NULLFUNC_ACKED; |
321 | ieee80211_queue_work(&local->hw, | ||
322 | &local->dynamic_ps_enable_work); | ||
323 | } else | 321 | } else |
324 | mod_timer(&local->dynamic_ps_timer, jiffies + | 322 | mod_timer(&local->dynamic_ps_timer, jiffies + |
325 | msecs_to_jiffies(10)); | 323 | msecs_to_jiffies(10)); |