All of lore.kernel.org
 help / color / mirror / Atom feed
From: Mike Snitzer <snitzer@kernel.org>
To: sashiko-bot@kernel.org
Cc: Keith Busch <kbusch@meta.com>,
	axboe@kernel.dk, dm-devel@lists.linux.dev, mpatocka@redhat.com,
	bmarzins@redhat.com, Keith Busch <kbusch@kernel.org>
Subject: Re: block: let a stacking device inherit its members' DMA alignment
Date: Fri, 23 Oct 2026 11:41:14 -0400	[thread overview]
Message-ID: <atuAGpRkY5cPLKfT@kernel.org> (raw)
In-Reply-To: <sashiko-outbox-181857@kernel.org>

On Thu, Oct 22, 2026 at 05:25:02PM +0000, sashiko-bot@kernel.org wrote:
> Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
> - [Critical] block: blk_set_stacking_limits() corrupts data in dm-ebs and dm-writecache
> --
> 
> commit da5c2e4cde4a12a4f8b254dc27480cf9078b36d2
> Author: Mike Snitzer <snitzer@kernel.org>
> 
> block: let a stacking device inherit its members' DMA alignment
> 
> This commit removes the default sector-size dma_alignment initialization in
> blk_set_stacking_limits() so that stacking devices inherit the strictest
> dma_alignment of their underlying member devices. When no member devices are
> stacked, blk_validate_limits() still applies the default sector alignment.
> 
> > diff --git a/block/blk-settings.c b/block/blk-settings.c
> > index e469baa1f08b7..36fcbecdf793b 100644
> > --- a/block/blk-settings.c
> > +++ b/block/blk-settings.c
> > @@ -41,7 +41,14 @@ void blk_set_stacking_limits(struct queue_limits *lim)
> >  	lim->physical_block_size = SECTOR_SIZE;
> >  	lim->io_min = SECTOR_SIZE;
> >  	lim->discard_granularity = SECTOR_SIZE;
> > -	lim->dma_alignment = SECTOR_SIZE - 1;
> > +	/*
> > +	 * No dma_alignment of its own: blk_stack_limits() keeps the strictest
> > +	 * of the devices stacked under it, and blk_validate_limits() gives the
> > +	 * sector-size default to a device left with none.  A driver that looks
> > +	 * at the data and needs it laid out by logical block says so in its
> > +	 * own limits, as dm-crypt, dm-integrity, dm-verity and dm-log-writes
> > +	 * do.
> > +	 */
> 
> [Severity: Critical]
> Could leaving lim->dma_alignment at 0 in blk_set_stacking_limits() cause data
> corruption regressions in targets like dm-ebs and dm-writecache that inspect
> bio data but do not set limits->dma_alignment in their io_hints callbacks?
> 
> When stacked over a device with sub-sector dma_alignment (such as an nvme
> device with dma_alignment = 3), direct I/O using a 4-byte-aligned user buffer
> that crosses a page boundary or uses sub-sector iovecs can produce bio_vec
> segments whose lengths are not multiples of 512 bytes (for example,
> bv_len = 4092 followed by bv_len = 4).

The code comment in the patch calls out the fact that
blk_validate_limits() will backfill dma_alignment if none is set (by
usual limits stacking or explicitly by DM targets).

But sure, there is potential for dragons if stacked block devices
like DM implicitly relied on block-core's coarse-grained default
dma_alignment.

Mikulas, would you be open to auditing the DM targets relative to
dma_alignment stacking after you're done with reviewing Keith's
related dm-crypt patchset?

Sorry for the extra work... but its worthwhile for DM to be more
precise with how it stacks dma_alignment

Thanks,
Mike

