block: callback-based statistics
Subsystems (2)
cover block: callback-based statistics
From: Omar Sandoval <[email protected]>
This patchset generalizes the blk-stats infrastructure to allow users to register a callback to be called at a given time with the statistics of requests completed during that window. Writeback throttling and hybrid polling are converted to the new infrastructure. The ultimate goal is to use this infrastructure for the mq I/O scheduler.
The details are in patch 4, which is the actual conversion. Patches 1-3 are preparation cleanups.
A couple of minor open issues:
- Since there is no single source of truth for stats anymore (it's per-callback now), I got rid of the sysfs/debugfs files. If we still want these, we could potentially expose the statistics that poll is using.
- Polling used to use per-hctx stats, but now the stats are gathered to a per-request_queue cache. This might make the stats noisier, but we can improve this in the future by splitting out stats by request size or something like that.
Omar Sandoval (4):
block: tear out blk-stat debugfs/sysfs entries
block: remove extra calls to wbt_exit()
block: change stats from array type to struct type
blk-stats: convert to callback-based statistics reporting
block/blk-core.c | 7 +- block/blk-mq-debugfs.c | 55 --------- block/blk-mq.c | 79 ++++++++---- block/blk-mq.h | 1 - block/blk-stat.c | 300 ++++++++++++++++++++------------------------- block/blk-stat.h | 98 ++++++++++++--- block/blk-sysfs.c | 31 +---- block/blk-wbt.c | 66 +++++----- block/blk-wbt.h | 2 +- include/linux/blk_types.h | 8 +- include/linux/blkdev.h | 10 +- include/trace/events/wbt.h | 20 +-- 12 files changed, 331 insertions(+), 346 deletions(-)
Signature · 1 line
2.12.0
PATCH 1/4 block: tear out blk-stat debugfs/sysfs entries
From: Omar Sandoval <[email protected]>
These made sense when there was a single stats buffer for the request queue, but that's going away.
--- block/blk-mq-debugfs.c | 55 -------------------------------------------------- block/blk-sysfs.c | 26 ------------------------ 2 files changed, 81 deletions(-)
block/blk-mq-debugfs.c +0 -55
@@ -322,60 +322,6 @@ static const struct file_operations hctx_io_poll_fops = { .release = single_release, }; -static void print_stat (struct seq_file *m,struct blk_rq_stat *stat) -{ -seq_printf (m,"samples=%d, mean=%lld, min=%llu, max=%llu" , - stat->nr_samples, stat->mean, stat->min, stat->max); -} - -static int hctx_stats_show (struct seq_file *m,void *v) -{ -struct blk_mq_hw_ctx *hctx = m->private; -struct blk_rq_stat stat[2 ]; - -blk_stat_init (&stat[BLK_STAT_READ ]); -blk_stat_init (&stat[BLK_STAT_WRITE ]); - -blk_hctx_stat_get (hctx, stat); - -seq_puts (m,"read: " ); -print_stat (m, &stat[BLK_STAT_READ ]); -seq_puts (m,"\n" ); - -seq_puts (m,"write: " ); -print_stat (m, &stat[BLK_STAT_WRITE ]); -seq_puts (m,"\n" ); -return 0 ; -} - -static int hctx_stats_open (struct inode *inode,struct file *file) -{ -return single_open (file, hctx_stats_show, inode->i_private); -} - -static ssize_t hctx_stats_write (struct file *file,const char __user *buf, -size_t count,loff_t *ppos) -{ -struct seq_file *m = file->private_data; -struct blk_mq_hw_ctx *hctx = m->private; -struct blk_mq_ctx *ctx; -int i; - -hctx_for_each_ctx (hctx, ctx, i) { -blk_stat_init (&ctx->stat[BLK_STAT_READ ]); -blk_stat_init (&ctx->stat[BLK_STAT_WRITE ]); - } -return count; -} - -static const struct file_operations hctx_stats_fops = { - .open = hctx_stats_open, - .read = seq_read, - .write = hctx_stats_write, - .llseek = seq_lseek, - .release = single_release, -}; -static int hctx_dispatched_show (struct seq_file *m,void *v) {struct blk_mq_hw_ctx *hctx = m->private;
@@ -646,7 +592,6 @@ static const struct blk_mq_debugfs_attr blk_mq_debugfs_hctx_attrs[] = { {"sched_tags", 0400, &hctx_sched_tags_fops}, {"sched_tags_bitmap", 0400, &hctx_sched_tags_bitmap_fops}, {"io_poll", 0600, &hctx_io_poll_fops}, - {"stats", 0600, &hctx_stats_fops}, {"dispatched", 0600, &hctx_dispatched_fops}, {"queued", 0600, &hctx_queued_fops}, {"run", 0600, &hctx_run_fops},
block/blk-sysfs.c +0 -26
@@ -503,26 +503,6 @@ static ssize_t queue_dax_show(struct request_queue *q, char *page)return queue_var_show (blk_queue_dax (q), page); } -static ssize_t print_stat (char *page,struct blk_rq_stat *stat,const char *pre) -{ -return sprintf (page,"%s samples=%llu, mean=%lld, min=%lld, max=%lld\n" , - pre, (long long ) stat->nr_samples, - (long long ) stat->mean, (long long ) stat->min, - (long long ) stat->max); -} - -static ssize_t queue_stats_show (struct request_queue *q,char *page) -{ -struct blk_rq_stat stat[2 ]; -ssize_t ret; - -blk_queue_stat_get (q, stat); - - ret =print_stat (page, &stat[BLK_STAT_READ ],"read :" ); - ret +=print_stat (page + ret, &stat[BLK_STAT_WRITE ],"write:" ); -return ret; -} -static struct queue_sysfs_entry queue_requests_entry = { .attr = {.name ="nr_requests" , .mode =S_IRUGO |S_IWUSR }, .show = queue_requests_show,
@@ -691,11 +671,6 @@ static struct queue_sysfs_entry queue_dax_entry = { .show = queue_dax_show, }; -static struct queue_sysfs_entry queue_stats_entry = { - .attr = {.name ="stats" , .mode =S_IRUGO }, - .show = queue_stats_show, -}; -static struct queue_sysfs_entry queue_wb_lat_entry = { .attr = {.name ="wbt_lat_usec" , .mode =S_IRUGO |S_IWUSR }, .show = queue_wb_lat_show,
@@ -733,7 +708,6 @@ static struct attribute *default_attrs[] = { &queue_poll_entry.attr, &queue_wc_entry.attr, &queue_dax_entry.attr, - &queue_stats_entry.attr, &queue_wb_lat_entry.attr, &queue_poll_delay_entry.attr, NULL,
Signature · 1 line
2.12.0
PATCH 2/4 block: remove extra calls to wbt_exit()
In reply to Omar SandovalFrom: Omar Sandoval <[email protected]>
We always call wbt_exit() from blk_release_queue(), so these are unnecessary.
--- block/blk-core.c | 1 - block/blk-mq.c | 2 -- 2 files changed, 3 deletions(-)
block/blk-core.c +0 -1
@@ -889,7 +889,6 @@ int blk_init_allocated_queue(struct request_queue *q) q->exit_rq_fn (q, q->fq->flush_rq); out_free_flush_queue:blk_free_flush_queue (q->fq); -wbt_exit (q);return -ENOMEM ; }EXPORT_SYMBOL (blk_init_allocated_queue);
block/blk-mq.c +0 -2
@@ -2430,8 +2430,6 @@ void blk_mq_free_queue(struct request_queue *q)list_del_init (&q->all_q_node);mutex_unlock (&all_q_mutex); -wbt_exit (q); -blk_mq_del_queue_tag_set (q);blk_mq_exit_hw_queues (q, set, set->nr_hw_queues);
Signature · 1 line
2.12.0
PATCH 3/4 block: change stats from array type to struct type
In reply to Omar SandovalFrom: Omar Sandoval <[email protected]>
This is cleanup in preparation for callback-based stats with a couple of (minor) advantages:
1. Allocating a percpu array type is awkward.
2. It makes the callback type signature more clear. With struct blk_rq_stat *, the only way to know that you're getting an array of read and write stats is to look at the implementation or other users. With the struct type, it's immediately obvious.
Note that I left q->rq_stats and ctx->stat alone since those are going away anyways.
--- block/blk-mq.c | 14 +++++++------- block/blk-stat.c | 34 +++++++++++++++++----------------- block/blk-stat.h | 4 ++-- block/blk-wbt.c | 28 ++++++++++++++-------------- include/linux/blk_types.h | 5 +++++ include/trace/events/wbt.h | 20 ++++++++++---------- 6 files changed, 55 insertions(+), 50 deletions(-)
block/blk-mq.c +7 -7
@@ -2743,7 +2743,7 @@ static unsigned long blk_mq_poll_nsecs(struct request_queue *q,struct blk_mq_hw_ctx *hctx, struct request *rq) { - struct blk_rq_stat stat[2]; + struct blk_stats stats;unsigned long ret =0 ;/*
@@ -2757,8 +2757,8 @@ static unsigned long blk_mq_poll_nsecs(struct request_queue *q,* We don't have to do this once per IO, should optimize this * to just use the current window of stats until it changes */ -memset (&stat,0 ,sizeof (stat)); -blk_hctx_stat_get (hctx, stat); +memset (&stats,0 ,sizeof (stats)); +blk_hctx_stat_get (hctx, &stats);/* * As an optimistic guess, use half of the mean service time
@@ -2768,10 +2768,10 @@ static unsigned long blk_mq_poll_nsecs(struct request_queue *q,* important on devices where the completion latencies are longer * than ~10 usec. */ -if (req_op (rq) ==REQ_OP_READ && stat[BLK_STAT_READ ].nr_samples) - ret = (stat[BLK_STAT_READ ].mean +1 ) /2 ; -else if (req_op (rq) ==REQ_OP_WRITE && stat[BLK_STAT_WRITE ].nr_samples) - ret = (stat[BLK_STAT_WRITE ].mean +1 ) /2 ; +if (req_op (rq) ==REQ_OP_READ && stats.read.nr_samples) + ret = (stats.read.mean +1 ) /2 ; +else if (req_op (rq) ==REQ_OP_WRITE && stats.write.nr_samples) + ret = (stats.write.mean +1 ) /2 ;return ret; }
block/blk-stat.c +17 -17
@@ -48,15 +48,15 @@ static void blk_stat_sum(struct blk_rq_stat *dst, struct blk_rq_stat *src) dst->nr_samples += src->nr_samples; } -static void blk_mq_stat_get (struct request_queue *q,struct blk_rq_stat *dst) +static void blk_mq_stat_get (struct request_queue *q,struct blk_stats *stats) {struct blk_mq_hw_ctx *hctx;struct blk_mq_ctx *ctx;uint64_t latest =0 ;int i, j, nr; -blk_stat_init (&dst[BLK_STAT_READ ]); -blk_stat_init (&dst[BLK_STAT_WRITE ]); +blk_stat_init (&stats->read); +blk_stat_init (&stats->write); nr =0 ; do {
@@ -89,12 +89,12 @@ static void blk_mq_stat_get(struct request_queue *q, struct blk_rq_stat *dst)queue_for_each_hw_ctx (q, hctx, i) {hctx_for_each_ctx (hctx, ctx, j) {if (ctx->stat[BLK_STAT_READ ].time == newest) { -blk_stat_sum (&dst[BLK_STAT_READ ], +blk_stat_sum (&stats->read, &ctx->stat[BLK_STAT_READ ]); nr++; }if (ctx->stat[BLK_STAT_WRITE ].time == newest) { -blk_stat_sum (&dst[BLK_STAT_WRITE ], +blk_stat_sum (&stats->write, &ctx->stat[BLK_STAT_WRITE ]); nr++; }
@@ -106,24 +106,24 @@ static void blk_mq_stat_get(struct request_queue *q, struct blk_rq_stat *dst)*/ }while (!nr); - dst[BLK_STAT_READ ].time = dst[BLK_STAT_WRITE ].time = latest; + stats->read.time = stats->write.time = latest; } -void blk_queue_stat_get (struct request_queue *q,struct blk_rq_stat *dst) +void blk_queue_stat_get (struct request_queue *q,struct blk_stats *stats) {if (q->mq_ops) -blk_mq_stat_get (q, dst); +blk_mq_stat_get (q, stats);else {blk_stat_flush_batch (&q->rq_stats[BLK_STAT_READ ]);blk_stat_flush_batch (&q->rq_stats[BLK_STAT_WRITE ]); -memcpy (&dst[BLK_STAT_READ ], &q->rq_stats[BLK_STAT_READ ], -sizeof (struct blk_rq_stat )); -memcpy (&dst[BLK_STAT_WRITE ], &q->rq_stats[BLK_STAT_WRITE ], -sizeof (struct blk_rq_stat )); +memcpy (&stats->read, &q->rq_stats[BLK_STAT_READ ], +sizeof (struct blk_rq_stat )); +memcpy (&stats->write, &q->rq_stats[BLK_STAT_WRITE ], +sizeof (struct blk_rq_stat )); } } -void blk_hctx_stat_get (struct blk_mq_hw_ctx *hctx,struct blk_rq_stat *dst) +void blk_hctx_stat_get (struct blk_mq_hw_ctx *hctx,struct blk_stats *stats) {struct blk_mq_ctx *ctx;unsigned int i, nr;
@@ -151,13 +151,13 @@ void blk_hctx_stat_get(struct blk_mq_hw_ctx *hctx, struct blk_rq_stat *dst)hctx_for_each_ctx (hctx, ctx, i) {if (ctx->stat[BLK_STAT_READ ].time == newest) { -blk_stat_sum (&dst[BLK_STAT_READ ], - &ctx->stat[BLK_STAT_READ ]); +blk_stat_sum (&stats->read, + &ctx->stat[BLK_STAT_READ ]); nr++; }if (ctx->stat[BLK_STAT_WRITE ].time == newest) { -blk_stat_sum (&dst[BLK_STAT_WRITE ], - &ctx->stat[BLK_STAT_WRITE ]); +blk_stat_sum (&stats->write, + &ctx->stat[BLK_STAT_WRITE ]); nr++; } }
block/blk-stat.h +2 -2
@@ -21,8 +21,8 @@ enum { };void blk_stat_add (struct blk_rq_stat *,struct request *); -void blk_hctx_stat_get (struct blk_mq_hw_ctx *,struct blk_rq_stat *); -void blk_queue_stat_get (struct request_queue *,struct blk_rq_stat *); +void blk_hctx_stat_get (struct blk_mq_hw_ctx *,struct blk_stats *); +void blk_queue_stat_get (struct request_queue *,struct blk_stats *);void blk_stat_clear (struct request_queue *);void blk_stat_init (struct blk_rq_stat *);bool blk_stat_is_current (struct blk_rq_stat *);
block/blk-wbt.c +14 -14
@@ -247,7 +247,7 @@ static bool calc_wb_limits(struct rq_wb *rwb)return ret; } -static inline bool stat_sample_valid (struct blk_rq_stat *stat) +static inline bool stat_sample_valid (struct blk_stats *stats) {/* * We need at least one read sample, and a minimum of
@@ -255,8 +255,8 @@ static inline bool stat_sample_valid(struct blk_rq_stat *stat)* that it's writes impacting us, and not just some sole read on * a device that is in a lower power state. */ -return stat[BLK_STAT_READ ].nr_samples >=1 && - stat[BLK_STAT_WRITE ].nr_samples >=RWB_MIN_WRITE_SAMPLES ; +return (stats->read.nr_samples >=1 && + stats->write.nr_samples >=RWB_MIN_WRITE_SAMPLES ); }static u64 rwb_sync_issue_lat (struct rq_wb *rwb)
@@ -277,7 +277,7 @@ enum { LAT_EXCEEDED, }; -static int __latency_exceeded (struct rq_wb *rwb,struct blk_rq_stat *stat) +static int __latency_exceeded (struct rq_wb *rwb,struct blk_stats *stats) {struct backing_dev_info *bdi = rwb->queue->backing_dev_info;u64 thislat;
@@ -293,7 +293,7 @@ static int __latency_exceeded(struct rq_wb *rwb, struct blk_rq_stat *stat)*/ thislat =rwb_sync_issue_lat (rwb);if (thislat > rwb->cur_win_nsec || - (thislat > rwb->min_lat_nsec && !stat[BLK_STAT_READ ].nr_samples)) { + (thislat > rwb->min_lat_nsec && !stats->read.nr_samples)) {trace_wbt_lat (bdi, thislat);return LAT_EXCEEDED ; }
@@ -301,14 +301,14 @@ static int __latency_exceeded(struct rq_wb *rwb, struct blk_rq_stat *stat)/* * No read/write mix, if stat isn't valid */ -if (!stat_sample_valid (stat)) { +if (!stat_sample_valid (stats)) {/* * If we had writes in this stat window and the window is * current, we're only doing writes. If a task recently * waited or still has writes in flights, consider us doing * just writes as well. */ -if ((stat[BLK_STAT_WRITE ].nr_samples &&blk_stat_is_current (stat)) || +if ((stats->write.nr_samples &&blk_stat_is_current (&stats->read)) ||wb_recent_wait (rwb) ||wbt_inflight (rwb))return LAT_UNKNOWN_WRITES ;return LAT_UNKNOWN ;
@@ -317,24 +317,24 @@ static int __latency_exceeded(struct rq_wb *rwb, struct blk_rq_stat *stat)/* * If the 'min' latency exceeds our target, step down. */ -if (stat[BLK_STAT_READ ].min > rwb->min_lat_nsec) { -trace_wbt_lat (bdi, stat[BLK_STAT_READ ].min); -trace_wbt_stat (bdi, stat); +if (stats->read.min > rwb->min_lat_nsec) { +trace_wbt_lat (bdi, stats->read.min); +trace_wbt_stat (bdi, stats);return LAT_EXCEEDED ; }if (rwb->scale_step) -trace_wbt_stat (bdi, stat); +trace_wbt_stat (bdi, stats);return LAT_OK ; }static int latency_exceeded (struct rq_wb *rwb) { -struct blk_rq_stat stat[2 ]; +struct blk_stats stats; -blk_queue_stat_get (rwb->queue, stat); -return __latency_exceeded (rwb, stat); +blk_queue_stat_get (rwb->queue, &stats); +return __latency_exceeded (rwb, &stats); }static void rwb_trace_step (struct rq_wb *rwb,const char *msg)
include/linux/blk_types.h +5 -0
@@ -299,4 +299,9 @@ struct blk_rq_stat { s64 time; }; +struct blk_stats { +struct blk_rq_stat read; +struct blk_rq_stat write; +}; +#endif /* __LINUX_BLK_TYPES_H */
include/trace/events/wbt.h +10 -10
@@ -13,9 +13,9 @@*/ TRACE_EVENT(wbt_stat, - TP_PROTO(struct backing_dev_info *bdi, struct blk_rq_stat *stat), + TP_PROTO(struct backing_dev_info *bdi, struct blk_stats *stats), - TP_ARGS(bdi, stat), + TP_ARGS(bdi, stats), TP_STRUCT__entry( __array(char, name, 32)
@@ -33,14 +33,14 @@ TRACE_EVENT(wbt_stat,TP_fast_assign ( strncpy(__entry->name, dev_name(bdi->dev), 32); - __entry->rmean = stat[0 ].mean; - __entry->rmin = stat[0 ].min; - __entry->rmax = stat[0 ].max; - __entry->rnr_samples = stat[0 ].nr_samples; - __entry->wmean = stat[1 ].mean; - __entry->wmin = stat[1 ].min; - __entry->wmax = stat[1 ].max; - __entry->wnr_samples = stat[1 ].nr_samples; + __entry->rmean = stats->read.mean; + __entry->rmin = stats->read.min; + __entry->rmax = stats->read.max; + __entry->rnr_samples = stats->read.nr_samples; + __entry->wmean = stats->write.mean; + __entry->wmin = stats->write.min; + __entry->wmax = stats->write.max; + __entry->wnr_samples = stats->write.nr_samples; ), TP_printk("%s: rmean=%llu, rmin=%llu, rmax=%llu, rsamples=%llu, "
Signature · 1 line
2.12.0
PATCH 4/4 blk-stats: convert to callback-based statistics reporting
In reply to Omar SandovalFrom: Omar Sandoval <[email protected]>
Currently, statistics are gathered in ~0.13s windows, and users grab the statistics whenever they need them. This is not ideal for both in-tree users:
1. Writeback throttling wants its own dynamically sized window of statistics. Since the blk-stats statistics are reset after every window and the wbt windows don't line up with the blk-stats windows, wbt doesn't see every I/O.
2. Polling currently grabs the statistics on every I/O. Again, depending on how the window lines up, we may miss some I/Os. It's also unnecessary overhead to get the statistics on every I/O; the hybrid polling heuristic would be just as happy with the statistics from the previous full window.
This reworks the blk-stats infrastructure to be callback-based: users register a callback that they want called at a given time with all of the statistics from the window during which the callback was active. The callbacks are kept on an RCU list, and each callback has percpu stats buffers. There will only be a few users, so the overhead on the I/O completion side is low. The stats flushing is also simplified
considerably: since the timer function is responsible for clearing the statistics, we don't have to worry about stale statistics.
wbt is a trivial conversion. After the conversion, the windowing problem mentioned above is fixed.
For polling, we register an extra callback that caches the previous window's statistics in the struct request_queue for the hybrid polling heuristic to use.
--- block/blk-core.c | 6 +- block/blk-mq.c | 77 ++++++++---- block/blk-mq.h | 1 - block/blk-stat.c | 300 +++++++++++++++++++++------------------------- block/blk-stat.h | 98 ++++++++++++--- block/blk-sysfs.c | 5 + block/blk-wbt.c | 48 +++----- block/blk-wbt.h | 2 +- include/linux/blk_types.h | 3 - include/linux/blkdev.h | 10 +- 10 files changed, 307 insertions(+), 243 deletions(-)
block/blk-core.c +5 -1
@@ -852,6 +852,10 @@ static blk_qc_t blk_queue_bio(struct request_queue *q, struct bio *bio);int blk_init_allocated_queue (struct request_queue *q) { + q->stats =blk_alloc_queue_stats (); +if (!q->stats) +return -ENOMEM ; + q->fq =blk_alloc_flush_queue (q,NUMA_NO_NODE , q->cmd_size);if (!q->fq)return -ENOMEM ;
@@ -2692,7 +2696,7 @@ void blk_finish_request(struct request *req, int error)struct request_queue *q = req->q;if (req->rq_flags &RQF_STATS ) -blk_stat_add (&q->rq_stats[rq_data_dir (req)], req); +blk_stat_add (req);if (req->rq_flags &RQF_QUEUED )blk_queue_end_tag (q, req);
block/blk-mq.c +53 -24
126 lines not on this page · raw message
block/blk-mq.h +0 -1
@@ -20,7 +20,6 @@ struct blk_mq_ctx {/* incremented at completion time */ unsigned long ____cacheline_aligned_in_smp rq_completed[2 ]; -struct blk_rq_stat stat[2 ];struct request_queue *queue;struct kobject kobj;
block/blk-stat.c +134 -166
354 lines not on this page · raw message
block/blk-stat.h +82 -16
116 lines not on this page · raw message
block/blk-sysfs.c +5 -0
@@ -785,6 +785,9 @@ static void blk_release_queue(struct kobject *kobj) container_of(kobj, struct request_queue, kobj);wbt_exit (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);bdi_put (q->backing_dev_info);blkcg_exit_queue (q);
@@ -793,6 +796,8 @@ static void blk_release_queue(struct kobject *kobj)elevator_exit (q->elevator); } +blk_free_queue_stats (q->stats); +blk_exit_rl (&q->root_rl);if (q->queue_tags)
block/blk-wbt.c +19 -29
133 lines not on this page · raw message
block/blk-wbt.h +1 -1
@@ -81,7 +81,7 @@ struct rq_wb {u64 win_nsec;/* default window size */ u64 cur_win_nsec;/* current window size */ -struct timer_list window_timer; +struct blk_stat_callback *cb;s64 sync_issue;void *sync_cookie;
include/linux/blk_types.h +0 -3
@@ -287,8 +287,6 @@ struct blk_issue_stat {u64 time; }; -#define BLK_RQ_STAT_BATCH 64 -struct blk_rq_stat {s64 mean;u64 min;
@@ -296,7 +294,6 @@ struct blk_rq_stat {s32 nr_samples;s32 nr_batch;u64 batch; -s64 time; };struct blk_stats {
include/linux/blkdev.h +8 -2
@@ -40,6 +40,8 @@ struct blkcg_gq;struct blk_flush_queue ;struct pr_ops ;struct rq_wb ; +struct blk_queue_stats ; +struct blk_stat_callback ;#define BLKDEV_MIN_RQ 4#define BLKDEV_MAX_RQ 128/* Default maximum */
@@ -388,6 +390,7 @@ struct request_queue {int nr_rqs[2 ];/* # allocated [a]sync rqs */ int nr_rqs_elvpriv;/* # allocated rqs w/ elvpriv */ +struct blk_queue_stats *stats;struct rq_wb *rq_wb;/*
@@ -505,8 +508,6 @@ struct request_queue {unsigned int nr_sorted;unsigned int in_flight[2 ]; -struct blk_rq_stat rq_stats[2 ]; -/* * Number of active block driver functions for which blk_drain_queue() * must wait. Must be incremented around functions that unlock the
@@ -516,6 +517,10 @@ struct request_queue {unsigned int rq_timeout;int poll_nsec; + +struct blk_stat_callback *poll_cb; +struct blk_stats poll_stats; +struct timer_list timeout;struct work_struct timeout_work;struct list_head timeout_list;
@@ -611,6 +616,7 @@ struct request_queue {#define QUEUE_FLAG_DAX 26/* device supports DAX */ #define QUEUE_FLAG_STATS 27/* track rq completion times */ #define QUEUE_FLAG_RESTART 28/* queue needs restart at completion */ +#define QUEUE_FLAG_POLL_STATS 29/* collecting stats for hybrid polling */ #define QUEUE_FLAG_DEFAULT ((1 << QUEUE_FLAG_IO_STAT) | \ (1 << QUEUE_FLAG_STACKABLE) | \
Signature · 1 line
2.12.0
Quoted 10 lines · Omar Sandoval
From: Omar Sandoval <[email protected]>
This patchset generalizes the blk-stats infrastructure to allow users to register a callback to be called at a given time with the statistics of requests completed during that window. Writeback throttling and hybrid polling are converted to the new infrastructure. The ultimate goal is to use this infrastructure for the mq I/O scheduler.
The details are in patch 4, which is the actual conversion. Patches 1-3 are preparation cleanups.
FYI, the diff of block/blk-stat.[ch] is a pain to read. It's easier to just apply the patches and look at the whole files.