[PATCH v3 5/5] block, nvme: add same-LBA multipath limit helpers

Yao Sang <[email protected]>
Newsgroups org.kernel.vger.linux-block,org.infradead.lists.linux-nvme
Message-ID <[email protected]>
A same-LBA multipath head and its paths address the same logical block
space, so they do not need mapped-range topology calculations.

Add blk_stack_mpath_limits() to stack limits that may differ between
paths, and blk_set_mpath_head_limits() to set head limits that are
expected to be identical across paths. Export both helpers and use them
for NVMe namespace heads.

Initialize max_hw_discard_sectors to UINT_MAX before stacking the first
path. A zero limit means that a path does not support discard, so it
disables discard for the head.

Signed-off-by: Yao Sang <[email protected]>
---
 block/blk-settings.c          | 58 +++++++++++++++++++++++++++++++++++
 drivers/nvme/host/core.c      | 34 ++-------------------
 drivers/nvme/host/multipath.c |  2 ++
 include/linux/blkdev.h        |  3 ++
 4 files changed, 65 insertions(+), 32 deletions(-)

diff --git a/block/blk-settings.c b/block/blk-settings.c
index eaba38370657..8f70bab0a814 100644
--- a/block/blk-settings.c
+++ b/block/blk-settings.c
@@ -800,6 +800,64 @@ static void blk_stack_path_limits(struct queue_limits *t,
 	t->dma_alignment = max(t->dma_alignment, b->dma_alignment);
 }
 
+/**
+ * blk_set_mpath_head_limits - set head limits common to all paths
+ * @t: limits for the multipath head
+ * @b: limits for one path
+ *
+ * Set head limits that are expected to be identical across paths. Stack
+ * limits that may differ between paths with blk_stack_mpath_limits().
+ */
+void blk_set_mpath_head_limits(struct queue_limits *t,
+			       struct queue_limits *b)
+{
+	t->logical_block_size = b->logical_block_size;
+	t->physical_block_size = b->physical_block_size;
+	t->alignment_offset = b->alignment_offset;
+	t->io_min = b->io_min;
+	t->io_opt = b->io_opt;
+	t->discard_granularity = b->discard_granularity;
+	t->discard_alignment = b->discard_alignment;
+	t->zone_write_granularity = b->zone_write_granularity;
+	t->max_write_streams = b->max_write_streams;
+	t->write_stream_granularity = b->write_stream_granularity;
+}
+EXPORT_SYMBOL_GPL(blk_set_mpath_head_limits);
+
+/**
+ * blk_stack_mpath_limits - stack limits across same-LBA multipath paths
+ * @t: limits for the multipath head
+ * @b: limits for one path
+ *
+ * Stack limits in @b that may differ between paths. Unlike
+ * blk_stack_limits(), this does not apply mapped-range topology or a mapping
+ * offset. Set limits that are expected to be identical across paths with
+ * blk_set_mpath_head_limits().
+ *
+ * Initialize @t with blk_set_stacking_limits() and set features that require
+ * support from every path before the first call. Set
+ * @t->max_hw_discard_sectors to UINT_MAX and call once for each path. A zero
+ * discard limit disables discard for the head.
+ */
+void blk_stack_mpath_limits(struct queue_limits *t, struct queue_limits *b)
+{
+	if (b->chunk_sectors)
+		t->chunk_sectors = gcd(t->chunk_sectors, b->chunk_sectors);
+
+	t->features |= b->features &
+		(BLK_FEAT_WRITE_CACHE | BLK_FEAT_FUA |
+		 BLK_FEAT_ROTATIONAL | BLK_FEAT_STABLE_WRITES);
+	blk_stack_path_limits(t, b);
+	STACK_MIN(t, b, max_hw_discard_sectors);
+	blk_stack_atomic_writes_hw_limits(t, b);
+
+	if (t->features & BLK_FEAT_ZONED) {
+		STACK_MIN_NOT_ZERO(t, b, max_open_zones);
+		STACK_MIN_NOT_ZERO(t, b, max_active_zones);
+	}
+}
+EXPORT_SYMBOL_GPL(blk_stack_mpath_limits);
+
 /*
  * Stack block sizes, I/O granularities, chunk boundaries and alignment for a
  * bottom-device range mapped at @start. Round maximum sector limits after the
diff --git a/drivers/nvme/host/core.c b/drivers/nvme/host/core.c
index 8e2b44ed4366..a16986ec1c8e 100644
--- a/drivers/nvme/host/core.c
+++ b/drivers/nvme/host/core.c
@@ -2530,14 +2530,6 @@ static int nvme_update_ns_info_block(struct nvme_ns *ns,
 	return ret;
 }
 
-static void nvme_stack_zone_resources(struct queue_limits *t,
-				      const struct queue_limits *b)
-{
-	t->max_open_zones = min_not_zero(t->max_open_zones, b->max_open_zones);
-	t->max_active_zones =
-		min_not_zero(t->max_active_zones, b->max_active_zones);
-}
-
 static int nvme_update_ns_head_limits(struct nvme_ns *ns,
 		struct nvme_ns_info *info, bool unsupported)
 {
@@ -2549,34 +2541,12 @@ static int nvme_update_ns_head_limits(struct nvme_ns *ns,
 
 	lim = queue_limits_start_update(head_q);
 	memflags = blk_mq_freeze_queue(head_q);
-	/*
-	 * queue_limits mixes hardware limitations for bio splitting with device
-	 * configuration.
-	 *
-	 * For NVMe the device configuration can change after e.g. a Format
-	 * command, and we really want to pick up the new format value here. But
-	 * we must still stack the queue limits to the least common denominator
-	 * for multipathing to split the bios properly.
-	 *
-	 * To work around this, we explicitly set the device configuration to
-	 * those that we just queried, but only stack the splitting limits in to
-	 * make sure we still obey possibly lower limitations of other
-	 * controllers.
-	 */
-	lim.logical_block_size = ns_lim->logical_block_size;
-	lim.physical_block_size = ns_lim->physical_block_size;
-	lim.io_min = ns_lim->io_min;
-	lim.io_opt = ns_lim->io_opt;
-	queue_limits_stack_bdev(&lim, ns->disk->part0, 0,
-				ns->head->disk->disk_name);
-	if (lim.features & BLK_FEAT_ZONED)
-		nvme_stack_zone_resources(&lim, ns_lim);
+	blk_set_mpath_head_limits(&lim, ns_lim);
+	blk_stack_mpath_limits(&lim, ns_lim);
 	if (unsupported)
 		ns->head->disk->flags |= GENHD_FL_HIDDEN;
 	else
 		nvme_init_integrity(ns->head, &lim, info);
