summaryrefslogtreecommitdiffstats
path: root/net/mac80211
diff options
context:
space:
mode:
Diffstat (limited to 'net/mac80211')
-rw-r--r--net/mac80211/sta_info.c57
-rw-r--r--net/mac80211/status.c5
2 files changed, 57 insertions, 5 deletions
diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c
index 863d59fe6886..8dabe66fc37f 100644
--- a/net/mac80211/sta_info.c
+++ b/net/mac80211/sta_info.c
@@ -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)
diff --git a/net/mac80211/status.c b/net/mac80211/status.c
index 8354dcb0e1e3..783542a8ea20 100644
--- a/net/mac80211/status.c
+++ b/net/mac80211/status.c
@@ -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;
}
OpenPOWER on IntegriCloud