All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH RFC v2 0/2] fix NVMe multipath partition diskstats
@ 2026-08-18 12:08 John Garry
  2026-08-18 12:08 ` [PATCH RFC v2 1/2] block: scan partitions for hidden disks John Garry
  2026-08-18 12:08 ` [PATCH RFC v2 2/2] nvme-multipath: fix diskstats for partitions John Garry
  0 siblings, 2 replies; 7+ messages in thread
From: John Garry @ 2026-08-18 12:08 UTC (permalink / raw)
  To: axboe, kbusch, hch, sagi; +Cc: linux-block, linux-nvme, John Garry

Currently the NVMe multipath partition diskstats are not maintained.
This is because the functionality to update the diskstats only updates
for the whole disk (and not per partition).

As an example:
$ ./fio_read nvme1n1p1 # run traffic on /dev/nvme1n1p1
...
$ more /proc/diskstats | grep nvme1
 259       2 nvme1c1n1 49857 0 400344 768565 0 0 0 0 0 2334 768565 0 0 0 0 0 0
 259       3 nvme1n1 99710 0 800680 1599285 0 0 0 0 0 2346 1599285 0 0 0 0 0 0
 259       5 nvme1n1p1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
 259       4 nvme1c2n1 49853 0 400336 831472 0 0 0 0 0 2315 831472 0 0 0 0 0 0

Notice how all values are 0 for nvme1n1p1.

This series addressed that problem by first updating the gendisk code to
create the partition table for hidden disks, as suggested by Keith.

Secondly the NVMe multipath functionality to handle the diskstats is
modified to handle the gendisk partition, by looking up this partition
from the per-path disk partition.

Original RFC is at the following:
https://lore.kernel.org/linux-nvme/20260721114553.1657841-1-john.g.garry@oracle.com/

John Garry (2):
  block: scan partitions for hidden disks
  nvme-multipath: fix diskstats for partitions

 block/genhd.c                 | 20 ++++++++++----------
 block/partitions/core.c       |  5 +++--
 drivers/nvme/host/core.c      |  4 ----
 drivers/nvme/host/multipath.c | 31 +++++++++++++++++++++++++------
 include/linux/blkdev.h        |  2 +-
 5 files changed, 39 insertions(+), 23 deletions(-)

-- 
2.43.7


^ permalink raw reply	[flat|nested] 7+ messages in thread

* [PATCH RFC v2 1/2] block: scan partitions for hidden disks
  2026-08-18 12:08 [PATCH RFC v2 0/2] fix NVMe multipath partition diskstats John Garry
