Hi,

This is a pre-requisite for proper nullfunc handling in PS mode, but
can also help delivery during background scans.

Flush gets called before we leave the operating channel for scans to ensure
pending TX packets on the channel have been processed.  This implementation
just sleeps until the packets have been processed by the tasklet or a
timeout is hit.  ath9k uses 200ms as well -- in my very limited testing I
slept around 150 ms at a low-ish bitrate but of course it depends on the #
of packets queued.

Our accounting for packets is currently messed up due to the
always-leave-one policy that we used in tx_processq to try to avoid DMA
problems.  I don't think keeping one buffer unprocessed is the right
approach, but just disabling it (as in this patch) can regress so I need
to think about how to do that properly.  This also affects the TX queue
hang timer.

(oh there's a coding style bug with the while loop but I'll fix that too.)

---
 drivers/net/wireless/ath/ath5k/ath5k.h        |    1 +
 drivers/net/wireless/ath/ath5k/base.c         |   62 ++++++++++++++++++++----
 drivers/net/wireless/ath/ath5k/mac80211-ops.c |   12 ++++-
 3 files changed, 63 insertions(+), 12 deletions(-)

diff --git a/drivers/net/wireless/ath/ath5k/ath5k.h 
b/drivers/net/wireless/ath/ath5k/ath5k.h
index 277d5cb..5104a99 100644
--- a/drivers/net/wireless/ath/ath5k/ath5k.h
+++ b/drivers/net/wireless/ath/ath5k/ath5k.h
@@ -1354,6 +1354,7 @@ int ath5k_beacon_update(struct ieee80211_hw *hw, struct 
ieee80211_vif *vif);
 void ath5k_beacon_config(struct ath5k_hw *ah);
 void ath5k_txbuf_free_skb(struct ath5k_hw *ah, struct ath5k_buf *bf);
 void ath5k_rxbuf_free_skb(struct ath5k_hw *ah, struct ath5k_buf *bf);
+void ath5k_flush_tx(struct ath5k_hw *ah, bool drop);
 
 /*Chip id helper functions */
 const char *ath5k_chip_name(enum ath5k_srev_type type, u_int16_t val);
diff --git a/drivers/net/wireless/ath/ath5k/base.c 
b/drivers/net/wireless/ath/ath5k/base.c
index a74d286..0d7953c 100644
--- a/drivers/net/wireless/ath/ath5k/base.c
+++ b/drivers/net/wireless/ath/ath5k/base.c
@@ -1020,16 +1020,26 @@ err:
        return ret;
 }
 
+static bool
+ath5k_has_pending_frames(struct ath5k_hw *ah, struct ath5k_txq *txq)
+{
+       bool pending;
+
+       spin_lock_bh(&txq->lock);
+       pending = txq->setup && txq->txq_len > 0;
+       spin_unlock_bh(&txq->lock);
+
+       return pending;
+}
+
 /**
  * ath5k_drain_tx_buffs - Empty tx buffers
  *
  * @ah The &struct ath5k_hw
  *
  * Empty tx buffers from all queues in preparation
- * of a reset or during shutdown.
- *
- * NB: this assumes output has been stopped and
- *     we do not need to block ath5k_tx_tasklet
+ * of a reset or during shutdown.  We take txq->lock
+ * to avoid races against ath5k_tx_processq().
  */
 static void
 ath5k_drain_tx_buffs(struct ath5k_hw *ah)
@@ -1060,6 +1070,38 @@ ath5k_drain_tx_buffs(struct ath5k_hw *ah)
        }
 }
 
+/**
+ * ath5k_flush_tx - wait for all pending tx to be processed
+ *
+ * @ah: The &struct ath5k_hw
+ * @drop: true if we should drop packets rather than wait
+ *
+ * Queuing of new packets should be stopped.  In any case we
+ * don't wait more than a small timeout (200 ms) for packets.
+ */
+void ath5k_flush_tx(struct ath5k_hw *ah, bool drop)
+{
+       int i;
+       bool pending = true;
+       unsigned long timeout = jiffies + msecs_to_jiffies(200);
+
+       if (drop)
+               pending = false;
+
+       /* sleep until some timeout occurs or all queues are empty */
+       while (pending && !time_after(jiffies, timeout))
+       {
+               pending = false;
+               for (i = 0; i < ARRAY_SIZE(ah->txqs); i++)
+                       pending |= ath5k_has_pending_frames(ah, &ah->txqs[i]);
+
+               if (pending)
+                       msleep(10);
+       }
+       ath5k_drain_tx_buffs(ah);
+}
+
+
 static void
 ath5k_txq_release(struct ath5k_hw *ah)
 {
@@ -1680,13 +1722,11 @@ ath5k_tx_processq(struct ath5k_hw *ah, struct ath5k_txq 
*txq)
                 * host memory and moved on.
                 * Always keep the last descriptor to avoid HW races...
                 */
-               if (ath5k_hw_get_txdp(ah, txq->qnum) != bf->daddr) {
-                       spin_lock(&ah->txbuflock);
-                       list_move_tail(&bf->list, &ah->txbuf);
-                       ah->txbuf_len++;
-                       txq->txq_len--;
-                       spin_unlock(&ah->txbuflock);
-               }
+               spin_lock(&ah->txbuflock);
+               list_move_tail(&bf->list, &ah->txbuf);
+               ah->txbuf_len++;
+               txq->txq_len--;
+               spin_unlock(&ah->txbuflock);
        }
        spin_unlock(&txq->lock);
        if (txq->txq_len < ATH5K_TXQ_LEN_LOW && txq->qnum < 4)
diff --git a/drivers/net/wireless/ath/ath5k/mac80211-ops.c 
b/drivers/net/wireless/ath/ath5k/mac80211-ops.c
index 2a715ca..2fc09bc 100644
--- a/drivers/net/wireless/ath/ath5k/mac80211-ops.c
+++ b/drivers/net/wireless/ath/ath5k/mac80211-ops.c
@@ -693,6 +693,16 @@ ath5k_set_coverage_class(struct ieee80211_hw *hw, u8 
coverage_class)
        mutex_unlock(&ah->lock);
 }
 
+static void
+ath5k_flush(struct ieee80211_hw *hw, bool drop)
+{
+       struct ath5k_hw *ah = hw->priv;
+
+       mutex_lock(&ah->lock);
+       ath5k_flush_tx(ah, drop);
+       mutex_unlock(&ah->lock);
+}
+
 
 static int
 ath5k_set_antenna(struct ieee80211_hw *hw, u32 tx_ant, u32 rx_ant)
@@ -802,7 +812,7 @@ const struct ieee80211_ops ath5k_hw_ops = {
        .get_survey             = ath5k_get_survey,
        .set_coverage_class     = ath5k_set_coverage_class,
        /* .rfkill_poll         = not implemented */
-       /* .flush               = not implemented */
+       .flush                  = ath5k_flush,
        /* .channel_switch      = not implemented */
        /* .napi_poll           = not implemented */
        .set_antenna            = ath5k_set_antenna,
-- 
1.7.6

-- 
Bob Copeland %% www.bobcopeland.com

_______________________________________________
ath5k-devel mailing list
ath5k-devel@lists.ath5k.org
https://lists.ath5k.org/mailman/listinfo/ath5k-devel

Reply via email to