[PATCH v2 10/10] commands: blkstats: add command to print block device statistics

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

 



To test proper operations of block device operations, add a command that
prints how many sectors were read/written/erased for a device so far.

Signed-off-by: Ahmad Fatoum <a.fatoum@xxxxxxxxxxxxxx>
---
v1 -> v2:
  - print header only once
  - increase width of device name to 16 characters
---
 commands/Kconfig    | 11 ++++++++
 commands/Makefile   |  1 +
 commands/blkstats.c | 66 +++++++++++++++++++++++++++++++++++++++++++++
 common/Kconfig      |  3 +++
 common/block.c      | 46 +++++++++++++++++++++++++------
 include/block.h     | 10 +++++++
 6 files changed, 129 insertions(+), 8 deletions(-)
 create mode 100644 commands/blkstats.c

diff --git a/commands/Kconfig b/commands/Kconfig
index a8b7037618cc..64e834d95a8f 100644
--- a/commands/Kconfig
+++ b/commands/Kconfig
@@ -255,6 +255,17 @@ config CMD_REGINFO
 	help
 	  Print register information.
 
+config CMD_BLKSTATS
+	bool
+	depends on BLOCK
+	select BLOCK_STATS
+	prompt "blkstats command"
+	help
+	  The blkstats displays statistics about a block devices' number of
+	  sectors read, written and erased. This should only be needed for
+	  development. Saying y here will start to collect these statistics
+	  and enable a command for querying them.
+
 config CMD_REGULATOR
 	bool
 	depends on REGULATOR
diff --git a/commands/Makefile b/commands/Makefile
index a9dbead4389f..ff5d713ca72c 100644
--- a/commands/Makefile
+++ b/commands/Makefile
@@ -125,6 +125,7 @@ obj-$(CONFIG_CMD_DRVINFO)	+= drvinfo.o
 obj-$(CONFIG_CMD_READF)		+= readf.o
 obj-$(CONFIG_CMD_MENUTREE)	+= menutree.o
 obj-$(CONFIG_CMD_2048)		+= 2048.o
+obj-$(CONFIG_CMD_BLKSTATS)	+= blkstats.o
 obj-$(CONFIG_CMD_REGULATOR)	+= regulator.o
 obj-$(CONFIG_CMD_PM_DOMAIN)	+= pm_domain.o
 obj-$(CONFIG_CMD_LSPCI)		+= lspci.o
diff --git a/commands/blkstats.c b/commands/blkstats.c
new file mode 100644
index 000000000000..6af9da765ebe
--- /dev/null
+++ b/commands/blkstats.c
@@ -0,0 +1,66 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include <common.h>
+#include <command.h>
+#include <block.h>
+#include <getopt.h>
+#include <fs.h>
+
+static int do_blkstats(int argc, char *argv[])
+{
+	struct block_device *blk;
+	const char *name;
+	bool first = false;
+	int opt;
+
+	while ((opt = getopt(argc, argv, "l")) > 0) {
+		switch (opt) {
+		case 'l':
+			for_each_block_device(blk) {
+				printf("%s\n", blk->cdev.name);
+			}
+		default:
+			return COMMAND_ERROR_USAGE;
+		}
+	}
+
+	argv += optind;
+	argc -= optind;
+
+	name = argv[0];
+
+	for_each_block_device(blk) {
+		struct block_device_stats *stats;
+
+		if (name && strcmp(name, blk->cdev.name))
+			continue;
+
+		if (first) {
+			printf("%-16s %10s %10s %10s\n",
+			       "Device", "Read", "Write", "Erase");
+			first = true;
+		}
+
+		stats = &blk->stats;
+
+		printf("%-16s %10llu %10llu %10llu\n", blk->cdev.name,
+		       stats->read_sectors, stats->write_sectors, stats->erase_sectors);
+	}
+
+	return 0;
+}
+
+BAREBOX_CMD_HELP_START(blkstats)
+BAREBOX_CMD_HELP_TEXT("Display a block device's number of read, written and erased sectors")
+BAREBOX_CMD_HELP_TEXT("")
+BAREBOX_CMD_HELP_TEXT("Options:")
+BAREBOX_CMD_HELP_OPT("-l",  "list all currently registered block devices")
+BAREBOX_CMD_HELP_END
+
+BAREBOX_CMD_START(blkstats)
+	.cmd		= do_blkstats,
+	BAREBOX_CMD_DESC("display block layer statistics")
+	BAREBOX_CMD_OPTS("[-l] [DEVICE]")
+	BAREBOX_CMD_GROUP(CMD_GRP_INFO)
+	BAREBOX_CMD_HELP(cmd_blkstats_help)
+BAREBOX_CMD_END
diff --git a/common/Kconfig b/common/Kconfig
index fea26262da86..4500feb66c92 100644
--- a/common/Kconfig
+++ b/common/Kconfig
@@ -41,6 +41,9 @@ config BLOCK
 config BLOCK_WRITE
 	bool
 