@ 2026-08-18 12:08 ` John Garry
  2026-08-19  9:00   ` Christoph Hellwig
  2026-08-18 12:08 ` [PATCH RFC v2 2/2] nvme-multipath: fix diskstats for partitions John Garry
  1 sibling, 1 reply; 7+ messages in thread
From: John Garry @ 2026-08-18 12:08 UTC (permalink / raw)
  To: axboe, kbusch, hch, sagi; +Cc: linux-block, linux-nvme, John Garry, John Garry

From: John Garry <john.garry@linux.dev>

If a disk is hidden (GENHD_FL_HIDDEN flags set), we currently do not scan
the disk partition table.

GENHD_FL_HIDDEN is used in NVMe multipath support to hide the per-path
disk.

However, it would be useful there to actually have the per-path disk
partition table available for situations where we want to send bios to
a specific per-path disk partition.

Change GENHD_FL_HIDDEN to scan partitions. For anyone wanting to avoid
scanning the partition, flag GENHD_FL_NO_PART can still be used.

In diskstats_show(), don't show per-path partitions as this info is not
too interesting and can just bloat the output.

Signed-off-by: John Garry <john.g.garry@oracle.com>
---
 block/genhd.c            | 20 ++++++++++----------
 block/partitions/core.c  |  5 +++--
 drivers/nvme/host/core.c |  4 ----
 include/linux/blkdev.h   |  2 +-
 4 files changed, 14 insertions(+), 17 deletions(-)

diff --git a/block/genhd.c b/block/genhd.c
index 30ac0ffe65174..05301ee5d4ca5 100644
--- a/block/genhd.c
+++ b/block/genhd.c
@@ -406,15 +406,15 @@ static void add_disk_final(struct gendisk *disk)
 {
 	struct device *ddev = disk_to_dev(disk);
 
-	if (!(disk->flags & GENHD_FL_HIDDEN)) {
-		/* Make sure the first partition scan will be proceed */
-		if (get_capacity(disk) && disk_has_partscan(disk))
-			set_bit(GD_NEED_PART_SCAN, &disk->state);
+	/* Make sure the first partition scan will be proceed */
+	if (get_capacity(disk) && disk_has_partscan(disk))
+		set_bit(GD_NEED_PART_SCAN, &disk->state);
 
-		bdev_add(disk->part0, ddev->devt);
-		if (get_capacity(disk))
-			disk_scan_partitions(disk, BLK_OPEN_READ);
+	bdev_add(disk->part0, ddev->devt);
+	if (get_capacity(disk))
+		disk_scan_partitions(disk, BLK_OPEN_READ);
 
+	if (!(disk->flags & GENHD_FL_HIDDEN)) {
 		/*
 		 * Announce the disk and partitions after all partitions are
 		 * created. (for hidden disks uevents remain suppressed forever)
@@ -491,8 +491,7 @@ static int __add_disk(struct device *parent, struct gendisk *disk,
 	dev_set_name(ddev, "%s", disk->disk_name);
 	if (fwnode)
 		device_set_node(ddev, fwnode);
-	if (!(disk->flags & GENHD_FL_HIDDEN))
-		ddev->devt = MKDEV(disk->major, disk->first_minor);
+	ddev->devt = MKDEV(disk->major, disk->first_minor);
 	ret = device_add(ddev);
 	if (ret)
 		goto out_free_ext_minor;
@@ -1368,7 +1367,8 @@ static int diskstats_show(struct seq_file *seqf, void *v)
 
 	rcu_read_lock();
 	xa_for_each(&gp->part_tbl, idx, hd) {
-		if (bdev_is_partition(hd) && !bdev_nr_sectors(hd))
+		if (bdev_is_partition(hd) &&
+		    (!bdev_nr_sectors(hd) ||(gp->flags & GENHD_FL_HIDDEN)))
 			continue;
 
 		inflight = bdev_count_inflight(hd);
diff --git a/block/partitions/core.c b/block/partitions/core.c
index b5c59b79ca7cb..a48896c6c791d 100644
--- a/block/partitions/core.c
+++ b/block/partitions/core.c
@@ -152,7 +152,8 @@ static struct parsed_partitions *check_partition(struct gendisk *hd)
 
 	}
 	if (res > 0) {
-		printk(KERN_INFO "%s", seq_buf_str(&state->pp_buf));
+		if (!(hd->flags & GENHD_FL_HIDDEN))
+			printk(KERN_INFO "%s", seq_buf_str(&state->pp_buf));
 
 		kfree(state->pp_buf.buffer);
 		return state;
@@ -164,7 +165,7 @@ static struct parsed_partitions *check_partition(struct gendisk *hd)
 	 */
 	if (err)
 		res = err;
-	if (res) {
+	if (res && !(hd->flags & GENHD_FL_HIDDEN)) {
 		seq_buf_puts(&state->pp_buf,
 			     " unable to read partition table\n");
 		printk(KERN_INFO "%s", seq_buf_str(&state->pp_buf));
diff --git a/drivers/nvme/host/core.c b/drivers/nvme/host/core.c
index 1322c678f4eb8..ed21f4ef36c9a 100644
--- a/drivers/nvme/host/core.c
+++ b/drivers/nvme/host/core.c
@@ -1808,10 +1808,6 @@ static void nvme_enable_aen(struct nvme_ctrl *ctrl)
 
 static int nvme_ns_open(struct nvme_ns *ns)
 {
-
-	/* should never be called due to GENHD_FL_HIDDEN */
-	if (WARN_ON_ONCE(nvme_ns_head_multipath(ns->head)))
-		goto fail;
 	if (!nvme_get_ns(ns))
 		goto fail;
 	if (!try_module_get(ns->ctrl->ops->module))
diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h
index 9213a5716f95a..a270cf1394c7a 100644
--- a/include/linux/blkdev.h
+++ b/include/linux/blkdev.h
@@ -260,7 +260,7 @@ static inline unsigned int disk_openers(struct gendisk *disk)
  */
 static inline bool disk_has_partscan(struct gendisk *disk)
 {
-	return !(disk->flags & (GENHD_FL_NO_PART | GENHD_FL_HIDDEN)) &&
+	return !(disk->flags & GENHD_FL_NO_PART) &&
 		!test_bit(GD_SUPPRESS_PART_SCAN, &disk->state);
 }
 
-- 
2.43.7


^ permalink raw reply related	[flat|nested] 7+ messages in thread

* [PATCH RFC v2 2/2] nvme-multipath: fix diskstats for partitions
  2026-08-18 12:08 [PATCH RFC v2 0/2] fix NVMe multipath partition diskstats John Garry
  2026-08-18 12:08 ` [PATCH RFC v2 1/2] block: scan partitions for hidden disks John Garry
