Linux RAID subsystem development
 help / color / mirror / Atom feed
* [PATCH 6.6.y] md/raid0: fix data corruption when chunk size is below PAGE_SIZE
@ 2026-10-13 13:32 Yunye Zhao
  2026-10-13 13:44 ` sashiko-bot
  2026-10-24  5:57 ` yu kuai
  0 siblings, 2 replies; 4+ messages in thread
From: Yunye Zhao @ 2026-10-13 13:32 UTC (permalink / raw)
  To: linux-raid
  Cc: Yu Kuai, Song Liu, Li Nan, Xiao Ni, Jan Kara, John Garry,
	Joseph Qi, YunyeZhao

From: YunyeZhao <yunye.zhao@linux.alibaba.com>

Commit 319ff40a5427 ("md/raid0: Fix performance regression for large sequential writes")
assumes that bios reaching RAID0 are no larger than the chunk size.

However, blk_queue_max_hw_sectors() raises limits smaller than
PAGE_SIZE to one page. With 64 KiB pages and a 16 KiB RAID0 chunk, a
bio can therefore span several chunks. raid0_make_request() splits it
only once and maps the multi-chunk remainder to one member device,
causing silent data corruption.

Set the queue chunk boundary after stacking the member limits, combined
with any inherited boundary using gcd(). This makes the block layer
split bios at chunk boundaries before they reach RAID0. Only install a
boundary aligned to the logical block size so that bio splitting
always makes progress.

Setting the boundary after disk_stack_limits() is required because
stacking may clear or reduce a boundary installed earlier. This adapts
the mechanism introduced by upstream commit 4b8beba60d32
("md/raid0: set chunk_sectors limit") to the queue limits API used by
6.6.y.

The corruption was reproduced on arm64 with 64 KiB pages and a 16 KiB
chunk. A 64 KiB chunk configuration is unaffected.

Fixes: 319ff40a5427 ("md/raid0: Fix performance regression for large sequential writes")
Signed-off-by: YunyeZhao <yunye.zhao@linux.alibaba.com>
---
 drivers/md/raid0.c | 14 ++++++++++++++
 1 file changed, 14 insertions(+)

diff --git a/drivers/md/raid0.c b/drivers/md/raid0.c
index c50a7abda744..5a57e327c6ba 100644
--- a/drivers/md/raid0.c
+++ b/drivers/md/raid0.c
@@ -11,6 +11,7 @@
 */
 
 #include <linux/blkdev.h>
+#include <linux/gcd.h>
 #include <linux/seq_file.h>
 #include <linux/module.h>
 #include <linux/slab.h>
@@ -382,6 +383,7 @@ static void raid0_free(struct mddev *mddev, void *priv)
 static int raid0_run(struct mddev *mddev)
 {
 	struct r0conf *conf;
+	unsigned int chunk_limit;
 	int ret;
 
 	if (mddev->chunk_sectors == 0) {
@@ -413,6 +415,18 @@ static int raid0_run(struct mddev *mddev)
 			disk_stack_limits(mddev->gendisk, rdev->bdev,
 					  rdev->data_offset << 9);
 		}
+
+		/*
+		 * Set the chunk boundary after stacking member limits, which
+		 * may clear or reduce it. Without it a bio may span several
+		 * chunks when chunk size < PAGE_SIZE. Keep it a multiple of
+		 * the logical block size so the block layer can split at it.
+		 */
+		chunk_limit = gcd(mddev->queue->limits.chunk_sectors,
+				  mddev->chunk_sectors);
+		if (!(chunk_limit %
+		      (mddev->queue->limits.logical_block_size >> SECTOR_SHIFT)))
+			blk_queue_chunk_sectors(mddev->queue, chunk_limit);
 	}
 
 	/* calculate array device size */
-- 
2.19.1.6.gb485710b


^ permalink raw reply related	[flat|nested] 4+ messages in thread

* Re: [PATCH 6.6.y] md/raid0: fix data corruption when chunk size is below PAGE_SIZE
  2026-10-13 13:32 [PATCH 6.6.y] md/raid0: fix data corruption when chunk size is below PAGE_SIZE Yunye Zhao
@ 2026-10-13 13:44 ` sashiko-bot
  2026-10-24  5:57 ` yu kuai
  1 sibling, 0 replies; 4+ messages in thread
From: sashiko-bot @ 2026-10-13 13:44 UTC (permalink / raw)
  To: Yunye Zhao, linux-raid; +Cc: yukuai

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [High] md: missing queue freeze before blk_queue_chunk_sectors()
--

