ath5k: add debugfs file frameerrors
authorBruno Randolf <br1@einfach.org>
Tue, 9 Mar 2010 07:56:00 +0000 (16:56 +0900)
committerJohn W. Linville <linville@tuxdriver.com>
Wed, 10 Mar 2010 22:44:39 +0000 (17:44 -0500)
add a debugfs file to see different RX and TX errors as reported in our status
descriptors. this can help to diagnose driver problems.

statistics can be cleared by writing 'clear' into the frameerrors file.

example:

# cat /sys/kernel/debug/ath5k/phy0/frameerrors
RX
---------------------
CRC     27      (11%)
PHY     3       (1%)
FIFO    0       (0%)
decrypt 0       (0%)
MIC     0       (0%)
process 0       (0%)
jumbo   0       (0%)
[RX all 245]

TX
---------------------
retry   2       (9%)
FIFO    0       (0%)
filter  0       (0%)
[TX all 21]

Signed-off-by: Bruno Randolf <br1@einfach.org>
Acked-by: Nick Kossifidis <mickflemm@gmail.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
drivers/net/wireless/ath/ath5k/base.c
drivers/net/wireless/ath/ath5k/base.h
drivers/net/wireless/ath/ath5k/debug.c
drivers/net/wireless/ath/ath5k/debug.h

index 79922cfd96b80298105593cd4db396e9734a2752..b142a78ed1e5eb3c326e1a2af32cdf824573dfe2 100644 (file)
@@ -1902,18 +1902,28 @@ ath5k_tasklet_rx(unsigned long data)
                        break;
                else if (unlikely(ret)) {
                        ATH5K_ERR(sc, "error in processing rx descriptor\n");
+                       sc->stats.rxerr_proc++;
                        spin_unlock(&sc->rxbuflock);
                        return;
                }
 
+               sc->stats.rx_all_count++;
+
                if (unlikely(rs.rs_more)) {
                        ATH5K_WARN(sc, "unsupported jumbo\n");
+                       sc->stats.rxerr_jumbo++;
                        goto next;
                }
 
                if (unlikely(rs.rs_status)) {
-                       if (rs.rs_status & AR5K_RXERR_PHY)
+                       if (rs.rs_status & AR5K_RXERR_CRC)
+                               sc->stats.rxerr_crc++;
+                       if (rs.rs_status & AR5K_RXERR_FIFO)
+                               sc->stats.rxerr_fifo++;
+                       if (rs.rs_status & AR5K_RXERR_PHY) {
+                               sc->stats.rxerr_phy++;
                                goto next;
+                       }
                        if (rs.rs_status & AR5K_RXERR_DECRYPT) {
                                /*
                                 * Decrypt error.  If the error occurred
@@ -1925,12 +1935,14 @@ ath5k_tasklet_rx(unsigned long data)
                                 *
                                 * XXX do key cache faulting
                                 */
+                               sc->stats.rxerr_decrypt++;
                                if (rs.rs_keyix == AR5K_RXKEYIX_INVALID &&
                                    !(rs.rs_status & AR5K_RXERR_CRC))
                                        goto accept;
                        }
                        if (rs.rs_status & AR5K_RXERR_MIC) {
                                rx_flag |= RX_FLAG_MMIC_ERROR;
+                               sc->stats.rxerr_mic++;
                                goto accept;
                        }
 
@@ -2056,6 +2068,7 @@ ath5k_tx_processq(struct ath5k_softc *sc, struct ath5k_txq *txq)
                        break;
                }
 
+               sc->stats.tx_all_count++;
                skb = bf->skb;
                info = IEEE80211_SKB_CB(skb);
                bf->skb = NULL;
@@ -2082,8 +2095,14 @@ ath5k_tx_processq(struct ath5k_softc *sc, struct ath5k_txq *txq)
 
                if (unlikely(ts.ts_status)) {
                        sc->ll_stats.dot11ACKFailureCount++;
-                       if (ts.ts_status & AR5K_TXERR_FILT)
+                       if (ts.ts_status & AR5K_TXERR_FILT) {
                                info->flags |= IEEE80211_TX_STAT_TX_FILTERED;
+                               sc->stats.txerr_filt++;
+                       }
+                       if (ts.ts_status & AR5K_TXERR_XRETRY)
+                               sc->stats.txerr_retry++;
+                       if (ts.ts_status & AR5K_TXERR_FIFO)
+                               sc->stats.txerr_fifo++;
                } else {
                        info->flags |= IEEE80211_TX_STAT_ACK;
                        info->status.ack_signal = ts.ts_rssi;
index ca525842d827cf6ed5ddf80b8aeac22de1420ca4..33f1d8b87ee10e33598a9f26eaa8fd1e5cbb98f5 100644 (file)
@@ -109,6 +109,18 @@ struct ath5k_rfkill {
 struct ath5k_statistics {
        unsigned int antenna_rx[5];     /* frames count per antenna RX */
        unsigned int antenna_tx[5];     /* frames count per antenna TX */
+       unsigned int rx_all_count;      /* all RX frames, including errors */
+       unsigned int tx_all_count;      /* all TX frames, including errors */
+       unsigned int rxerr_crc;
+       unsigned int rxerr_phy;
+       unsigned int rxerr_fifo;
+       unsigned int rxerr_decrypt;
+       unsigned int rxerr_mic;
+       unsigned int rxerr_proc;
+       unsigned int rxerr_jumbo;
+       unsigned int txerr_retry;
+       unsigned int txerr_fifo;
+       unsigned int txerr_filt;
 };
 
 #if CHAN_DEBUG
index 236f20f9cd409c65099d718d3e20eb285f585123..bccd4a78027ee5467192dd0e133f99e2b8400125 100644 (file)
@@ -465,6 +465,106 @@ static const struct file_operations fops_antenna = {
 };
 
 
+/* debugfs: frameerrors */
+
+static ssize_t read_file_frameerrors(struct file *file, char __user *user_buf,
+                                  size_t count, loff_t *ppos)
+{
+       struct ath5k_softc *sc = file->private_data;
+       struct ath5k_statistics *st = &sc->stats;
+       char buf[700];
+       unsigned int len = 0;
+
+       len += snprintf(buf+len, sizeof(buf)-len,
+                       "RX\n---------------------\n");
+       len += snprintf(buf+len, sizeof(buf)-len, "CRC\t%d\t(%d%%)\n",
+                       st->rxerr_crc,
+                       st->rx_all_count > 0 ?
+                               st->rxerr_crc*100/st->rx_all_count : 0);
+       len += snprintf(buf+len, sizeof(buf)-len, "PHY\t%d\t(%d%%)\n",
+                       st->rxerr_phy,
+                       st->rx_all_count > 0 ?
+                               st->rxerr_phy*100/st->rx_all_count : 0);
+       len += snprintf(buf+len, sizeof(buf)-len, "FIFO\t%d\t(%d%%)\n",
+                       st->rxerr_fifo,
+                       st->rx_all_count > 0 ?
+                               st->rxerr_fifo*100/st->rx_all_count : 0);
+       len += snprintf(buf+len, sizeof(buf)-len, "decrypt\t%d\t(%d%%)\n",
+                       st->rxerr_decrypt,
+                       st->rx_all_count > 0 ?
+                               st->rxerr_decrypt*100/st->rx_all_count : 0);
+       len += snprintf(buf+len, sizeof(buf)-len, "MIC\t%d\t(%d%%)\n",
+                       st->rxerr_mic,
+                       st->rx_all_count > 0 ?
+                               st->rxerr_mic*100/st->rx_all_count : 0);
+       len += snprintf(buf+len, sizeof(buf)-len, "process\t%d\t(%d%%)\n",
+                       st->rxerr_proc,
+                       st->rx_all_count > 0 ?
+                               st->rxerr_proc*100/st->rx_all_count : 0);
+       len += snprintf(buf+len, sizeof(buf)-len, "jumbo\t%d\t(%d%%)\n",
+                       st->rxerr_jumbo,
+                       st->rx_all_count > 0 ?
+                               st->rxerr_jumbo*100/st->rx_all_count : 0);
+       len += snprintf(buf+len, sizeof(buf)-len, "[RX all\t%d]\n",
+                       st->rx_all_count);
+
+       len += snprintf(buf+len, sizeof(buf)-len,
+                       "\nTX\n---------------------\n");
+       len += snprintf(buf+len, sizeof(buf)-len, "retry\t%d\t(%d%%)\n",
+                       st->txerr_retry,
+                       st->tx_all_count > 0 ?
+                               st->txerr_retry*100/st->tx_all_count : 0);
+       len += snprintf(buf+len, sizeof(buf)-len, "FIFO\t%d\t(%d%%)\n",
+                       st->txerr_fifo,
+                       st->tx_all_count > 0 ?
+                               st->txerr_fifo*100/st->tx_all_count : 0);
+       len += snprintf(buf+len, sizeof(buf)-len, "filter\t%d\t(%d%%)\n",
+                       st->txerr_filt,
+                       st->tx_all_count > 0 ?
+                               st->txerr_filt*100/st->tx_all_count : 0);
+       len += snprintf(buf+len, sizeof(buf)-len, "[TX all\t%d]\n",
+                       st->tx_all_count);
+
+       return simple_read_from_buffer(user_buf, count, ppos, buf, len);
+}
+
+static ssize_t write_file_frameerrors(struct file *file,
+                                const char __user *userbuf,
+                                size_t count, loff_t *ppos)
+{
+       struct ath5k_softc *sc = file->private_data;
+       struct ath5k_statistics *st = &sc->stats;
+       char buf[20];
+
+       if (copy_from_user(buf, userbuf, min(count, sizeof(buf))))
+               return -EFAULT;
+
+       if (strncmp(buf, "clear", 5) == 0) {
+               st->rxerr_crc = 0;
+               st->rxerr_phy = 0;
+               st->rxerr_fifo = 0;
+               st->rxerr_decrypt = 0;
+               st->rxerr_mic = 0;
+               st->rxerr_proc = 0;
+               st->rxerr_jumbo = 0;
+               st->rx_all_count = 0;
+               st->txerr_retry = 0;
+               st->txerr_fifo = 0;
+               st->txerr_filt = 0;
+               st->tx_all_count = 0;
+               printk(KERN_INFO "ath5k debug: cleared frameerrors stats\n");
+       }
+       return count;
+}
+
+static const struct file_operations fops_frameerrors = {
+       .read = read_file_frameerrors,
+       .write = write_file_frameerrors,
+       .open = ath5k_debugfs_open,
+       .owner = THIS_MODULE,
+};
+
+
 /* init */
 
 void
@@ -498,6 +598,11 @@ ath5k_debug_init_device(struct ath5k_softc *sc)
        sc->debug.debugfs_antenna = debugfs_create_file("antenna",
                                S_IWUSR | S_IRUSR,
                                sc->debug.debugfs_phydir, sc, &fops_antenna);
+
+       sc->debug.debugfs_frameerrors = debugfs_create_file("frameerrors",
+                               S_IWUSR | S_IRUSR,
+                               sc->debug.debugfs_phydir, sc,
+                               &fops_frameerrors);
 }
 
 void
@@ -514,6 +619,7 @@ ath5k_debug_finish_device(struct ath5k_softc *sc)
        debugfs_remove(sc->debug.debugfs_beacon);
        debugfs_remove(sc->debug.debugfs_reset);
        debugfs_remove(sc->debug.debugfs_antenna);
+       debugfs_remove(sc->debug.debugfs_frameerrors);
        debugfs_remove(sc->debug.debugfs_phydir);
 }
 
index 018612711045cdca20442fa286865ea89ce3a356..da24ff52e2744b2dc87b3faeeae44b43ad8fdf1c 100644 (file)
@@ -75,6 +75,7 @@ struct ath5k_dbg_info {
        struct dentry           *debugfs_beacon;
        struct dentry           *debugfs_reset;
        struct dentry           *debugfs_antenna;
+       struct dentry           *debugfs_frameerrors;
 };
 
 /**