> In drivers/md/dm-ebs-target.c, ebs_io_hints() does not set
> limits->dma_alignment:
> 
> drivers/md/dm-ebs-target.c:ebs_io_hints() {
> 	struct ebs_c *ec = ti->private;
> 
> 	limits->logical_block_size = to_bytes(ec->e_bs);
> 	limits->physical_block_size = to_bytes(ec->u_bs);
> 	limits->alignment_offset = limits->physical_block_size;
> 	limits->io_min = limits->logical_block_size;
> }
> 
> When __ebs_rw_bio() iterates over the bio with bio_for_each_bvec() and calls
> __ebs_rw_bvec(), __ebs_rw_bvec() computes the buffer offset and block number
> from iter->bi_sector:
> 
> drivers/md/dm-ebs-target.c:__ebs_rw_bvec() {
> 	unsigned int bv_len = bv->bv_len;
> 	unsigned int buf_off = to_bytes(__block_mod(iter->bi_sector, ec->u_bs));
> 	sector_t block = __sector_to_block(ec, iter->bi_sector);
> 
> Because bio_for_each_bvec() advances iter via bio_advance_iter_single() in
> include/linux/bio.h:
> 
> include/linux/bio.h:bio_advance_iter_single() {
> 	iter->bi_sector += bytes >> 9;
> 
> a segment with bv_len = 4092 advances iter.bi_sector by 4092 >> 9 = 7 sectors
> (3584 bytes) instead of 4092 bytes, wouldn't the subsequent bio_vec read or
> write at the wrong offset in the dm-bufio block?
> 
> Similarly, in drivers/md/dm-writecache.c, writecache_io_hints() only calls
> dm_stack_bs_limits() and leaves limits->dma_alignment untouched:
> 
> drivers/md/dm-writecache.c:writecache_io_hints() {
> 	struct dm_writecache *wc = ti->private;
> 
> 	dm_stack_bs_limits(limits, wc->block_size);
> }
> 
> In persistent memory mode, writecache_map_read() and writecache_map_write()
> process multi-block bios in a loop using bio->bi_iter.bi_sector to look up
> and record cache entry sectors via write_original_sector_seq_count(), calling
> bio_copy_block() for each block:
> 
> drivers/md/dm-writecache.c:bio_copy_block() {
> 	do {
> 		struct bio_vec bv = bio_iter_iovec(bio, bio->bi_iter);
> 		...
> 		size = bv.bv_len;
> 		if (unlikely(size > remaining_size))
> 			size = remaining_size;
> 		...
> 		data = (char *)data + size;
> 		remaining_size -= size;
> 		bio_advance(bio, size);
> 	} while (unlikely(remaining_size));
> }
> 
> When a cache block spans multiple bio_vec segments with sub-sector lengths
> (such as 4092 and 4 for a 4096-byte block), bio_advance() ->
> bio_advance_iter() updates bio->bi_iter.bi_sector += size >> 9 on each step,
> advancing bi_sector by 7 sectors instead of 8 and corrupting
> bio->bi_iter.bi_sector for subsequent blocks in the bio.
> 
> In addition, when bio_copy_block() calls memcpy_flushcache_optimized() with a
> non-64-byte-multiple size >= 768 (such as 4092), doesn't the loop return
> early without copying the remaining size % 64 bytes?
> 
> drivers/md/dm-writecache.c:memcpy_flushcache_optimized() {
> 	if (cpu_feature_enabled(X86_FEATURE_CLFLUSHOPT) &&
> 	    likely(boot_cpu_data.x86_clflush_size == 64) &&
> 	    likely(size >= 768)) {
> 		do {
> 			memcpy((void *)dest, (void *)source, 64);
> 			clflushopt((void *)dest);
> 			dest += 64;
> 			source += 64;
> 			size -= 64;
> 		} while (size >= 64);
> 		return;
> 	}
> }
> 
> Finally, in solid-state cache mode, writecache_iterate_devices() only stacks
> limits from wc->dev and not wc->ssd_dev:
> 
> drivers/md/dm-writecache.c:writecache_iterate_devices() {
> 	struct dm_writecache *wc = ti->private;
> 
> 	return fn(ti, wc->dev, 0, ti->len, data);
> }
> 
> If wc->dev has dma_alignment = 3 while wc->ssd_dev has dma_alignment = 511,
> wouldn't dm-writecache advertise dma_alignment = 3 and remap 4-byte-aligned
> bios to wc->ssd_dev->bdev, causing bio_split_io_at() to reject them?
> 
> >  	lim->seg_boundary_mask = BLK_SEG_BOUNDARY_MASK;
> >  
> >  	/* Inherit limits from component devices */
> 
> -- 
> Sashiko AI review · https://sashiko.dev/#/patchset/atpBVtPqGDWZ2AFo@kernel.org?part=1

  reply	other threads:[~2026-10-23 15:41 UTC|newest]

Thread overview: 12+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-07-09 16:15 [PATCHv5 0/3] dm-crypt: finer grained memory alignment Keith Busch
2026-07-09 16:15 ` [PATCHv5 1/3] dm: initialize dma_alignment to 0 Keith Busch
2026-10-22 17:01   ` [PATCH] block: let a stacking device inherit its members' DMA alignment Mike Snitzer
2026-10-22 17:25     ` sashiko-bot
2026-10-23 15:41       ` Mike Snitzer [this message]
2026-10-23 20:47         ` Mikulas Patocka
2026-07-09 16:15 ` [PATCHv5 2/3] dm-crypt: handle memory allocation failures Keith Busch
2026-07-09 16:15 ` [PATCHv5 3/3] dm-crypt: allow unaligned bio_vecs for direct io Keith Busch
2026-07-11  0:54   ` Eric Biggers
2026-07-11  4:14     ` Keith Busch
2026-07-11  4:30       ` Eric Biggers
2026-07-13 18:54         ` Keith Busch

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=atuAGpRkY5cPLKfT@kernel.org \
    --to=snitzer@kernel.org \
    --cc=axboe@kernel.dk \
    --cc=bmarzins@redhat.com \
    --cc=dm-devel@lists.linux.dev \
    --cc=kbusch@kernel.org \
    --cc=kbusch@meta.com \
    --cc=mpatocka@redhat.com \
    --cc=sashiko-bot@kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
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.