commit b6776719727e7cf128eed60b1bbe45a3b0836f15
Author: YunyeZhao <yunye.zhao@linux.alibaba.com>

md/raid0: fix data corruption when chunk size is below PAGE_SIZE

Fixes silent data corruption that occurs when bios spanning multiple RAID0
chunks are not split properly by the block layer. Sets the chunk boundary
after stacking member limits to ensure block-layer bio splitting respects
the RAID0 chunk size.

> diff --git a/drivers/md/raid0.c b/drivers/md/raid0.c
> index c50a7abda744a..5a57e327c6ba4 100644
> --- a/drivers/md/raid0.c
> +++ b/drivers/md/raid0.c
[ ... ]
> @@ -413,6 +415,18 @@ static int raid0_run(struct mddev *mddev)
>  			disk_stack_limits(mddev->gendisk, rdev->bdev,
>  					  rdev->data_offset << 9);
>  		}
> +
> +		/*
> +		 * Set the chunk boundary after stacking member limits, which
> +		 * may clear or reduce it. Without it a bio may span several
> +		 * chunks when chunk size < PAGE_SIZE. Keep it a multiple of
> +		 * the logical block size so the block layer can split at it.
> +		 */
> +		chunk_limit = gcd(mddev->queue->limits.chunk_sectors,
> +				  mddev->chunk_sectors);
> +		if (!(chunk_limit %
> +		      (mddev->queue->limits.logical_block_size >> SECTOR_SHIFT)))
> +			blk_queue_chunk_sectors(mddev->queue, chunk_limit);

[Severity: High]
Could modifying the block queue limits here without freezing the queue expose
the block layer to torn limit reads?

When an array is reconfigured, raid0_run() is invoked to update limits. While
mddev_suspend() is used during reconfiguration, it only drains
mddev->active_io, which pauses bios deep in the MD stack at
md_handle_request().

It does not prevent new bios from entering the generic block layer via
submit_bio_noacct() and executing blk_queue_split(). Because the queue is not
frozen via blk_mq_freeze_queue(), these concurrent bios might read torn
values of q->limits.chunk_sectors while raid0_run() modifies it, potentially
leading to inconsistent splitting and silent data corruption.

>  	}
>  
>  	/* calculate array device size */

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20261013133243.42145-1-yunye.zhao@linux.alibaba.com?part=1

