Re: [PATCH 0/8] Address recent SCST comments

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Monday 27 December 2010 14:35:33 Bart Van Assche wrote:
> The paches in this patch series address the following issues:
> - Make sure that all SCST sysfs attributes have only one value per file.
> [ ... ]

Note: someone was so kind to inform me (via private e-mail) that two more
classes of sysfs files did not yet satisfy the one-value-per-file rule, namely
the SGV cache statistics and the I/O latency statistics. The two patches
below move these sysfs files to debugfs too:

[SCSI] scst: Move SGV cache statistics to debugfs

Signed-off-by: Bart Van Assche <bvanassche@xxxxxxx>
---
 Documentation/ABI/stable/sysfs-devices-scst |   31 -----
 drivers/scst/Makefile                       |    4 +-
 drivers/scst/scst_mem.c                     |  168 +++-----------------------
 drivers/scst/scst_mem.h                     |   22 ++++-
 drivers/scst/scst_mem_stats.c               |  168 +++++++++++++++++++++++++++
 drivers/scst/scst_mem_stats.h               |   21 ++++
 drivers/scst/scst_priv.h                    |    2 -
 drivers/scst/scst_sysfs.c                   |    9 --
 drivers/scst/scst_tracing.c                 |   17 +++-
 9 files changed, 246 insertions(+), 196 deletions(-)
 create mode 100644 drivers/scst/scst_mem_stats.c
 create mode 100644 drivers/scst/scst_mem_stats.h

diff --git a/Documentation/ABI/stable/sysfs-devices-scst b/Documentation/ABI/stable/sysfs-devices-scst
index e25d1a4..8e02cff 100644
--- a/Documentation/ABI/stable/sysfs-devices-scst
+++ b/Documentation/ABI/stable/sysfs-devices-scst
@@ -18,37 +18,6 @@ Description:
 		specific identifier ID and USN of virtual devices. Must be
 		set before any virtual devices are created. Read-write.
 
-What:		/sys/devices/scst/sgv/global_stats
-Date:		December 2010
-Contact:	Bart Van Assche <bvanassche@xxxxxxx>
-Description:
-		Global SGV (scatter/gather vector) cache statistics. Read-only.
-		An example:
-
-		$ cat sgv/global_stats
-		Inactive/active pages			0/0
-		Hi/lo watermarks [pages]		62208/0
-		Hi watermark releases/failures		0/0
-		Other allocs				0
-
-What:		/sys/devices/scst/sgv/sgv/stats
-Date:		December 2010
-Contact:	Bart Van Assche <bvanassche@xxxxxxx>
-Description:
-		Statistics for the regular SGV cache. Read-only.
-
-What:		/sys/devices/scst/sgv/sgv-clust/stats
-Date:		December 2010
-Contact:	Bart Van Assche <bvanassche@xxxxxxx>
-Description:
-		Statistics for the clustering SGV cache. Read-only.
-
-What:		/sys/devices/scst/sgv/sgv-dma/stats
-Date:		December 2010
-Contact:	Bart Van Assche <bvanassche@xxxxxxx>
-Description:
-		Statistics for the DMA SGV cache. Read-only.
-
 What:		/sys/devices/scst/threads
 Date:		December 2010
 Contact:	Bart Van Assche <bvanassche@xxxxxxx>
diff --git a/drivers/scst/Makefile b/drivers/scst/Makefile
index 38b3f4e..a27cf92 100644
--- a/drivers/scst/Makefile
+++ b/drivers/scst/Makefile
@@ -7,7 +7,7 @@ scst-y        += scst_lib.o
 scst-y        += scst_sysfs.o
 scst-y        += scst_mem.o
 scst-y        += scst_debug.o
-scst-$(CONFIG_SCST_DEBUG)	+= scst_tracing.o
-scst-$(CONFIG_SCST_TRACING)	+= scst_tracing.o
+scst-$(CONFIG_SCST_DEBUG)	+= scst_tracing.o scst_mem_stats.o
+scst-$(CONFIG_SCST_TRACING)	+= scst_tracing.o scst_mem_stats.o
 
 obj-$(CONFIG_SCST)   += scst.o dev_handlers/ scst_local/ srpt/
diff --git a/drivers/scst/scst_mem.c b/drivers/scst/scst_mem.c
index 048f848..b9d84f5 100644
--- a/drivers/scst/scst_mem.c
+++ b/drivers/scst/scst_mem.c
@@ -29,6 +29,7 @@
 #include <scst/scst.h>
 #include "scst_priv.h"
 #include "scst_mem.h"
+#include "scst_mem_stats.h"
 
 #define SGV_DEFAULT_PURGE_INTERVAL	(60 * HZ)
 #define SGV_MIN_SHRINK_INTERVAL		(1 * HZ)
@@ -66,12 +67,6 @@ static struct shrinker sgv_shrinker;
  */
 static LIST_HEAD(sgv_pools_list);
 