-	lim.max_write_streams = ns_lim->max_write_streams;
-	lim.write_stream_granularity = ns_lim->write_stream_granularity;
 	ret = queue_limits_commit_update(head_q, &lim);
 	if (ret)
 		goto unfreeze_head_queue;
diff --git a/drivers/nvme/host/multipath.c b/drivers/nvme/host/multipath.c
index 75dbb58286a3..448c7f33c687 100644
--- a/drivers/nvme/host/multipath.c
+++ b/drivers/nvme/host/multipath.c
@@ -760,6 +760,8 @@ int nvme_mpath_alloc_disk(struct nvme_ctrl *ctrl, struct nvme_ns_head *head)
 		return 0;
 
 	blk_set_stacking_limits(&lim);
+	/* No path discard limit has been stacked yet. */
+	lim.max_hw_discard_sectors = UINT_MAX;
 	lim.dma_alignment = 3;
 	lim.features |= BLK_FEAT_IO_STAT | BLK_FEAT_NOWAIT |
 		BLK_FEAT_POLL | BLK_FEAT_ATOMIC_WRITES | BLK_FEAT_PCI_P2PDMA;
diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h
index 9213a5716f95..2fe711c196c9 100644
--- a/include/linux/blkdev.h
+++ b/include/linux/blkdev.h
@@ -1153,6 +1153,9 @@ static inline void blk_queue_disable_write_zeroes(struct request_queue *q)
  */
 extern void blk_set_queue_depth(struct request_queue *q, unsigned int depth);
 extern void blk_set_stacking_limits(struct queue_limits *lim);
+void blk_set_mpath_head_limits(struct queue_limits *t,
+			       struct queue_limits *b);
+void blk_stack_mpath_limits(struct queue_limits *t, struct queue_limits *b);
 extern int blk_stack_limits(struct queue_limits *t, struct queue_limits *b,
 			    sector_t offset);
 void queue_limits_stack_bdev(struct queue_limits *t, struct block_device *bdev,
-- 
2.25.1
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.