[PATCH v3 5/5] block, nvme: add same-LBA multipath limit helpers
Yao Sang
sangyao at kylinos.cn
Fri Aug 21 03:10:50 PDT 2026
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 <sangyao at kylinos.cn>
---
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
More information about the Linux-nvme
mailing list