mac80211: also expire filtered frames
authorJohannes Berg <johannes.berg@intel.com>
Thu, 29 Sep 2011 14:04:28 +0000 (16:04 +0200)
committerJohn W. Linville <linville@tuxdriver.com>
Fri, 30 Sep 2011 19:57:11 +0000 (15:57 -0400)
mac80211 will expire normal PS-buffered frames, but
if the device rejected some frames for a sleeping
station, these won't be on the ps_tx_buf queue but
on the tx_filtered queue instead; this is done to
avoid reordering.

However, mac80211 will not expire frames from the
filtered queue, let's fix that.

Also add a more comments to what all this expiry is
doing and how it works.

Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
net/mac80211/sta_info.c
net/mac80211/status.c

index 863d59f..8dabe66 100644 (file)
@@ -709,6 +709,39 @@ static bool sta_info_cleanup_expire_buffered(struct ieee80211_local *local,
        if (!sta->sdata->bss)
                return false;
 
+       /*
+        * First check for frames that should expire on the filtered
+        * queue. Frames here were rejected by the driver and are on
+        * a separate queue to avoid reordering with normal PS-buffered
+        * frames. They also aren't accounted for right now in the
+        * total_ps_buffered counter.
+        */
+       for (;;) {
+               spin_lock_irqsave(&sta->tx_filtered.lock, flags);
+               skb = skb_peek(&sta->tx_filtered);
+               if (sta_info_buffer_expired(sta, skb))
+                       skb = __skb_dequeue(&sta->tx_filtered);
+               else
+                       skb = NULL;
+               spin_unlock_irqrestore(&sta->tx_filtered.lock, flags);
+
+               /*
+                * Frames are queued in order, so if this one
+                * hasn't expired yet we can stop testing. If
+                * we actually reached the end of the queue we
+                * also need to stop, of course.
+                */
+               if (!skb)
+                       break;
+               dev_kfree_skb(skb);
+       }
+
+       /*
+        * Now also check the normal PS-buffered queue, this will
+        * only find something if the filtered queue was emptied
+        * since the filtered frames are all before the normal PS
+        * buffered frames.
+        */
        for (;;) {
                spin_lock_irqsave(&sta->ps_tx_buf.lock, flags);
                skb = skb_peek(&sta->ps_tx_buf);
@@ -718,6 +751,11 @@ static bool sta_info_cleanup_expire_buffered(struct ieee80211_local *local,
                        skb = NULL;
                spin_unlock_irqrestore(&sta->ps_tx_buf.lock, flags);
 
+               /*
+                * frames are queued in order, so if this one
+                * hasn't expired yet (or we reached the end of
+                * the queue) we can stop testing
+                */
                if (!skb)
                        break;
 
@@ -727,13 +765,22 @@ static bool sta_info_cleanup_expire_buffered(struct ieee80211_local *local,
                       sta->sta.addr);
 #endif
                dev_kfree_skb(skb);
-
-               /* if the queue is now empty recalc TIM bit */
-               if (skb_queue_empty(&sta->ps_tx_buf))
-                       sta_info_recalc_tim(sta);
        }
 
-       return !skb_queue_empty(&sta->ps_tx_buf);
+       /*
+        * Finally, recalculate the TIM bit for this station -- it might
+        * now be clear because the station was too slow to retrieve its
+        * frames.
+        */
+       sta_info_recalc_tim(sta);
+
+       /*
+        * Return whether there are any frames still buffered, this is
+        * used to check whether the cleanup timer still needs to run,
+        * if there are no frames we don't need to rearm the timer.
+        */
+       return !(skb_queue_empty(&sta->ps_tx_buf) &&
+                skb_queue_empty(&sta->tx_filtered));
 }
 
 static int __must_check __sta_info_destroy(struct sta_info *sta)
index 8354dcb..783542a 100644 (file)
@@ -107,6 +107,11 @@ static void ieee80211_handle_filtered_frame(struct ieee80211_local *local,
            skb_queue_len(&sta->tx_filtered) < STA_MAX_TX_BUFFER) {
                skb_queue_tail(&sta->tx_filtered, skb);
                sta_info_recalc_tim(sta);
+
+               if (!timer_pending(&local->sta_cleanup))
+                       mod_timer(&local->sta_cleanup,
+                                 round_jiffies(jiffies +
+                                               STA_INFO_CLEANUP_INTERVAL));
                return;
        }