[RFTv2 5/5] ath10k: improve tx flushing

Michal Kazior michal.kazior at tieto.com
Wed Apr 9 06:48:51 EDT 2014


Flushing was prone to timeouts when powersaving
clients were involved that went missing while
asleep. The common culprit was NullFunc frame
being stuck in FW/HW queues.

Introduce a two-pass flushing with WMI flush
commands interleaved to force frame drops if
necessary. This allows for a decreased flush
timeout time and should get rid of some warnings.

Signed-off-by: Michal Kazior <michal.kazior at tieto.com>
---
 drivers/net/wireless/ath/ath10k/core.h |  2 +-
 drivers/net/wireless/ath/ath10k/mac.c  | 97 +++++++++++++++++++++++++++-------
 drivers/net/wireless/ath/ath10k/wmi.h  |  1 +
 3 files changed, 81 insertions(+), 19 deletions(-)

diff --git a/drivers/net/wireless/ath/ath10k/core.h b/drivers/net/wireless/ath/ath10k/core.h
index 61e325a..0d60e70 100644
--- a/drivers/net/wireless/ath/ath10k/core.h
+++ b/drivers/net/wireless/ath/ath10k/core.h
@@ -38,7 +38,7 @@
 
 #define ATH10K_SCAN_ID 0
 #define WMI_READY_TIMEOUT (5 * HZ)
-#define ATH10K_FLUSH_TIMEOUT_HZ (5*HZ)
+#define ATH10K_FLUSH_TIMEOUT_HZ (1*HZ)
 #define ATH10K_NUM_CHANS 38
 
 /* Antenna noise floor */
diff --git a/drivers/net/wireless/ath/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c
index 1d95500..dc176b5 100644
--- a/drivers/net/wireless/ath/ath10k/mac.c
+++ b/drivers/net/wireless/ath/ath10k/mac.c
@@ -3769,25 +3769,31 @@ static int ath10k_set_frag_threshold(struct ieee80211_hw *hw, u32 value)
 	return ret;
 }
 
-static void ath10k_flush(struct ieee80211_hw *hw, u32 queues, bool drop)
+static int ath10k_flush_all_peer_tids(struct ath10k *ar)
 {
-	struct ath10k *ar = hw->priv;
-	bool skip;
+	struct ath10k_peer *peer;
 	int ret;
 
-	/* mac80211 doesn't care if we really xmit queued frames or not
-	 * we'll collect those frames either way if we stop/delete vdevs */
-	if (drop)
-		return;
-
-	mutex_lock(&ar->conf_mutex);
+	lockdep_assert_held(&ar->conf_mutex);
 
-	if (ar->state == ATH10K_STATE_WEDGED) {
-		ret = -EBUSY;
-		goto skip;
+	list_for_each_entry(peer, &ar->peers, list) {
+		ret = ath10k_wmi_peer_flush(ar, peer->vdev_id, peer->addr,
+					    WMI_PEER_TID_ALL_MASK);
+		if (ret) {
+			ath10k_warn("failed to request peer %pM on vdev %i to flush %08x: %d\n",
+				    peer->addr, peer->vdev_id,
+				    WMI_PEER_TID_ALL_MASK, ret);
+			return ret;
+		}
 	}
 
-	ret = wait_event_timeout(ar->htt.empty_tx_wq, ({
+	return 0;
+}
+
+static int ath10k_flush_wait(struct ath10k *ar)
+{
+	bool skip;
+	int ret = wait_event_timeout(ar->htt.empty_tx_wq, ({
 			bool htt_empty, wmi_empty;
 			unsigned long flags;
 
@@ -3805,16 +3811,71 @@ static void ath10k_flush(struct ieee80211_hw *hw, u32 queues, bool drop)
 			((htt_empty && wmi_empty) || skip);
 		}), ATH10K_FLUSH_TIMEOUT_HZ);
 
-	if (ret <= 0 || skip)
-		ath10k_warn("failed to flush transmit queue (skip %i ar-state %i): %i\n",
-			    skip, ar->state, ret);
+	if (ret == 0)
+		ret = -ETIMEDOUT;
+	else if (ret > 0)
+		ret = 0;
+
+	if (skip) {
+		ath10k_warn("ignoring flushing result because hardware is wedged\n");
+		ret = -EBUSY;
+	}
+
+	return ret;
+}
+
+static void ath10k_flush(struct ieee80211_hw *hw, u32 queues, bool drop)
+{
+	struct ath10k *ar = hw->priv;
+	int ret;
+
+	mutex_lock(&ar->conf_mutex);
+
+	if (drop) {
+		ath10k_mgmt_over_wmi_tx_purge(ar);
+
+		ret = ath10k_flush_all_peer_tids(ar);
+		if (ret) {
+			ath10k_warn("failed to flush all peer tids: %d\n", ret);
+			goto out;
+		}
 
-skip:
+		goto out;
+	}
+
+	if (ar->state == ATH10K_STATE_WEDGED) {
+		ath10k_warn("skipping flushing because hardware is wedged\n");
+		ret = -EBUSY;
+		goto out;
+	}
+
+	ret = ath10k_flush_wait(ar);
+	if (ret) {
+		ath10k_dbg(ATH10K_DBG_MAC,
+			   "failed to wait for tx to flush: %d, forcing\n",
+			   ret);
+
+		ath10k_mgmt_over_wmi_tx_purge(ar);
+
+		ret = ath10k_flush_all_peer_tids(ar);
+		if (ret) {
+			ath10k_warn("failed to flush all peer tids: %d\n", ret);
+			goto out;
+		}
+
+		ret = ath10k_flush_wait(ar);
+		if (ret) {
+			ath10k_warn("failed to flush tx: %d\n", ret);
+			goto out;
+		}
+	}
+
+out:
 	mutex_unlock(&ar->conf_mutex);
 
 	/* empty mgmt tx queue doesn't mean mgmt tx is flushed because the last
 	 * frame still may be processed by a worker */
-	if (ret > 0 && !skip)
+	if (ret == 0)
 		cancel_work_sync(&ar->wmi_mgmt_tx_work);
 }
 
diff --git a/drivers/net/wireless/ath/ath10k/wmi.h b/drivers/net/wireless/ath/ath10k/wmi.h
index 90fe2e9..2b37c4a 100644
--- a/drivers/net/wireless/ath/ath10k/wmi.h
+++ b/drivers/net/wireless/ath/ath10k/wmi.h
@@ -3846,6 +3846,7 @@ struct wmi_peer_delete_cmd {
 
 #define WMI_PEER_TID_MGMT 17
 #define WMI_PEER_TID_MGMT_MASK BIT(WMI_PEER_TID_MGMT)
+#define WMI_PEER_TID_ALL_MASK 0xFFFFFFFF
 
 struct wmi_peer_flush_tids_cmd {
 	__le32 vdev_id;
-- 
1.8.5.3




More information about the ath10k mailing list