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 --- 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