[PATCH 2/7] block: Use blk-stat infrastructure to collect per queue request stats

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

 



From: Jes Sorensen <jsorensen@xxxxxx>

Track request sectors in 8 buckets for read, write, and discard.

Enable stats by writing 1 to /sys/block/<dev>/queue/reqstat, disable
by writing 0.

Signed-off-by: Jes Sorensen <jsorensen@xxxxxx>
---
 block/blk-mq.c         | 52 ++++++++++++++++++++++++++++++++++++++++++
 block/blk-stat.h       |  1 +
 block/blk-sysfs.c      | 40 ++++++++++++++++++++++++++++++++
 include/linux/blkdev.h |  7 ++++++
 4 files changed, 100 insertions(+)

diff --git a/block/blk-mq.c b/block/blk-mq.c
index d92088dec6c3..4aff0903546c 100644
--- a/block/blk-mq.c
+++ b/block/blk-mq.c
@@ -60,6 +60,52 @@ static int blk_mq_poll_stats_bkt(const struct request *rq)
 	return bucket;
 }
 
+/*
+ * 8 buckets for each of read, write, and discard
+ */
+static int blk_req_stats_bkt(const struct request *rq)
+{
+	int grp, bucket;
+
+	grp = op_stat_group(req_op(rq));
+
+	bucket = grp + 3 * ilog2(blk_rq_stats_sectors(rq));
+
+	if (bucket < 0)
+		return -1;
+	else if (bucket >= BLK_REQ_STATS_BKTS)
+		return grp + BLK_REQ_STATS_BKTS - 3;
+
+	return bucket;
+}
+
+/*
+ * Copy out the stats to their official location
+ */
+static void blk_req_stats_cb(struct blk_stat_callback *cb)
+{
+	struct request_queue *q = cb->data;
+	int bucket;
+
+	for (bucket = 0; bucket < BLK_REQ_STATS_BKTS; bucket++) {
+		if (cb->stat[bucket].nr_samples) {
+			q->req_stat[bucket].sectors +=
+				cb->stat[bucket].sectors;
+			q->req_stat[bucket].nr_samples +=
+				cb->stat[bucket].nr_samples;
+		}
+	}
+
+	if (!blk_stat_is_active(cb))
+		blk_stat_activate_msecs(cb, 100);
+}
+
+void blk_req_stats_free(struct request_queue *q)
+{
+	blk_stat_remove_callback(q, q->reqstat_cb);
+	blk_stat_free_callback(q->reqstat_cb);
+}
+
 /*
  * Check if any of the ctx, dispatch list or elevator
  * have pending work in this hardware queue.
@@ -2910,6 +2956,12 @@ struct request_queue *blk_mq_init_allocated_queue(struct blk_mq_tag_set *set,
 	if (!q->nr_hw_queues)
 		goto err_hctxs;
 
+	q->reqstat_cb = blk_stat_alloc_callback(blk_req_stats_cb,
+						blk_req_stats_bkt,
+						BLK_REQ_STATS_BKTS, q);
+	if (!q->reqstat_cb)
+		goto err_hctxs;
+
 	INIT_WORK(&q->timeout_work, blk_mq_timeout_work);
 	blk_queue_rq_timeout(q, set->timeout ? set->timeout : 30 * HZ);
 
diff --git a/block/blk-stat.h b/block/blk-stat.h
index ea893c4a9af1..e592bbf50d38 100644
--- a/block/blk-stat.h
+++ b/block/blk-stat.h
@@ -168,4 +168,5 @@ void blk_rq_stat_add(struct blk_rq_stat *, u64, u64);
 void blk_rq_stat_sum(struct blk_rq_stat *, struct blk_rq_stat *);
 void blk_rq_stat_init(struct blk_rq_stat *);
 
+void blk_req_stats_free(struct request_queue *q);
 #endif
diff --git a/block/blk-sysfs.c b/block/blk-sysfs.c
index fca9b158f4a0..8841146cad54 100644
--- a/block/blk-sysfs.c
+++ b/block/blk-sysfs.c
@@ -529,6 +529,37 @@ static ssize_t queue_dax_show(struct request_queue *q, char *page)
 	return queue_var_show(blk_queue_dax(q), page);
 }
 
+static ssize_t queue_reqstat_show(struct request_queue *q, char *page)
+{
+	return queue_var_show(test_bit(QUEUE_FLAG_REQSTATS,
+				       &q->queue_flags), page);
+}
+
+static ssize_t queue_reqstat_store(struct request_queue *q, const char *page,
+				    size_t size)
+{
+	unsigned long reqstat_on;
+	ssize_t ret;
+
+	ret = queue_var_store(&reqstat_on, page, size);
+	if (ret < 0)
+		return ret;
+
+	if (reqstat_on) {
+		if (!blk_queue_flag_test_and_set(QUEUE_FLAG_REQSTATS, q))
+			blk_stat_add_callback(q, q->reqstat_cb);
+		if (!blk_stat_is_active(q->reqstat_cb))
+			blk_stat_activate_msecs(q->reqstat_cb, 100);
+	} else {
+		if (test_bit(QUEUE_FLAG_REQSTATS, &q->queue_flags)) {
+			blk_stat_remove_callback(q, q->reqstat_cb);
+			blk_queue_flag_clear(QUEUE_FLAG_REQSTATS, q);
+		}
+	}
+
+	return ret;
+}
+
 static struct queue_sysfs_entry queue_requests_entry = {
 	.attr = {.name = "nr_requests", .mode = 0644 },
 	.show = queue_requests_show,
@@ -727,6 +758,12 @@ static struct queue_sysfs_entry throtl_sample_time_entry = {
 };
 #endif
 
+static struct queue_sysfs_entry queue_reqstat_entry = {
+	.attr = {.name = "reqstat", .mode = 0644 },
+	.show = queue_reqstat_show,
+	.store = queue_reqstat_store,
+};
+
 static struct attribute *queue_attrs[] = {
 	&queue_requests_entry.attr,
 	&queue_ra_entry.attr,
@@ -766,6 +803,7 @@ static struct attribute *queue_attrs[] = {
 #ifdef CONFIG_BLK_DEV_THROTTLING_LOW
 	&throtl_sample_time_entry.attr,
 #endif
+	&queue_reqstat_entry.attr,
 	NULL,
 };
 
@@ -877,6 +915,8 @@ static void __blk_release_queue(struct work_struct *work)
 {
 	struct request_queue *q = container_of(work, typeof(*q), release_work);
 
+	blk_req_stats_free(q);
+
 	if (test_bit(QUEUE_FLAG_POLL_STATS, &q->queue_flags))
 		blk_stat_remove_callback(q, q->poll_cb);
 	blk_stat_free_callback(q->poll_cb);
diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h
index 10455b2bbbb4..58abab51ed9f 100644
--- a/include/linux/blkdev.h
+++ b/include/linux/blkdev.h
@@ -53,6 +53,9 @@ struct blk_stat_callback;
 /* Doing classic polling */
 #define BLK_MQ_POLL_CLASSIC -1
 