+config BLOCK_STATS
+	bool
+
 config FILETYPE
 	bool
 
diff --git a/common/block.c b/common/block.c
index eaec454ecbb2..3101fceea761 100644
--- a/common/block.c
+++ b/common/block.c
@@ -32,19 +32,40 @@ static int writebuffer_io_len(struct block_device *blk, struct chunk *chunk)
 	return min_t(blkcnt_t, blk->rdbufsize, blk->num_blocks - chunk->block_start);
 }
 
+#ifdef CONFIG_BLOCK_STATS
+static void blk_stats_record_read(struct block_device *blk, blkcnt_t count)
+{
+	blk->stats.read_sectors += count;
+}
+static void blk_stats_record_write(struct block_device *blk, blkcnt_t count)
+{
+	blk->stats.write_sectors += count;
+}
+static void blk_stats_record_erase(struct block_device *blk, blkcnt_t count)
+{
+	blk->stats.erase_sectors += count;
+}
+#else
+static void blk_stats_record_read(struct block_device *blk, blkcnt_t count) { }
+static void blk_stats_record_write(struct block_device *blk, blkcnt_t count) { }
+static void blk_stats_record_erase(struct block_device *blk, blkcnt_t count) { }
+#endif
+
 static int chunk_flush(struct block_device *blk, struct chunk *chunk)
 {
+	size_t len;
 	int ret;
 
 	if (!chunk->dirty)
 		return 0;
 
-	ret = blk->ops->write(blk, chunk->data,
-			      chunk->block_start,
-			      writebuffer_io_len(blk, chunk));
+	len = writebuffer_io_len(blk, chunk);
+	ret = blk->ops->write(blk, chunk->data, chunk->block_start, len);
 	if (ret < 0)
 		return ret;
 
+	blk_stats_record_write(blk, len);
+
 	chunk->dirty = 0;
 
 	return 0;
@@ -145,6 +166,7 @@ static struct chunk *get_chunk(struct block_device *blk)
 static int block_cache(struct block_device *blk, sector_t block)
 {
 	struct chunk *chunk;
+	size_t len;
 	int ret;
 
 	chunk = get_chunk(blk);
@@ -156,20 +178,22 @@ static int block_cache(struct block_device *blk, sector_t block)
 	dev_dbg(blk->dev, "%s: %llu to %d\n", __func__, chunk->block_start,
 		chunk->num);
 
+	len = writebuffer_io_len(blk, chunk);
 	if (chunk->block_start * BLOCKSIZE(blk) >= blk->discard_start &&
-	    chunk->block_start * BLOCKSIZE(blk) + writebuffer_io_len(blk, chunk)
+	    chunk->block_start * BLOCKSIZE(blk) + len
 	    <= blk->discard_start + blk->discard_size) {
-		memset(chunk->data, 0, writebuffer_io_len(blk, chunk));
+		memset(chunk->data, 0, len);
 		list_add(&chunk->list, &blk->buffered_blocks);
 		return 0;
 	}
 
-	ret = blk->ops->read(blk, chunk->data, chunk->block_start,
-			     writebuffer_io_len(blk, chunk));
+	ret = blk->ops->read(blk, chunk->data, chunk->block_start, len);
 	if (ret) {
 		list_add_tail(&chunk->list, &blk->idle_blocks);
 		return ret;
 	}
+
+	blk_stats_record_read(blk, len);
 	list_add(&chunk->list, &blk->buffered_blocks);
 
 	return 0;
@@ -402,7 +426,13 @@ static __maybe_unused int block_op_erase(struct cdev *cdev, loff_t count, loff_t
 		}
 	}
 
-	return blk->ops->erase(blk, offset, count);
+	ret = blk->ops->erase(blk, offset, count);
+	if (ret)
+		return ret;
+
+	blk_stats_record_erase(blk, count);
+
+	return 0;
 }
 
 static struct cdev_operations block_ops = {
diff --git a/include/block.h b/include/block.h
index eb319b953d32..b57d99a3fc08 100644
--- a/include/block.h
+++ b/include/block.h
@@ -31,6 +31,12 @@ enum blk_type {
 
 const char *blk_type_str(enum blk_type);
 
+struct block_device_stats {
+	blkcnt_t read_sectors;
+	blkcnt_t write_sectors;
+	blkcnt_t erase_sectors;
+};
+
 struct block_device {
 	struct device *dev;
 	struct list_head list;
@@ -50,6 +56,10 @@ struct block_device {
 	struct cdev cdev;
 
 	bool need_reparse;
+
+#ifdef CONFIG_BLOCK_STATS
+	struct block_device_stats stats;
+#endif
 };
 
 #define BLOCKSIZE(blk)	(1u << (blk)->blockbits)
-- 
2.39.2





[Index of Archives]     [Linux Embedded]     [Linux USB Devel]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]     [XFree86]

  Powered by Linux