aboutsummaryrefslogtreecommitdiffstats
path: root/net/mac80211/sta_info.c
diff options
context:
space:
mode:
authorJohannes Berg <johannes@sipsolutions.net>2009-11-06 05:35:50 -0500
committerJohn W. Linville <linville@tuxdriver.com>2009-11-06 16:49:10 -0500
commitaf81858172cc0f3da81946aab919c26e4b364efc (patch)
tree8e7a4bf30ff7c23636d810c5a912ff7e3ddb7333 /net/mac80211/sta_info.c
parent70d9f405d09e334b609702d88ee03b6119c4b45e (diff)
mac80211: async station powersave handling
Some devices require that all frames to a station are flushed when that station goes into powersave mode before being able to send frames to that station again when it wakes up or polls -- all in order to avoid reordering and too many or too few frames being sent to the station when it polls. Normally, this is the case unless the station goes to sleep and wakes up very quickly again. But in that case, frames for it may be pending on the hardware queues, and thus races could happen in the case of multiple hardware queues used for QoS/WMM. Normally this isn't a problem, but with the iwlwifi mechanism we need to make sure the race doesn't happen. This makes mac80211 able to cope with the race with driver help by a new WLAN_STA_PS_DRIVER per-station flag that can be controlled by the driver and tells mac80211 whether it can transmit frames or not. This flag must be set according to very specific rules outlined in the documentation for the function that controls it. When we buffer new frames for the station, we normally set the TIM bit right away, but while the driver has blocked transmission to that sta we need to avoid that as well since we cannot respond to the station if it wakes up due to the TIM bit. Once the driver unblocks, we can set the TIM bit. Similarly, when the station just wakes up, we need to wait until all other frames are flushed before we can transmit frames to that station, so the same applies here, we need to wait for the driver to give the OK. Signed-off-by: Johannes Berg <johannes@sipsolutions.net> Signed-off-by: John W. Linville <linville@tuxdriver.com>
Diffstat (limited to 'net/mac80211/sta_info.c')
-rw-r--r--net/mac80211/sta_info.c118
1 files changed, 117 insertions, 1 deletions
diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c
index cde2da7a74d..be59456e8a4 100644
--- a/net/mac80211/sta_info.c
+++ b/net/mac80211/sta_info.c
@@ -171,6 +171,8 @@ void sta_info_destroy(struct sta_info *sta)
171 171
172 local = sta->local; 172 local = sta->local;
173 173
174 cancel_work_sync(&sta->drv_unblock_wk);
175
174 rate_control_remove_sta_debugfs(sta); 176 rate_control_remove_sta_debugfs(sta);
175 ieee80211_sta_debugfs_remove(sta); 177 ieee80211_sta_debugfs_remove(sta);
176 178
@@ -259,6 +261,21 @@ static void sta_info_hash_add(struct ieee80211_local *local,
259 rcu_assign_pointer(local->sta_hash[STA_HASH(sta->sta.addr)], sta); 261 rcu_assign_pointer(local->sta_hash[STA_HASH(sta->sta.addr)], sta);
260} 262}
261 263
264static void sta_unblock(struct work_struct *wk)
265{
266 struct sta_info *sta;
267
268 sta = container_of(wk, struct sta_info, drv_unblock_wk);
269
270 if (sta->dead)
271 return;
272
273 if (!test_sta_flags(sta, WLAN_STA_PS_STA))
274 ieee80211_sta_ps_deliver_wakeup(sta);
275 else if (test_and_clear_sta_flags(sta, WLAN_STA_PSPOLL))
276 ieee80211_sta_ps_deliver_poll_response(sta);
277}
278
262struct sta_info *sta_info_alloc(struct ieee80211_sub_if_data *sdata, 279struct sta_info *sta_info_alloc(struct ieee80211_sub_if_data *sdata,
263 u8 *addr, gfp_t gfp) 280 u8 *addr, gfp_t gfp)
264{ 281{
@@ -272,6 +289,7 @@ struct sta_info *sta_info_alloc(struct ieee80211_sub_if_data *sdata,
272 289
273 spin_lock_init(&sta->lock); 290 spin_lock_init(&sta->lock);
274 spin_lock_init(&sta->flaglock); 291 spin_lock_init(&sta->flaglock);
292 INIT_WORK(&sta->drv_unblock_wk, sta_unblock);
275 293
276 memcpy(sta->sta.addr, addr, ETH_ALEN); 294 memcpy(sta->sta.addr, addr, ETH_ALEN);
277 sta->local = local; 295 sta->local = local;
@@ -478,8 +496,10 @@ static void __sta_info_unlink(struct sta_info **sta)
478 } 496 }
479 497
480 list_del(&(*sta)->list); 498 list_del(&(*sta)->list);
499 (*sta)->dead = true;
481 500
482 if (test_and_clear_sta_flags(*sta, WLAN_STA_PS)) { 501 if (test_and_clear_sta_flags(*sta,
502 WLAN_STA_PS_STA | WLAN_STA_PS_DRIVER)) {
483 BUG_ON(!sdata->bss); 503 BUG_ON(!sdata->bss);
484 504
485 atomic_dec(&sdata->bss->num_sta_ps); 505 atomic_dec(&sdata->bss->num_sta_ps);
@@ -825,3 +845,99 @@ struct ieee80211_sta *ieee80211_find_sta(struct ieee80211_vif *vif,
825 return ieee80211_find_sta_by_hw(&sdata->local->hw, addr); 845 return ieee80211_find_sta_by_hw(&sdata->local->hw, addr);
826} 846}
827EXPORT_SYMBOL(ieee80211_find_sta); 847EXPORT_SYMBOL(ieee80211_find_sta);
848
849/* powersave support code */
850void ieee80211_sta_ps_deliver_wakeup(struct sta_info *sta)
851{
852 struct ieee80211_sub_if_data *sdata = sta->sdata;
853 struct ieee80211_local *local = sdata->local;
854 int sent, buffered;
855
856 drv_sta_notify(local, &sdata->vif, STA_NOTIFY_AWAKE, &sta->sta);
857
858 if (!skb_queue_empty(&sta->ps_tx_buf))
859 sta_info_clear_tim_bit(sta);
860
861 /* Send all buffered frames to the station */
862 sent = ieee80211_add_pending_skbs(local, &sta->tx_filtered);
863 buffered = ieee80211_add_pending_skbs(local, &sta->ps_tx_buf);
864 sent += buffered;
865 local->total_ps_buffered -= buffered;
866
867#ifdef CONFIG_MAC80211_VERBOSE_PS_DEBUG
868 printk(KERN_DEBUG "%s: STA %pM aid %d sending %d filtered/%d PS frames "
869 "since STA not sleeping anymore\n", sdata->dev->name,
870 sta->sta.addr, sta->sta.aid, sent - buffered, buffered);
871#endif /* CONFIG_MAC80211_VERBOSE_PS_DEBUG */
872}
873
874void ieee80211_sta_ps_deliver_poll_response(struct sta_info *sta)
875{
876 struct ieee80211_sub_if_data *sdata = sta->sdata;
877 struct ieee80211_local *local = sdata->local;
878 struct sk_buff *skb;
879 int no_pending_pkts;
880
881 skb = skb_dequeue(&sta->tx_filtered);
882 if (!skb) {
883 skb = skb_dequeue(&sta->ps_tx_buf);
884 if (skb)
885 local->total_ps_buffered--;
886 }
887 no_pending_pkts = skb_queue_empty(&sta->tx_filtered) &&
888 skb_queue_empty(&sta->ps_tx_buf);
889
890 if (skb) {
891 struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
892 struct ieee80211_hdr *hdr =
893 (struct ieee80211_hdr *) skb->data;
894
895 /*
896 * Tell TX path to send this frame even though the STA may
897 * still remain is PS mode after this frame exchange.
898 */
899 info->flags |= IEEE80211_TX_CTL_PSPOLL_RESPONSE;
900
901#ifdef CONFIG_MAC80211_VERBOSE_PS_DEBUG
902 printk(KERN_DEBUG "STA %pM aid %d: PS Poll (entries after %d)\n",
903 sta->sta.addr, sta->sta.aid,
904 skb_queue_len(&sta->ps_tx_buf));
905#endif /* CONFIG_MAC80211_VERBOSE_PS_DEBUG */
906
907 /* Use MoreData flag to indicate whether there are more
908 * buffered frames for this STA */
909 if (no_pending_pkts)
910 hdr->frame_control &= cpu_to_le16(~IEEE80211_FCTL_MOREDATA);
911 else
912 hdr->frame_control |= cpu_to_le16(IEEE80211_FCTL_MOREDATA);
913
914 ieee80211_add_pending_skb(local, skb);
915
916 if (no_pending_pkts)
917 sta_info_clear_tim_bit(sta);
918#ifdef CONFIG_MAC80211_VERBOSE_PS_DEBUG
919 } else {
920 /*
921 * FIXME: This can be the result of a race condition between
922 * us expiring a frame and the station polling for it.
923 * Should we send it a null-func frame indicating we
924 * have nothing buffered for it?
925 */
926 printk(KERN_DEBUG "%s: STA %pM sent PS Poll even "
927 "though there are no buffered frames for it\n",
928 sdata->dev->name, sta->sta.addr);
929#endif /* CONFIG_MAC80211_VERBOSE_PS_DEBUG */
930 }
931}
932
933void ieee80211_sta_block_awake(struct ieee80211_hw *hw,
934 struct ieee80211_sta *pubsta, bool block)
935{
936 struct sta_info *sta = container_of(pubsta, struct sta_info, sta);
937
938 if (block)
939 set_sta_flags(sta, WLAN_STA_PS_DRIVER);
940 else
941 ieee80211_queue_work(hw, &sta->drv_unblock_wk);
942}
943EXPORT_SYMBOL(ieee80211_sta_block_awake);