diff options
author | Johannes Berg <johannes.berg@intel.com> | 2010-11-16 11:50:28 -0800 |
---|---|---|
committer | John W. Linville <linville@tuxdriver.com> | 2010-11-17 16:19:33 -0500 |
commit | 50a9432daeece6fc1309bef1dc0a7b8fde8204cb (patch) | |
tree | 11b8bdf724dd9951391ea7f963e6539ca86ea4b6 /net/mac80211/util.c | |
parent | 4bce22b9b84032c77c7e038b07b24fcc706dfc10 (diff) | |
download | blackbird-op-linux-50a9432daeece6fc1309bef1dc0a7b8fde8204cb.tar.gz blackbird-op-linux-50a9432daeece6fc1309bef1dc0a7b8fde8204cb.zip |
mac80211: fix powersaving clients races
The code to handle powersaving stations has a race:
when the powersave flag is lifted from a station,
we could transmit a packet that is being processed
for TX at the same time right away, even if there
are other frames queued for it. This would cause
frame reordering. To fix this, lift the flag only
under the appropriate lock that blocks TX.
Additionally, the code to allow drivers to block a
station while frames for it are on the HW queue is
never re-enabled the station, so traffic would get
stuck indefinitely. Fix this by clearing the flag
for this appropriately.
Finally, as an optimisation, don't do anything if
the driver unblocks an already unblocked station.
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
Diffstat (limited to 'net/mac80211/util.c')
-rw-r--r-- | net/mac80211/util.c | 14 |
1 files changed, 12 insertions, 2 deletions
diff --git a/net/mac80211/util.c b/net/mac80211/util.c index 68d0518254dd..e497476174ce 100644 --- a/net/mac80211/util.c +++ b/net/mac80211/util.c @@ -368,8 +368,9 @@ void ieee80211_add_pending_skb(struct ieee80211_local *local, spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags); } -int ieee80211_add_pending_skbs(struct ieee80211_local *local, - struct sk_buff_head *skbs) +int ieee80211_add_pending_skbs_fn(struct ieee80211_local *local, + struct sk_buff_head *skbs, + void (*fn)(void *data), void *data) { struct ieee80211_hw *hw = &local->hw; struct sk_buff *skb; @@ -394,6 +395,9 @@ int ieee80211_add_pending_skbs(struct ieee80211_local *local, __skb_queue_tail(&local->pending[queue], skb); } + if (fn) + fn(data); + for (i = 0; i < hw->queues; i++) __ieee80211_wake_queue(hw, i, IEEE80211_QUEUE_STOP_REASON_SKB_ADD); @@ -402,6 +406,12 @@ int ieee80211_add_pending_skbs(struct ieee80211_local *local, return ret; } +int ieee80211_add_pending_skbs(struct ieee80211_local *local, + struct sk_buff_head *skbs) +{ + return ieee80211_add_pending_skbs_fn(local, skbs, NULL, NULL); +} + void ieee80211_stop_queues_by_reason(struct ieee80211_hw *hw, enum queue_stop_reason reason) { |