[PATCH v2 2/4] block: factor block size and alignment handling out of blk_stack_limits
Yao Sang <[email protected]> Thu, 6 Aug 2026 10:46:56 +0800
| Newsgroups | org.infradead.lists.linux-nvme,org.kernel.vger.linux-block |
|---|---|
| Message-ID | <[email protected]> |
Topology limits are the block size and alignment limits that the top device exposes after its data is placed at an offset on a bottom device. blk_stack_limits() uses start, the first data sector in the bottom device used by the top device, to calculate alignment_offset. It then stacks logical_block_size, physical_block_size, io_min, io_opt and chunk_sectors, checks that they are compatible, and rounds max_sectors, max_hw_sectors and max_dev_sectors down to the final logical_block_size. Keep these operations together because the checks and rounding must run after the final logical and physical block sizes are known. Factor them into a static blk_stack_topology_limits() helper. There is no behavior change. Signed-off-by: Yao Sang <[email protected]> --- block/blk-settings.c | 169 ++++++++++++++++++++++++------------------- 1 file changed, 93 insertions(+), 76 deletions(-) diff --git a/block/blk-settings.c b/block/blk-settings.c index 8274631290db..1aff818aaaac 100644 --- a/block/blk-settings.c +++ b/block/blk-settings.c @@ -756,6 +756,96 @@ static void blk_stack_atomic_writes_limits(struct queue_limits *t, t->atomic_write_hw_boundary = 0; } +/* + * Stack and check logical_block_size, physical_block_size, io_min, io_opt, + * chunk_sectors and alignment_offset for a bottom-device range, then round + * max_sectors, max_hw_sectors and max_dev_sectors to logical_block_size. + */ +static int blk_stack_topology_limits(struct queue_limits *t, + const struct queue_limits *b, sector_t start) +{ + unsigned int top, bottom, alignment; + int ret = 0; + + t->flags |= b->flags & BLK_FLAG_MISALIGNED; + + alignment = queue_limit_alignment_offset(b, start); + + /* + * The bottom device has a different alignment. Check that it is + * compatible with the current top alignment. + */ + if (t->alignment_offset != alignment) { + top = max(t->physical_block_size, t->io_min) + + t->alignment_offset; + bottom = max(b->physical_block_size, b->io_min) + alignment; + + /* Verify that top and bottom intervals line up. */ + if (max(top, bottom) % min(top, bottom)) { + t->flags |= BLK_FLAG_MISALIGNED; + ret = -1; + } + } + + t->logical_block_size = max(t->logical_block_size, + b->logical_block_size); + t->physical_block_size = max(t->physical_block_size, + b->physical_block_size); + t->io_min = max(t->io_min, b->io_min); + t->io_opt = lcm_not_zero(t->io_opt, b->io_opt); + + /* Set non-power-of-2 compatible chunk_sectors boundary. */ + if (b->chunk_sectors) + t->chunk_sectors = gcd(t->chunk_sectors, b->chunk_sectors); + + /* Physical block size a multiple of the logical block size? */ + if (t->physical_block_size & (t->logical_block_size - 1)) { + t->physical_block_size = t->logical_block_size; + t->flags |= BLK_FLAG_MISALIGNED; + ret = -1; + } + + /* Minimum I/O a multiple of the physical block size? */ + if (t->io_min & (t->physical_block_size - 1)) { + t->io_min = t->physical_block_size; + t->flags |= BLK_FLAG_MISALIGNED; + ret = -1; + } + + /* Optimal I/O a multiple of the physical block size? */ + if (t->io_opt & (t->physical_block_size - 1)) { + t->io_opt = 0; + t->flags |= BLK_FLAG_MISALIGNED; + ret = -1; + } + + /* chunk_sectors a multiple of the physical block size? */ + if (t->chunk_sectors % (t->physical_block_size >> SECTOR_SHIFT)) { + t->chunk_sectors = 0; + t->flags |= BLK_FLAG_MISALIGNED; + ret = -1; + } + + /* Find lowest common alignment_offset. */ + t->alignment_offset = lcm_not_zero(t->alignment_offset, alignment) % + max(t->physical_block_size, t->io_min); + + /* Verify that new alignment_offset is on a logical block boundary. */ + if (t->alignment_offset & (t->logical_block_size - 1)) { + t->flags |= BLK_FLAG_MISALIGNED; + ret = -1; + } + + t->max_sectors = blk_round_down_sectors(t->max_sectors, + t->logical_block_size); + t->max_hw_sectors = blk_round_down_sectors(t->max_hw_sectors, + t->logical_block_size); + t->max_dev_sectors = blk_round_down_sectors(t->max_dev_sectors, + t->logical_block_size); + + return ret; +} + /** * blk_stack_limits - adjust queue_limits for stacked devices * @t: the stacking driver limits (top device) @@ -780,8 +870,8 @@ static void blk_stack_atomic_writes_limits(struct queue_limits *t, int blk_stack_limits(struct queue_limits *t, struct queue_limits *b, sector_t start) { - unsigned int top, bottom, alignment; - int ret = 0; + unsigned int alignment; + int ret; t->features |= (b->features & BLK_FEAT_INHERIT_MASK); @@ -798,8 +888,6 @@ int blk_stack_limits(struct queue_limits *t, struct queue_limits *b, if (!(b->features & BLK_FEAT_PCI_P2PDMA)) t->features &= ~BLK_FEAT_PCI_P2PDMA; - t->flags |= (b->flags & BLK_FLAG_MISALIGNED); - t->max_sectors = min_not_zero(t->max_sectors, b->max_sectors); t->max_user_sectors = min_not_zero(t->max_user_sectors, b->max_user_sectors); @@ -830,80 +918,9 @@ int blk_stack_limits(struct queue_limits *t, struct queue_limits *b, t->max_segment_size = min_not_zero(t->max_segment_size, b->max_segment_size); - - alignment = queue_limit_alignment_offset(b, start); - - /* Bottom device has different alignment. Check that it is - * compatible with the current top alignment. - */ - if (t->alignment_offset != alignment) { - - top = max(t->physical_block_size, t->io_min) - + t->alignment_offset; - bottom = max(b->physical_block_size, b->io_min) + alignment; - - /* Verify that top and bottom intervals line up */ - if (max(top, bottom) % min(top, bottom)) { - t->flags |= BLK_FLAG_MISALIGNED; - ret = -1; - } - } - - t->logical_block_size = max(t->logical_block_size, - b->logical_block_size); - - t->physical_block_size = max(t->physical_block_size, - b->physical_block_size); - - t->io_min = max(t->io_min, b->io_min); - t->io_opt = lcm_not_zero(t->io_opt, b->io_opt); t->dma_alignment = max(t->dma_alignment, b->dma_alignment); - /* Set non-power-of-2 compatible chunk_sectors boundary */ - if (b->chunk_sectors) - t->chunk_sectors = gcd(t->chunk_sectors, b->chunk_sectors); - - /* Physical block size a multiple of the logical block size? */ - if (t->physical_block_size & (t->logical_block_size - 1)) { - t->physical_block_size = t->logical_block_size; - t->flags |= BLK_FLAG_MISALIGNED; - ret = -1; - } - - /* Minimum I/O a multiple of the physical block size? */ - if (t->io_min & (t->physical_block_size - 1)) { - t->io_min = t->physical_block_size; - t->flags |= BLK_FLAG_MISALIGNED; - ret = -1; - } - - /* Optimal I/O a multiple of the physical block size? */ - if (t->io_opt & (t->physical_block_size - 1)) { - t->io_opt = 0; - t->flags |= BLK_FLAG_MISALIGNED; - ret = -1; - } - - /* chunk_sectors a multiple of the physical block size? */ - if (t->chunk_sectors % (t->physical_block_size >> SECTOR_SHIFT)) { - t->chunk_sectors = 0; - t->flags |= BLK_FLAG_MISALIGNED; - ret = -1; - } - - /* Find lowest common alignment_offset */ - t->alignment_offset = lcm_not_zero(t->alignment_offset, alignment) - % max(t->physical_block_size, t->io_min); - - /* Verify that new alignment_offset is on a logical block boundary */ - if (t->alignment_offset & (t->logical_block_size - 1)) { - t->flags |= BLK_FLAG_MISALIGNED; - ret = -1; - } - - t->max_sectors = blk_round_down_sectors(t->max_sectors, t->logical_block_size); - t->max_hw_sectors = blk_round_down_sectors(t->max_hw_sectors, t->logical_block_size); - t->max_dev_sectors = blk_round_down_sectors(t->max_dev_sectors, t->logical_block_size); + ret = blk_stack_topology_limits(t, b, start); /* Discard alignment and granularity */ if (b->discard_granularity) { -- 2.25.1