aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJohannes Berg <johannes@sipsolutions.net>2009-07-24 07:23:09 -0400
committerJohn W. Linville <linville@tuxdriver.com>2009-07-27 15:24:19 -0400
commit3fa52056f3a8e755708241d5795e6d3e6f55ad85 (patch)
tree17986e56e775dc55627e4ba9aa0639182ee34339
parent91a3bd76155085d41520cf41ede39e8b7f01aeff (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.c2
-rw-r--r--include/net/mac80211.h4
-rw-r--r--net/mac80211/rx.c11
-rw-r--r--net/mac80211/sta_info.h13
-rw-r--r--net/mac80211/tx.c19
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 */
247enum mac80211_tx_control_flags { 250enum 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
362static 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
373static inline u32 test_sta_flags(struct sta_info *sta, const u32 flags) 360static 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