-static struct kobj_type pool_ktype;
-
-static struct kobject *scst_sgv_kobj;
-static int scst_sgv_sysfs_create(struct sgv_pool *pool, struct kobject *parent);
-static void scst_sgv_sysfs_del(struct sgv_pool *pool);
-
 static inline bool sgv_pool_clustered(const struct sgv_pool *pool)
 {
 	return pool->clustering_type != sgv_no_clustering;
@@ -1412,7 +1407,7 @@ static int sgv_pool_init(struct sgv_pool *pool, const char *name,
 	list_add_tail(&pool->sgv_pools_list_entry, &sgv_pools_list);
 	spin_unlock_bh(&sgv_pools_lock);
 
-	res = scst_sgv_sysfs_create(pool, scst_sgv_kobj);
+	res = scst_sgv_pool_debugfs_create(pool);
 	if (res != 0)
 		goto out_del;
 
@@ -1486,6 +1481,8 @@ EXPORT_SYMBOL_GPL(sgv_pool_flush);
 
 static void sgv_pool_destroy(struct sgv_pool *pool)
 {
+	int i;
+
 	cancel_delayed_work_sync(&pool->sgv_purge_work);
 
 	sgv_pool_flush(pool);
@@ -1496,10 +1493,15 @@ static void sgv_pool_destroy(struct sgv_pool *pool)
 	spin_unlock_bh(&sgv_pools_lock);
 	mutex_unlock(&sgv_pools_mutex);
 
-	scst_sgv_sysfs_del(pool);
+	scst_sgv_pool_debugfs_del(pool);
 
-	kobject_put(&pool->sgv_kobj);
-	return;
+	for (i = 0; i < pool->max_caches; i++) {
+		if (pool->caches[i])
+			kmem_cache_destroy(pool->caches[i]);
+		pool->caches[i] = NULL;
+	}
+
+	kfree(pool);
 }
 
 /**
@@ -1523,17 +1525,6 @@ void sgv_pool_set_allocator(struct sgv_pool *pool,
 EXPORT_SYMBOL_GPL(sgv_pool_set_allocator);
 
 /**
- * sgv_kobj_to_pool() - Convert a kobject pointer to a pool pointer.
- *
- * Must be called from inside an sgv pool sysfs .show() or .store() callback
- * function only.
- */
-static inline struct sgv_pool *sgv_kobj_to_pool(struct kobject *kobj)
-{
-	return container_of(kobj, struct sgv_pool, sgv_kobj);
-}
-
-/**
  * sgv_pool_create - creates and initializes an SGV pool
  * @name:	the name of the SGV pool
  * @clustered:	sets type of the pages clustering.
@@ -1591,8 +1582,6 @@ struct sgv_pool *sgv_pool_create(const char *name,
 		goto out_unlock;
 	}
 
-	kobject_init(&pool->sgv_kobj, &pool_ktype);
-
 	rc = sgv_pool_init(pool, name, clustering_type, single_alloc_pages,
 				purge_interval);
 	if (rc != 0)
@@ -1603,7 +1592,7 @@ out_unlock:
 	return pool;
 
 out_free:
-	kobject_put(&pool->sgv_kobj);
+	kfree(pool);
 	goto out_unlock;
 }
 EXPORT_SYMBOL_GPL(sgv_pool_create);
@@ -1707,15 +1696,13 @@ void scst_sgv_pools_deinit(void)
 	return;
 }
 
-static ssize_t sgv_sysfs_stat_show(struct kobject *kobj,
-	struct kobj_attribute *attr, char *buf)
+#if defined(CONFIG_SCST_DEBUG) || defined(CONFIG_SCST_TRACING)
+
+ssize_t sgv_pool_stat_show(struct sgv_pool *pool, char *buf)
 {
-	struct sgv_pool *pool;
 	int i, total = 0, hit = 0, merged = 0, allocated = 0;
 	int oa, om, res;
 
-	pool = sgv_kobj_to_pool(kobj);
-
 	for (i = 0; i < SGV_POOL_ELEMENTS; i++) {
 		int t;
 
@@ -1763,14 +1750,10 @@ static ssize_t sgv_sysfs_stat_show(struct kobject *kobj,
 	return res;
 }
 
-static ssize_t sgv_sysfs_stat_reset(struct kobject *kobj,
-	struct kobj_attribute *attr, const char *buf, size_t count)
+void sgv_pool_stat_reset(struct sgv_pool *pool)
 {
-	struct sgv_pool *pool;
 	int i;
 
-	pool = sgv_kobj_to_pool(kobj);
-
 	for (i = 0; i < SGV_POOL_ELEMENTS; i++) {
 		atomic_set(&pool->cache_acc[i].hit_alloc, 0);
 		atomic_set(&pool->cache_acc[i].total_alloc, 0);
@@ -1785,11 +1768,9 @@ static ssize_t sgv_sysfs_stat_reset(struct kobject *kobj,
 	atomic_set(&pool->other_alloc, 0);
 
 	PRINT_INFO("Statistics for SGV pool %s reset", pool->name);
-	return count;
 }
 
-static ssize_t sgv_sysfs_global_stat_show(struct kobject *kobj,
-	struct kobj_attribute *attr, char *buf)
+ssize_t sgv_global_stat_show(char *buf)
 {
 	struct sgv_pool *pool;
 	int inactive_pages = 0, res;
@@ -1813,122 +1794,13 @@ static ssize_t sgv_sysfs_global_stat_show(struct kobject *kobj,
 	return res;
 }
 
-static ssize_t sgv_sysfs_global_stat_reset(struct kobject *kobj,
-	struct kobj_attribute *attr, const char *buf, size_t count)
+void sgv_global_stat_reset(void)
 {
 	atomic_set(&sgv_releases_on_hiwmk, 0);
 	atomic_set(&sgv_releases_on_hiwmk_failed, 0);
 	atomic_set(&sgv_other_total_alloc, 0);
 
 	PRINT_INFO("%s", "Global SGV pool statistics reset");
-	return count;
-}
-
-static struct kobj_attribute sgv_stat_attr =
-	__ATTR(stats, S_IRUGO | S_IWUSR, sgv_sysfs_stat_show,
-		sgv_sysfs_stat_reset);
-
-static struct attribute *sgv_attrs[] = {
-	&sgv_stat_attr.attr,
-	NULL,
-};
-
-static void scst_release_pool(struct kobject *kobj)
-{
-	struct sgv_pool *pool;
-	int i;
-
-	pool = sgv_kobj_to_pool(kobj);
-
-	for (i = 0; i < pool->max_caches; i++) {
-		if (pool->caches[i])
-			kmem_cache_destroy(pool->caches[i]);
-		pool->caches[i] = NULL;
-	}
-
-	kfree(pool);
-}
-
-static struct kobj_type pool_ktype = {
-	.release = scst_release_pool,
-	.sysfs_ops = &scst_sysfs_ops,
-	.default_attrs = sgv_attrs,
-};
-
-static int scst_sgv_sysfs_create(struct sgv_pool *pool, struct kobject *parent)
-{
-	int res;
-
-	res = kobject_add(&pool->sgv_kobj, parent, pool->name);
-	if (res != 0) {
-		PRINT_ERROR("Can't add sgv pool %s to sysfs", pool->name);
-		goto out;
-	}
-
-out:
-	return res;
-}
-
-static void scst_sgv_sysfs_del(struct sgv_pool *pool)
-{
-	kobject_del(&pool->sgv_kobj);
-}
-
-/**
- ** SGV directory implementation
- **/
-
-static struct kobj_attribute sgv_global_stat_attr =
-	__ATTR(global_stats, S_IRUGO | S_IWUSR, sgv_sysfs_global_stat_show,
-		sgv_sysfs_global_stat_reset);
-
-static struct attribute *sgv_default_attrs[] = {
-	&sgv_global_stat_attr.attr,
-	NULL,
-};
-
-static void scst_sysfs_release(struct kobject *kobj)
-{
-	kfree(kobj);
-}
-
-static struct kobj_type sgv_ktype = {
-	.sysfs_ops = &scst_sysfs_ops,
-	.release = scst_sysfs_release,
-	.default_attrs = sgv_default_attrs,
-};
-
-/**
- * scst_add_sgv_kobj() - Initialize and add the root SGV kernel object.
- */
-int scst_add_sgv_kobj(struct kobject *parent, const char *name)
-{
-	int res;
-
-	WARN_ON(scst_sgv_kobj);
-	res = -ENOMEM;
-	scst_sgv_kobj = kzalloc(sizeof(*scst_sgv_kobj), GFP_KERNEL);
-	if (!scst_sgv_kobj)
-		goto out;
-	res = kobject_init_and_add(scst_sgv_kobj, &sgv_ktype, parent, name);
-	if (res != 0)
-		goto out_free;
-out:
-	return res;
-out_free:
-	kobject_put(scst_sgv_kobj);
-	scst_sgv_kobj = NULL;
-	goto out;
-}
-
-/**
- * scst_del_put_sgv_kobj() - Remove the root SGV kernel object.
- */
-void scst_del_put_sgv_kobj(void)
-{
-	WARN_ON(!scst_sgv_kobj);
-	kobject_del(scst_sgv_kobj);
-	kobject_put(scst_sgv_kobj);
-	scst_sgv_kobj = NULL;
 }
 
+#endif /*defined(CONFIG_SCST_DEBUG) || defined(CONFIG_SCST_TRACING)*/
diff --git a/drivers/scst/scst_mem.h b/drivers/scst/scst_mem.h
index ea3f549..18b7e8c 100644
--- a/drivers/scst/scst_mem.h
+++ b/drivers/scst/scst_mem.h
@@ -122,7 +122,9 @@ struct sgv_pool {
 
 	struct list_head sgv_pools_list_entry;
 
-	struct kobject sgv_kobj;
+#if defined(CONFIG_SCST_DEBUG) || defined(CONFIG_SCST_TRACING)
+	struct dentry *debugfs_dir;
+#endif
 };
 
 static inline struct scatterlist *sgv_pool_sg(struct sgv_pool_obj *obj)
@@ -136,3 +138,21 @@ void scst_sgv_pools_deinit(void);
 void scst_sgv_pool_use_norm(struct scst_tgt_dev *tgt_dev);
 void scst_sgv_pool_use_norm_clust(struct scst_tgt_dev *tgt_dev);
 void scst_sgv_pool_use_dma(struct scst_tgt_dev *tgt_dev);
+
+#if defined(CONFIG_SCST_DEBUG) || defined(CONFIG_SCST_TRACING)
+
+int scst_sgv_debugfs_create(struct dentry *parent);
+void scst_sgv_debugfs_del(void);
+
+#else /*defined(CONFIG_SCST_DEBUG) || defined(CONFIG_SCST_TRACING)*/
+
+static inline int scst_sgv_debugfs_create(struct dentry *parent)
+{
+	return 0;
+}
+
+static inline void scst_sgv_debugfs_del(void)
+{
+}
+
+#endif /*defined(CONFIG_SCST_DEBUG) || defined(CONFIG_SCST_TRACING)*/
diff --git a/drivers/scst/scst_mem_stats.c b/drivers/scst/scst_mem_stats.c
new file mode 100644
index 0000000..8095e61
--- /dev/null
+++ b/drivers/scst/scst_mem_stats.c
@@ -0,0 +1,168 @@
+/*
+ *  scst_mem.c
+ *
+ *  Copyright (C) 2006 - 2010 Vladislav Bolkhovitin <vst@xxxxxxxx>
+ *  Copyright (C) 2007 - 2010 ID7 Ltd.
+ *  Copyright (C) 2010 Bart Van Assche <bvanassche@xxxxxxx>.
+ *
+ *  This program is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU General Public License
+ *  as published by the Free Software Foundation, version 2
+ *  of the License.
+ *
+ *  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.
+ */
+
+#include <linux/debugfs.h>
+#include <scst/scst.h>
+#include "scst_priv.h"
+#include "scst_mem.h"
+#include "scst_mem_stats.h"
+
+static struct dentry *scst_debug_sgv;
+
+static int sgv_debugfs_open(struct inode *inode, struct file *file)
+{
+	if (inode->i_private)
+		file->private_data = inode->i_private;
+
+	return 0;
+}
+
+static ssize_t sgv_pool_read_file(struct file *file, char __user *buf,
+				      size_t count, loff_t *ppos)
+{
+	struct sgv_pool *pool = file->private_data;
+	unsigned long pg;
+	void *contents;
+	int len;
+	ssize_t res;
+
+	if (*ppos > PAGE_SIZE)
+		return -EINVAL;
+	pg = __get_free_page(GFP_KERNEL);
+	if (!pg)
+		return -ENOMEM;
+	contents = (void *)pg;
+	len = sgv_pool_stat_show(pool, contents);
+	free_page(pg);
+	res = min_t(ssize_t, count, len - *ppos);
+	if (copy_to_user(buf, contents + *ppos, res))
+		return -EFAULT;
+	*ppos += res;
+	return res;
+}
+
+static ssize_t sgv_pool_write_file(struct file *file,
+				       const char __user *buf,
+				       size_t count, loff_t *ppos)
+{
+	struct sgv_pool *pool = file->private_data;
+
+	sgv_pool_stat_reset(pool);
+	return count;
+}
+
+static const struct file_operations sgv_pool_fops = {
+	.read	=	sgv_pool_read_file,
+	.write	=	sgv_pool_write_file,
+	.open	=	sgv_debugfs_open,
+	.llseek	=	noop_llseek,
+};
+
+int scst_sgv_pool_debugfs_create(struct sgv_pool *pool)
+{
+	struct dentry *file;
+	int res;
+
+	res = -ENOMEM;
+	pool->debugfs_dir = debugfs_create_dir(pool->name, scst_debug_sgv);
+	if (!pool->debugfs_dir)
+		goto out;
+	file = debugfs_create_file("stats", S_IRUGO | S_IWUSR,
+				   pool->debugfs_dir, pool, &sgv_pool_fops);
+	if (!file)
+		goto err;
+	res = 0;
+out:
+	return res;
+err:
+	debugfs_remove_recursive(pool->debugfs_dir);
+	pool->debugfs_dir = NULL;
+	goto out;
+}
+
+void scst_sgv_pool_debugfs_del(struct sgv_pool *pool)
+{
+	debugfs_remove_recursive(pool->debugfs_dir);
+	pool->debugfs_dir = NULL;
+}
+
+static ssize_t sgv_global_read_file(struct file *file, char __user *buf,
+				      size_t count, loff_t *ppos)
+{
+	unsigned long pg;
+	void *contents;
+	int len;
+	ssize_t res;
+
+	if (*ppos > PAGE_SIZE)
+		return -EINVAL;
+	pg = __get_free_page(GFP_KERNEL);
+	if (!pg)
+		return -ENOMEM;
+	contents = (void *)pg;
+	len = sgv_global_stat_show(contents);
+	free_page(pg);
+	res = min_t(ssize_t, count, len - *ppos);
+	if (copy_to_user(buf, contents + *ppos, res))
+		return -EFAULT;
+	*ppos += res;
+	return res;
+}
+
+static ssize_t sgv_global_write_file(struct file *file,
+				     const char __user *buf,
+				     size_t count, loff_t *ppos)
+{
+	sgv_global_stat_reset();
+	return count;
+}
+
+static const struct file_operations sgv_global_fops = {
+	.read	=	sgv_global_read_file,
+	.write	=	sgv_global_write_file,
+	.open	=	sgv_debugfs_open,
+	.llseek	=	noop_llseek,
+};
+
+int scst_sgv_debugfs_create(struct dentry *parent)
+{
+	struct dentry *file;
+	int res;
+
+	res = -ENOMEM;
+	scst_debug_sgv = debugfs_create_dir("sgv", parent);
+	if (!scst_debug_sgv)
+		goto out;
+	file = debugfs_create_file("global_stats", S_IRUGO | S_IWUSR,
+				   scst_debug_sgv, NULL, &sgv_global_fops);
+	if (!file)
+		goto err;
+	res = 0;
+out:
+	return res;
+err:
+	debugfs_remove_recursive(scst_debug_sgv);
+	scst_debug_sgv = NULL;
+	goto out;
+}
+
+void scst_sgv_debugfs_del(void)
+{
+	debugfs_remove_recursive(scst_debug_sgv);
+	scst_debug_sgv = NULL;
+}
diff --git a/drivers/scst/scst_mem_stats.h b/drivers/scst/scst_mem_stats.h
new file mode 100644
index 0000000..b51d294
--- /dev/null
+++ b/drivers/scst/scst_mem_stats.h
@@ -0,0 +1,21 @@
+#if defined(CONFIG_SCST_DEBUG) || defined(CONFIG_SCST_TRACING)
+
+int scst_sgv_pool_debugfs_create(struct sgv_pool *pool);
+void scst_sgv_pool_debugfs_del(struct sgv_pool *pool);
+ssize_t sgv_pool_stat_show(struct sgv_pool *pool, char *buf);
+void sgv_pool_stat_reset(struct sgv_pool *pool);
+ssize_t sgv_global_stat_show(char *buf);
+void sgv_global_stat_reset(void);
+
+#else /*defined(CONFIG_SCST_DEBUG) || defined(CONFIG_SCST_TRACING)*/
+
+static inline int scst_sgv_pool_debugfs_create(struct sgv_pool *pool)
+{
+	return 0;
+}
+
+static void scst_sgv_pool_debugfs_del(struct sgv_pool *pool)
+{
+}
+
+#endif /*defined(CONFIG_SCST_DEBUG) || defined(CONFIG_SCST_TRACING)*/
diff --git a/drivers/scst/scst_priv.h b/drivers/scst/scst_priv.h
index 7a5a815..74e68a4 100644
--- a/drivers/scst/scst_priv.h
+++ b/drivers/scst/scst_priv.h
@@ -376,8 +376,6 @@ void scst_tgt_sysfs_put(struct scst_tgt *tgt);
 int scst_sess_sysfs_create(struct scst_session *sess);
 void scst_sess_sysfs_del(struct scst_session *sess);
 int scst_recreate_sess_luns_link(struct scst_session *sess);
-int scst_add_sgv_kobj(struct kobject *parent, const char *name);
-void scst_del_put_sgv_kobj(void);
 int scst_devt_sysfs_init(struct scst_dev_type *devt);
 int scst_devt_sysfs_create(struct scst_dev_type *devt);
 void scst_devt_sysfs_del(struct scst_dev_type *devt);
diff --git a/drivers/scst/scst_sysfs.c b/drivers/scst/scst_sysfs.c
index aadb066..84dc9ee 100644
--- a/drivers/scst/scst_sysfs.c
+++ b/drivers/scst/scst_sysfs.c
@@ -3860,16 +3860,9 @@ int __init scst_sysfs_init(void)
 		goto out_remove_files;
 	}
 
-	res = scst_add_sgv_kobj(&scst_device->kobj, "sgv");
-	if (res) {
-		PRINT_ERROR("%s", "Creation of SCST sgv kernel object failed.");
-		goto out_remove_trace_files;
-	}
-
 out:
 	return res;
 
-out_remove_trace_files:
 	scst_main_remove_trace_files();
 out_remove_files:
 	device_remove_files(scst_device, scst_root_default_attrs);
@@ -3891,8 +3884,6 @@ void scst_sysfs_cleanup(void)
 {
 	PRINT_INFO("%s", "Exiting SCST sysfs hierarchy...");
 
-	scst_del_put_sgv_kobj();
-
 	scst_main_remove_trace_files();
 
 	device_remove_files(scst_device, scst_root_default_attrs);
diff --git a/drivers/scst/scst_tracing.c b/drivers/scst/scst_tracing.c
index 7b97224..9b9aef4 100644
--- a/drivers/scst/scst_tracing.c
+++ b/drivers/scst/scst_tracing.c
@@ -18,6 +18,7 @@
 #include <linux/debugfs.h>
 #include <scst/scst.h>
 #include <scst/scst_debug.h>
+#include "scst_mem.h"
 #include "scst_priv.h"
 #include "scst_pres.h"
 #include "scst_tracing.h"
@@ -230,31 +231,41 @@ int scst_debugfs_init(void)
 		goto out;
 	}
 
+	res = scst_sgv_debugfs_create(scst_debug_root);
+	if (res) {
+		PRINT_ERROR("%s", "Creation of /sys/kernel/debug/scst/sgv"
+			    " failed");
+		goto err;
+	}
+
 	scst_debug_target = debugfs_create_dir("target", scst_debug_root);
 	if (!scst_debug_target) {
 		PRINT_ERROR("%s", "Creation of /sys/kernel/debug/scst/target"
 			    " failed");
-		goto out;
+		goto err;
 	}
 
 	scst_debug_devt = debugfs_create_dir("device_type", scst_debug_root);
 	if (!scst_debug_devt) {
 		PRINT_ERROR("%s", "Creation of /sys/kernel/debug/scst/"
 			    "device_type failed");
-		goto out;
+		goto err;
 	}
 
 	scst_debug_dev = debugfs_create_dir("device", scst_debug_root);
 	if (!scst_debug_dev) {
 		PRINT_ERROR("%s", "Creation of /sys/kernel/debug/scst/device"
 			    " failed");
-		goto out;
+		goto err;
 	}
 
 	res = 0;
 
 out:
 	return res;
+err:
+	scst_debugfs_cleanup();
+	goto out;
 }
 
 void scst_debugfs_cleanup(void)