@ 2026-08-18 12:08 ` John Garry
  1 sibling, 0 replies; 7+ messages in thread
From: John Garry @ 2026-08-18 12:08 UTC (permalink / raw)
  To: axboe, kbusch, hch, sagi; +Cc: linux-block, linux-nvme, John Garry, John Garry

From: John Garry <john.garry@linux.dev>

Currently diskstats for partitions are never updated:
$ ./fio_read nvme1n1p1 # run traffic on /dev/nvme1n1p1
...
$ more /proc/diskstats | grep nvme1
 259       2 nvme1c1n1 49857 0 400344 768565 0 0 0 0 0 2334 768565 0 0 0 0 0 0
 259       3 nvme1n1 99710 0 800680 1599285 0 0 0 0 0 2346 1599285 0 0 0 0 0 0
 259       5 nvme1n1p1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
 259       4 nvme1c2n1 49853 0 400336 831472 0 0 0 0 0 2315 831472 0 0 0 0 0 0

This is because we only ever update the diskstats for the multipath disk in
nvme_mpath_end_request(), and we never take into account that the original
bi_bdev may been a partition of this disk.

Functions bdev_start_io_acct() and bdev_start_io_acct() do handle
updating diskstats for a partition, in that they also update the whole
disk also (if a partition), so use the partition (if applicable) when
calling those functions.

Change any functionality which used to lookup the gendisk part0 to now
lookup the specific gendisk partition.

Signed-off-by: John Garry <john.g.garry@oracle.com>
---
 drivers/nvme/host/multipath.c | 31 +++++++++++++++++++++++++------
 1 file changed, 25 insertions(+), 6 deletions(-)

diff --git a/drivers/nvme/host/multipath.c b/drivers/nvme/host/multipath.c
index 75dbb58286a32..5c95a2ab7755f 100644
--- a/drivers/nvme/host/multipath.c
+++ b/drivers/nvme/host/multipath.c
@@ -144,6 +144,19 @@ void nvme_mpath_start_freeze(struct nvme_subsystem *subsys)
 			blk_freeze_queue_start(h->disk->queue);
 }
 
+static struct block_device *nvme_to_disk_part(struct gendisk *disk, u8 partno)
+{
+	struct block_device *bdev;
+
+	/* Quick lookup for whole disk */
+	if (!partno)
+		return disk->part0;
+	rcu_read_lock();
+	bdev = xa_load(&disk->part_tbl, partno);
+	rcu_read_unlock();
+	return bdev;
+}
+
 void nvme_failover_req(struct request *req)
 {
 	struct nvme_ns *ns = req->q->queuedata;
@@ -165,8 +178,10 @@ void nvme_failover_req(struct request *req)
 	}
 
 	spin_lock_irqsave(&ns->head->requeue_lock, flags);
-	for (bio = req->bio; bio; bio = bio->bi_next)
-		bio_set_dev(bio, ns->head->disk->part0);
+	for (bio = req->bio; bio; bio = bio->bi_next) {
+		bio_set_dev(bio, nvme_to_disk_part(ns->head->disk,
+			bdev_partno(bio->bi_bdev)));
+	}
 	blk_steal_bios(&ns->head->requeue_list, req);
 	spin_unlock_irqrestore(&ns->head->requeue_lock, flags);
 
@@ -194,8 +209,9 @@ void nvme_mpath_start_request(struct request *rq)
 		return;
 
 	nvme_req(rq)->flags |= NVME_MPATH_IO_STATS;
-	nvme_req(rq)->start_time = bdev_start_io_acct(disk->part0, req_op(rq),
-						      jiffies);
+	nvme_req(rq)->start_time = bdev_start_io_acct(
+			nvme_to_disk_part(disk, bdev_partno(rq->part)),
+			req_op(rq), jiffies);
 }
 EXPORT_SYMBOL_GPL(nvme_mpath_start_request);
 
@@ -208,7 +224,8 @@ void nvme_mpath_end_request(struct request *rq)
 
 	if (!(nvme_req(rq)->flags & NVME_MPATH_IO_STATS))
 		return;
-	bdev_end_io_acct(ns->head->disk->part0, req_op(rq),
+	bdev_end_io_acct(nvme_to_disk_part(ns->head->disk,
+			 bdev_partno(rq->part)), req_op(rq),
 			 blk_rq_bytes(rq) >> SECTOR_SHIFT,
 			 nvme_req(rq)->start_time);
 }
@@ -549,7 +566,9 @@ static void nvme_ns_head_submit_bio(struct bio *bio)
 	srcu_idx = srcu_read_lock(&head->srcu);
 	ns = nvme_find_path(head);
 	if (likely(ns)) {
-		bio_set_dev(bio, ns->disk->part0);
+		bio_set_dev(bio, nvme_to_disk_part(ns->disk,
+				bdev_partno(bio->bi_bdev)));
+
 		/*
 		 * Use BIO_REMAPPED to skip bio_check_eod() when this bio
 		 * enters submit_bio_noacct() for the per-path device. The EOD
-- 
2.43.7


^ permalink raw reply related	[flat|nested] 7+ messages in thread

* Re: [PATCH RFC v2 1/2] block: scan partitions for hidden disks
  2026-08-18 12:08 ` [PATCH RFC v2 1/2] block: scan partitions for hidden disks John Garry
