mac80211: Fix incorrect num_sta_ps decrement in ap_sta_ps_end
authorHelmut Schaa <helmut.schaa@googlemail.com>
Mon, 30 Jan 2012 14:18:00 +0000 (15:18 +0100)
committerJohn W. Linville <linville@tuxdriver.com>
Mon, 30 Jan 2012 20:48:20 +0000 (15:48 -0500)
If the driver blocked this specific STA with the help of
ieee80211_sta_block_awake we won't clear WLAN_STA_PS_STA later but
still decrement num_sta_ps. Hence, the next data frame from this
STA will trigger ap_sta_ps_end again and also decrement num_sta_ps
again leading to an incorrect num_sta_ps counter.

This can result in problems with powersaving clients not waking up
from PS because the TIM calculation might be skipped due to the
incorrect num_sta_ps counter.

Signed-off-by: Helmut Schaa <helmut.schaa@googlemail.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
net/mac80211/rx.c
net/mac80211/sta_info.c

index 73d213810a374579d58fb89a99014ece838a35f4..ab29253fb4f22e445235640f22ba20ae639f01b3 100644 (file)
@@ -1145,19 +1145,15 @@ static void ap_sta_ps_start(struct sta_info *sta)
 
 static void ap_sta_ps_end(struct sta_info *sta)
 {
-       struct ieee80211_sub_if_data *sdata = sta->sdata;
-
-       atomic_dec(&sdata->bss->num_sta_ps);
-
 #ifdef CONFIG_MAC80211_VERBOSE_PS_DEBUG
        printk(KERN_DEBUG "%s: STA %pM aid %d exits power save mode\n",
-              sdata->name, sta->sta.addr, sta->sta.aid);
+              sta->sdata->name, sta->sta.addr, sta->sta.aid);
 #endif /* CONFIG_MAC80211_VERBOSE_PS_DEBUG */
 
        if (test_sta_flag(sta, WLAN_STA_PS_DRIVER)) {
 #ifdef CONFIG_MAC80211_VERBOSE_PS_DEBUG
                printk(KERN_DEBUG "%s: STA %pM aid %d driver-ps-blocked\n",
-                      sdata->name, sta->sta.addr, sta->sta.aid);
+                      sta->sdata->name, sta->sta.addr, sta->sta.aid);
 #endif /* CONFIG_MAC80211_VERBOSE_PS_DEBUG */
                return;
        }
index 0c79593b1bbf78a998ab06bec24a30b6cc3807b2..1fb4770a7d1376dba25db0d123cb49a340027188 100644 (file)
@@ -997,9 +997,11 @@ EXPORT_SYMBOL(ieee80211_find_sta);
 static void clear_sta_ps_flags(void *_sta)
 {
        struct sta_info *sta = _sta;
+       struct ieee80211_sub_if_data *sdata = sta->sdata;
 
        clear_sta_flag(sta, WLAN_STA_PS_DRIVER);
-       clear_sta_flag(sta, WLAN_STA_PS_STA);
+       if (test_and_clear_sta_flag(sta, WLAN_STA_PS_STA))
+               atomic_dec(&sdata->bss->num_sta_ps);
 }
 
 /* powersave support code */