-- 
1.7.1




[SCSI] scst: Move latency statistics to debugfs

Signed-off-by: Bart Van Assche <bvanassche@xxxxxxx>
---
 Documentation/ABI/stable/sysfs-devices-scst_target |   16 -
 drivers/scst/Kconfig                               |    2 +-
 drivers/scst/Makefile                              |    1 +
 drivers/scst/scst_lat_stats.c                      |  435 ++++++++++++++++++++
 drivers/scst/scst_lat_stats.h                      |   28 ++
 drivers/scst/scst_sysfs.c                          |  371 ++---------------
 drivers/scst/scst_tracing.c                        |  204 +++++++---
 drivers/scst/scst_tracing.h                        |   57 ++-
 include/scst/scst.h                                |   21 +
 9 files changed, 719 insertions(+), 416 deletions(-)
 create mode 100644 drivers/scst/scst_lat_stats.c
 create mode 100644 drivers/scst/scst_lat_stats.h

diff --git a/Documentation/ABI/stable/sysfs-devices-scst_target b/Documentation/ABI/stable/sysfs-devices-scst_target
index 2d3a388..1792aa3 100644
--- a/Documentation/ABI/stable/sysfs-devices-scst_target
+++ b/Documentation/ABI/stable/sysfs-devices-scst_target
@@ -182,28 +182,12 @@ Description:
 		Session name. For most target drivers this is a name that
 		identifies the initiator. Read-only.
 
-What:		/sys/bus/scst_target/devices/*/sessions/<session>/latency
-Date:		December 2010
-Contact:	Bart Van Assche <bvanassche@xxxxxxx>
-Description:
-		Latency statistics for this session. Only available if
-		CONFIG_SCST_MEASURE_LATENCY has been enabled in the
-		kernel configuration. Read-only.
-
 What:		/sys/bus/scst_target/devices/*/sessions/<session>/lun<number>/active_commands
 Date:		December 2010
 Contact:	Bart Van Assche <bvanassche@xxxxxxx>
 Description:
 		Number of active commands. Read-only.
 
-What:		/sys/bus/scst_target/devices/*/sessions/<session>/lun<number>/latency
-Date:		December 2010
-Contact:	Bart Van Assche <bvanassche@xxxxxxx>
-Description:
-		Latency statistics for this LUN. Only available if
-		CONFIG_SCST_MEASURE_LATENCY has been enabled in the kernel
-		configuration. Read-only.
-
 What:		/sys/bus/scst_target/devices/*/sessions/<session>/luns
 Date:		December 2010
 Contact:	Bart Van Assche <bvanassche@xxxxxxx>
diff --git a/drivers/scst/Kconfig b/drivers/scst/Kconfig
index 75b0f97..62cb227 100644
--- a/drivers/scst/Kconfig
+++ b/drivers/scst/Kconfig
@@ -228,7 +228,7 @@ config SCST_TM_DBG_GO_OFFLINE
 
 config SCST_MEASURE_LATENCY
 	bool "Commands processing latency measurement facility"
-	depends on SCST
+	depends on SCST && (SCST_DEBUG || SCST_TRACING)
 	help
 	  This option enables commands processing latency measurement
 	  facility in SCST. It will provide in the sysfs interface
diff --git a/drivers/scst/Makefile b/drivers/scst/Makefile
index a27cf92..f2f9253 100644
--- a/drivers/scst/Makefile
+++ b/drivers/scst/Makefile
@@ -9,5 +9,6 @@ scst-y        += scst_mem.o
 scst-y        += scst_debug.o
 scst-$(CONFIG_SCST_DEBUG)	+= scst_tracing.o scst_mem_stats.o
 scst-$(CONFIG_SCST_TRACING)	+= scst_tracing.o scst_mem_stats.o
+scst-$(CONFIG_SCST_MEASURE_LATENCY)	+= scst_lat_stats.o
 
 obj-$(CONFIG_SCST)   += scst.o dev_handlers/ scst_local/ srpt/
