ath9k: Add debug counters for TX
authorSujith <Sujith.Manoharan@atheros.com>
Mon, 27 Jul 2009 06:38:16 +0000 (12:08 +0530)
committerJohn W. Linville <linville@tuxdriver.com>
Wed, 29 Jul 2009 19:46:09 +0000 (15:46 -0400)
Location: ath9k/phy#/xmit

Signed-off-by: Sujith <Sujith.Manoharan@atheros.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
drivers/net/wireless/ath/ath9k/ath9k.h
drivers/net/wireless/ath/ath9k/debug.c
drivers/net/wireless/ath/ath9k/debug.h
drivers/net/wireless/ath/ath9k/xmit.c

index 3a978bf..bda0f30 100644 (file)
@@ -237,7 +237,6 @@ struct ath_txq {
        spinlock_t axq_lock;
        u32 axq_depth;
        u8 axq_aggr_depth;
-       u32 axq_totalqueued;
        bool stopped;
        bool axq_tx_inprogress;
        struct ath_buf *axq_linkbuf;
index 9f99f00..9e36920 100644 (file)
@@ -486,6 +486,83 @@ static const struct file_operations fops_wiphy = {
        .owner = THIS_MODULE
 };
 
+#define PR(str, elem)                                                  \
+       do {                                                            \
+               len += snprintf(buf + len, size - len,                  \
+                               "%s%13u%11u%10u%10u\n", str,            \
+               sc->debug.stats.txstats[sc->tx.hwq_map[ATH9K_WME_AC_BE]].elem, \
+               sc->debug.stats.txstats[sc->tx.hwq_map[ATH9K_WME_AC_BK]].elem, \
+               sc->debug.stats.txstats[sc->tx.hwq_map[ATH9K_WME_AC_VI]].elem, \
+               sc->debug.stats.txstats[sc->tx.hwq_map[ATH9K_WME_AC_VO]].elem); \
+} while(0)
+
+static ssize_t read_file_xmit(struct file *file, char __user *user_buf,
+                             size_t count, loff_t *ppos)
+{
+       struct ath_softc *sc = file->private_data;
+       char *buf;
+       unsigned int len = 0, size = 2048;
+       ssize_t retval = 0;
+
+       buf = kzalloc(size, GFP_KERNEL);
+       if (buf == NULL)
+               return 0;
+
+       len += sprintf(buf, "%30s %10s%10s%10s\n\n", "BE", "BK", "VI", "VO");
+
+       PR("MPDUs Queued:    ", queued);
+       PR("MPDUs Completed: ", completed);
+       PR("Aggregates:      ", a_aggr);
+       PR("AMPDUs Queued:   ", a_queued);
+       PR("AMPDUs Completed:", a_completed);
+       PR("AMPDUs Retried:  ", a_retries);
+       PR("AMPDUs XRetried: ", a_xretries);
+       PR("FIFO Underrun:   ", fifo_underrun);
+       PR("TXOP Exceeded:   ", xtxop);
+       PR("TXTIMER Expiry:  ", timer_exp);
+       PR("DESC CFG Error:  ", desc_cfg_err);
+       PR("DATA Underrun:   ", data_underrun);
+       PR("DELIM Underrun:  ", delim_underrun);
+
+       retval = simple_read_from_buffer(user_buf, count, ppos, buf, len);
+       kfree(buf);
+
+       return retval;
+}
+
+void ath_debug_stat_tx(struct ath_softc *sc, struct ath_txq *txq,
+                      struct ath_buf *bf)
+{
+       struct ath_desc *ds = bf->bf_desc;
+
+       if (bf_isampdu(bf)) {
+               if (bf_isxretried(bf))
+                       TX_STAT_INC(txq->axq_qnum, a_xretries);
+               else
+                       TX_STAT_INC(txq->axq_qnum, a_completed);
+       } else {
+               TX_STAT_INC(txq->axq_qnum, completed);
+       }
+
+       if (ds->ds_txstat.ts_status & ATH9K_TXERR_FIFO)
+               TX_STAT_INC(txq->axq_qnum, fifo_underrun);
+       if (ds->ds_txstat.ts_status & ATH9K_TXERR_XTXOP)
+               TX_STAT_INC(txq->axq_qnum, xtxop);
+       if (ds->ds_txstat.ts_status & ATH9K_TXERR_TIMER_EXPIRED)
+               TX_STAT_INC(txq->axq_qnum, timer_exp);
+       if (ds->ds_txstat.ts_flags & ATH9K_TX_DESC_CFG_ERR)
+               TX_STAT_INC(txq->axq_qnum, desc_cfg_err);
+       if (ds->ds_txstat.ts_flags & ATH9K_TX_DATA_UNDERRUN)
+               TX_STAT_INC(txq->axq_qnum, data_underrun);
+       if (ds->ds_txstat.ts_flags & ATH9K_TX_DELIM_UNDERRUN)
+               TX_STAT_INC(txq->axq_qnum, delim_underrun);
+}
+
+static const struct file_operations fops_xmit = {
+       .read = read_file_xmit,
+       .open = ath9k_debugfs_open,
+       .owner = THIS_MODULE
+};
 
 int ath9k_init_debug(struct ath_softc *sc)
 {
@@ -529,6 +606,13 @@ int ath9k_init_debug(struct ath_softc *sc)
        if (!sc->debug.debugfs_wiphy)
                goto err;
 
+       sc->debug.debugfs_xmit = debugfs_create_file("xmit",
+                                                    S_IRUSR,
+                                                    sc->debug.debugfs_phy,
+                                                    sc, &fops_xmit);
+       if (!sc->debug.debugfs_xmit)
+               goto err;
+
        return 0;
 err:
        ath9k_exit_debug(sc);
@@ -537,6 +621,7 @@ err:
 
 void ath9k_exit_debug(struct ath_softc *sc)
 {
+       debugfs_remove(sc->debug.debugfs_xmit);
        debugfs_remove(sc->debug.debugfs_wiphy);
        debugfs_remove(sc->debug.debugfs_rcstat);
        debugfs_remove(sc->debug.debugfs_interrupt);
index edda15b..5e56b79 100644 (file)
@@ -35,6 +35,15 @@ enum ATH_DEBUG {
 
 #define DBG_DEFAULT (ATH_DBG_FATAL)
 
+struct ath_txq;
+struct ath_buf;
+
+#ifdef CONFIG_ATH9K_DEBUG
+#define TX_STAT_INC(q, c) sc->debug.stats.txstats[q].c++
+#else
+#define TX_STAT_INC(q, c) do { } while (0)
+#endif
+
 #ifdef CONFIG_ATH9K_DEBUG
 
 /**
@@ -87,9 +96,45 @@ struct ath_rc_stats {
        u8 per;
 };
 
+/**
+ * struct ath_tx_stats - Statistics about TX
+ * @queued: Total MPDUs (non-aggr) queued
+ * @completed: Total MPDUs (non-aggr) completed
+ * @a_aggr: Total no. of aggregates queued
+ * @a_queued: Total AMPDUs queued
+ * @a_completed: Total AMPDUs completed
+ * @a_retries: No. of AMPDUs retried (SW)
+ * @a_xretries: No. of AMPDUs dropped due to xretries
+ * @fifo_underrun: FIFO underrun occurrences
+       Valid only for:
+               - non-aggregate condition.
+               - first packet of aggregate.
+ * @xtxop: No. of frames filtered because of TXOP limit
+ * @timer_exp: Transmit timer expiry
+ * @desc_cfg_err: Descriptor configuration errors
+ * @data_urn: TX data underrun errors
+ * @delim_urn: TX delimiter underrun errors
+ */
+struct ath_tx_stats {
+       u32 queued;
+       u32 completed;
+       u32 a_aggr;
+       u32 a_queued;
+       u32 a_completed;
+       u32 a_retries;
+       u32 a_xretries;
+       u32 fifo_underrun;
+       u32 xtxop;
+       u32 timer_exp;
+       u32 desc_cfg_err;
+       u32 data_underrun;
+       u32 delim_underrun;
+};
+
 struct ath_stats {
        struct ath_interrupt_stats istats;
        struct ath_rc_stats rcstats[RATE_TABLE_SIZE];
+       struct ath_tx_stats txstats[ATH9K_NUM_TX_QUEUES];
 };
 
 struct ath9k_debug {
@@ -100,6 +145,7 @@ struct ath9k_debug {
        struct dentry *debugfs_interrupt;
        struct dentry *debugfs_rcstat;
        struct dentry *debugfs_wiphy;
+       struct dentry *debugfs_xmit;
        struct ath_stats stats;
 };
 
@@ -110,6 +156,8 @@ int ath9k_debug_create_root(void);
 void ath9k_debug_remove_root(void);
 void ath_debug_stat_interrupt(struct ath_softc *sc, enum ath9k_int status);
 void ath_debug_stat_rc(struct ath_softc *sc, struct sk_buff *skb);
+void ath_debug_stat_tx(struct ath_softc *sc, struct ath_txq *txq,
+                      struct ath_buf *bf);
 void ath_debug_stat_retries(struct ath_softc *sc, int rix,
                            int xretries, int retries, u8 per);
 
@@ -148,6 +196,12 @@ static inline void ath_debug_stat_rc(struct ath_softc *sc,
 {
 }
 
+static inline void ath_debug_stat_tx(struct ath_softc *sc,
+                                    struct ath_txq *txq,
+                                    struct ath_buf *bf)
+{
+}
+
 static inline void ath_debug_stat_retries(struct ath_softc *sc, int rix,
                                          int xretries, int retries, u8 per)
 {
index 6eb2927..b7806e2 100644 (file)
@@ -59,6 +59,7 @@ static void ath_tx_send_ht_normal(struct ath_softc *sc, struct ath_txq *txq,
                                  struct ath_atx_tid *tid,
                                  struct list_head *bf_head);
 static void ath_tx_complete_buf(struct ath_softc *sc, struct ath_buf *bf,
+                               struct ath_txq *txq,
                                struct list_head *bf_q,
                                int txok, int sendbar);
 static void ath_tx_txqaddbuf(struct ath_softc *sc, struct ath_txq *txq,
@@ -212,7 +213,7 @@ static void ath_tid_drain(struct ath_softc *sc, struct ath_txq *txq,
                        ath_tx_update_baw(sc, tid, bf->bf_seqno);
 
                spin_unlock(&txq->axq_lock);
-               ath_tx_complete_buf(sc, bf, &bf_head, 0, 0);
+               ath_tx_complete_buf(sc, bf, txq, &bf_head, 0, 0);
                spin_lock(&txq->axq_lock);
        }
 
@@ -220,13 +221,15 @@ static void ath_tid_drain(struct ath_softc *sc, struct ath_txq *txq,
        tid->baw_tail = tid->baw_head;
 }
 
-static void ath_tx_set_retry(struct ath_softc *sc, struct ath_buf *bf)
+static void ath_tx_set_retry(struct ath_softc *sc, struct ath_txq *txq,
+                            struct ath_buf *bf)
 {
        struct sk_buff *skb;
        struct ieee80211_hdr *hdr;
 
        bf->bf_state.bf_type |= BUF_RETRY;
        bf->bf_retries++;
+       TX_STAT_INC(txq->axq_qnum, a_retries);
 
        skb = bf->bf_mpdu;
        hdr = (struct ieee80211_hdr *)skb->data;
@@ -328,7 +331,7 @@ static void ath_tx_complete_aggr(struct ath_softc *sc, struct ath_txq *txq,
                        if (!(tid->state & AGGR_CLEANUP) &&
                            ds->ds_txstat.ts_flags != ATH9K_TX_SW_ABORTED) {
                                if (bf->bf_retries < ATH_MAX_SW_RETRIES) {
-                                       ath_tx_set_retry(sc, bf);
+                                       ath_tx_set_retry(sc, txq, bf);
                                        txpending = 1;
                                } else {
                                        bf->bf_state.bf_type |= BUF_XRETRY;
@@ -375,7 +378,7 @@ static void ath_tx_complete_aggr(struct ath_softc *sc, struct ath_txq *txq,
                                ath_tx_rc_status(bf, ds, nbad, txok, false);
                        }
 
-                       ath_tx_complete_buf(sc, bf, &bf_head, !txfail, sendbar);
+                       ath_tx_complete_buf(sc, bf, txq, &bf_head, !txfail, sendbar);
                } else {
                        /* retry the un-acked ones */
                        if (bf->bf_next == NULL && bf_last->bf_stale) {
@@ -395,8 +398,8 @@ static void ath_tx_complete_aggr(struct ath_softc *sc, struct ath_txq *txq,
                                        bf->bf_state.bf_type |= BUF_XRETRY;
                                        ath_tx_rc_status(bf, ds, nbad,
                                                         0, false);
-                                       ath_tx_complete_buf(sc, bf, &bf_head,
-                                                           0, 0);
+                                       ath_tx_complete_buf(sc, bf, txq,
+                                                           &bf_head, 0, 0);
                                        break;
                                }
 
@@ -569,6 +572,7 @@ static int ath_compute_num_delims(struct ath_softc *sc, struct ath_atx_tid *tid,
 }
 
 static enum ATH_AGGR_STATUS ath_tx_form_aggr(struct ath_softc *sc,
+                                            struct ath_txq *txq,
                                             struct ath_atx_tid *tid,
                                             struct list_head *bf_q)
 {
@@ -633,6 +637,7 @@ static enum ATH_AGGR_STATUS ath_tx_form_aggr(struct ath_softc *sc,
                        bf_prev->bf_desc->ds_link = bf->bf_daddr;
                }
                bf_prev = bf;
+
        } while (!list_empty(&tid->buf_q));
 
        bf_first->bf_al = al;
@@ -655,7 +660,7 @@ static void ath_tx_sched_aggr(struct ath_softc *sc, struct ath_txq *txq,
 
                INIT_LIST_HEAD(&bf_q);
 
-               status = ath_tx_form_aggr(sc, tid, &bf_q);
+               status = ath_tx_form_aggr(sc, txq, tid, &bf_q);
 
                /*
                 * no frames picked up to be aggregated;
@@ -686,6 +691,7 @@ static void ath_tx_sched_aggr(struct ath_softc *sc, struct ath_txq *txq,
 
                txq->axq_aggr_depth++;
                ath_tx_txqaddbuf(sc, txq, &bf_q);
+               TX_STAT_INC(txq->axq_qnum, a_aggr);
 
        } while (txq->axq_depth < ATH_AGGR_MIN_QDEPTH &&
                 status != ATH_AGGR_BAW_CLOSED);
@@ -737,7 +743,7 @@ void ath_tx_aggr_stop(struct ath_softc *sc, struct ieee80211_sta *sta, u16 tid)
                }
                list_move_tail(&bf->list, &bf_head);
                ath_tx_update_baw(sc, txtid, bf->bf_seqno);
-               ath_tx_complete_buf(sc, bf, &bf_head, 0, 0);
+               ath_tx_complete_buf(sc, bf, txq, &bf_head, 0, 0);
        }
        spin_unlock_bh(&txq->axq_lock);
 
@@ -859,7 +865,6 @@ struct ath_txq *ath_txq_setup(struct ath_softc *sc, int qtype, int subtype)
                spin_lock_init(&txq->axq_lock);
                txq->axq_depth = 0;
                txq->axq_aggr_depth = 0;
-               txq->axq_totalqueued = 0;
                txq->axq_linkbuf = NULL;
                txq->axq_tx_inprogress = false;
                sc->tx.txqsetup |= 1<<qnum;
@@ -1025,7 +1030,7 @@ void ath_draintxq(struct ath_softc *sc, struct ath_txq *txq, bool retry_tx)
                if (bf_isampdu(bf))
                        ath_tx_complete_aggr(sc, txq, bf, &bf_head, 0);
                else
-                       ath_tx_complete_buf(sc, bf, &bf_head, 0, 0);
+                       ath_tx_complete_buf(sc, bf, txq, &bf_head, 0, 0);
        }
 
        spin_lock_bh(&txq->axq_lock);
@@ -1176,7 +1181,6 @@ static void ath_tx_txqaddbuf(struct ath_softc *sc, struct ath_txq *txq,
 
        list_splice_tail_init(head, &txq->axq_q);
        txq->axq_depth++;
-       txq->axq_totalqueued++;
        txq->axq_linkbuf = list_entry(txq->axq_q.prev, struct ath_buf, list);
 
        DPRINTF(sc, ATH_DBG_QUEUE,
@@ -1224,6 +1228,7 @@ static void ath_tx_send_ampdu(struct ath_softc *sc, struct ath_atx_tid *tid,
 
        bf = list_first_entry(bf_head, struct ath_buf, list);
        bf->bf_state.bf_type |= BUF_AMPDU;
+       TX_STAT_INC(txctl->txq->axq_qnum, a_queued);
 
        /*
         * Do not queue to h/w when any of the following conditions is true:
@@ -1270,6 +1275,7 @@ static void ath_tx_send_ht_normal(struct ath_softc *sc, struct ath_txq *txq,
        bf->bf_lastbf = bf;
        ath_buf_set_rate(sc, bf);
        ath_tx_txqaddbuf(sc, txq, bf_head);
+       TX_STAT_INC(txq->axq_qnum, queued);
 }
 
 static void ath_tx_send_normal(struct ath_softc *sc, struct ath_txq *txq,
@@ -1283,6 +1289,7 @@ static void ath_tx_send_normal(struct ath_softc *sc, struct ath_txq *txq,
        bf->bf_nframes = 1;
        ath_buf_set_rate(sc, bf);
        ath_tx_txqaddbuf(sc, txq, bf_head);
+       TX_STAT_INC(txq->axq_qnum, queued);
 }
 
 static enum ath9k_pkt_type get_hw_packet_type(struct sk_buff *skb)
@@ -1808,6 +1815,7 @@ static void ath_tx_complete(struct ath_softc *sc, struct sk_buff *skb,
 }
 
 static void ath_tx_complete_buf(struct ath_softc *sc, struct ath_buf *bf,
+                               struct ath_txq *txq,
                                struct list_head *bf_q,
                                int txok, int sendbar)
 {
@@ -1815,7 +1823,6 @@ static void ath_tx_complete_buf(struct ath_softc *sc, struct ath_buf *bf,
        unsigned long flags;
        int tx_flags = 0;
 
-
        if (sendbar)
                tx_flags = ATH_TX_BAR;
 
@@ -1828,6 +1835,7 @@ static void ath_tx_complete_buf(struct ath_softc *sc, struct ath_buf *bf,
 
        dma_unmap_single(sc->dev, bf->bf_dmacontext, skb->len, DMA_TO_DEVICE);
        ath_tx_complete(sc, skb, tx_flags);
+       ath_debug_stat_tx(sc, txq, bf);
 
        /*
         * Return the list of ath_buf of this mpdu to free queue
@@ -2015,7 +2023,7 @@ static void ath_tx_processq(struct ath_softc *sc, struct ath_txq *txq)
                if (bf_isampdu(bf))
                        ath_tx_complete_aggr(sc, txq, bf, &bf_head, txok);
                else
-                       ath_tx_complete_buf(sc, bf, &bf_head, txok, 0);
+                       ath_tx_complete_buf(sc, bf, txq, &bf_head, txok, 0);
 
                ath_wake_mac80211_queue(sc, txq);