@ 2026-08-19  9:00   ` Christoph Hellwig
  2026-08-19  9:12     ` John Garry
  0 siblings, 1 reply; 7+ messages in thread
From: Christoph Hellwig @ 2026-08-19  9:00 UTC (permalink / raw)
  To: John Garry; +Cc: axboe, kbusch, hch, sagi, linux-block, linux-nvme, John Garry

On Tue, Aug 18, 2026 at 12:08:53PM +0000, John Garry wrote:
> From: John Garry <john.garry@linux.dev>
> 
> If a disk is hidden (GENHD_FL_HIDDEN flags set), we currently do not scan
> the disk partition table.
> 
> GENHD_FL_HIDDEN is used in NVMe multipath support to hide the per-path
> disk.

It also is used for giving a handle to NVMe devices that we can't support
as block devices and for which we only want a handle to issue passthrough
commands.  For those attempting a partition scan is rather suboptimal.

As is in general doing an extra scan per controller when we know it must
give the same results.  Wouldn't it make more sense to just propagate
the partition table from the ns_head to each individual path?



^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [PATCH RFC v2 1/2] block: scan partitions for hidden disks
  2026-08-19  9:00   ` Christoph Hellwig
@ 2026-08-19  9:12     ` John Garry
  2026-08-19  9:13       ` Christoph Hellwig
  0 siblings, 1 reply; 7+ messages in thread
From: John Garry @ 2026-08-19  9:12 UTC (permalink / raw)
  To: Christoph Hellwig, John Garry
  Cc: axboe, kbusch, sagi, linux-block, linux-nvme

On 8/19/26 10:00, Christoph Hellwig wrote:
> On Tue, Aug 18, 2026 at 12:08:53PM +0000, John Garry wrote:
>> From: John Garry<john.garry@linux.dev>
>>
>> If a disk is hidden (GENHD_FL_HIDDEN flags set), we currently do not scan
>> the disk partition table.
>>
>> GENHD_FL_HIDDEN is used in NVMe multipath support to hide the per-path
>> disk.
> It also is used for giving a handle to NVMe devices that we can't support
> as block devices and for which we only want a handle to issue passthrough
> commands.  For those attempting a partition scan is rather suboptimal.

We could set GENHD_FL_NO_PART in that case (as well as the hidden flag).

> 
> As is in general doing an extra scan per controller when we know it must
> give the same results.  Wouldn't it make more sense to just propagate
> the partition table from the ns_head to each individual path?

So you mean that we would have something which can clone the head 
gendisk partitions onto the per-path gendisk by itering  >part_tbl and 
calling something like blk_add_partition(), right?


^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [PATCH RFC v2 1/2] block: scan partitions for hidden disks
  2026-08-19  9:12     ` John Garry