diff --git a/drivers/scst/scst_lat_stats.c b/drivers/scst/scst_lat_stats.c
new file mode 100644
index 0000000..0bee2b9
--- /dev/null
+++ b/drivers/scst/scst_lat_stats.c
@@ -0,0 +1,435 @@
+/*
+ *  scst_lat_stats.c
+ *
+ *  Copyright (C) 2009 - 2010 Vladislav Bolkhovitin <vst@xxxxxxxx>
+ *  Copyright (C) 2010 Bart Van Assche <bvanassche@xxxxxxx>
+ *
+ *  This program is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU General Public License
+ *  as published by the Free Software Foundation, version 2
+ *  of the License.
+ *
+ *  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.
+ */
+
+#include <linux/debugfs.h>
+#include <linux/list.h>
+#include <linux/types.h>
+#include <scst/scst.h>
+#include <scst/scst_debug.h>
+#include "scst_priv.h"
+#include "scst_lat_stats.h"
+
+/* Per LUN latency statistics. */
+
+static char *scst_io_size_names[] = {
+	"<=8K  ",
+	"<=32K ",
+	"<=128K",
+	"<=512K",
+	">512K "
+};
+
+static ssize_t scst_tgt_dev_latency_show(struct scst_tgt_dev *tgt_dev,
+					 char *buffer)
+{
+	int res, i;
+	char buf[50];
+
+	res = 0;
+	for (i = 0; i < SCST_LATENCY_STATS_NUM; i++) {
+		uint64_t scst_time_wr, tgt_time_wr, dev_time_wr;
+		unsigned int processed_cmds_wr;
+		uint64_t scst_time_rd, tgt_time_rd, dev_time_rd;
+		unsigned int processed_cmds_rd;
+		struct scst_ext_latency_stat *latency_stat;
+
+		latency_stat = &tgt_dev->dev_latency_stat[i];
+		scst_time_wr = latency_stat->scst_time_wr;
+		scst_time_rd = latency_stat->scst_time_rd;
+		tgt_time_wr = latency_stat->tgt_time_wr;
+		tgt_time_rd = latency_stat->tgt_time_rd;
+		dev_time_wr = latency_stat->dev_time_wr;
+		dev_time_rd = latency_stat->dev_time_rd;
+		processed_cmds_wr = latency_stat->processed_cmds_wr;
+		processed_cmds_rd = latency_stat->processed_cmds_rd;
+
+		res += scnprintf(&buffer[res], PAGE_SIZE - res,
+			 "%-5s %-9s %-15lu ", "Write", scst_io_size_names[i],
+			(unsigned long)processed_cmds_wr);
+		if (processed_cmds_wr == 0)
+			processed_cmds_wr = 1;
+
+		do_div(scst_time_wr, processed_cmds_wr);
+		snprintf(buf, sizeof(buf), "%lu/%lu/%lu/%lu",
+			(unsigned long)latency_stat->min_scst_time_wr,
+			(unsigned long)scst_time_wr,
+			(unsigned long)latency_stat->max_scst_time_wr,
+			(unsigned long)latency_stat->scst_time_wr);
+		res += scnprintf(&buffer[res], PAGE_SIZE - res, "%-47s", buf);
+
+		do_div(tgt_time_wr, processed_cmds_wr);
+		snprintf(buf, sizeof(buf), "%lu/%lu/%lu/%lu",
+			(unsigned long)latency_stat->min_tgt_time_wr,
+			(unsigned long)tgt_time_wr,
+			(unsigned long)latency_stat->max_tgt_time_wr,
+			(unsigned long)latency_stat->tgt_time_wr);
+		res += scnprintf(&buffer[res], PAGE_SIZE - res, "%-47s", buf);
+
+		do_div(dev_time_wr, processed_cmds_wr);
+		snprintf(buf, sizeof(buf), "%lu/%lu/%lu/%lu",
+			(unsigned long)latency_stat->min_dev_time_wr,
+			(unsigned long)dev_time_wr,
+			(unsigned long)latency_stat->max_dev_time_wr,
+			(unsigned long)latency_stat->dev_time_wr);
+		res += scnprintf(&buffer[res], PAGE_SIZE - res, "%-47s\n", buf);
+
+		res += scnprintf(&buffer[res], PAGE_SIZE - res,
+			"%-5s %-9s %-15lu ", "Read", scst_io_size_names[i],
+			(unsigned long)processed_cmds_rd);
+		if (processed_cmds_rd == 0)
+			processed_cmds_rd = 1;
+
+		do_div(scst_time_rd, processed_cmds_rd);
+		snprintf(buf, sizeof(buf), "%lu/%lu/%lu/%lu",
+			(unsigned long)latency_stat->min_scst_time_rd,
+			(unsigned long)scst_time_rd,
+			(unsigned long)latency_stat->max_scst_time_rd,
+			(unsigned long)latency_stat->scst_time_rd);
+		res += scnprintf(&buffer[res], PAGE_SIZE - res, "%-47s", buf);
+
+		do_div(tgt_time_rd, processed_cmds_rd);
+		snprintf(buf, sizeof(buf), "%lu/%lu/%lu/%lu",
+			(unsigned long)latency_stat->min_tgt_time_rd,
+			(unsigned long)tgt_time_rd,
+			(unsigned long)latency_stat->max_tgt_time_rd,
+			(unsigned long)latency_stat->tgt_time_rd);
+		res += scnprintf(&buffer[res], PAGE_SIZE - res, "%-47s", buf);
+
+		do_div(dev_time_rd, processed_cmds_rd);
+		snprintf(buf, sizeof(buf), "%lu/%lu/%lu/%lu",
+			(unsigned long)latency_stat->min_dev_time_rd,
+			(unsigned long)dev_time_rd,
+			(unsigned long)latency_stat->max_dev_time_rd,
+			(unsigned long)latency_stat->dev_time_rd);
+		res += scnprintf(&buffer[res], PAGE_SIZE - res, "%-47s\n", buf);
+	}
+	return res;
+}
+
+static int scst_debugfs_open(struct inode *inode, struct file *file)
+{
+	if (inode->i_private)
+		file->private_data = inode->i_private;
+
+	return 0;
+}
+
+static ssize_t tgt_dev_lat_read_file(struct file *file, char __user *buf,
+				      size_t count, loff_t *ppos)
+{
+	struct scst_tgt_dev *tgt_dev = file->private_data;
+	unsigned long pg;
+	void *contents;
+	int len;
+	ssize_t res;
+
+	if (*ppos > PAGE_SIZE)
+		return -EINVAL;
+	pg = __get_free_page(GFP_KERNEL);
+	if (!pg)
+		return -ENOMEM;
+	contents = (void *)pg;
+	len = scst_tgt_dev_latency_show(tgt_dev, contents);
+	free_page(pg);
+	res = min_t(ssize_t, count, len - *ppos);
+	if (copy_to_user(buf, contents + *ppos, res))
+		return -EFAULT;
+	*ppos += res;
+	return res;
+}
+
+static ssize_t tgt_dev_lat_write_file(struct file *file,
+				       const char __user *buf,
+				       size_t count, loff_t *ppos)
+{
+	return -EINVAL;
+}
+
+static const struct file_operations tgt_dev_lat_fops = {
+	.read	=	tgt_dev_lat_read_file,
+	.write	=	tgt_dev_lat_write_file,
+	.open	=	scst_debugfs_open,
+	.llseek	=	noop_llseek,
+};
+
+int scst_tgt_dev_lat_create(struct scst_tgt_dev *tgt_dev)
+{
+	tgt_dev->latency_file = debugfs_create_file("latency",
+				    S_IRUGO | S_IWUSR, tgt_dev->debugfs_dir,
+				    tgt_dev, &tgt_dev_lat_fops);
+	return tgt_dev->latency_file ? 0 : -ENOMEM;
+}
+
+void scst_tgt_dev_lat_remove(struct scst_tgt_dev *tgt_dev)
+{
+	debugfs_remove(tgt_dev->latency_file);
+	tgt_dev->latency_file = NULL;
+}
+
+/* Per session latency statistics. */
+
+static ssize_t scst_sess_latency_show(struct scst_session *sess, char *buffer)
+{
+	ssize_t res;
+	int i;
+	char buf[50];
+	uint64_t scst_time, tgt_time, dev_time;
+	unsigned int processed_cmds;
+
+	res = 0;
+	res += scnprintf(&buffer[res], PAGE_SIZE - res,
+		"%-15s %-15s %-46s %-46s %-46s\n",
+		"T-L names", "Total commands", "SCST latency",
+		"Target latency", "Dev latency (min/avg/max/all ns)");
+
+	spin_lock_bh(&sess->lat_lock);
+
+	for (i = 0; i < SCST_LATENCY_STATS_NUM ; i++) {
+		uint64_t scst_time_wr, tgt_time_wr, dev_time_wr;
+		unsigned int processed_cmds_wr;
+		uint64_t scst_time_rd, tgt_time_rd, dev_time_rd;
+		unsigned int processed_cmds_rd;
+		struct scst_ext_latency_stat *latency_stat;
+
+		latency_stat = &sess->sess_latency_stat[i];
+		scst_time_wr = latency_stat->scst_time_wr;
+		scst_time_rd = latency_stat->scst_time_rd;
+		tgt_time_wr = latency_stat->tgt_time_wr;
+		tgt_time_rd = latency_stat->tgt_time_rd;
+		dev_time_wr = latency_stat->dev_time_wr;
+		dev_time_rd = latency_stat->dev_time_rd;
+		processed_cmds_wr = latency_stat->processed_cmds_wr;
+		processed_cmds_rd = latency_stat->processed_cmds_rd;
+
+		res += scnprintf(&buffer[res], PAGE_SIZE - res,
+			"%-5s %-9s %-15lu ",
+			"Write", scst_io_size_names[i],
+			(unsigned long)processed_cmds_wr);
+		if (processed_cmds_wr == 0)
+			processed_cmds_wr = 1;
+
+		do_div(scst_time_wr, processed_cmds_wr);
+		snprintf(buf, sizeof(buf), "%lu/%lu/%lu/%lu",
+			(unsigned long)latency_stat->min_scst_time_wr,
+			(unsigned long)scst_time_wr,
+			(unsigned long)latency_stat->max_scst_time_wr,
+			(unsigned long)latency_stat->scst_time_wr);
+		res += scnprintf(&buffer[res], PAGE_SIZE - res,
+			"%-47s", buf);
+
+		do_div(tgt_time_wr, processed_cmds_wr);
+		snprintf(buf, sizeof(buf), "%lu/%lu/%lu/%lu",
+			(unsigned long)latency_stat->min_tgt_time_wr,
+			(unsigned long)tgt_time_wr,
+			(unsigned long)latency_stat->max_tgt_time_wr,
+			(unsigned long)latency_stat->tgt_time_wr);
+		res += scnprintf(&buffer[res], PAGE_SIZE - res,
+			"%-47s", buf);
+
+		do_div(dev_time_wr, processed_cmds_wr);
+		snprintf(buf, sizeof(buf), "%lu/%lu/%lu/%lu",
+			(unsigned long)latency_stat->min_dev_time_wr,
+			(unsigned long)dev_time_wr,
+			(unsigned long)latency_stat->max_dev_time_wr,
+			(unsigned long)latency_stat->dev_time_wr);
+		res += scnprintf(&buffer[res], PAGE_SIZE - res,
+			"%-47s\n", buf);
+
+		res += scnprintf(&buffer[res], PAGE_SIZE - res,
+			"%-5s %-9s %-15lu ",
+			"Read", scst_io_size_names[i],
+			(unsigned long)processed_cmds_rd);
+		if (processed_cmds_rd == 0)
+			processed_cmds_rd = 1;
+
+		do_div(scst_time_rd, processed_cmds_rd);
+		snprintf(buf, sizeof(buf), "%lu/%lu/%lu/%lu",
+			(unsigned long)latency_stat->min_scst_time_rd,
+			(unsigned long)scst_time_rd,
+			(unsigned long)latency_stat->max_scst_time_rd,
+			(unsigned long)latency_stat->scst_time_rd);
+		res += scnprintf(&buffer[res], PAGE_SIZE - res,
+			"%-47s", buf);
+
+		do_div(tgt_time_rd, processed_cmds_rd);
+		snprintf(buf, sizeof(buf), "%lu/%lu/%lu/%lu",
+			(unsigned long)latency_stat->min_tgt_time_rd,
+			(unsigned long)tgt_time_rd,
+			(unsigned long)latency_stat->max_tgt_time_rd,
+			(unsigned long)latency_stat->tgt_time_rd);
+		res += scnprintf(&buffer[res], PAGE_SIZE - res,
+			"%-47s", buf);
+
+		do_div(dev_time_rd, processed_cmds_rd);
+		snprintf(buf, sizeof(buf), "%lu/%lu/%lu/%lu",
+			(unsigned long)latency_stat->min_dev_time_rd,
+			(unsigned long)dev_time_rd,
+			(unsigned long)latency_stat->max_dev_time_rd,
+			(unsigned long)latency_stat->dev_time_rd);
+		res += scnprintf(&buffer[res], PAGE_SIZE - res,
+			"%-47s\n", buf);
+	}
+
+	scst_time = sess->scst_time;
+	tgt_time = sess->tgt_time;
+	dev_time = sess->dev_time;
+	processed_cmds = sess->processed_cmds;
+
+	res += scnprintf(&buffer[res], PAGE_SIZE - res,
+		"\n%-15s %-16d", "Overall ", processed_cmds);
+
+	if (processed_cmds == 0)
+		processed_cmds = 1;
+
+	do_div(scst_time, processed_cmds);
+	snprintf(buf, sizeof(buf), "%lu/%lu/%lu/%lu",
+		(unsigned long)sess->min_scst_time,
+		(unsigned long)scst_time,
+		(unsigned long)sess->max_scst_time,
+		(unsigned long)sess->scst_time);
+	res += scnprintf(&buffer[res], PAGE_SIZE - res,
+		"%-47s", buf);
+
+	do_div(tgt_time, processed_cmds);
+	snprintf(buf, sizeof(buf), "%lu/%lu/%lu/%lu",
+		(unsigned long)sess->min_tgt_time,
+		(unsigned long)tgt_time,
+		(unsigned long)sess->max_tgt_time,
+		(unsigned long)sess->tgt_time);
+	res += scnprintf(&buffer[res], PAGE_SIZE - res,
+		"%-47s", buf);
+
+	do_div(dev_time, processed_cmds);
+	snprintf(buf, sizeof(buf), "%lu/%lu/%lu/%lu",
+		(unsigned long)sess->min_dev_time,
+		(unsigned long)dev_time,
+		(unsigned long)sess->max_dev_time,
+		(unsigned long)sess->dev_time);
+	res += scnprintf(&buffer[res], PAGE_SIZE - res,
+		"%-47s\n\n", buf);
+
+	spin_unlock_bh(&sess->lat_lock);
+	return res;
+}
+
+static int scst_sess_zero_latency(struct scst_session *sess)
+{
+	int res, t;
+
+	res = mutex_lock_interruptible(&scst_mutex);
+	if (res)
+		goto out;
+
+	PRINT_INFO("Zeroing latency statistics for initiator "
+		"%s", sess->initiator_name);
+
+	spin_lock_bh(&sess->lat_lock);
+
+	sess->scst_time = 0;
+	sess->tgt_time = 0;
+	sess->dev_time = 0;
+	sess->min_scst_time = 0;
+	sess->min_tgt_time = 0;
+	sess->min_dev_time = 0;
+	sess->max_scst_time = 0;
+	sess->max_tgt_time = 0;
+	sess->max_dev_time = 0;
+	sess->processed_cmds = 0;
+	memset(sess->sess_latency_stat, 0,
+		sizeof(sess->sess_latency_stat));
+
+	for (t = SESS_TGT_DEV_LIST_HASH_SIZE-1; t >= 0; t--) {
+		struct list_head *head = &sess->sess_tgt_dev_list[t];
+		struct scst_tgt_dev *tgt_dev;
+		list_for_each_entry(tgt_dev, head, sess_tgt_dev_list_entry) {
+			tgt_dev->scst_time = 0;
+			tgt_dev->tgt_time = 0;
+			tgt_dev->dev_time = 0;
+			tgt_dev->processed_cmds = 0;
+			memset(tgt_dev->dev_latency_stat, 0,
+				sizeof(tgt_dev->dev_latency_stat));
+		}
+	}
+
+	spin_unlock_bh(&sess->lat_lock);
+
+	mutex_unlock(&scst_mutex);
+
+out:
+	return res;
+}
+
+static ssize_t sess_lat_read_file(struct file *file, char __user *buf,
+				      size_t count, loff_t *ppos)
+{
+	struct scst_session *sess = file->private_data;
+	unsigned long pg;
+	void *contents;
+	int len;
+	ssize_t res;
+
+	if (*ppos > PAGE_SIZE)
+		return -EINVAL;
+	pg = __get_free_page(GFP_KERNEL);
+	if (!pg)
+		return -ENOMEM;
+	contents = (void *)pg;
+	len = scst_sess_latency_show(sess, contents);
+	free_page(pg);
+	res = min_t(ssize_t, count, len - *ppos);
+	if (copy_to_user(buf, contents + *ppos, res))
+		return -EFAULT;
+	*ppos += res;
+	return res;
+}
+
+static ssize_t sess_lat_write_file(struct file *file,
+				       const char __user *buf,
+				       size_t count, loff_t *ppos)
+{
+	struct scst_session *sess = file->private_data;
+	int res;
+
+	res = scst_sess_zero_latency(sess);
+	if (res)
+		goto out;
+	*ppos += count;
+	res = count;
+out:
+	return res;
+}
+
+static const struct file_operations sess_lat_fops = {
+	.read	=	sess_lat_read_file,
+	.write	=	sess_lat_write_file,
+	.open	=	scst_debugfs_open,
+	.llseek	=	noop_llseek,
+};
+
+int scst_sess_lat_create(struct scst_session *sess)
+{
+	sess->latency_file = debugfs_create_file("latency",
+				    S_IRUGO | S_IWUSR, sess->debugfs_dir,
+				    sess, &sess_lat_fops);
+	return sess->latency_file ? 0 : -ENOMEM;
+}
+
+void scst_sess_lat_remove(struct scst_session *sess)
+{
+	debugfs_remove(sess->latency_file);
+	sess->latency_file = NULL;
+}
diff --git a/drivers/scst/scst_lat_stats.h b/drivers/scst/scst_lat_stats.h
new file mode 100644
index 0000000..50c89ee
--- /dev/null
+++ b/drivers/scst/scst_lat_stats.h
@@ -0,0 +1,28 @@
+#if defined(CONFIG_SCST_MEASURE_LATENCY)
+
+int scst_tgt_dev_lat_create(struct scst_tgt_dev *tgt_dev);
+void scst_tgt_dev_lat_remove(struct scst_tgt_dev *tgt_dev);
+int scst_sess_lat_create(struct scst_session *sess);
+void scst_sess_lat_remove(struct scst_session *sess);
+
+#else /*defined(CONFIG_SCST_MEASURE_LATENCY)*/
+
+static inline int scst_tgt_dev_lat_create(struct scst_tgt_dev *tgt_dev)
+{
+	return 0;
+}
+
+static inline void scst_tgt_dev_lat_remove(struct scst_tgt_dev *tgt_dev)
+{
+}
+
+static inline int scst_sess_lat_create(struct scst_session *sess)
+{
+	return 0;
+}
+
+static inline void scst_sess_lat_remove(struct scst_session *sess)
+{
+}
+
+#endif /*defined(CONFIG_SCST_MEASURE_LATENCY)*/
diff --git a/drivers/scst/scst_sysfs.c b/drivers/scst/scst_sysfs.c
index 84dc9ee..c9c4c28 100644
--- a/drivers/scst/scst_sysfs.c
+++ b/drivers/scst/scst_sysfs.c
@@ -46,6 +46,7 @@
 #include "scst_priv.h"
 #include "scst_mem.h"
 #include "scst_tracing.h"
