[PATCH v3 3/8] md/raid1: serialize non-write-behind writes on CollisionCheck rdevs

From: Mykola Marzhan

Date: Tue Jul 21 2026 - 13:57:49 EST


A write that skips write-behind (full behind queue, a waiting
reader, failed behind-bio allocation) races the in-flight behind
write it overlaps: nothing orders the two on the write-mostly
member, and if the older behind data lands last the member keeps
stale data for sectors already acknowledged as rewritten.

Serialize on CollisionCheck, which marks exactly the rdevs owning a
serial tree: serialize_policy rdevs plus write-mostly members when
write-behind arms serialization -- the case the MD_SERIALIZE_POLICY
test misses. remove_serial() under the same condition. This also
avoids the old gate's latent NULL deref for rdevs hot-added under
serialize_policy (they never get a serial tree).

Fixes: d0d2d8ba0494 ("md/raid1: introduce wait_for_serialization")
Cc: stable@xxxxxxxxxxxxxxx
Assisted-by: Claude:claude-fable-5
Signed-off-by: Mykola Marzhan <mykola@xxxxxxxxxxx>
---
drivers/md/raid1.c | 8 ++++++--
1 file changed, 6 insertions(+), 2 deletions(-)

diff --git a/drivers/md/raid1.c b/drivers/md/raid1.c
index afe2ca96ad8c..997f79c05db7 100644
--- a/drivers/md/raid1.c
+++ b/drivers/md/raid1.c
@@ -564,7 +564,7 @@ static void raid1_end_write_request(struct bio *bio)
call_bio_endio(r1_bio);
}
}
- } else if (test_bit(MD_SERIALIZE_POLICY, &rdev->mddev->flags))
+ } else if (test_bit(CollisionCheck, &rdev->flags))
remove_serial(rdev, lo, hi);
if (r1_bio->bios[mirror] == NULL)
rdev_dec_pending(rdev, conf->mddev);
@@ -1677,7 +1677,11 @@ static bool raid1_write_request(struct mddev *mddev, struct bio *bio,
mbio = bio_alloc_clone(rdev->bdev, bio, GFP_NOIO,
&mddev->bio_set);

- if (test_bit(MD_SERIALIZE_POLICY, &mddev->flags))
+ /*
+ * CollisionCheck marks every rdev with a serial
+ * tree; order against in-flight write-behind I/O.
+ */
+ if (test_bit(CollisionCheck, &rdev->flags))
wait_for_serialization(rdev, r1_bio);
}

--
2.52.0