@ 2026-08-19  9:13       ` Christoph Hellwig
  2026-08-19  9:43         ` John Garry
  0 siblings, 1 reply; 7+ messages in thread
From: Christoph Hellwig @ 2026-08-19  9:13 UTC (permalink / raw)
  To: John Garry
  Cc: Christoph Hellwig, John Garry, axboe, kbusch, sagi, linux-block,
	linux-nvme

On Wed, Aug 19, 2026 at 10:12:50AM +0100, John Garry wrote:
>> It also is used for giving a handle to NVMe devices that we can't support
>> as block devices and for which we only want a handle to issue passthrough
>> commands.  For those attempting a partition scan is rather suboptimal.
>
> We could set GENHD_FL_NO_PART in that case (as well as the hidden flag).

I guess we could do that.  It still feels wrong, though :

>> As is in general doing an extra scan per controller when we know it must
>> give the same results.  Wouldn't it make more sense to just propagate
>> the partition table from the ns_head to each individual path?
>
> So you mean that we would have something which can clone the head gendisk 
> partitions onto the per-path gendisk by itering  >part_tbl and calling 
> something like blk_add_partition(), right?

Yes.


^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [PATCH RFC v2 1/2] block: scan partitions for hidden disks
  2026-08-19  9:13       ` Christoph Hellwig
@ 2026-08-19  9:43         ` John Garry
  0 siblings, 0 replies; 7+ messages in thread
From: John Garry @ 2026-08-19  9:43 UTC (permalink / raw)
  To: Christoph Hellwig
  Cc: John Garry, axboe, kbusch, sagi, linux-block, linux-nvme

On 8/19/26 10:13, Christoph Hellwig wrote:
> On Wed, Aug 19, 2026 at 10:12:50AM +0100, John Garry wrote:
>>> It also is used for giving a handle to NVMe devices that we can't support
>>> as block devices and for which we only want a handle to issue passthrough
>>> commands.  For those attempting a partition scan is rather suboptimal.
>>
>> We could set GENHD_FL_NO_PART in that case (as well as the hidden flag).
> 
> I guess we could do that.  It still feels wrong, though :

Maybe we just don't change the GENHD_FL_HIDDEN behavior. As long as 
/proc/diskstats and /proc/partitions don't show those per-path partitions.

It seems to me that we need to have a dedicated function to clone the 
partitions, and it would be called per NS after nvme_mpath_set_live() -> 
device_add_disk(). I am not sure if that works, though. We may get 
nvme_ns_head_submit_bio() looking up the per-path partition before they 
are available. In that case I would need to keep the path disabled until 
after the partition table is available.

> 
>>> As is in general doing an extra scan per controller when we know it must
>>> give the same results.  Wouldn't it make more sense to just propagate
>>> the partition table from the ns_head to each individual path?
>>
>> So you mean that we would have something which can clone the head gendisk
>> partitions onto the per-path gendisk by itering  >part_tbl and calling
>> something like blk_add_partition(), right?
> 
> Yes.

ok


^ permalink raw reply	[flat|nested] 7+ messages in thread

end of thread, other threads:[~2026-08-19  9:44 UTC | newest]

Thread overview: 7+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-08-18 12:08 [PATCH RFC v2 0/2] fix NVMe multipath partition diskstats John Garry
2026-08-18 12:08 ` [PATCH RFC v2 1/2] block: scan partitions for hidden disks John Garry
2026-08-19  9:00   ` Christoph Hellwig
2026-08-19  9:12     ` John Garry
2026-08-19  9:13       ` Christoph Hellwig
2026-08-19  9:43         ` John Garry
2026-08-18 12:08 ` [PATCH RFC v2 2/2] nvme-multipath: fix diskstats for partitions John Garry

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.