+#include "scst_lat_stats.h"
 
 enum mgmt_path_type {
 	PATH_NOT_RECOGNIZED,
@@ -442,7 +443,7 @@ int scst_tgtt_sysfs_create(struct scst_tgt_template *tgtt)
 		}
 	}
 
-	res = scst_tgtt_create_trace_files(tgtt);
+	res = scst_tgtt_debugfs_create(tgtt);
 	if (res) {
 		PRINT_ERROR("Can't create tracing files for target driver %s",
 			    tgtt->name);
@@ -459,7 +460,7 @@ out_del:
 
 void scst_tgtt_sysfs_del(struct scst_tgt_template *tgtt)
 {
-	scst_tgtt_remove_trace_files(tgtt);
+	scst_tgtt_debugfs_remove(tgtt);
 	driver_unregister(&tgtt->tgtt_drv);
 }
 
@@ -1049,6 +1050,10 @@ int scst_tgt_sysfs_create(struct scst_tgt *tgt)
 		}
 	}
 
+	res = scst_tgt_debugfs_create(tgt);
+	if (res)
+		goto out_err;
+
 out:
 	return res;
 
@@ -1062,6 +1067,7 @@ out_err:
 
 void scst_tgt_sysfs_del(struct scst_tgt *tgt)
 {
+	scst_tgt_debugfs_remove(tgt);
 	kobject_del(tgt->tgt_sess_kobj);
 	kobject_del(tgt->tgt_luns_kobj);
 	kobject_del(tgt->tgt_ini_grp_kobj);
@@ -1502,112 +1508,6 @@ void scst_dev_sysfs_put(struct scst_device *dev)
  ** Tgt_dev implementation
  **/
 
-#ifdef CONFIG_SCST_MEASURE_LATENCY
-
-static char *scst_io_size_names[] = {
-	"<=8K  ",
-	"<=32K ",
-	"<=128K",
-	"<=512K",
-	">512K "
-};
-
-static ssize_t scst_tgt_dev_latency_show(struct kobject *kobj,
-	struct kobj_attribute *attr, char *buffer)
-{
-	int res, i;
-	char buf[50];
-	struct scst_tgt_dev *tgt_dev;
-
-	tgt_dev = scst_kobj_to_tgt_dev(kobj);
-
-	res = 0;
-	for (i = 0; i < SCST_LATENCY_STATS_NUM; i++) {
-		uint64_t scst_time_wr, tgt_time_wr, dev_time_wr;
-		unsigned int processed_cmds_wr;
-		uint64_t scst_time_rd, tgt_time_rd, dev_time_rd;
-		unsigned int processed_cmds_rd;
-		struct scst_ext_latency_stat *latency_stat;
-
-		latency_stat = &tgt_dev->dev_latency_stat[i];
-		scst_time_wr = latency_stat->scst_time_wr;
-		scst_time_rd = latency_stat->scst_time_rd;
-		tgt_time_wr = latency_stat->tgt_time_wr;
-		tgt_time_rd = latency_stat->tgt_time_rd;
-		dev_time_wr = latency_stat->dev_time_wr;
-		dev_time_rd = latency_stat->dev_time_rd;
-		processed_cmds_wr = latency_stat->processed_cmds_wr;
-		processed_cmds_rd = latency_stat->processed_cmds_rd;
-
-		res += scnprintf(&buffer[res], PAGE_SIZE - res,
-			 "%-5s %-9s %-15lu ", "Write", scst_io_size_names[i],
-			(unsigned long)processed_cmds_wr);
-		if (processed_cmds_wr == 0)
-			processed_cmds_wr = 1;
-
-		do_div(scst_time_wr, processed_cmds_wr);
-		snprintf(buf, sizeof(buf), "%lu/%lu/%lu/%lu",
-			(unsigned long)latency_stat->min_scst_time_wr,
-			(unsigned long)scst_time_wr,
-			(unsigned long)latency_stat->max_scst_time_wr,
-			(unsigned long)latency_stat->scst_time_wr);
-		res += scnprintf(&buffer[res], PAGE_SIZE - res, "%-47s", buf);
-
-		do_div(tgt_time_wr, processed_cmds_wr);
-		snprintf(buf, sizeof(buf), "%lu/%lu/%lu/%lu",
-			(unsigned long)latency_stat->min_tgt_time_wr,
-			(unsigned long)tgt_time_wr,
-			(unsigned long)latency_stat->max_tgt_time_wr,
-			(unsigned long)latency_stat->tgt_time_wr);
-		res += scnprintf(&buffer[res], PAGE_SIZE - res, "%-47s", buf);
-
-		do_div(dev_time_wr, processed_cmds_wr);
-		snprintf(buf, sizeof(buf), "%lu/%lu/%lu/%lu",
-			(unsigned long)latency_stat->min_dev_time_wr,
-			(unsigned long)dev_time_wr,
-			(unsigned long)latency_stat->max_dev_time_wr,
-			(unsigned long)latency_stat->dev_time_wr);
-		res += scnprintf(&buffer[res], PAGE_SIZE - res, "%-47s\n", buf);
-
-		res += scnprintf(&buffer[res], PAGE_SIZE - res,
-			"%-5s %-9s %-15lu ", "Read", scst_io_size_names[i],
-			(unsigned long)processed_cmds_rd);
-		if (processed_cmds_rd == 0)
-			processed_cmds_rd = 1;
-
-		do_div(scst_time_rd, processed_cmds_rd);
-		snprintf(buf, sizeof(buf), "%lu/%lu/%lu/%lu",
-			(unsigned long)latency_stat->min_scst_time_rd,
-			(unsigned long)scst_time_rd,
-			(unsigned long)latency_stat->max_scst_time_rd,
-			(unsigned long)latency_stat->scst_time_rd);
-		res += scnprintf(&buffer[res], PAGE_SIZE - res, "%-47s", buf);
-
-		do_div(tgt_time_rd, processed_cmds_rd);
-		snprintf(buf, sizeof(buf), "%lu/%lu/%lu/%lu",
-			(unsigned long)latency_stat->min_tgt_time_rd,
-			(unsigned long)tgt_time_rd,
-			(unsigned long)latency_stat->max_tgt_time_rd,
-			(unsigned long)latency_stat->tgt_time_rd);
-		res += scnprintf(&buffer[res], PAGE_SIZE - res, "%-47s", buf);
-
-		do_div(dev_time_rd, processed_cmds_rd);
-		snprintf(buf, sizeof(buf), "%lu/%lu/%lu/%lu",
-			(unsigned long)latency_stat->min_dev_time_rd,
-			(unsigned long)dev_time_rd,
-			(unsigned long)latency_stat->max_dev_time_rd,
-			(unsigned long)latency_stat->dev_time_rd);
-		res += scnprintf(&buffer[res], PAGE_SIZE - res, "%-47s\n", buf);
-	}
-	return res;
-}
-
-static struct kobj_attribute tgt_dev_latency_attr =
-	__ATTR(latency, S_IRUGO,
-		scst_tgt_dev_latency_show, NULL);
-
-#endif /* CONFIG_SCST_MEASURE_LATENCY */
-
 static ssize_t scst_tgt_dev_active_commands_show(struct kobject *kobj,
 			    struct kobj_attribute *attr, char *buf)
 {
@@ -1627,9 +1527,6 @@ static struct kobj_attribute tgt_dev_active_commands_attr =
 
 struct attribute *scst_tgt_dev_attrs[] = {
 	&tgt_dev_active_commands_attr.attr,
-#ifdef CONFIG_SCST_MEASURE_LATENCY
-	&tgt_dev_latency_attr.attr,
-#endif
 	NULL,
 };
 
@@ -1645,12 +1542,24 @@ int scst_tgt_dev_sysfs_create(struct scst_tgt_dev *tgt_dev)
 		goto out;
 	}
 
+	res = scst_tgt_dev_debugfs_create(tgt_dev);
+	if (res)
+		goto err;
+
+	res = scst_tgt_dev_lat_create(tgt_dev);
+	if (res)
+		goto err;
 out:
 	return res;
+err:
+	scst_tgt_dev_sysfs_del(tgt_dev);
+	goto out;
 }
 
 void scst_tgt_dev_sysfs_del(struct scst_tgt_dev *tgt_dev)
 {
+	scst_tgt_dev_lat_remove(tgt_dev);
+	scst_tgt_dev_debugfs_remove(tgt_dev);
 	kobject_del(&tgt_dev->tgt_dev_kobj);
 }
 
