From: Keith Busch <kbusch@kernel.org> mainline inclusion from mainline-v6.13-rc1 commit 110234da18ab482f6f583d28eff26b9569bf5622 category: feature bugzilla: https://atomgit.com/openeuler/kernel/issues/10302 Reference: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?i... -------------------------------- Applications using the passthrough interfaces for IO want to continue seeing the disk stats. These requests had been fenced off from this block layer feature. While the block layer doesn't necessarily know what a passthrough command does, we do know the data size and direction, which is enough to account for the command's stats. Since tracking these has the potential to produce unexpected results, the passthrough stats are locked behind a new queue flag that needs to be enabled with the /sys/block/<dev>/queue/iostats_passthrough attribute. Signed-off-by: Keith Busch <kbusch@kernel.org> Reviewed-by: Christoph Hellwig <hch@lst.de> Signed-off-by: Jens Axboe <axboe@kernel.dk> Conflicts: block/blk.h block/blk-sysfs.c block/blk-mq-debugfs.c include/linux/blkdev.h Documentation/ABI/stable/sysfs-block [6.6 uses queue flags instead of queue_limits/BLK_FEAT_*. Mainline puts blk_rq_passthrough_stats() in block/blk-mq.c because it changes blk_account_io_start() there. In 6.6 the passthrough gate is blk_do_io_stat() in block/blk.h, which is also used from block/blk-merge.c, so blk_rq_passthrough_stats() is defined in block/blk.h and called from blk_do_io_stat(). The new QUEUE_FLAG_IOSTATS_PASSTHROUGH uses free bit 23 and is toggled with blk_queue_flag_set/clear; its name is added to blk_queue_flag_name[] in block/blk-mq-debugfs.c. The sysfs callbacks take struct request_queue *q instead of struct gendisk *disk.] Signed-off-by: Zizhi Wo <wozizhi@huawei.com> --- Documentation/ABI/stable/sysfs-block | 8 +++++++ block/blk-mq-debugfs.c | 1 + block/blk-sysfs.c | 26 ++++++++++++++++++++++ block/blk.h | 33 +++++++++++++++++++++++++++- include/linux/blkdev.h | 3 +++ 5 files changed, 70 insertions(+), 1 deletion(-) diff --git a/Documentation/ABI/stable/sysfs-block b/Documentation/ABI/stable/sysfs-block index 97fc8034a271..8967a4927047 100644 --- a/Documentation/ABI/stable/sysfs-block +++ b/Documentation/ABI/stable/sysfs-block @@ -427,10 +427,18 @@ Description: accounted by sampling and the result is not accurate; If this value is 2, iostats accounting is enabled and io_ticks is accounted precisely, but there will be slightly more overhead. +What: /sys/block/<disk>/queue/iostats_passthrough +Date: October 2024 +Contact: linux-block@vger.kernel.org +Description: + [RW] This file is used to control (on/off) the iostats + accounting of the disk for passthrough commands. + + What: /sys/block/<disk>/queue/logical_block_size Date: May 2009 Contact: Martin K. Petersen <martin.petersen@oracle.com> Description: [RO] This is the smallest unit the storage device can address. diff --git a/block/blk-mq-debugfs.c b/block/blk-mq-debugfs.c index 033f0171c8ba..8ac15fb05ee7 100644 --- a/block/blk-mq-debugfs.c +++ b/block/blk-mq-debugfs.c @@ -98,10 +98,11 @@ static const char *const blk_queue_flag_name[] = { QUEUE_FLAG_NAME(WC), QUEUE_FLAG_NAME(FUA), QUEUE_FLAG_NAME(DAX), QUEUE_FLAG_NAME(STATS), QUEUE_FLAG_NAME(REGISTERED), + QUEUE_FLAG_NAME(IOSTATS_PASSTHROUGH), QUEUE_FLAG_NAME(QUIESCED), QUEUE_FLAG_NAME(PCI_P2PDMA), QUEUE_FLAG_NAME(ZONE_RESETALL), QUEUE_FLAG_NAME(RQ_ALLOC_TIME), QUEUE_FLAG_NAME(HCTX_ACTIVE), diff --git a/block/blk-sysfs.c b/block/blk-sysfs.c index 89a142955c28..3384e6793c0c 100644 --- a/block/blk-sysfs.c +++ b/block/blk-sysfs.c @@ -356,10 +356,34 @@ static ssize_t queue_max_open_zones_show(struct request_queue *q, char *page) static ssize_t queue_max_active_zones_show(struct request_queue *q, char *page) { return queue_var_show(bdev_max_active_zones(q->disk->part0), page); } +static ssize_t queue_iostats_passthrough_show(struct request_queue *q, + char *page) +{ + return queue_var_show(blk_queue_passthrough_stat(q), page); +} + +static ssize_t queue_iostats_passthrough_store(struct request_queue *q, + const char *page, size_t count) +{ + unsigned long ios; + ssize_t ret; + + ret = queue_var_store(&ios, page, count); + if (ret < 0) + return ret; + + if (ios) + blk_queue_flag_set(QUEUE_FLAG_IOSTATS_PASSTHROUGH, q); + else + blk_queue_flag_clear(QUEUE_FLAG_IOSTATS_PASSTHROUGH, q); + + return count; +} + static ssize_t queue_nomerges_show(struct request_queue *q, char *page) { return queue_var_show((blk_queue_nomerges(q) << 1) | blk_queue_noxmerges(q), page); } @@ -585,10 +609,11 @@ QUEUE_RO_ENTRY(queue_zoned, "zoned"); QUEUE_RO_ENTRY(queue_nr_zones, "nr_zones"); QUEUE_RO_ENTRY(queue_max_open_zones, "max_open_zones"); QUEUE_RO_ENTRY(queue_max_active_zones, "max_active_zones"); QUEUE_RW_ENTRY(queue_nomerges, "nomerges"); +QUEUE_RW_ENTRY(queue_iostats_passthrough, "iostats_passthrough"); QUEUE_RW_ENTRY(queue_rq_affinity, "rq_affinity"); QUEUE_RW_ENTRY(queue_poll, "io_poll"); QUEUE_RW_ENTRY(queue_poll_delay, "io_poll_delay"); QUEUE_RW_ENTRY(queue_wc, "write_cache"); QUEUE_RO_ENTRY(queue_fua, "fua"); @@ -713,10 +738,11 @@ static struct attribute *queue_attrs[] = { &queue_zoned_entry.attr, &queue_nr_zones_entry.attr, &queue_max_open_zones_entry.attr, &queue_max_active_zones_entry.attr, &queue_nomerges_entry.attr, + &queue_iostats_passthrough_entry.attr, &queue_iostats_entry.attr, &queue_stable_writes_entry.attr, &queue_random_entry.attr, &queue_poll_entry.attr, &queue_wc_entry.attr, diff --git a/block/blk.h b/block/blk.h index 0edc05898ca0..9ab353171702 100644 --- a/block/blk.h +++ b/block/blk.h @@ -358,19 +358,50 @@ bool blk_rq_merge_ok(struct request *rq, struct bio *bio); enum elv_merge blk_try_merge(struct request *rq, struct bio *bio); void blk_set_default_limits(struct queue_limits *lim); int blk_dev_init(void); +static inline bool blk_rq_passthrough_stats(struct request *req) +{ + struct bio *bio = req->bio; + + if (!blk_queue_passthrough_stat(req->q)) + return false; + + /* Requests without a bio do not transfer data. */ + if (!bio) + return false; + + /* + * Stats are accumulated in the bdev, so must have one attached to a + * bio to track stats. Most drivers do not set the bdev for passthrough + * requests, but nvme is one that will set it. + */ + if (!bio->bi_bdev) + return false; + + /* + * We don't know what a passthrough command does, but we know the + * payload size and data direction. Ensuring the size is aligned to the + * block size filters out most commands with payloads that don't + * represent sector access. + */ + if (blk_rq_bytes(req) & (bdev_logical_block_size(bio->bi_bdev) - 1)) + return false; + return true; +} + /* * Contribute to IO statistics IFF: * * a) it's attached to a gendisk, and * b) the queue had IO stats enabled when this request was started */ static inline bool blk_do_io_stat(struct request *rq) { - return (rq->rq_flags & RQF_IO_STAT) && !blk_rq_is_passthrough(rq); + return (rq->rq_flags & RQF_IO_STAT) && + (!blk_rq_is_passthrough(rq) || blk_rq_passthrough_stats(rq)); } void update_io_ticks(struct block_device *part, unsigned long now, bool end); unsigned int part_in_flight(struct block_device *part); diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h index 442c5c82b36b..d1abffe59af9 100644 --- a/include/linux/blkdev.h +++ b/include/linux/blkdev.h @@ -612,10 +612,11 @@ struct request_queue { #define QUEUE_FLAG_WC 17 /* Write back caching */ #define QUEUE_FLAG_FUA 18 /* device supports FUA writes */ #define QUEUE_FLAG_DAX 19 /* device supports DAX */ #define QUEUE_FLAG_STATS 20 /* track IO start and completion times */ #define QUEUE_FLAG_REGISTERED 22 /* queue has been registered to a disk */ +#define QUEUE_FLAG_IOSTATS_PASSTHROUGH 23 /* passthrough command IO accounting */ #define QUEUE_FLAG_QUIESCED 24 /* queue has been quiesced */ #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_HCTX_ACTIVE 28 /* at least one blk-mq hctx is active */ @@ -639,10 +640,12 @@ bool blk_queue_flag_test_and_set(unsigned int flag, struct request_queue *q); test_bit(QUEUE_FLAG_NOXMERGES, &(q)->queue_flags) #define blk_queue_nonrot(q) test_bit(QUEUE_FLAG_NONROT, &(q)->queue_flags) #define blk_queue_stable_writes(q) \ test_bit(QUEUE_FLAG_STABLE_WRITES, &(q)->queue_flags) #define blk_queue_io_stat(q) test_bit(QUEUE_FLAG_IO_STAT, &(q)->queue_flags) +#define blk_queue_passthrough_stat(q) \ + test_bit(QUEUE_FLAG_IOSTATS_PASSTHROUGH, &(q)->queue_flags) #define blk_queue_precise_io_stat(q) \ test_bit(QUEUE_FLAG_PRECISE_IO_STAT, &(q)->queue_flags) #define blk_queue_add_random(q) test_bit(QUEUE_FLAG_ADD_RANDOM, &(q)->queue_flags) #define blk_queue_zone_resetall(q) \ test_bit(QUEUE_FLAG_ZONE_RESETALL, &(q)->queue_flags) -- 2.52.0