diff options
author | Johannes Berg <johannes@sipsolutions.net> | 2009-07-24 07:23:09 -0400 |
---|---|---|
committer | John W. Linville <linville@tuxdriver.com> | 2009-07-27 15:24:19 -0400 |
commit | 3fa52056f3a8e755708241d5795e6d3e6f55ad85 (patch) | |
tree | 17986e56e775dc55627e4ba9aa0639182ee34339 | |
parent | 91a3bd76155085d41520cf41ede39e8b7f01aeff (diff) |
mac80211: fix PS-poll response, race
When a station queries us for a PS-poll response, we wrongly
queue the frame on the virtual interface's queue rather than
the pending queue.
Additionally, fix a race condition where we could potentially
send multiple frames to the sleeping station due to using a
station flag rather than a packet flag. When converting to a
packet flag, we can also convert p54 and remove the filter
clearing we added for it.
(Also remove a now dead function)
Signed-off-by: Johannes Berg <johannes@sipsolutions.net>
Reported-by: Bob Copeland <me@bobcopeland.com>
Tested-by: Bob Copeland <me@bobcopeland.com>
Cc: Christian Lamparter <chunkeey@web.de>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
-rw-r--r-- | drivers/net/wireless/p54/txrx.c | 2 | ||||
-rw-r--r-- | include/net/mac80211.h | 4 | ||||
-rw-r--r-- | net/mac80211/rx.c | 11 | ||||
-rw-r--r-- | net/mac80211/sta_info.h | 13 | ||||
-rw-r--r-- | net/mac80211/tx.c | 19 |
5 files changed, 12 insertions, 37 deletions
diff --git a/drivers/net/wireless/p54/txrx.c b/drivers/net/wireless/p54/txrx.c index 0d589d68e547..c32a0d2fa1f7 100644 --- a/drivers/net/wireless/p54/txrx.c +++ b/drivers/net/wireless/p54/txrx.c | |||
@@ -614,7 +614,7 @@ static void p54_tx_80211_header(struct p54_common *priv, struct sk_buff *skb, | |||
614 | if (info->flags & IEEE80211_TX_CTL_ASSIGN_SEQ) | 614 | if (info->flags & IEEE80211_TX_CTL_ASSIGN_SEQ) |
615 | *flags |= P54_HDR_FLAG_DATA_OUT_SEQNR; | 615 | *flags |= P54_HDR_FLAG_DATA_OUT_SEQNR; |
616 | 616 | ||
617 | if (info->flags & IEEE80211_TX_CTL_CLEAR_PS_FILT) | 617 | if (info->flags & IEEE80211_TX_CTL_PSPOLL_RESPONSE) |
618 | *flags |= P54_HDR_FLAG_DATA_OUT_NOCANCEL; | 618 | *flags |= P54_HDR_FLAG_DATA_OUT_NOCANCEL; |
619 | 619 | ||
620 | *queue = skb_get_queue_mapping(skb) + P54_QUEUE_DATA; | 620 | *queue = skb_get_queue_mapping(skb) + P54_QUEUE_DATA; |
diff --git a/include/net/mac80211.h b/include/net/mac80211.h index 7dd67a1ff4d5..d4e09a06b4a2 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h | |||
@@ -243,6 +243,9 @@ struct ieee80211_bss_conf { | |||
243 | * used to indicate that a frame was already retried due to PS | 243 | * used to indicate that a frame was already retried due to PS |
244 | * @IEEE80211_TX_INTFL_DONT_ENCRYPT: completely internal to mac80211, | 244 | * @IEEE80211_TX_INTFL_DONT_ENCRYPT: completely internal to mac80211, |
245 | * used to indicate frame should not be encrypted | 245 | * used to indicate frame should not be encrypted |
246 | * @IEEE80211_TX_CTL_PSPOLL_RESPONSE: (internal?) | ||
247 | * This frame is a response to a PS-poll frame and should be sent | ||
248 | * although the station is in powersave mode. | ||
246 | */ | 249 | */ |
247 | enum mac80211_tx_control_flags { | 250 | enum mac80211_tx_control_flags { |
248 | IEEE80211_TX_CTL_REQ_TX_STATUS = BIT(0), | 251 | IEEE80211_TX_CTL_REQ_TX_STATUS = BIT(0), |
@@ -262,6 +265,7 @@ enum mac80211_tx_control_flags { | |||
262 | IEEE80211_TX_INTFL_NEED_TXPROCESSING = BIT(14), | 265 | IEEE80211_TX_INTFL_NEED_TXPROCESSING = BIT(14), |
263 | IEEE80211_TX_INTFL_RETRIED = BIT(15), | 266 | IEEE80211_TX_INTFL_RETRIED = BIT(15), |
264 | IEEE80211_TX_INTFL_DONT_ENCRYPT = BIT(16), | 267 | IEEE80211_TX_INTFL_DONT_ENCRYPT = BIT(16), |
268 | IEEE80211_TX_CTL_PSPOLL_RESPONSE = BIT(17), | ||
265 | }; | 269 | }; |
266 | 270 | ||
267 | /** | 271 | /** |
diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c index cb95a3116034..f195705146bd 100644 --- a/net/mac80211/rx.c +++ b/net/mac80211/rx.c | |||
@@ -783,7 +783,7 @@ static void ap_sta_ps_start(struct sta_info *sta) | |||
783 | struct ieee80211_local *local = sdata->local; | 783 | struct ieee80211_local *local = sdata->local; |
784 | 784 | ||
785 | atomic_inc(&sdata->bss->num_sta_ps); | 785 | atomic_inc(&sdata->bss->num_sta_ps); |
786 | set_and_clear_sta_flags(sta, WLAN_STA_PS, WLAN_STA_PSPOLL); | 786 | set_sta_flags(sta, WLAN_STA_PS); |
787 | drv_sta_notify(local, &sdata->vif, STA_NOTIFY_SLEEP, &sta->sta); | 787 | drv_sta_notify(local, &sdata->vif, STA_NOTIFY_SLEEP, &sta->sta); |
788 | #ifdef CONFIG_MAC80211_VERBOSE_PS_DEBUG | 788 | #ifdef CONFIG_MAC80211_VERBOSE_PS_DEBUG |
789 | printk(KERN_DEBUG "%s: STA %pM aid %d enters power save mode\n", | 789 | printk(KERN_DEBUG "%s: STA %pM aid %d enters power save mode\n", |
@@ -799,7 +799,7 @@ static int ap_sta_ps_end(struct sta_info *sta) | |||
799 | 799 | ||
800 | atomic_dec(&sdata->bss->num_sta_ps); | 800 | atomic_dec(&sdata->bss->num_sta_ps); |
801 | 801 | ||
802 | clear_sta_flags(sta, WLAN_STA_PS | WLAN_STA_PSPOLL); | 802 | clear_sta_flags(sta, WLAN_STA_PS); |
803 | drv_sta_notify(local, &sdata->vif, STA_NOTIFY_AWAKE, &sta->sta); | 803 | drv_sta_notify(local, &sdata->vif, STA_NOTIFY_AWAKE, &sta->sta); |
804 | 804 | ||
805 | if (!skb_queue_empty(&sta->ps_tx_buf)) | 805 | if (!skb_queue_empty(&sta->ps_tx_buf)) |
@@ -1117,14 +1117,15 @@ ieee80211_rx_h_ps_poll(struct ieee80211_rx_data *rx) | |||
1117 | skb_queue_empty(&rx->sta->ps_tx_buf); | 1117 | skb_queue_empty(&rx->sta->ps_tx_buf); |
1118 | 1118 | ||
1119 | if (skb) { | 1119 | if (skb) { |
1120 | struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); | ||
1120 | struct ieee80211_hdr *hdr = | 1121 | struct ieee80211_hdr *hdr = |
1121 | (struct ieee80211_hdr *) skb->data; | 1122 | (struct ieee80211_hdr *) skb->data; |
1122 | 1123 | ||
1123 | /* | 1124 | /* |
1124 | * Tell TX path to send one frame even though the STA may | 1125 | * Tell TX path to send this frame even though the STA may |
1125 | * still remain is PS mode after this frame exchange. | 1126 | * still remain is PS mode after this frame exchange. |
1126 | */ | 1127 | */ |
1127 | set_sta_flags(rx->sta, WLAN_STA_PSPOLL); | 1128 | info->flags |= IEEE80211_TX_CTL_PSPOLL_RESPONSE; |
1128 | 1129 | ||
1129 | #ifdef CONFIG_MAC80211_VERBOSE_PS_DEBUG | 1130 | #ifdef CONFIG_MAC80211_VERBOSE_PS_DEBUG |
1130 | printk(KERN_DEBUG "STA %pM aid %d: PS Poll (entries after %d)\n", | 1131 | printk(KERN_DEBUG "STA %pM aid %d: PS Poll (entries after %d)\n", |
@@ -1139,7 +1140,7 @@ ieee80211_rx_h_ps_poll(struct ieee80211_rx_data *rx) | |||
1139 | else | 1140 | else |
1140 | hdr->frame_control |= cpu_to_le16(IEEE80211_FCTL_MOREDATA); | 1141 | hdr->frame_control |= cpu_to_le16(IEEE80211_FCTL_MOREDATA); |
1141 | 1142 | ||
1142 | dev_queue_xmit(skb); | 1143 | ieee80211_add_pending_skb(rx->local, skb); |
1143 | 1144 | ||
1144 | if (no_pending_pkts) | 1145 | if (no_pending_pkts) |
1145 | sta_info_clear_tim_bit(rx->sta); | 1146 | sta_info_clear_tim_bit(rx->sta); |
diff --git a/net/mac80211/sta_info.h b/net/mac80211/sta_info.h index 4ecf10a9bd00..ccc3adf962c7 100644 --- a/net/mac80211/sta_info.h +++ b/net/mac80211/sta_info.h | |||
@@ -30,7 +30,6 @@ | |||
30 | * @WLAN_STA_ASSOC_AP: We're associated to that station, it is an AP. | 30 | * @WLAN_STA_ASSOC_AP: We're associated to that station, it is an AP. |
31 | * @WLAN_STA_WME: Station is a QoS-STA. | 31 | * @WLAN_STA_WME: Station is a QoS-STA. |
32 | * @WLAN_STA_WDS: Station is one of our WDS peers. | 32 | * @WLAN_STA_WDS: Station is one of our WDS peers. |
33 | * @WLAN_STA_PSPOLL: Station has just PS-polled us. | ||
34 | * @WLAN_STA_CLEAR_PS_FILT: Clear PS filter in hardware (using the | 33 | * @WLAN_STA_CLEAR_PS_FILT: Clear PS filter in hardware (using the |
35 | * IEEE80211_TX_CTL_CLEAR_PS_FILT control flag) when the next | 34 | * IEEE80211_TX_CTL_CLEAR_PS_FILT control flag) when the next |
36 | * frame to this station is transmitted. | 35 | * frame to this station is transmitted. |
@@ -47,7 +46,6 @@ enum ieee80211_sta_info_flags { | |||
47 | WLAN_STA_ASSOC_AP = 1<<5, | 46 | WLAN_STA_ASSOC_AP = 1<<5, |
48 | WLAN_STA_WME = 1<<6, | 47 | WLAN_STA_WME = 1<<6, |
49 | WLAN_STA_WDS = 1<<7, | 48 | WLAN_STA_WDS = 1<<7, |
50 | WLAN_STA_PSPOLL = 1<<8, | ||
51 | WLAN_STA_CLEAR_PS_FILT = 1<<9, | 49 | WLAN_STA_CLEAR_PS_FILT = 1<<9, |
52 | WLAN_STA_MFP = 1<<10, | 50 | WLAN_STA_MFP = 1<<10, |
53 | WLAN_STA_SUSPEND = 1<<11 | 51 | WLAN_STA_SUSPEND = 1<<11 |
@@ -359,17 +357,6 @@ static inline void clear_sta_flags(struct sta_info *sta, const u32 flags) | |||
359 | spin_unlock_irqrestore(&sta->flaglock, irqfl); | 357 | spin_unlock_irqrestore(&sta->flaglock, irqfl); |
360 | } | 358 | } |
361 | 359 | ||
362 | static inline void set_and_clear_sta_flags(struct sta_info *sta, | ||
363 | const u32 set, const u32 clear) | ||
364 | { | ||
365 | unsigned long irqfl; | ||
366 | |||
367 | spin_lock_irqsave(&sta->flaglock, irqfl); | ||
368 | sta->flags |= set; | ||
369 | sta->flags &= ~clear; | ||
370 | spin_unlock_irqrestore(&sta->flaglock, irqfl); | ||
371 | } | ||
372 | |||
373 | static inline u32 test_sta_flags(struct sta_info *sta, const u32 flags) | 360 | static inline u32 test_sta_flags(struct sta_info *sta, const u32 flags) |
374 | { | 361 | { |
375 | u32 ret; | 362 | u32 ret; |
diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c index 70ff4f065665..edacad1fb1dc 100644 --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c | |||
@@ -373,7 +373,7 @@ ieee80211_tx_h_unicast_ps_buf(struct ieee80211_tx_data *tx) | |||
373 | staflags = get_sta_flags(sta); | 373 | staflags = get_sta_flags(sta); |
374 | 374 | ||
375 | if (unlikely((staflags & WLAN_STA_PS) && | 375 | if (unlikely((staflags & WLAN_STA_PS) && |
376 | !(staflags & WLAN_STA_PSPOLL))) { | 376 | !(info->flags & IEEE80211_TX_CTL_PSPOLL_RESPONSE))) { |
377 | #ifdef CONFIG_MAC80211_VERBOSE_PS_DEBUG | 377 | #ifdef CONFIG_MAC80211_VERBOSE_PS_DEBUG |
378 | printk(KERN_DEBUG "STA %pM aid %d: PS buffer (entries " | 378 | printk(KERN_DEBUG "STA %pM aid %d: PS buffer (entries " |
379 | "before %d)\n", | 379 | "before %d)\n", |
@@ -412,24 +412,7 @@ ieee80211_tx_h_unicast_ps_buf(struct ieee80211_tx_data *tx) | |||
412 | sta->sta.addr); | 412 | sta->sta.addr); |
413 | } | 413 | } |
414 | #endif /* CONFIG_MAC80211_VERBOSE_PS_DEBUG */ | 414 | #endif /* CONFIG_MAC80211_VERBOSE_PS_DEBUG */ |
415 | if (test_and_clear_sta_flags(sta, WLAN_STA_PSPOLL)) { | ||
416 | /* | ||
417 | * The sleeping station with pending data is now snoozing. | ||
418 | * It queried us for its buffered frames and will go back | ||
419 | * to deep sleep once it got everything. | ||
420 | * | ||
421 | * inform the driver, in case the hardware does powersave | ||
422 | * frame filtering and keeps a station blacklist on its own | ||
423 | * (e.g: p54), so that frames can be delivered unimpeded. | ||
424 | * | ||
425 | * Note: It should be safe to disable the filter now. | ||
426 | * As, it is really unlikely that we still have any pending | ||
427 | * frame for this station in the hw's buffers/fifos left, | ||
428 | * that is not rejected with a unsuccessful tx_status yet. | ||
429 | */ | ||
430 | 415 | ||
431 | info->flags |= IEEE80211_TX_CTL_CLEAR_PS_FILT; | ||
432 | } | ||
433 | return TX_CONTINUE; | 416 | return TX_CONTINUE; |
434 | } | 417 | } |
435 | 418 | ||