@@ -1658,223 +1567,6 @@ void scst_tgt_dev_sysfs_del(struct scst_tgt_dev *tgt_dev)
  ** Sessions subdirectory implementation
  **/
 
-#ifdef CONFIG_SCST_MEASURE_LATENCY
-
-static ssize_t scst_sess_latency_show(struct kobject *kobj,
-	struct kobj_attribute *attr, char *buffer)
-{
-	ssize_t res;
-	struct scst_session *sess;
-	int i;
-	char buf[50];
-	uint64_t scst_time, tgt_time, dev_time;
-	unsigned int processed_cmds;
-
-	sess = scst_kobj_to_sess(kobj);
-
-	res = 0;
-	res += scnprintf(&buffer[res], PAGE_SIZE - res,
-		"%-15s %-15s %-46s %-46s %-46s\n",
-		"T-L names", "Total commands", "SCST latency",
-		"Target latency", "Dev latency (min/avg/max/all ns)");
-
-	spin_lock_bh(&sess->lat_lock);
-
-	for (i = 0; i < SCST_LATENCY_STATS_NUM ; i++) {
-		uint64_t scst_time_wr, tgt_time_wr, dev_time_wr;
-		unsigned int processed_cmds_wr;
-		uint64_t scst_time_rd, tgt_time_rd, dev_time_rd;
-		unsigned int processed_cmds_rd;
-		struct scst_ext_latency_stat *latency_stat;
-
-		latency_stat = &sess->sess_latency_stat[i];
-		scst_time_wr = latency_stat->scst_time_wr;
-		scst_time_rd = latency_stat->scst_time_rd;
-		tgt_time_wr = latency_stat->tgt_time_wr;
-		tgt_time_rd = latency_stat->tgt_time_rd;
-		dev_time_wr = latency_stat->dev_time_wr;
-		dev_time_rd = latency_stat->dev_time_rd;
-		processed_cmds_wr = latency_stat->processed_cmds_wr;
-		processed_cmds_rd = latency_stat->processed_cmds_rd;
-
-		res += scnprintf(&buffer[res], PAGE_SIZE - res,
-			"%-5s %-9s %-15lu ",
-			"Write", scst_io_size_names[i],
-			(unsigned long)processed_cmds_wr);
-		if (processed_cmds_wr == 0)
-			processed_cmds_wr = 1;
-
-		do_div(scst_time_wr, processed_cmds_wr);
-		snprintf(buf, sizeof(buf), "%lu/%lu/%lu/%lu",
-			(unsigned long)latency_stat->min_scst_time_wr,
-			(unsigned long)scst_time_wr,
-			(unsigned long)latency_stat->max_scst_time_wr,
-			(unsigned long)latency_stat->scst_time_wr);
-		res += scnprintf(&buffer[res], PAGE_SIZE - res,
-			"%-47s", buf);
-
-		do_div(tgt_time_wr, processed_cmds_wr);
-		snprintf(buf, sizeof(buf), "%lu/%lu/%lu/%lu",
-			(unsigned long)latency_stat->min_tgt_time_wr,
-			(unsigned long)tgt_time_wr,
-			(unsigned long)latency_stat->max_tgt_time_wr,
-			(unsigned long)latency_stat->tgt_time_wr);
-		res += scnprintf(&buffer[res], PAGE_SIZE - res,
-			"%-47s", buf);
-
-		do_div(dev_time_wr, processed_cmds_wr);
-		snprintf(buf, sizeof(buf), "%lu/%lu/%lu/%lu",
-			(unsigned long)latency_stat->min_dev_time_wr,
-			(unsigned long)dev_time_wr,
-			(unsigned long)latency_stat->max_dev_time_wr,
-			(unsigned long)latency_stat->dev_time_wr);
-		res += scnprintf(&buffer[res], PAGE_SIZE - res,
-			"%-47s\n", buf);
-
-		res += scnprintf(&buffer[res], PAGE_SIZE - res,
-			"%-5s %-9s %-15lu ",
-			"Read", scst_io_size_names[i],
-			(unsigned long)processed_cmds_rd);
-		if (processed_cmds_rd == 0)
-			processed_cmds_rd = 1;
-
-		do_div(scst_time_rd, processed_cmds_rd);
-		snprintf(buf, sizeof(buf), "%lu/%lu/%lu/%lu",
-			(unsigned long)latency_stat->min_scst_time_rd,
-			(unsigned long)scst_time_rd,
-			(unsigned long)latency_stat->max_scst_time_rd,
-			(unsigned long)latency_stat->scst_time_rd);
-		res += scnprintf(&buffer[res], PAGE_SIZE - res,
-			"%-47s", buf);
-
-		do_div(tgt_time_rd, processed_cmds_rd);
-		snprintf(buf, sizeof(buf), "%lu/%lu/%lu/%lu",
-			(unsigned long)latency_stat->min_tgt_time_rd,
-			(unsigned long)tgt_time_rd,
-			(unsigned long)latency_stat->max_tgt_time_rd,
-			(unsigned long)latency_stat->tgt_time_rd);
-		res += scnprintf(&buffer[res], PAGE_SIZE - res,
-			"%-47s", buf);
-
-		do_div(dev_time_rd, processed_cmds_rd);
-		snprintf(buf, sizeof(buf), "%lu/%lu/%lu/%lu",
-			(unsigned long)latency_stat->min_dev_time_rd,
-			(unsigned long)dev_time_rd,
-			(unsigned long)latency_stat->max_dev_time_rd,
-			(unsigned long)latency_stat->dev_time_rd);
-		res += scnprintf(&buffer[res], PAGE_SIZE - res,
-			"%-47s\n", buf);
-	}
-
-	scst_time = sess->scst_time;
-	tgt_time = sess->tgt_time;
-	dev_time = sess->dev_time;
-	processed_cmds = sess->processed_cmds;
-
-	res += scnprintf(&buffer[res], PAGE_SIZE - res,
-		"\n%-15s %-16d", "Overall ", processed_cmds);
-
-	if (processed_cmds == 0)
-		processed_cmds = 1;
-
-	do_div(scst_time, processed_cmds);
-	snprintf(buf, sizeof(buf), "%lu/%lu/%lu/%lu",
-		(unsigned long)sess->min_scst_time,
-		(unsigned long)scst_time,
-		(unsigned long)sess->max_scst_time,
-		(unsigned long)sess->scst_time);
-	res += scnprintf(&buffer[res], PAGE_SIZE - res,
-		"%-47s", buf);
-
-	do_div(tgt_time, processed_cmds);
-	snprintf(buf, sizeof(buf), "%lu/%lu/%lu/%lu",
-		(unsigned long)sess->min_tgt_time,
-		(unsigned long)tgt_time,
-		(unsigned long)sess->max_tgt_time,
-		(unsigned long)sess->tgt_time);
-	res += scnprintf(&buffer[res], PAGE_SIZE - res,
-		"%-47s", buf);
-
-	do_div(dev_time, processed_cmds);
-	snprintf(buf, sizeof(buf), "%lu/%lu/%lu/%lu",
-		(unsigned long)sess->min_dev_time,
-		(unsigned long)dev_time,
-		(unsigned long)sess->max_dev_time,
-		(unsigned long)sess->dev_time);
-	res += scnprintf(&buffer[res], PAGE_SIZE - res,
-		"%-47s\n\n", buf);
-
-	spin_unlock_bh(&sess->lat_lock);
-	return res;
-}
-
-static int scst_sess_zero_latency(struct scst_session *sess)
-{
-	int res, t;
-
-	res = mutex_lock_interruptible(&scst_mutex);
-	if (res != 0)
-		goto out;
-
-	PRINT_INFO("Zeroing latency statistics for initiator "
-		"%s", sess->initiator_name);
-
-	spin_lock_bh(&sess->lat_lock);
-
-	sess->scst_time = 0;
-	sess->tgt_time = 0;
-	sess->dev_time = 0;
-	sess->min_scst_time = 0;
-	sess->min_tgt_time = 0;
-	sess->min_dev_time = 0;
-	sess->max_scst_time = 0;
-	sess->max_tgt_time = 0;
-	sess->max_dev_time = 0;
-	sess->processed_cmds = 0;
-	memset(sess->sess_latency_stat, 0,
-		sizeof(sess->sess_latency_stat));
-
-	for (t = SESS_TGT_DEV_LIST_HASH_SIZE-1; t >= 0; t--) {
-		struct list_head *head = &sess->sess_tgt_dev_list[t];
-		struct scst_tgt_dev *tgt_dev;
-		list_for_each_entry(tgt_dev, head, sess_tgt_dev_list_entry) {
-			tgt_dev->scst_time = 0;
-			tgt_dev->tgt_time = 0;
-			tgt_dev->dev_time = 0;
-			tgt_dev->processed_cmds = 0;
-			memset(tgt_dev->dev_latency_stat, 0,
-				sizeof(tgt_dev->dev_latency_stat));
-		}
-	}
-
-	spin_unlock_bh(&sess->lat_lock);
-
-	mutex_unlock(&scst_mutex);
-
-out:
-	return res;
-}
-
-static ssize_t scst_sess_latency_store(struct kobject *kobj,
-	struct kobj_attribute *attr, const char *buf, size_t count)
-{
-	int res;
-	struct scst_session *sess;
-
-	sess = scst_kobj_to_sess(kobj);
-
-	res = scst_sess_zero_latency(sess);
-	if (res == 0)
-		res = count;
-	return res;
-}
-
-static struct kobj_attribute session_latency_attr =
-	__ATTR(latency, S_IRUGO | S_IWUSR, scst_sess_latency_show,
-	       scst_sess_latency_store);
-
-#endif /* CONFIG_SCST_MEASURE_LATENCY */
-
 static ssize_t scst_sess_sysfs_commands_show(struct kobject *kobj,
 			    struct kobj_attribute *attr, char *buf)
 {
@@ -1949,9 +1641,6 @@ struct attribute *scst_session_attrs[] = {
 	&session_commands_attr.attr,
 	&session_active_commands_attr.attr,
 	&session_initiator_name_attr.attr,
-#ifdef CONFIG_SCST_MEASURE_LATENCY
-	&session_latency_attr.attr,
-#endif /* CONFIG_SCST_MEASURE_LATENCY */
 	NULL,
 };
 
@@ -2010,6 +1699,14 @@ int scst_sess_sysfs_create(struct scst_session *sess)
 	if (res)
 		goto out_free;
 
+	res = scst_sess_debugfs_create(sess);
+	if (res)
+		goto out_free;
+
+	res = scst_sess_lat_create(sess);
+	if (res)
+		goto out_free;
+
 out:
 	return res;
 out_free:
@@ -2019,6 +1716,8 @@ out_free:
 
 void scst_sess_sysfs_del(struct scst_session *sess)
 {
+	scst_sess_lat_remove(sess);
+	scst_sess_debugfs_remove(sess);
 	kobject_del(&sess->sess_kobj);
 }
 
@@ -3183,7 +2882,7 @@ int scst_devt_sysfs_create(struct scst_dev_type *devt)
 		}
 	}
 
