ath9k: Keep track of stations for debugfs.
authorBen Greear <greearb@candelatech.com>
Mon, 10 Jan 2011 07:11:49 +0000 (23:11 -0800)
committerJohn W. Linville <linville@tuxdriver.com>
Fri, 21 Jan 2011 20:32:22 +0000 (15:32 -0500)
The stations hold the ath_node, which holds the tid
and other xmit logic structures.  In order to debug
stuck xmit logic, we need a way to print out the tid
state for the stations.

Signed-off-by: Ben Greear <greearb@candelatech.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/init.c
drivers/net/wireless/ath/ath9k/main.c

index aadb5de9ac76e8fb6560cdc6f302d607072f74da..d4640117fa8ccd04b3d2a5126655770713f6f119 100644 (file)
@@ -254,7 +254,10 @@ struct ath_atx_tid {
 };
 
 struct ath_node {
-       struct ath_common *common;
+#ifdef CONFIG_ATH9K_DEBUGFS
+       struct list_head list; /* for sc->nodes */
+       struct ieee80211_sta *sta; /* station struct we're part of */
+#endif
        struct ath_atx_tid tid[WME_NUM_TID];
        struct ath_atx_ac ac[WME_NUM_AC];
        u16 maxampdu;
@@ -638,6 +641,8 @@ struct ath_softc {
 
 #ifdef CONFIG_ATH9K_DEBUGFS
        struct ath9k_debug debug;
+       spinlock_t nodes_lock;
+       struct list_head nodes; /* basically, stations */
 #endif
        struct ath_beacon_config cur_beacon_conf;
        struct delayed_work tx_complete_work;
index faf84e499c8f8687303f7f0bcaf5d3d6c674fbff..650f00f59d79a2e337ec83d22bd3d963f7c85451 100644 (file)
@@ -587,6 +587,8 @@ static const struct file_operations fops_wiphy = {
                sc->debug.stats.txstats[WME_AC_BK].elem, \
                sc->debug.stats.txstats[WME_AC_VI].elem, \
                sc->debug.stats.txstats[WME_AC_VO].elem); \
+               if (len >= size)                          \
+                       goto done;                        \
 } while(0)
 
 #define PRX(str, elem)                                                 \
@@ -597,6 +599,8 @@ do {                                                                        \
                        (unsigned int)(sc->tx.txq[WME_AC_BK].elem),     \
                        (unsigned int)(sc->tx.txq[WME_AC_VI].elem),     \
                        (unsigned int)(sc->tx.txq[WME_AC_VO].elem));    \
+       if (len >= size)                                                \
+               goto done;                                              \
 } while(0)
 
 #define PRQLE(str, elem)                                               \
@@ -607,6 +611,8 @@ do {                                                                        \
                        list_empty(&sc->tx.txq[WME_AC_BK].elem),        \
                        list_empty(&sc->tx.txq[WME_AC_VI].elem),        \
                        list_empty(&sc->tx.txq[WME_AC_VO].elem));       \