+/* Must be consistent with blk_part_stats_bkt() */
+#define BLK_REQ_STATS_BKTS (3 * 8)
+
 /*
  * Maximum number of blkcg policies allowed to be registered concurrently.
  * Defined here to simplify include dependency.
@@ -480,6 +483,9 @@ struct request_queue {
 	struct blk_stat_callback	*poll_cb;
 	struct blk_rq_stat	poll_stat[BLK_MQ_POLL_STATS_BKTS];
 
+	struct blk_stat_callback	*reqstat_cb;
+	struct blk_rq_stat	req_stat[BLK_REQ_STATS_BKTS];
+
 	struct timer_list	timeout;
 	struct work_struct	timeout_work;
 
@@ -612,6 +618,7 @@ struct request_queue {
 #define QUEUE_FLAG_PCI_P2PDMA	25	/* device supports PCI p2p requests */
 #define QUEUE_FLAG_ZONE_RESETALL 26	/* supports Zone Reset All */
 #define QUEUE_FLAG_RQ_ALLOC_TIME 27	/* record rq->alloc_time_ns */
+#define QUEUE_FLAG_REQSTATS	28	/* request stats enabled if set */
 
 #define QUEUE_FLAG_MQ_DEFAULT	((1 << QUEUE_FLAG_IO_STAT) |		\
 				 (1 << QUEUE_FLAG_SAME_COMP))
-- 
2.17.1




[Index of Archives]     [Linux RAID]     [Linux SCSI]     [Linux ATA RAID]     [IDE]     [Linux Wireless]     [Linux Kernel]     [ATH6KL]     [Linux Bluetooth]     [Linux Netdev]     [Kernel Newbies]     [Security]     [Git]     [Netfilter]     [Bugtraq]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Device Mapper]

  Powered by Linux