* [PATCH] md/raid5: fix write stalls when a write is queued during a drain
@ 2026-11-14 22:46 Miroslav Cimerman
0 siblings, 0 replies; only message in thread
From: Miroslav Cimerman @ 2026-11-14 22:46 UTC (permalink / raw)
To: Song Liu, Yu Kuai
Cc: Li Nan, Xiao Ni, Tejun Heo, NeilBrown, linux-raid, linux-kernel,
Anton Gavriliuk
With continuous O_DIRECT writes to a RAID-5/6 array that uses
read-modify-writes, a few 4k writes can take seconds to complete, up
to the length of the run.
The pre-read permission (STRIPE_PREREAD_ACTIVE) belongs to a stripe.
A REQ_SYNC write queued while an earlier write on the same stripe is
being drained finds the bit set and gets no count of its own in
preread_active_stripes. handle_stripe() clears the bit when the drain
result is handled, so the queued write gets STRIPE_DELAYED, and the
stripe goes to delayed_list together with the completion of the
drained write. raid5_activate_delayed() only moves it on once
preread_active_stripes is below IO_THRESHOLD, which with other
REQ_SYNC writes in flight may happen until the load stops.
Check for writes queued on the stripe, under stripe_lock, after
clearing the bit, and keep the bit and its count if there are any.
A write queued later tests the bit after the clear and either takes
the permission itself or finds it kept. Do this for any queued write:
a queued buffered write would otherwise hold back the completion of
the drained write the same way.
md-7.4, RAM disks, 4k random writes, io_uring, QD64, 20 s, slowest
write without -> with this patch, md defaults (1 job) and
group_thread_cnt=8 (16 jobs):
RAID-6 4+2 (rmw_level=2) 18.0 s -> 2.0 ms 19.3 s -> 5.8 ms
RAID-6 8+2 13.4 s -> 2.2 ms 13.0 s -> 7.0 ms
RAID-5 5+1 16.9 s -> 1.8 ms 18.3 s -> 4.9 ms
RAID-5 9+1 17.4 s -> 2.2 ms 12.0 s -> 5.8 ms
IOPS are unchanged with md defaults and within a few percent with 8
workers. Buffered writes are not affected.
Fixes: e9c7469bb4f5 ("md: implment REQ_FLUSH/FUA support")
Cc: stable@vger.kernel.org
Signed-off-by: Miroslav Cimerman <mc@raidlab.org>
---
drivers/md/raid5.c | 41 +++++++++++++++++++++++++++++++++++++++--
drivers/md/raid5.h | 4 +++-
2 files changed, 42 insertions(+), 3 deletions(-)
diff --git a/drivers/md/raid5.c b/drivers/md/raid5.c
index efb4b0d37cb0..b103cc8d142a 100644
--- a/drivers/md/raid5.c
+++ b/drivers/md/raid5.c
@@ -5011,6 +5011,27 @@ static void break_stripe_batch_list(struct stripe_head *head_sh,
set_bit(STRIPE_HANDLE, &head_sh->state);
}
+/*
+ * Check if a stripe head has any writes queued. Takes sh->stripe_lock under
+ * which writes are queued.
+ */
+static bool stripe_has_queued_writes(struct stripe_head *sh)
+{
+ bool queued = false;
+ int i;
+
+ spin_lock(&sh->stripe_lock);
+ for (i = 0; i < sh->disks; i++) {
+ if (sh->dev[i].towrite) {
+ queued = true;
+ break;
+ }
+ }
+ spin_unlock(&sh->stripe_lock);
+
+ return queued;
+}
+
/*
* handle_stripe - do things to a stripe.
*
@@ -5165,8 +5186,24 @@ static void handle_stripe(struct stripe_head *sh)
set_bit(STRIPE_INSYNC, &sh->state);
}
}
- if (test_and_clear_bit(STRIPE_PREREAD_ACTIVE, &sh->state))
- s.dec_preread_active = 1;
+ if (test_and_clear_bit(STRIPE_PREREAD_ACTIVE, &sh->state)) {
+ /*
+ * A write queued while the previous one was drained
+ * has no pre-read permission of its own: a REQ_SYNC
+ * write or discard found the bit set, other writes
+ * never take it. Keep the permission for it, or it
+ * gets STRIPE_DELAYED and the stripe, together with
+ * the completion of the drained write, may wait on
+ * delayed_list until preread_active_stripes drops
+ * below IO_THRESHOLD. A write queued after
+ * stripe_has_queued_writes() tests the bit after our
+ * clear, so it takes the permission itself or finds
+ * it kept.
+ */
+ if (!stripe_has_queued_writes(sh) ||
+ test_and_set_bit(STRIPE_PREREAD_ACTIVE, &sh->state))
+ s.dec_preread_active = 1;
+ }
}
/*
diff --git a/drivers/md/raid5.h b/drivers/md/raid5.h
index 1a61b474fc22..c3feb7779dbe 100644
--- a/drivers/md/raid5.h
+++ b/drivers/md/raid5.h
@@ -448,7 +448,9 @@ enum {
* When preread is initiated on a stripe, we set PREREAD_ACTIVE and add
* it to the count of prereading stripes.
* When write is initiated, or the stripe refcnt == 0 (just in case) we
- * clear the PREREAD_ACTIVE flag and decrement the count
+ * clear the PREREAD_ACTIVE flag and decrement the count. When write is
+ * initiated and another write is already queued on the stripe, the flag and
+ * the count are kept for that write (see handle_stripe()).
* Whenever the 'handle' queue is empty and the device is not plugged, we
* move any strips from delayed to handle and clear the DELAYED flag and set
* PREREAD_ACTIVE.
base-commit: 47e1b18f38d74015cb9fd30b5ff2a1eb88e77d31
--
2.53.0
^ permalink raw reply related [flat|nested] only message in thread
only message in thread, other threads:[~2026-11-14 22:48 UTC | newest]
Thread overview: (only message) (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-11-14 22:46 [PATCH] md/raid5: fix write stalls when a write is queued during a drain Miroslav Cimerman
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.