+       if (len >= size)                                                \
+               goto done;                                              \
 } while (0)
 
 static ssize_t read_file_xmit(struct file *file, char __user *user_buf,
@@ -614,7 +620,7 @@ static ssize_t read_file_xmit(struct file *file, char __user *user_buf,
 {
        struct ath_softc *sc = file->private_data;
        char *buf;
-       unsigned int len = 0, size = 4000;
+       unsigned int len = 0, size = 8000;
        int i;
        ssize_t retval = 0;
        char tmp[32];
@@ -623,7 +629,10 @@ static ssize_t read_file_xmit(struct file *file, char __user *user_buf,
        if (buf == NULL)
                return -ENOMEM;
 
-       len += sprintf(buf, "%30s %10s%10s%10s\n\n", "BE", "BK", "VI", "VO");
+       len += sprintf(buf, "Num-Tx-Queues: %i  tx-queues-setup: 0x%x\n"
+                      "%30s %10s%10s%10s\n\n",
+                      ATH9K_NUM_TX_QUEUES, sc->tx.txqsetup,
+                      "BE", "BK", "VI", "VO");
 
        PR("MPDUs Queued:    ", queued);
        PR("MPDUs Completed: ", completed);
@@ -644,6 +653,14 @@ static ssize_t read_file_xmit(struct file *file, char __user *user_buf,
        PR("hw-put-tx-buf:   ", puttxbuf);
        PR("hw-tx-start:     ", txstart);
        PR("hw-tx-proc-desc: ", txprocdesc);
+       len += snprintf(buf + len, size - len,
+                       "%s%11p%11p%10p%10p\n", "txq-memory-address:",
+                       &(sc->tx.txq[WME_AC_BE]),
+                       &(sc->tx.txq[WME_AC_BK]),
+                       &(sc->tx.txq[WME_AC_VI]),
+                       &(sc->tx.txq[WME_AC_VO]));
+       if (len >= size)
+               goto done;
 
        PRX("axq-qnum:        ", axq_qnum);
        PRX("axq-depth:       ", axq_depth);
@@ -661,6 +678,68 @@ static ssize_t read_file_xmit(struct file *file, char __user *user_buf,
                snprintf(tmp, sizeof(tmp) - 1, "txq_fifo[%i] empty: ", i);
                PRQLE(tmp, txq_fifo[i]);
        }
+
+done:
+       if (len > size)
+               len = size;
+
+       retval = simple_read_from_buffer(user_buf, count, ppos, buf, len);
+       kfree(buf);
+
+       return retval;
+}
+
+static ssize_t read_file_stations(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 = 64000;
+       struct ath_node *an = NULL;
+       ssize_t retval = 0;
+       int q;
+
+       buf = kzalloc(size, GFP_KERNEL);
+       if (buf == NULL)
+               return -ENOMEM;
+
+       len += snprintf(buf + len, size - len,
+                       "Stations:\n"
+                       " tid: addr sched paused buf_q-empty an ac\n"
+                       " ac: addr sched tid_q-empty txq\n");
+
+       spin_lock(&sc->nodes_lock);
+       list_for_each_entry(an, &sc->nodes, list) {
+               len += snprintf(buf + len, size - len,
+                               "%pM\n", an->sta->addr);
+               if (len >= size)
+                       goto done;
+
+               for (q = 0; q < WME_NUM_TID; q++) {
+                       struct ath_atx_tid *tid = &(an->tid[q]);
+                       len += snprintf(buf + len, size - len,
+                                       " tid: %p %s %s %i %p %p\n",
+                                       tid, tid->sched ? "sched" : "idle",
+                                       tid->paused ? "paused" : "running",
+                                       list_empty(&tid->buf_q),
+                                       tid->an, tid->ac);
+                       if (len >= size)
+                               goto done;
+               }
+
+               for (q = 0; q < WME_NUM_AC; q++) {
+                       struct ath_atx_ac *ac = &(an->ac[q]);
+                       len += snprintf(buf + len, size - len,
+                                       " ac: %p %s %i %p\n",
+                                       ac, ac->sched ? "sched" : "idle",
+                                       list_empty(&ac->tid_q), ac->txq);
+                       if (len >= size)
+                               goto done;
+               }
+       }
+
+done:
+       spin_unlock(&sc->nodes_lock);
        if (len > size)
                len = size;
 
@@ -708,6 +787,13 @@ static const struct file_operations fops_xmit = {
        .llseek = default_llseek,
 };
 
+static const struct file_operations fops_stations = {
+       .read = read_file_stations,
+       .open = ath9k_debugfs_open,
+       .owner = THIS_MODULE,
+       .llseek = default_llseek,
+};
+
 static ssize_t read_file_recv(struct file *file, char __user *user_buf,
                              size_t count, loff_t *ppos)
 {
@@ -945,6 +1031,10 @@ int ath9k_init_debug(struct ath_hw *ah)
                        sc, &fops_xmit))
                goto err;
 
+       if (!debugfs_create_file("stations", S_IRUSR, sc->debug.debugfs_phy,
+                       sc, &fops_stations))
+               goto err;
+
        if (!debugfs_create_file("recv", S_IRUSR, sc->debug.debugfs_phy,
                        sc, &fops_recv))
                goto err;
index 23b299818b18dc71f1b6fe80bba1d03c694b88b7..59c01ca4379ee9137d08226145a272bf5c755116 100644 (file)
@@ -559,6 +559,10 @@ static int ath9k_init_softc(u16 devid, struct ath_softc *sc, u16 subsysid,
        spin_lock_init(&sc->sc_serial_rw);
        spin_lock_init(&sc->sc_pm_lock);
        mutex_init(&sc->mutex);
+#ifdef CONFIG_ATH9K_DEBUGFS
+       spin_lock_init(&sc->nodes_lock);
+       INIT_LIST_HEAD(&sc->nodes);
+#endif
        tasklet_init(&sc->intr_tq, ath9k_tasklet, (unsigned long)sc);
        tasklet_init(&sc->bcon_tasklet, ath_beacon_tasklet,
                     (unsigned long)sc);
index c03184e7bffeb9bc57fe426b998d3793f31304d7..bed6eb97fac93abc9d2021c901e519a7b26b201b 100644 (file)
@@ -548,6 +548,12 @@ static void ath_node_attach(struct ath_softc *sc, struct ieee80211_sta *sta)
        struct ath_hw *ah = sc->sc_ah;
        an = (struct ath_node *)sta->drv_priv;
 
+#ifdef CONFIG_ATH9K_DEBUGFS
+       spin_lock(&sc->nodes_lock);
+       list_add(&an->list, &sc->nodes);
+       spin_unlock(&sc->nodes_lock);
+       an->sta = sta;
+#endif
        if ((ah->caps.hw_caps) & ATH9K_HW_CAP_APM)
                sc->sc_flags |= SC_OP_ENABLE_APM;
 
@@ -563,6 +569,13 @@ static void ath_node_detach(struct ath_softc *sc, struct ieee80211_sta *sta)
 {
        struct ath_node *an = (struct ath_node *)sta->drv_priv;
 
+#ifdef CONFIG_ATH9K_DEBUGFS
+       spin_lock(&sc->nodes_lock);
+       list_del(&an->list);
+       spin_unlock(&sc->nodes_lock);
+       an->sta = NULL;
+#endif
+
        if (sc->sc_flags & SC_OP_TXAGGR)
                ath_tx_node_cleanup(sc, an);
 }