-	res = scst_devt_create_trace_files(devt);
+	res = scst_devt_debugfs_create(devt);
 	if (res) {
 		PRINT_ERROR("Can't create tracing files for device type %s",
 			    devt->name);
@@ -3200,7 +2899,7 @@ out_err:
 
 void scst_devt_sysfs_del(struct scst_dev_type *devt)
 {
-	scst_devt_remove_trace_files(devt);
+	scst_devt_debugfs_remove(devt);
 }
 
 void scst_devt_sysfs_put(struct scst_dev_type *devt)
@@ -3854,7 +3553,7 @@ int __init scst_sysfs_init(void)
 		goto out_unregister_device;
 	}
 
-	res = scst_main_create_trace_files();
+	res = scst_main_debugfs_create();
 	if (res) {
 		PRINT_ERROR("%s", "Creating SCST trace files failed.");
 		goto out_remove_files;
@@ -3863,7 +3562,7 @@ int __init scst_sysfs_init(void)
 out:
 	return res;
 
-	scst_main_remove_trace_files();
+	scst_main_debugfs_remove();
 out_remove_files:
 	device_remove_files(scst_device, scst_root_default_attrs);
 out_unregister_device:
@@ -3884,7 +3583,7 @@ void scst_sysfs_cleanup(void)
 {
 	PRINT_INFO("%s", "Exiting SCST sysfs hierarchy...");
 
-	scst_main_remove_trace_files();
+	scst_main_debugfs_remove();
 
 	device_remove_files(scst_device, scst_root_default_attrs);
 
diff --git a/drivers/scst/scst_tracing.c b/drivers/scst/scst_tracing.c
index 9b9aef4..c87597b 100644
--- a/drivers/scst/scst_tracing.c
+++ b/drivers/scst/scst_tracing.c
@@ -24,7 +24,8 @@
 #include "scst_tracing.h"
 
 static struct dentry *scst_debug_root;
-static struct dentry *scst_debug_target;
+static struct dentry *scst_debug_tgtt;
+static struct dentry *scst_debug_tgt;
 static struct dentry *scst_debug_devt;
 static struct dentry *scst_debug_dev;
 static struct dentry *scst_main_tracing_dir;
@@ -167,29 +168,21 @@ static const struct file_operations scst_tracing_fops = {
 	.llseek	=	noop_llseek,
 };
 
-static struct dentry *scst_create_trace_files(struct dentry *root,
-					      const char *dir_name,
+static struct dentry *scst_create_trace_files(struct dentry *dir,
 					      struct scst_trace_data *td)
 {
 	int i;
-	struct dentry *subdir, *tracing_dir, *file;
+	struct dentry *tracing_dir, *file;
 	const struct scst_trace_log *p;
 	const struct scst_trace_log *tracing_table[] = {
 		scst_trace_tbl,
 		td->trace_tbl,
 	};
 
-	BUG_ON(!root);
-	BUG_ON(!dir_name);
+	BUG_ON(!dir);
 	BUG_ON(!td);
 
-	subdir = debugfs_create_dir(dir_name, root);
-	if (!subdir) {
-		PRINT_ERROR("Creation of directory %s failed", dir_name);
-		goto out;
-	}
-
-	tracing_dir = debugfs_create_dir("tracing", subdir);
+	tracing_dir = debugfs_create_dir("tracing", dir);
 	if (!tracing_dir) {
 		PRINT_ERROR("%s", "Creation of tracing dir failed");
 		goto err;
@@ -208,18 +201,13 @@ static struct dentry *scst_create_trace_files(struct dentry *root,
 		}
 	}
 out:
-	return subdir;
+	return tracing_dir;
 err:
-	debugfs_remove_recursive(subdir);
-	subdir = NULL;
+	debugfs_remove_recursive(tracing_dir);
+	tracing_dir = NULL;
 	goto out;
 }
 
-static void scst_remove_trace_files(struct dentry *dir)
-{
-	debugfs_remove_recursive(dir);
-}
-
 int scst_debugfs_init(void)
 {
 	int res;
@@ -238,8 +226,15 @@ int scst_debugfs_init(void)
 		goto err;
 	}
 
-	scst_debug_target = debugfs_create_dir("target", scst_debug_root);
-	if (!scst_debug_target) {
+	scst_debug_tgtt = debugfs_create_dir("target_driver", scst_debug_root);
+	if (!scst_debug_tgtt) {
+		PRINT_ERROR("%s", "Creation of /sys/kernel/debug/scst/"
+			    "target_driver failed");
+		goto err;
+	}
+
+	scst_debug_tgt = debugfs_create_dir("target", scst_debug_root);
+	if (!scst_debug_tgt) {
 		PRINT_ERROR("%s", "Creation of /sys/kernel/debug/scst/target"
 			    " failed");
 		goto err;
@@ -273,57 +268,164 @@ void scst_debugfs_cleanup(void)
 	debugfs_remove_recursive(scst_debug_root);
 	scst_debug_dev = NULL;
 	scst_debug_devt = NULL;
-	scst_debug_target = NULL;
+	scst_debug_tgt = NULL;
+	scst_debug_tgtt = NULL;
 	scst_debug_root = NULL;
 }
 
-int scst_main_create_trace_files(void)
+int scst_main_debugfs_create(void)
 {
-	scst_main_tracing_dir = scst_create_trace_files(scst_debug_root,
-							"main",
-							&scst_main_trace_data);
-	return scst_main_tracing_dir ? 0 : -EINVAL;
+	int res;
+
+	res = -ENOMEM;
+	scst_main_tracing_dir = debugfs_create_dir("main", scst_debug_root);
+	if (!scst_main_tracing_dir)
+		goto out;
+
+	if (!scst_create_trace_files(scst_main_tracing_dir,
+				     &scst_main_trace_data)) {
+		goto out;
+	}
+	res = 0;
+out:
+	return res;
 }
 
-void scst_main_remove_trace_files(void)
+void scst_main_debugfs_remove(void)
 {
-	scst_remove_trace_files(scst_main_tracing_dir);
+	debugfs_remove_recursive(scst_main_tracing_dir);
 	scst_main_tracing_dir = NULL;
 }
 
-int scst_tgtt_create_trace_files(struct scst_tgt_template *tgtt)
+int scst_tgtt_debugfs_create(struct scst_tgt_template *tgtt)
 {
-	if (tgtt->trace_data.trace_flags) {
-		tgtt->tracing_dir = scst_create_trace_files(scst_debug_target,
-							    tgtt->name,
-							    &tgtt->trace_data);
-		if (!tgtt->tracing_dir)
-			return -EINVAL;
+	int res;
+
+	res = -ENOMEM;
+	tgtt->tracing_dir = debugfs_create_dir(tgtt->name, scst_debug_tgtt);
+	if (!tgtt->tracing_dir) {
+		PRINT_ERROR("Creation of directory %s failed", tgtt->name);
+		goto out;
 	}
-	return 0;
+
+	if (tgtt->trace_data.trace_flags &&
+	    !scst_create_trace_files(tgtt->tracing_dir, &tgtt->trace_data)) {
+			res = -ENOMEM;
+			goto out;
+	}
+	res = 0;
+out:
+	return res;
 }
 
-void scst_tgtt_remove_trace_files(struct scst_tgt_template *tgtt)
+void scst_tgtt_debugfs_remove(struct scst_tgt_template *tgtt)
 {
-	scst_remove_trace_files(tgtt->tracing_dir);
+	debugfs_remove_recursive(tgtt->tracing_dir);
 	tgtt->tracing_dir = NULL;
 }
 
-int scst_devt_create_trace_files(struct scst_dev_type *devt)
+int scst_tgt_debugfs_create(struct scst_tgt *tgt)
 {
-	if (devt->trace_data.trace_flags) {
-		devt->tracing_dir = scst_create_trace_files(scst_debug_devt,
-							    devt->name,
-							    &devt->trace_data);
-		if (!devt->tracing_dir)
-			return -EINVAL;
+	int res;
+
+	res = -ENOMEM;
+	tgt->debugfs_dir = debugfs_create_dir(tgt->tgt_name, scst_debug_tgt);
+	if (!tgt->debugfs_dir) {
+		PRINT_ERROR("Creation of directory %s failed", tgt->tgt_name);
+		goto out;
 	}
-	return 0;
+	tgt->sessions_dir = debugfs_create_dir("sessions", tgt->debugfs_dir);
+	if (!tgt->sessions_dir) {
+		PRINT_ERROR("Creation of directory %s/sessions failed",
+			    tgt->tgt_name);
+		goto out;
+	}
+	res = 0;
+out:
+	return res;
+}
+
+void scst_tgt_debugfs_remove(struct scst_tgt *tgt)
+{
+	debugfs_remove_recursive(tgt->debugfs_dir);
+	tgt->debugfs_dir = NULL;
+}
+
+int scst_sess_debugfs_create(struct scst_session *sess)
+{
+	int res;
+
+	res = -ENOMEM;
+	sess->debugfs_dir = debugfs_create_dir(sess->initiator_name,
+					       sess->tgt->sessions_dir);
+	if (!sess->debugfs_dir) {
+		PRINT_ERROR("Creation of directory %s failed",
+			    sess->initiator_name);
+		goto out;
+	}
+	sess->luns_dir = debugfs_create_dir("luns", sess->debugfs_dir);
+	if (!sess->luns_dir) {
+		PRINT_ERROR("Creation of directory %s/sessions failed",
+			    sess->initiator_name);
+		goto out;
+	}
+	res = 0;
+out:
+	return res;
+}
+
+void scst_sess_debugfs_remove(struct scst_session *sess)
+{
+	debugfs_remove_recursive(sess->debugfs_dir);
+	sess->debugfs_dir = NULL;
+}
+
+int scst_tgt_dev_debugfs_create(struct scst_tgt_dev *tgt_dev)
+{
+	int res;
+	char lun[16];
+
+	res = -ENOMEM;
+	snprintf(lun, sizeof(lun), "%llx", tgt_dev->lun);
+	tgt_dev->debugfs_dir = debugfs_create_dir(lun, tgt_dev->sess->luns_dir);
+	if (!tgt_dev->debugfs_dir) {
+		PRINT_ERROR("Creation of directory %s/luns/%s failed",
+			    tgt_dev->sess->initiator_name, lun);
+		goto out;
+	}
+	res = 0;
+out:
+	return res;
+}
+
+void scst_tgt_dev_debugfs_remove(struct scst_tgt_dev *tgt_dev)
+{
+	debugfs_remove_recursive(tgt_dev->debugfs_dir);
+	tgt_dev->debugfs_dir = NULL;
+}
+
+int scst_devt_debugfs_create(struct scst_dev_type *devt)
+{
+	int res;
+
+	res = -ENOMEM;
+	devt->tracing_dir = debugfs_create_dir(devt->name, scst_debug_devt);
+	if (!devt->tracing_dir)
+		goto out;
+	if (devt->trace_data.trace_flags &&
+	    !scst_create_trace_files(devt->tracing_dir, &devt->trace_data))
+		goto err;
+	res = 0;
+out:
+	return res;
+err:
+	scst_devt_debugfs_remove(devt);
+	goto out;
 }
 
-void scst_devt_remove_trace_files(struct scst_dev_type *devt)
+void scst_devt_debugfs_remove(struct scst_dev_type *devt)
 {
-	scst_remove_trace_files(devt->tracing_dir);
+	debugfs_remove_recursive(devt->tracing_dir);
 	devt->tracing_dir = NULL;
 }
 
diff --git a/drivers/scst/scst_tracing.h b/drivers/scst/scst_tracing.h
index 8fd40bd..23b820b 100644
--- a/drivers/scst/scst_tracing.h
+++ b/drivers/scst/scst_tracing.h
@@ -4,12 +4,18 @@
 
 int scst_debugfs_init(void);
 void scst_debugfs_cleanup(void);
-int scst_main_create_trace_files(void);
-void scst_main_remove_trace_files(void);
-int scst_tgtt_create_trace_files(struct scst_tgt_template *tgtt);
-void scst_tgtt_remove_trace_files(struct scst_tgt_template *tgtt);
-int scst_devt_create_trace_files(struct scst_dev_type *devt);
-void scst_devt_remove_trace_files(struct scst_dev_type *devt);
+int scst_main_debugfs_create(void);
+void scst_main_debugfs_remove(void);
+int scst_tgtt_debugfs_create(struct scst_tgt_template *tgtt);
+void scst_tgtt_debugfs_remove(struct scst_tgt_template *tgtt);
+int scst_tgt_debugfs_create(struct scst_tgt *tgtt);
+void scst_tgt_debugfs_remove(struct scst_tgt *tgtt);
+int scst_sess_debugfs_create(struct scst_session *tgtt);
+void scst_sess_debugfs_remove(struct scst_session *tgtt);
+int scst_tgt_dev_debugfs_create(struct scst_tgt_dev *tgtt);
+void scst_tgt_dev_debugfs_remove(struct scst_tgt_dev *tgtt);
+int scst_devt_debugfs_create(struct scst_dev_type *devt);
+void scst_devt_debugfs_remove(struct scst_dev_type *devt);
 int scst_dev_create_debug_files(struct scst_device *dev);
 void scst_dev_remove_debug_files(struct scst_device *dev);
 
@@ -24,30 +30,57 @@ static inline void scst_debugfs_cleanup(void)
 {
 }
 
-static inline int scst_main_create_trace_files(void)
+static inline int scst_main_debugfs_create(void)
 {
 	return 0;
 }
 
-static inline void scst_main_remove_trace_files(void)
+static inline void scst_main_debugfs_remove(void)
 {
 }
 
-static inline int scst_tgtt_create_trace_files(struct scst_tgt_template *tgtt)
+static inline int scst_tgtt_debugfs_create(struct scst_tgt_template *tgtt)
 {
 	return 0;
 }
 
-static inline void scst_tgtt_remove_trace_files(struct scst_tgt_template *tgtt)
+static inline void scst_tgtt_debugfs_remove(struct scst_tgt_template *tgtt)
 {
 }
 
-static inline int scst_devt_create_trace_files(struct scst_dev_type *devt)
+static inline int scst_tgt_debugfs_create(struct scst_tgt *tgtt)
 {
 	return 0;
 }
 
-static inline void scst_devt_remove_trace_files(struct scst_dev_type *devt)
+static inline void scst_tgt_debugfs_remove(struct scst_tgt *tgtt)
+{
+}
+
+static inline int scst_sess_debugfs_create(struct scst_session *tgtt)
+{
+	return 0;
+}
+
+static inline void scst_sess_debugfs_remove(struct scst_session *tgtt)
+{
+}
+
+static inline int scst_tgt_dev_debugfs_create(struct scst_tgt_dev *tgtt)
+{
+	return 0;
+}
+
+static inline void scst_tgt_dev_debugfs_remove(struct scst_tgt_dev *tgtt)
+{
+}
+
+static inline int scst_devt_debugfs_create(struct scst_dev_type *devt)
+{
+	return 0;
+}
+
+static inline void scst_devt_debugfs_remove(struct scst_dev_type *devt)
 {
 }
 
diff --git a/include/scst/scst.h b/include/scst/scst.h
index 9034624..1f59ec1 100644
--- a/include/scst/scst.h
+++ b/include/scst/scst.h
@@ -1316,6 +1316,11 @@ struct scst_tgt {
 	struct kobject *tgt_sess_kobj; /* target/sessions/ */
 	struct kobject *tgt_luns_kobj; /* target/luns/ */
 	struct kobject *tgt_ini_grp_kobj; /* target/ini_groups/ */
+
+#if defined(CONFIG_SCST_DEBUG) || defined(CONFIG_SCST_TRACING)
+	struct dentry *debugfs_dir;
+	struct dentry *sessions_dir;
+#endif
 };
 
 #ifdef CONFIG_SCST_MEASURE_LATENCY
@@ -1449,6 +1454,10 @@ struct scst_session {
 				int result);
 	void (*unreg_done_fn) (struct scst_session *sess);
 
+#if defined(CONFIG_SCST_DEBUG) || defined(CONFIG_SCST_TRACING)
+	struct dentry *debugfs_dir;
+	struct dentry *luns_dir;
+#endif
 #ifdef CONFIG_SCST_MEASURE_LATENCY
 	/*
 	 * Must be the last to allow to work with drivers who don't know
@@ -1460,6 +1469,8 @@ struct scst_session {
 	uint64_t min_scst_time, min_tgt_time, min_dev_time;
 	uint64_t max_scst_time, max_tgt_time, max_dev_time;
 	struct scst_ext_latency_stat sess_latency_stat[SCST_LATENCY_STATS_NUM];
+
+	struct dentry *latency_file;
 #endif
 };
 
@@ -1813,6 +1824,9 @@ struct scst_cmd {
 
 	struct scst_cmd *orig_cmd; /* Used to issue REQUEST SENSE */
 
+#if defined(CONFIG_SCST_DEBUG) || defined(CONFIG_SCST_TRACING)
+	struct dentry *debugfs_dir;
+#endif
 #ifdef CONFIG_SCST_MEASURE_LATENCY
 	/*
 	 * Must be the last to allow to work with drivers who don't know
@@ -1822,6 +1836,8 @@ struct scst_cmd {
 	uint64_t restart_waiting_time, rdy_to_xfer_time;
 	uint64_t pre_exec_time, exec_time, dev_done_time;
 	uint64_t xmit_time, tgt_on_free_time, dev_on_free_time;
+
+	struct dentry *latency_file;
 #endif
 };
 
@@ -2228,6 +2244,9 @@ struct scst_tgt_dev {
 
 	struct kobject tgt_dev_kobj; /* kobject for this struct */
 
+#if defined(CONFIG_SCST_DEBUG) || defined(CONFIG_SCST_TRACING)
+	struct dentry *debugfs_dir;
+#endif
 #ifdef CONFIG_SCST_MEASURE_LATENCY
 	/*
 	 * Must be the last to allow to work with drivers who don't know
@@ -2238,6 +2257,8 @@ struct scst_tgt_dev {
 	uint64_t scst_time, tgt_time, dev_time;
 	unsigned int processed_cmds;
 	struct scst_ext_latency_stat dev_latency_stat[SCST_LATENCY_STATS_NUM];
+
+	struct dentry *latency_file;
 #endif
 };
 
-- 
1.7.1

--
To unsubscribe from this list: send the line "unsubscribe linux-scsi" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[Index of Archives]     [SCSI Target Devel]     [Linux SCSI Target Infrastructure]     [Kernel Newbies]     [IDE]     [Security]     [Git]     [Netfilter]     [Bugtraq]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Linux ATA RAID]     [Linux IIO]     [Samba]     [Device Mapper]
  Powered by Linux