^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: [PATCH 6.6.y] md/raid0: fix data corruption when chunk size is below PAGE_SIZE
  2026-10-13 13:32 [PATCH 6.6.y] md/raid0: fix data corruption when chunk size is below PAGE_SIZE Yunye Zhao
  2026-10-13 13:44 ` sashiko-bot
@ 2026-10-24  5:57 ` yu kuai
  2026-10-24 10:12   ` Yunye Zhao
  1 sibling, 1 reply; 4+ messages in thread
From: yu kuai @ 2026-10-24  5:57 UTC (permalink / raw)
  To: Yunye Zhao, linux-raid, yu kuai
  Cc: Song Liu, Li Nan, Xiao Ni, Jan Kara, John Garry, Joseph Qi

Hi,

在 2026/10/13 21:32, Yunye Zhao 写道:
> From: YunyeZhao <yunye.zhao@linux.alibaba.com>
>
> Commit 319ff40a5427 ("md/raid0: Fix performance regression for large sequential writes")
> assumes that bios reaching RAID0 are no larger than the chunk size.
>
> However, blk_queue_max_hw_sectors() raises limits smaller than
> PAGE_SIZE to one page. With 64 KiB pages and a 16 KiB RAID0 chunk, a
> bio can therefore span several chunks. raid0_make_request() splits it
> only once and maps the multi-chunk remainder to one member device,
> causing silent data corruption.
>
> Set the queue chunk boundary after stacking the member limits, combined
> with any inherited boundary using gcd(). This makes the block layer
> split bios at chunk boundaries before they reach RAID0. Only install a
> boundary aligned to the logical block size so that bio splitting
> always makes progress.
>
> Setting the boundary after disk_stack_limits() is required because
> stacking may clear or reduce a boundary installed earlier. This adapts
> the mechanism introduced by upstream commit 4b8beba60d32
> ("md/raid0: set chunk_sectors limit") to the queue limits API used by
> 6.6.y.

Please explain why backporting is not preferred, backporting is always the
first choice.

>
> The corruption was reproduced on arm64 with 64 KiB pages and a 16 KiB
> chunk. A 64 KiB chunk configuration is unaffected.
>
> Fixes: 319ff40a5427 ("md/raid0: Fix performance regression for large sequential writes")
> Signed-off-by: YunyeZhao <yunye.zhao@linux.alibaba.com>
> ---
>   drivers/md/raid0.c | 14 ++++++++++++++
>   1 file changed, 14 insertions(+)
>
> diff --git a/drivers/md/raid0.c b/drivers/md/raid0.c
> index c50a7abda744..5a57e327c6ba 100644
> --- a/drivers/md/raid0.c
> +++ b/drivers/md/raid0.c
> @@ -11,6 +11,7 @@
>   */
>   
>   #include <linux/blkdev.h>
> +#include <linux/gcd.h>
>   #include <linux/seq_file.h>
>   #include <linux/module.h>
>   #include <linux/slab.h>
> @@ -382,6 +383,7 @@ static void raid0_free(struct mddev *mddev, void *priv)
>   static int raid0_run(struct mddev *mddev)
>   {
>   	struct r0conf *conf;
> +	unsigned int chunk_limit;
>   	int ret;
>   
>   	if (mddev->chunk_sectors == 0) {
> @@ -413,6 +415,18 @@ static int raid0_run(struct mddev *mddev)
>   			disk_stack_limits(mddev->gendisk, rdev->bdev,
>   					  rdev->data_offset << 9);
>   		}
> +
> +		/*
> +		 * Set the chunk boundary after stacking member limits, which
> +		 * may clear or reduce it. Without it a bio may span several
> +		 * chunks when chunk size < PAGE_SIZE. Keep it a multiple of
> +		 * the logical block size so the block layer can split at it.
> +		 */
> +		chunk_limit = gcd(mddev->queue->limits.chunk_sectors,
> +				  mddev->chunk_sectors);
> +		if (!(chunk_limit %
> +		      (mddev->queue->limits.logical_block_size >> SECTOR_SHIFT)))
> +			blk_queue_chunk_sectors(mddev->queue, chunk_limit);
>   	}
>   
>   	/* calculate array device size */

-- 
Thanks,
Kuai

^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: [PATCH 6.6.y] md/raid0: fix data corruption when chunk size is below PAGE_SIZE
  2026-10-24  5:57 ` yu kuai
@ 2026-10-24 10:12   ` Yunye Zhao
  0 siblings, 0 replies; 4+ messages in thread
From: Yunye Zhao @ 2026-10-24 10:12 UTC (permalink / raw)
  To: linux-raid
  Cc: Yu Kuai, Song Liu, Li Nan, Xiao Ni, Jan Kara, John Garry,
	Joseph Qi, YunyeZhao

From: YunyeZhao <yunye.zhao@linux.alibaba.com>

Hi Kuai,

Thanks for the comment. I agree that backporting should be the first
choice.

I first tested a straightforward backport of 4b8beba60d32 adapted to
the 6.6 queue-limits API, by setting chunk_sectors before
disk_stack_limits(). However, it does not fix the reported
configuration.

On the reproducer, the RAID0 chunk is 16 KiB (32 sectors), while the
member physical block size is 64 KiB (128 sectors).
blk_stack_limits() therefore clears chunk_sectors because 32 is not a
multiple of 128.

This was verified on QEMU arm64 with 64 KiB pages and 512-byte logical
blocks:

        pre-stacking backport:
                queue/chunk_sectors = 0, corruption reproduced

        current patch:
                queue/chunk_sectors = 32, 6/6 readback tests passed

Splitting at 16 KiB remains valid because it is aligned to the
512-byte logical block size.

Mainline uses the newer queue-limits API and rejects this configuration
when the RAID chunk is smaller than PAGE_SIZE. In contrast, 6.6 accepts
such arrays, and they worked correctly before 319ff40a5427, so
rejecting existing arrays would be a compatibility regression.

Therefore, the current patch is not an alternative to a working
backport, but the necessary adaptation of 4b8beba60d32 to the 6.6
queue-limits behavior. Since stacking the member limits clears the
chunk boundary in this configuration, the RAID0 boundary has to be
restored after stacking completes.

Thanks,
Yunye

^ permalink raw reply	[flat|nested] 4+ messages in thread

end of thread, other threads:[~2026-10-24 10:12 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-10-13 13:32 [PATCH 6.6.y] md/raid0: fix data corruption when chunk size is below PAGE_SIZE Yunye Zhao
2026-10-13 13:44 ` sashiko-bot
2026-10-24  5:57 ` yu kuai
2026-10-24 10:12   ` Yunye Zhao

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox