diff options
| author | Lee Susman <lsusman@codeaurora.org> | 2013-08-12 16:16:19 +0300 |
|---|---|---|
| committer | David Keitel <dkeitel@codeaurora.org> | 2016-03-22 10:55:21 -0700 |
| commit | c92fad3700aa3f7fe1fb3d35ce9c5ae45192c792 (patch) | |
| tree | b4b01992b53225219029e0a4bff9f16efc309a53 | |
| parent | b045711fdc9ea3fd6aad97f3e77684d079ab13da (diff) | |
scsi: ufs: add debugfs for ufs
Adding debugfs capability for ufshcd. This patch also implements
command tag statistics enabling, disabling, printing and resetting.
Change-Id: Idea0323391029c446a0a75aa5bea7070e3ab8125
Signed-off-by: Lee Susman <lsusman@codeaurora.org>
[gbroner@codeaurora.org: fix minor merge conflict]
Signed-off-by: Gilad Broner <gbroner@codeaurora.org>
[subhashj@codeaurora.org: resolved trivial merge conflicts]
Signed-off-by: Subhash Jadavani <subhashj@codeaurora.org>
| -rw-r--r-- | drivers/scsi/ufs/Makefile | 1 | ||||
| -rw-r--r-- | drivers/scsi/ufs/debugfs.c | 184 | ||||
| -rw-r--r-- | drivers/scsi/ufs/debugfs.h | 32 | ||||
| -rw-r--r-- | drivers/scsi/ufs/ufshcd.c | 26 | ||||
| -rw-r--r-- | drivers/scsi/ufs/ufshcd.h | 21 |
5 files changed, 264 insertions, 0 deletions
diff --git a/drivers/scsi/ufs/Makefile b/drivers/scsi/ufs/Makefile index 0352e47b8397..9cddfaaa60a3 100644 --- a/drivers/scsi/ufs/Makefile +++ b/drivers/scsi/ufs/Makefile @@ -4,3 +4,4 @@ obj-$(CONFIG_SCSI_UFSHCD) += ufshcd.o obj-$(CONFIG_SCSI_UFSHCD_PCI) += ufshcd-pci.o obj-$(CONFIG_SCSI_UFSHCD_PLATFORM) += ufshcd-pltfrm.o obj-$(CONFIG_SCSI_UFS_TEST) += ufs_test.o +obj-$(CONFIG_DEBUG_FS) += debugfs.o diff --git a/drivers/scsi/ufs/debugfs.c b/drivers/scsi/ufs/debugfs.c new file mode 100644 index 000000000000..5c515a0da3c8 --- /dev/null +++ b/drivers/scsi/ufs/debugfs.c @@ -0,0 +1,184 @@ +/* Copyright (c) 2013, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * UFS debugfs - add debugfs interface to the ufshcd. + * This is currently used for statistics collection and exporting from the + * UFS driver. + * This infrastructure can be used for debugging or direct tweaking + * of the driver from userspace. + * + */ + +#include "debugfs.h" + +static int ufsdbg_tag_stats_show(struct seq_file *file, void *data) +{ + struct ufs_hba *hba = (struct ufs_hba *)file->private; + struct ufs_stats *ufs_stats; + int i; + int max_depth; + bool is_tag_empty = true; + + if (!hba) + goto exit; + + ufs_stats = &hba->ufs_stats; + + if (!ufs_stats->enabled) { + pr_debug("%s: ufs statistics are disabled\n", __func__); + goto exit; + } + + max_depth = hba->nutrs; + + mutex_lock(&ufs_stats->lock); + + pr_debug("%s: UFS tag statistics:\n", __func__); + pr_debug("%s: Max tagged command queue depth is %d", + __func__, max_depth); + + for (i = 0 ; i < max_depth ; ++i) { + if (hba->ufs_stats.tag_stats[i] != 0) { + is_tag_empty = false; + seq_printf(file, + "%s: Dispatched tag %d - %llu times\n", + __func__, i, ufs_stats->tag_stats[i]); + + pr_debug("%s: Dispatched tag %d - %llu times\n", + __func__, i, ufs_stats->tag_stats[i]); + } + } + mutex_unlock(&ufs_stats->lock); + + if (is_tag_empty) + pr_debug("%s: All tags statistics are empty", __func__); + +exit: + return 0; +} + +static int ufsdbg_tag_stats_open(struct inode *inode, struct file *file) +{ + return single_open(file, ufsdbg_tag_stats_show, inode->i_private); +} + +static ssize_t ufsdbg_tag_stats_write(struct file *filp, + const char __user *ubuf, size_t cnt, + loff_t *ppos) +{ + struct ufs_hba *hba = filp->f_mapping->host->i_private; + struct ufs_stats *ufs_stats; + int val = 0; + int ret; + + ret = kstrtoint_from_user(ubuf, cnt, 0, &val); + if (ret) { + dev_err(hba->dev, "%s: Invalid argument\n", __func__); + return ret; + } + + ufs_stats = &hba->ufs_stats; + mutex_lock(&ufs_stats->lock); + + if (!val) { + ufs_stats->enabled = false; + pr_debug("%s: Disabling UFS tag statistics", __func__); + } else { + ufs_stats->enabled = true; + pr_debug("%s: Enabling & Resetting UFS tag statistics", + __func__); + memset(ufs_stats->tag_stats, 0, + sizeof(unsigned int) * hba->nutrs); + } + + mutex_unlock(&ufs_stats->lock); + return cnt; +} + +static const struct file_operations ufsdbg_tag_stats_fops = { + .open = ufsdbg_tag_stats_open, + .read = seq_read, + .write = ufsdbg_tag_stats_write, +}; + +static int ufshcd_init_tag_statistics(struct ufs_hba *hba) +{ + int ret = 0; + + mutex_init(&hba->ufs_stats.lock); + + hba->ufs_stats.tag_stats = kzalloc(hba->nutrs * sizeof(u64), + GFP_KERNEL); + if (!hba->ufs_stats.tag_stats) { + dev_err(hba->dev, + "%s: Unable to allocate UFS tag_stats", __func__); + ret = -ENOMEM; + goto exit; + } + + hba->ufs_stats.enabled = false; + +exit: + return ret; +} + +void ufsdbg_add_debugfs(struct ufs_hba *hba) +{ + if (!hba) { + dev_err(hba->dev, "%s: NULL hba, exiting", __func__); + goto err_no_root; + } + + hba->debugfs_files.debugfs_root = debugfs_create_dir("ufs", NULL); + if (IS_ERR(hba->debugfs_files.debugfs_root)) + /* Don't complain -- debugfs just isn't enabled */ + goto err_no_root; + if (!hba->debugfs_files.debugfs_root) { + /* + * Complain -- debugfs is enabled, but it failed to + * create the directory + */ + dev_err(hba->dev, + "%s: NULL debugfs root directory, exiting", __func__); + goto err_no_root; + } + + hba->debugfs_files.tag_stats = + debugfs_create_file("tag_stats", S_IRUSR, + hba->debugfs_files.debugfs_root, hba, + &ufsdbg_tag_stats_fops); + if (!hba->debugfs_files.tag_stats) { + dev_err(hba->dev, "%s: NULL tag stats file, exiting", + __func__); + goto err; + } + + if (ufshcd_init_tag_statistics(hba)) { + dev_err(hba->dev, "%s: Error initializing tag stats", + __func__); + goto err; + } + + return; + +err: + debugfs_remove_recursive(hba->debugfs_files.debugfs_root); + hba->debugfs_files.debugfs_root = NULL; +err_no_root: + dev_err(hba->dev, "%s: failed to initialize debugfs\n", __func__); +} + +void ufsdbg_remove_debugfs(struct ufs_hba *hba) +{ + debugfs_remove_recursive(hba->debugfs_files.debugfs_root); + kfree(hba->ufs_stats.tag_stats); + +} diff --git a/drivers/scsi/ufs/debugfs.h b/drivers/scsi/ufs/debugfs.h new file mode 100644 index 000000000000..9e6cd5d34c4c --- /dev/null +++ b/drivers/scsi/ufs/debugfs.h @@ -0,0 +1,32 @@ +/* Copyright (c) 2013, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * UFS debugfs - add debugfs interface to the ufshcd. + * This is currently used for statistics collection and exporting from the + * UFS driver. + * This infrastructure can be used for debugging or direct tweaking + * of the driver from userspace. + * + */ + +#ifndef _UFS_DEBUGFS_H +#define _UFS_DEBUGFS_H + +#include <linux/debugfs.h> +#include "ufshcd.h" + +#ifdef CONFIG_DEBUG_FS +void ufsdbg_add_debugfs(struct ufs_hba *hba); + +void ufsdbg_remove_debugfs(struct ufs_hba *hba); +#endif + +#endif /* End of Header */ diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c index 65da78e0d3f7..ea4273c34999 100644 --- a/drivers/scsi/ufs/ufshcd.c +++ b/drivers/scsi/ufs/ufshcd.c @@ -42,6 +42,28 @@ #include "ufshcd.h" #include "unipro.h" +#include "debugfs.h" + +#ifdef CONFIG_DEBUG_FS +#define UFSHCD_UPDATE_TAG_STATS(hba, tag) \ + do { \ + if (hba->ufs_stats.enabled) { \ + mutex_lock(&hba->ufs_stats.lock); \ + hba->ufs_stats.tag_stats[tag]++; \ + mutex_unlock(&hba->ufs_stats.lock); \ + } \ + } while (0); + +#define UFSDBG_ADD_DEBUGFS(hba) ufsdbg_add_debugfs(hba); + +#define UFSDBG_REMOVE_DEBUGFS(hba) ufsdbg_remove_debugfs(hba); + +#else +#define UFSHCD_UPDATE_TAG_STATS(hba, tag) do {} while (0); +#define UFSDBG_ADD_DEBUGFS(hba) do {} while (0); +#define UFSDBG_REMOVE_DEBUGFS(hba) do {} while (0); + +#endif #define UFSHCD_ENABLE_INTRS (UTP_TRANSFER_REQ_COMPL |\ UTP_TASK_REQ_COMPL |\ @@ -802,6 +824,7 @@ void ufshcd_send_command(struct ufs_hba *hba, unsigned int task_tag) ufshcd_clk_scaling_start_busy(hba); __set_bit(task_tag, &hba->outstanding_reqs); ufshcd_writel(hba, 1 << task_tag, REG_UTP_TRANSFER_REQ_DOOR_BELL); + UFSHCD_UPDATE_TAG_STATS(hba, task_tag) } /** @@ -5656,6 +5679,8 @@ int ufshcd_init(struct ufs_hba *hba, void __iomem *mmio_base, unsigned int irq) async_schedule(ufshcd_async_scan, hba); + UFSDBG_ADD_DEBUGFS(hba) + return 0; out_remove_scsi_host: @@ -5665,6 +5690,7 @@ exit_gating: out_disable: hba->is_irq_enabled = false; scsi_host_put(host); + UFSDBG_REMOVE_DEBUGFS(hba) ufshcd_hba_exit(hba); out_error: return err; diff --git a/drivers/scsi/ufs/ufshcd.h b/drivers/scsi/ufs/ufshcd.h index 2570d9477b37..ebc0996735fc 100644 --- a/drivers/scsi/ufs/ufshcd.h +++ b/drivers/scsi/ufs/ufshcd.h @@ -333,6 +333,19 @@ struct ufs_init_prefetch { u32 icc_level; }; +#ifdef CONFIG_DEBUG_FS +struct ufs_stats { + u64 *tag_stats; + struct mutex lock; + bool enabled; +}; + +struct debugfs_files { + struct dentry *debugfs_root; + struct dentry *tag_stats; +}; +#endif + /** * struct ufs_hba - per adapter private structure * @mmio_base: UFSHCI base register address @@ -378,6 +391,8 @@ struct ufs_init_prefetch { * @dev_cmd: ufs device management command information * @last_dme_cmd_tstamp: time stamp of the last completed DME command * @auto_bkops_enabled: to track whether bkops is enabled in device + * @ufs_stats: ufshcd statistics to be used via debugfs + * @debugfs_files: debugfs files associated with the ufs stats * @vreg_info: UFS device voltage regulator information * @clk_list_head: UFS host controller clocks list node head * @pwr_info: holds current power mode @@ -503,6 +518,12 @@ struct ufs_hba { /* Keeps information of the UFS device connected to this host */ struct ufs_dev_info dev_info; bool auto_bkops_enabled; + +#ifdef CONFIG_DEBUG_FS + struct ufs_stats ufs_stats; + struct debugfs_files debugfs_files; +#endif + struct ufs_vreg_info vreg_info; struct list_head clk_list_head; |
