@@ -345,6 +345,7 @@ struct ath10k {
struct ieee80211_hw *hw;
struct device *dev;
u8 mac_addr[ETH_ALEN];
+ u8 tx_flush_failed; /* failure case counter */
u32 chip_id;
u32 target_version;
@@ -378,6 +379,13 @@ struct ath10k {
struct ath10k_htc htc;
struct ath10k_htt htt;
+ /* Stats to help debug tx hang issues */
+ u64 htc_send_tot;
+ u64 htc_tx;
+ u64 htc_tx_compl;
+ u64 htc_mgt_tx;
+ u64 htc_mgt_compl;
+
struct ath10k_hw_params {
u32 id;
const char *name;
@@ -191,6 +191,8 @@ int ath10k_htc_send(struct ath10k_htc *htc,
if (ret)
goto err_unmap;
+ htc->ar->htc_send_tot++;
+
return 0;
err_unmap:
@@ -56,6 +56,10 @@ int ath10k_htt_attach(struct ath10k *ar)
htt->ar = ar;
htt->max_throughput_mbps = 800;
+ /* Clear some htc related counters */
+ ar->htc_send_tot = ar->htc_tx = ar->htc_tx_compl = ar->htc_mgt_tx = 0;
+ ar->htc_mgt_compl = 0;
+
/*
* Connect to HTC service.
* This has to be done before calling ath10k_htt_rx_attach,
@@ -1421,6 +1421,7 @@ static void ath10k_htt_rx_frm_tx_compl(struct ath10k *ar,
msdu_id = resp->data_tx_completion.msdus[i];
tx_done.msdu_id = __le16_to_cpu(msdu_id);
ath10k_txrx_tx_unref(htt, &tx_done);
+ ar->htc_tx_compl++;
}
}
@@ -1484,6 +1485,7 @@ void ath10k_htt_t2h_msg_handler(struct ath10k *ar, struct sk_buff *skb)
spin_lock_bh(&htt->tx_lock);
ath10k_txrx_tx_unref(htt, &tx_done);
+ ar->htc_mgt_compl++;
spin_unlock_bh(&htt->tx_lock);
break;
}
@@ -364,6 +364,7 @@ int ath10k_htt_mgmt_tx(struct ath10k_htt *htt, struct sk_buff *msdu)
if (res)
goto err_unmap_msdu;
+ htt->ar->htc_mgt_tx++;
return 0;
err_unmap_msdu:
@@ -521,6 +522,7 @@ int ath10k_htt_tx(struct ath10k_htt *htt, struct sk_buff *msdu)
if (res)
goto err_unmap_msdu;
+ htt->ar->htc_tx++;
return 0;
err_unmap_msdu:
@@ -3636,6 +3636,14 @@ static void ath10k_flush(struct ieee80211_hw *hw, u32 queues, bool drop)
struct ath10k *ar = hw->priv;
bool skip;
int ret;
+ s32 hw_queued;
+ s32 hw_reaped;
+ int num_pending_tx;
+ u64 htc_send_tot;
+ u64 htc_tx;
+ u64 htc_tx_compl;
+ u64 htc_mgt_tx;
+ u64 htc_mgt_compl;
/* mac80211 doesn't care if we really xmit queued frames or not
* we'll collect those frames either way if we stop/delete vdevs */
@@ -3647,6 +3655,19 @@ static void ath10k_flush(struct ieee80211_hw *hw, u32 queues, bool drop)
if (ar->state == ATH10K_STATE_WEDGED)
goto skip;
+ /* Refresh firmware stats to aid debugging */
+ ath10k_refresh_peer_stats(ar);
+
+ hw_queued = ar->debug.target_stats.hw_queued;
+ hw_reaped = ar->debug.target_stats.hw_reaped;
+ num_pending_tx = ar->htt.num_pending_tx;
+
+ htc_send_tot = ar->htc_send_tot;
+ htc_tx = ar->htc_tx;
+ htc_tx_compl = ar->htc_tx_compl;
+ htc_mgt_tx = ar->htc_mgt_tx;
+ htc_mgt_compl = ar->htc_mgt_compl;
+
ret = wait_event_timeout(ar->htt.empty_tx_wq, ({
bool empty;
@@ -3659,9 +3680,58 @@ static void ath10k_flush(struct ieee80211_hw *hw, u32 queues, bool drop)
(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 || skip) {
+ int i;
+ /* Refresh firmware stats to aid debugging */
+ ath10k_refresh_peer_stats(ar);
+
+ ath10k_err("failed to flush transmit queue (skip %i ar-state %i pending_tx %i pre-pending-tx: %i): %i\n",
+ skip, ar->state, ar->htt.num_pending_tx,
+ num_pending_tx, ret);
+ ath10k_err("pre: htc-send-tot: %llu htt-tx %llu tx-compl %llu mgt-tx %llu mgt-compl %llu\n",
+ htc_send_tot, htc_tx, htc_tx_compl,
+ htc_mgt_tx, htc_mgt_compl);
+ ath10k_err("post: htc-send-tot: %llu htt-tx %llu tx-compl %llu mgt-tx %llu mgt-compl %llu\n",
+ ar->htc_send_tot, ar->htc_tx, ar->htc_tx_compl,
+ ar->htc_mgt_tx, ar->htc_mgt_compl);
+ ath10k_err("pre: hw-queued: %d hw-reaped: %d\n",
+ hw_queued, hw_reaped);
+ ath10k_err("post: hw-queued: %d hw-reaped: %d\n",
+ ar->debug.target_stats.hw_queued,
+ ar->debug.target_stats.hw_reaped);
+
+ for (i = 0; i < ar->htt.max_num_pending_tx; i++) {
+ int q;
+
+ if (!ar->htt.pending_tx[i])
+ continue;
+
+ ath10k_err("stuck-skb: %p len %d tx-id %d\n",
+ ar->htt.pending_tx[i],
+ ar->htt.pending_tx[i]->len, i);
+ /* Only print skb contents if debug data is
+ * enabled.
+ */
+ if (!(ath10k_debug_mask & ATH10K_DBG_DATA))
+ continue;
+ for (q = 0; q < ar->htt.pending_tx[i]->len; q++) {
+ printk("%02hx ",
+ ar->htt.pending_tx[i]->data[q]);
+ if (((q + 1) & 0x1f) == 0x1f)
+ printk("\n");
+ }
+ }
+ if ((++ar->tx_flush_failed > 1) &&
+ (ar->state != ATH10K_STATE_RESTARTING)) {
+ /* This does not appear recoverable, attempt reset. */
+ ath10k_err("failed to flush transmit queue %d times, attempting hardware reset.\n",
+ ar->tx_flush_failed);
+ ar->tx_flush_failed = 0;
+ queue_work(ar->workqueue, &ar->restart_work);
+ }
+ } else {
+ ar->tx_flush_failed = 0;
+ }
skip:
mutex_unlock(&ar->conf_mutex);