* [PATCH AUTOSEL 6.18-5.10] md: add cond_resched() to md_do_sync()'s skip path
[not found] <20261019135441.1420968-1-sashal@kernel.org>
@ 2026-10-19 13:46 ` Sasha Levin
2026-10-19 13:49 ` [PATCH AUTOSEL 6.18] md: skip bitmap accounting for empty write ranges Sasha Levin
` (2 subsequent siblings)
3 siblings, 0 replies; 6+ messages in thread
From: Sasha Levin @ 2026-10-19 13:46 UTC (permalink / raw)
To: patches, stable
Cc: Yunye Zhao, Yu Kuai, Sasha Levin, song, linux-raid, linux-kernel
From: Yunye Zhao <yunye.zhao@linux.alibaba.com>
[ Upstream commit 162eb5ba791f6260fdaabae75fa2df20134f67d8 ]
When sync_request() reports a skipped region (*skipped == 1),
md_do_sync()'s main loop advances the cursor and takes an early
continue:
j += sectors;
...
if (last_check + window > io_sectors || j == max_sectors)
continue;
If the personality returns a small span per call (raid10 recovery
returns only 128 sectors), syncing a large, mostly clean array iterates
this branch an enormous number of times without ever yielding the CPU.
On a non-preemptive kernel the resync thread then trips the soft-lockup
watchdog:
watchdog: BUG: soft lockup - CPU#149 stuck for 313s! [mdX_resync]
md_bitmap_start_sync+0x6f/0xe0
raid10_sync_request+0x2c9/0x1530 [raid10]
md_do_sync+0x810/0x1030
md_thread+0xa7/0x150
Add a cond_resched(). This does not reduce the wasted iterations; the
excessive iteration count is a raid10 problem addressed separately.
Signed-off-by: Yunye Zhao <yunye.zhao@linux.alibaba.com>
Link: https://patch.msgid.link/20260723135535.101995-3-yunye.zhao@linux.alibaba.com
Signed-off-by: Yu Kuai <yukuai@fygo.io>
Signed-off-by: Sasha Levin <sashal@kernel.org>
---
LLM Generated explanations, may be completely bogus:
The checked-out tree is **6.18.53** (`pending-6.18`). The skip path in
`md_do_sync()` is present, has no yield, and matches this patch. That is
a real soft-lockup fix.
## Phase 1: Commit message forensics
**Step 1.1 — Subject**
- Record: `[md] [add] add cond_resched() to md_do_sync()'s skip path`
- Subsystem: MD RAID core. Verb “add” here is a yield on an existing
loop, not a feature.
**Step 1.2 — Tags**
- Record:
- `Signed-off-by: Yunye Zhao <yunye.zhao@linux.alibaba.com>` (author)
- `Link: https://patch.msgid.link/20260723135535.101995-3-
yunye.zhao@linux.alibaba.com`
- `Signed-off-by: Yu Kuai <yukuai@fygo.io>` (md maintainer; applied)
- No `Fixes:` / `Reported-by:` / `Tested-by:` in the committed text
- Original v2 submission **did** include `Cc: stable@vger.kernel.org`;
Yu Kuai dropped it when applying (“Remove the cc stable as there is
no fixtag”). That is process, not a technical NAK.
- Pipeline `Signed-off-by: Sasha Levin` / `[Upstream commit …]`
ignored as instructed.
**Step 1.3 — Body**
- Record:
- **Bug:** when `sync_request()` returns `*skipped == 1` with a small
span (RAID10 recovery: 128 sectors), `md_do_sync()` takes `continue`
and never reaches the existing `cond_resched()`.
- **Symptom:** `watchdog: BUG: soft lockup - CPU#149 stuck for 313s!
[mdX_resync]` with `md_bitmap_start_sync` → `raid10_sync_request` →
`md_do_sync` → `md_thread`.
- **Trigger:** large, mostly-clean RAID10 recovery (author:
2^40-sector device → ~2^33 iterations).
- **Root cause of CPU burn:** RAID10 recovery discards the bitmap’s
large clean span and returns 128; that is patch 3/3. This commit
only yields.
- Author tested on 6.6.102 and mainline.
**Step 1.4 — Hidden bug fix?**
- Record: Not hidden. Explicit soft-lockup fix. Series cover letter
calls it a “safety net, any personality/layout.”
## Phase 2: Diff analysis
**Step 2.1 — Inventory**
- Record: `drivers/md/md.c` only; ~3 insertions / 1 deletion in
`md_do_sync()`. Surgical, single-function.
**Step 2.2 — Flow**
- Record: **Before:** skip/early-continue path never yields. **After:**
`cond_resched()` then `continue`. Normal I/O path still uses the later
`cond_resched()` at the speed-limit check.
**Step 2.3 — Bug mechanism**
- Record: **Hang / soft-lockup** (non-preemptive). Skip path bypasses
the yield that already exists later in the same loop.
Verified in this tree:
```9751:9753:drivers/md/md.c
if (last_check + window > io_sectors || j ==
max_sectors)
continue;
```
```9779:9779:drivers/md/md.c
cond_resched();
```
Skip `continue` never reaches line 9779. `window` is `32 * (PAGE_SIZE /
512)` (256 sectors). If `skipped`, `io_sectors` does not increase, so
`last_check + window > io_sectors` stays true.
RAID10 recovery still does `max_sync = RESYNC_PAGES << (PAGE_SHIFT-9)`
(128 sectors). On a clean bitmap, `biolist == NULL` → `giveup` clamps to
`max_sync` and, because `max_sector` is a parameter (not reset at
`skipped:`), the call returns 128 to `md_do_sync()`.
**Step 2.4 — Fix quality**
- Record: Obviously correct. Same primitive already used in this
function. Process context (`md_thread`). No new API/locks. Regression
risk very low. Might slightly slow tight skip loops; that is the goal.
## Phase 3: Git history
**Step 3.1 — Blame**
- Record: Skip `continue` dates to early MD (`57afd89f98a99` / 2.6 era).
Long-standing. RAID10 128-sector recovery cap is equally old
(`RESYNC_BLOCK_SIZE` 64KiB).
**Step 3.2 — Fixes: tag**
- Record: None. Not a regression from a single later commit; interaction
of skip-continue and RAID10 recovery skip size.
**Step 3.3 — Related commits**
- Record: Patch 2/3 of “md/raid10: fix recovery corruption and soft
lockup on large arrays”.
- 1/3 `md/raid10: fix still_degraded being inverted` — **already in
this tree** (`0efabe6229dc6`). Independent.
- 3/3 `md/raid10: skip clean regions in bulk` — **not** in this tree.
Independent; this yield still needed for far/offset layouts and
other personalities.
- No `cond_resched` on this skip path in `HEAD`.
**Step 3.4 — Author**
- Record: Yunye Zhao (Alibaba). Applied by Yu Kuai (md maintainer).
**Step 3.5 — Dependencies**
- Record: Standalone. Uses existing `cond_resched()`. 6.18
`md_do_sync()` already has `update:`, `MD_RESYNC_ACTIVE`, and 4-arg
`sync_request()` — same context as the patch. Clean apply.
## Phase 4: Mailing list
**Step 4.1 — Discussion**
- Record: https://lore.kernel.org/linux-
raid/20260723135535.101995-3-yunye.zhao@linux.alibaba.com/ (also
openwall mirrors).
- v1: single `cond_resched` patch. Yu Kuai initially thought it was a
dead loop.
- v2: 3 patches; this one retitled; v1 “io_sectors never increases”
claim corrected.
- Yu Kuai on v1 follow-up: “This patch is still valuable, even a 5s
lockup is not acceptable.”
- Applied to md-7.3. No NAK on the yield itself.
**Step 4.2 — Recipients**
- Record: Song Liu, Yu Kuai; Cc Li Nan, Xiao Ni, linux-raid, linux-
kernel.
**Step 4.3 — Bug report**
- Record: Author stack trace; 2^40-sector array; ~8.6e9 iterations;
313s. Cover letter: before patch 3, 37.2s 100% CPU on 8T-per-disk
sparse recovery; `softlockup_panic=1`.
**Step 4.4 — Series**
- Record: 2/3 is the md-core safety net. Does not depend on 1/3 or 3/3.
**Step 4.5 — stable@**
- Record: No useful stable-list thread found (lore.kernel.org/stable
403). Maintainer dropped `Cc: stable` only for missing `Fixes:`.
## Phase 5: Semantics
**Step 5.1 — Functions**
- Record: `md_do_sync()`.
**Step 5.2 — Callers**
- Record: Registered as the MD resync thread
(`md_register_thread(md_do_sync, …)`). Kernel thread, not IRQ.
Userspace triggers via array recovery/resync/check (`mdadm`).
**Step 5.3 — Callees**
- Record: `pers->sync_request()`, bitmap start/end, `cond_resched()`,
speed-limit waits.
**Step 5.4 — Reachability**
- Record: Yes. Disk replace / rebuild / `--syncaction` on RAID10 with
internal bitmap. Production path.
**Step 5.5 — Similar patterns**
- Record: Same file already yields on the I/O path. RAID1/5/10 use
`cond_resched()` in other loops. Skip path was the hole.
## Phase 6: This tree (6.18.53)
**Step 6.1 — Buggy code present?**
- Record: **Yes.** `drivers/md/md.c` ~9752 skip `continue` with no
yield. RAID10 `giveup` still advances by `max_sync` (128). `git
describe HEAD`: `p-6.18-1-g03b85e06ca9bf`. `make kernelversion`:
`6.18.53`.
**Step 6.2 — Backport difficulty**
- Record: **Clean apply.** Local hunk matches the submitted diff
(`MD_RESYNC_ACTIVE`, `update:`).
**Step 6.3 — Already fixed?**
- Record: No. 1/3 is in tree; 2/3 and 3/3 are not. Older `71e8e4f288e74`
(bitmap size vs array size) is a different lockup.
## Phase 7: Subsystem
**Step 7.1**
- Record: `drivers/md` — **IMPORTANT** (enterprise RAID, dm-raid). Not
staging.
**Step 7.2**
- Record: Active; recent RAID10/md fixes on this branch.
## Phase 8: Impact and risk
**Step 8.1 — Who**
- Record: MD/RAID users, especially RAID10 recovery of large mostly-
clean arrays. Distro/enterprise kernels often `PREEMPT_NONE` /
`PREEMPT_VOLUNTARY`.
**Step 8.2 — Trigger**
- Record: Recovery/resync of a large sparse array. Not unprivileged
syscall; rebuild is a privileged/admin path. Common after disk
replace. Timing-independent tight loop.
**Step 8.3 — Failure mode**
- Record: Soft lockup (313s in the report). With `softlockup_panic=1`,
panic. **CRITICAL**.
**Step 8.4 — Risk vs benefit**
- Record: Benefit high (stops watchdog/panic). Risk very low (3 lines,
existing helper). Ratio strongly in favor. Remaining CPU spin is patch
3; this still prevents the BUG.
## Phase 9: Synthesis
**For backporting**
- Real, reproduced soft lockup with stack trace.
- Hole is in this tree; patch matches.
- Tiny, obviously correct; maintainer applied it and called it valuable.
- Independent of 3/3; still needed after 1/3.
- Classic stable pattern: `cond_resched()` to stop a lockup.
**Against**
- Does not cut the 128-sector crawl (author is explicit).
- No `Fixes:` (maintainer dropped `Cc: stable` for that).
- Pre-existing `md_new_event()` spam on `last_check == 0` (not
introduced here).
**Unresolved:** none that change the verdict. Could not fetch
lore.kernel.org/stable (403); discussion from the patch mbox is enough.
**Stable rules**
1. Obviously correct and tested — **PASS** (mainline + QEMU tests;
maintainer applied).
2. Real user-visible bug — **PASS** (soft lockup on RAID10 recovery).
3. Important (crash/hang) — **PASS** (soft lockup / possible panic).
4. Small and contained — **PASS** (~3 lines, one function).
5. No new features/APIs — **PASS**.
6. Applies to this tree — **PASS** (clean).
**Exception category:** none required; it is a hang fix.
This matters on 6.18 because RAID rebuild of large arrays is normal
operations, and a 5–313s lockup (or panic) is exactly what stable is
for. The yield is the same one the I/O path already uses.
## Verification
- [Phase 1] Tags: author SOB, Yu Kuai SOB, msgid link; v2 had `Cc:
stable`, dropped on apply for missing Fixes.
- [Phase 2] Diff: 3 lines in `md_do_sync()` skip `continue`; 6.18 code
at 9752–9753 / 9779 matches.
- [Phase 2] RAID10: `max_sync = RESYNC_PAGES << (PAGE_SHIFT-9)` = 128;
`biolist == NULL` → `giveup`.
- [Phase 3] `git describe HEAD` / `make kernelversion`: 6.18.53, branch
`pending-6.18`.
- [Phase 3] 1/3 already ancestor (`0efabe6229dc6`); this commit is not
in HEAD.
- [Phase 4] `b4 mbox` 21-message thread; Yu Kuai: yield still valuable;
applied to md-7.3.
- [Phase 5] Caller: `md_register_thread(md_do_sync)` resync thread.
- [Phase 6] Skip path present; context matches; expected clean apply.
- [Phase 8] Failure: soft lockup 313s; severity CRITICAL.
- UNVERIFIED: lore.kernel.org/stable search (HTTP 403). Does not change
the verdict.
**YES**
drivers/md/md.c | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/drivers/md/md.c b/drivers/md/md.c
index ce3bd127e3bb3..44f44219ed7fe 100644
--- a/drivers/md/md.c
+++ b/drivers/md/md.c
@@ -9751,8 +9751,10 @@ void md_do_sync(struct md_thread *thread)
*/
md_new_event();
- if (last_check + window > io_sectors || j == max_sectors)
+ if (last_check + window > io_sectors || j == max_sectors) {
+ cond_resched();
continue;
+ }
last_check = io_sectors;
repeat:
--
2.53.0
^ permalink raw reply related [flat|nested] 6+ messages in thread* [PATCH AUTOSEL 6.18] md: skip bitmap accounting for empty write ranges
[not found] <20261019135441.1420968-1-sashal@kernel.org>
2026-10-19 13:46 ` [PATCH AUTOSEL 6.18-5.10] md: add cond_resched() to md_do_sync()'s skip path Sasha Levin
@ 2026-10-19 13:49 ` Sasha Levin
2026-10-19 15:34 ` sashiko-bot
2026-10-19 13:50 ` [PATCH AUTOSEL 6.18-5.10] md/raid5: reject zero-sector reshape chunks Sasha Levin
2026-10-19 13:52 ` [PATCH AUTOSEL 6.18] md/raid5: complete discard bios while reshape is active Sasha Levin
3 siblings, 1 reply; 6+ messages in thread
From: Sasha Levin @ 2026-10-19 13:49 UTC (permalink / raw)
To: patches, stable
Cc: Yu Kuai, Mykola Marzhan, Sasha Levin, song, linux-raid,
linux-kernel
From: Yu Kuai <yukuai@fygo.io>
[ Upstream commit 87c10252e3d6c28769d93bea9e6a9b592e5809d1 ]
mkfs.ext4 can submit zero-sector flush/FUA bios. These bios are WRITE
bios for md_write_start() purposes, but they do not cover any data sector
and must not dirty bitmap bits.
md bitmap accounting currently passes such bios to bitmap start_write().
For llbitmap this reaches llbitmap_start_write() with sectors == 0,
which underflows the end chunk calculation.
Personality bitmap mapping can also turn a non-empty bio into an empty
bitmap range when the requested sectors are outside the active bitmap
geometry. Treat both cases as not started, so the completion path will not
call end_write() for an empty range.
Tested-by: Mykola Marzhan <mykola@meshstor.io>
Link: https://patch.msgid.link/20260802195038.164272-11-yukuai@kernel.org
Signed-off-by: Yu Kuai <yukuai@fygo.io>
Signed-off-by: Sasha Levin <sashal@kernel.org>
---
LLM Generated explanations, may be completely bogus:
The local tree is **6.18.53** (`p-6.18`). The empty-range skip is
missing, and the llbitmap underflow path is present. Full analysis
follows.
===============================================================
PHASE 1: COMMIT MESSAGE FORENSICS
===============================================================
**Step 1.1: PARSE THE SUBJECT LINE**
Record: **[md] [skip] skip bitmap accounting for empty write ranges**
- Subsystem: `md` (MD RAID core)
- Action verb: **skip** (prevent incorrect handling)
- Intent: do not run bitmap start/end accounting when the write covers
no data sectors
**Step 1.2: PARSE ALL COMMIT MESSAGE TAGS**
Record:
- `Tested-by: Mykola Marzhan <mykola@meshstor.io>` — independent
hardware/user verification
- `Link:
https://patch.msgid.link/20260802195038.164272-11-yukuai@kernel.org` —
v5 series, patch 10/29 (msgid `-11` is 1-based with cover)
- `Signed-off-by: Yu Kuai <yukuai@fygo.io>` — author (MD contributor;
ignore pipeline SoBs)
- No `Fixes:` tag (expected)
- No `Cc: stable` (expected)
- No `Reported-by:` / syzbot
- No `Reviewed-by:` on this revision (v1 had Reviewed-by from Su Yue on
earlier series patches)
**Step 1.3: ANALYZE THE COMMIT BODY TEXT**
Record:
- **Bug:** mkfs.ext4 can submit zero-sector flush/FUA bios. They are
WRITE bios for `md_write_start()`, but they cover no data and must not
dirty bitmap bits. They are still passed to bitmap `start_write()`.
- **llbitmap failure mode:** `llbitmap_start_write()` with `sectors ==
0` underflows the end-chunk calculation (`offset + sectors - 1`).
- **Second case:** personality `bitmap_sector()` can map a non-empty bio
to an empty bitmap range (outside active geometry). Completion must
not call `end_write()` for a start that never ran / was empty.
- **Symptom:** bitmap accounting of a zero-length range → unsigned wrap
in llbitmap, then a huge page/chunk walk (barriers + state machine),
i.e. hang, bitmap corruption, or crash.
- **Version info:** none in the message; code is the post-6.14 upper-
layer bitmap path + llbitmap (2025).
**Step 1.4: DETECT HIDDEN BUG FIXES**
Record: **Not hidden.** It is an explicit correctness fix: skip empty
ranges so start/end stay balanced and llbitmap does not underflow.
===============================================================
PHASE 2: DIFF ANALYSIS
===============================================================
**Step 2.1: INVENTORY THE CHANGES**
Record:
- File: `drivers/md/md.c` only (~+7 / −2, four tight edits)
- Functions: `md_bitmap_start()`, `md_end_clone_io()`, `md_clone_bio()`
- Scope: **single-file surgical fix**
**Step 2.2: UNDERSTAND THE CODE FLOW CHANGE**
Record:
1. **`md_bitmap_start`:** after optional `bitmap_sector()` remap,
**return if `sectors == 0`** instead of calling
`start_write`/`start_discard`.
2. **`md_end_clone_io`:** call `md_bitmap_end()` only if
`md_io_clone->sectors != 0` (paired with start).
3. **`md_clone_bio`:** set `md_io_clone->sectors = 0` (mempool reuse).
Skip the start path unless `bio_sectors(*bio) != 0`.
4. **Affected paths:** WRITE clone accounting (`md_account_bio` →
`md_clone_bio`), including 0-sector flush/FUA and post-map empty
ranges. Not a refactor of the hot data path.
**Step 2.3: IDENTIFY THE BUG MECHANISM**
Record:
- **Category:** logic / integer underflow (unsigned wrap) + start/end
accounting mismatch
- **Mechanism:** `llbitmap_start_write()` does
`end = (offset + sectors - 1) >> chunkshift`.
`sectors == 0` underflows. If `offset == 0`, `end` becomes a huge
chunk index; the `page_start <= page_end` loop and
`llbitmap_state_machine()` then walk a massive range.
- Legacy `bitmap_start_write()` uses `while (sectors)` and is a no-op
for 0; the crash is **llbitmap-specific**, but the skip is correct for
both backends.
- The `sectors = 0` init is required: after skipping the start `if`,
completion must not see a stale mempool `sectors` value.
**Step 2.4: ASSESS THE FIX QUALITY**
Record:
- **Obviously correct:** empty range → don’t start, don’t end, init
`sectors` to 0.
- **Minimal:** four related edits, no API change.
- **Regression risk: low.** Worst case is skipping bitmap bits for a
0-sector I/O (correct). Start/end stay balanced.
- **Local extra path:** this tree’s `md_free_cloned_bio()` also calls
`md_bitmap_end()` without a `sectors` check (raid5 reshape-wait
abort). A faithful backport should add the same guard there. That is a
small extra hunk, not a reason to reject.
===============================================================
PHASE 3: GIT HISTORY INVESTIGATION
===============================================================
**Step 3.1: BLAME THE CHANGED LINES**
Record:
- `md_bitmap_start` / clone accounting introduced by `cd5fc65338181`
(“md/md-bitmap: move bitmap_{start, end}write to md upper layer”,
2025-01-09) — **is an ancestor of this HEAD**.
- Discard ops wiring: `ac9dad8faaa7b`.
- `else md_io_clone->start_time = 0` already here from `d81822fd5fa81`
(“md: avoid stale clone I/O accounting timestamps”).
- llbitmap: `5ab829f1971dc` (“md/md-llbitmap: introduce new lockless
bitmap”, 2025-09-06) — **present**.
- Bug is in **this** 6.18 tree, not only in later mainline.
**Step 3.2: FOLLOW THE FIXES: TAG**
Record: **No Fixes: tag.** Introducers are `cd5fc65338181` (upper-layer
accounting of all WRITE bios, including 0-sector) and `5ab829f1971dc`
(llbitmap underflow). Both are in this tree (`git merge-base --is-
ancestor` exit 0 for `cd5fc65338181`).
**Step 3.3: CHECK FILE HISTORY FOR RELATED CHANGES**
Record: recent `drivers/md/md.c` on this branch includes:
- `73881ff7a7591` md: wait for behind writes before destroying bitmap
(v5 patch 6)
- `d81822fd5fa81` md: avoid stale clone I/O accounting timestamps (v5
patch 7)
Same series already on this tree:
- `930cc4f1af5a1` md/md-llbitmap: prevent create failure bitmap UAF
(patch 8)
- `c55aa6c17f019` md/md-llbitmap: stop daemon timer rearm on destroy
(patch 9)
This commit is **v5 10/29**, the next accounting fix. Standalone vs
later reshape patches (11+). **No missing prerequisite** among 6–9 for
this hunk.
**Step 3.4: CHECK THE AUTHOR'S OTHER COMMITS**
Record: Yu Kuai is a primary MD/llbitmap author on this tree (bitmap
move, llbitmap, this series). Weight: high.
**Step 3.5: CHECK FOR DEPENDENT/PREREQUISITE COMMITS**
Record: Needs `md_bitmap_start` / `md_io_clone` /
`bitmap_ops->start_write` — all present. Does **not** need patch 12
(`prepare_range` / exact mapping); v5 commit text was updated to
`bitmap_sector` so it stands alone. **Can apply standalone** with small
context tweaks.
===============================================================
PHASE 4: MAILING LIST AND EXTERNAL RESEARCH
===============================================================
**Step 4.1: FIND THE ORIGINAL PATCH DISCUSSION**
Record:
- Lore msgid `20260802195038.164272-11-yukuai@kernel.org` (v5 10/29).
- Cover: [PATCH v5 00/29] md: improve lockless bitmap reshape support
https://lists.openwall.net/linux-kernel/2026/08/02/769
- Earlier: v1 2026-06-05, v2, v4 07/25, v5 10/29.
- Cover: patches 6–11 are “md core and llbitmap lifetime/accounting
prerequisites”; this one is “empty bitmap ranges”.
- v5 note: “Fix patch 10 to keep empty-range handling valid before the
exact mapping hook is introduced.”
- **No NAK** in the fetched cover/patch bodies.
- `b4 mbox`/`b4 dig -c` did not resolve without a mainline SHA (local
master is older than this series). Thread recovered via
gnuweeb/IU/openwall.
**Step 4.2: CHECK WHO REVIEWED THE PATCH**
Record: To/Cc Song Liu, Li Nan, Xiao Ni; lists linux-raid, linux-kernel.
Tested-by Mykola Marzhan (v4, carried to v5). Su Yue Reviewed-by on v1
series (not re-stated on this hunk in v5).
**Step 4.3: SEARCH FOR THE BUG REPORT**
Record: No syzbot/bugzilla. Trigger described as **mkfs.ext4 zero-sector
flush/FUA** — common userspace. Tested-by supports real-world impact.
**UNVERIFIED:** did not reproduce mkfs.ext4 on this kernel.
**Step 4.4: CHECK FOR RELATED PATCHES AND SERIES**
Record: Independent of reshape (11+). Empty-range skip is valid before
`prepare_range`. Same-series 6–9 already in this tree.
**Step 4.5: CHECK STABLE MAILING LIST HISTORY**
Record: gnuweeb `/stable/?q=skip+bitmap+accounting...` returned
unrelated stable-rc noise, **no dedicated stable thread**. Not a
negative signal.
===============================================================
PHASE 5: CODE SEMANTIC ANALYSIS
===============================================================
**Step 5.1: KEY FUNCTIONS**
Record: `md_bitmap_start`, `md_bitmap_end` (unchanged body, callers
gated), `md_end_clone_io`, `md_clone_bio`. Callee of interest:
`llbitmap_start_write` / `bitmap_start_write`. Local extra:
`md_free_cloned_bio`.
**Step 5.2: TRACE CALLERS**
Record:
- `md_clone_bio` ← `md_account_bio`
- `md_account_bio` ← raid0, raid1, raid10, raid5, md-linear
`make_request`
- `md_end_clone_io` = clone `bi_end_io`
- `md_free_cloned_bio` ← raid5 `STRIPE_WAIT_RESHAPE` abort
- Context: block submit from userspace (mkfs, etc.), not init-only
**Step 5.3: TRACE CALLEES**
Record: `md_bitmap_start` → `pers->bitmap_sector` (raid5) →
`bitmap_ops->start_write` (`llbitmap_start_write` or
`bitmap_start_write`). llbitmap: unsigned end chunk, per-page barriers,
`llbitmap_state_machine(..., BitmapActionStartwrite)`.
**Step 5.4: FOLLOW THE CALL CHAIN**
Record: **Userspace-reachable.** raid1/raid5 still send PREFLUSH empty
bios to `md_flush_request()`, which completes `bio_sectors == 0` without
accounting. FUA-only 0-sector WRITEs, or PREFLUSH that falls through
(`-EAGAIN` journal fallback in raid5), hit `md_account_bio`.
raid0/linear call `md_account_bio` on the I/O path. Confirmed in-tree:
`raid5_make_request` calls `md_account_bio` at line 6182 after flush
handling.
**Step 5.5: SEARCH FOR SIMILAR PATTERNS**
Record: `llbitmap_end_write` / `llbitmap_start_discard` use the same
`offset + sectors - 1` pattern. This patch avoids calling them with 0.
Legacy bitmap is safe via `while (sectors)`. No duplicate core skip
already present (`rg` found no `if (!md_io_clone->sectors)`).
===============================================================
PHASE 6: CROSS-REFERENCING AGAINST THE LOCAL TREE
===============================================================
**Step 6.1: DOES THE BUGGY CODE EXIST IN THIS TREE?**
Record: **Yes.**
- Tree: `git describe HEAD` → `p-6.18-1-g03b85e06ca9bf`; `make
kernelversion` → **6.18.53**
- `md_bitmap_start()` at `drivers/md/md.c:9261` calls `fn()` with no
empty-range check
- `md_clone_bio()` at 9319 starts bitmap on every WRITE if bitmap
enabled, including `bio_sectors == 0`
- `llbitmap_start_write()` at `md-llbitmap.c:1074` does `offset +
sectors - 1`
- `raid5_bitmap_sector()` can set `*sectors = end - start` to 0 when the
mapped range is empty
**Step 6.2: CHECK FOR BACKPORT COMPLICATIONS**
Record: **Minor conflicts / small extra hunk.**
- Local `md_end_clone_io` uses `bio->bi_private` as `md_io_clone*`;
candidate uses `container_of` + reshape completion. The `sectors &&`
condition still matches.
- Local already has `else start_time = 0` (patch 7).
- Add the same `md_io_clone->sectors &&` guard to `md_free_cloned_bio()`
(6.18-only helper).
**Step 6.3: CHECK IF RELATED FIXES ARE ALREADY HERE**
Record: Empty-range skip **not** present. Series patches 6–9 **are**
present. No alternate fix for this underflow.
===============================================================
PHASE 7: SUBSYSTEM AND MAINTAINER CONTEXT
===============================================================
**Step 7.1: SUBSYSTEM AND CRITICALITY**
Record: **md (MD RAID bitmap accounting).** **IMPORTANT** — not core
mm/vfs, but RAID+bitmap is widely used; mkfs on an MD device is a normal
path. llbitmap is the lockless bitmap backend in 6.18.
**Step 7.2: SUBSYSTEM ACTIVITY**
Record: **Active** on this branch (llbitmap, clone accounting, this Yu
Kuai series). Still a real bug in shipping 6.18 code, not a speculative
cleanup.
===============================================================
PHASE 8: IMPACT AND RISK ASSESSMENT
===============================================================
**Step 8.1: WHO IS AFFECTED**
Record: **MD users with a bitmap enabled**, especially **llbitmap**.
Triggered by 0-sector WRITE/flush/FUA (mkfs.ext4) or raid5
`bitmap_sector` mapping a request to an empty range. Config: `CONFIG_MD`
+ bitmap (llbitmap or classic).
**Step 8.2: TRIGGER CONDITIONS**
Record:
- Common: mkfs.ext4 / empty flush+FUA on an MD array with bitmap
- Also: raid5 geometry mapping to a zero bitmap range
- Unprivileged user with write access to the array (typical admin/mkfs)
- Not a narrow race; it is size==0 handling
**Step 8.3: FAILURE MODE SEVERITY**
Record:
- llbitmap: unsigned wrap → enormous chunk/page walk, barriers, state
machine on invalid range → **hang, bitmap corruption, possible crash**
- Classic bitmap: `while (sectors)` no-op, but start/end still run with
0; skip is still correct
- Severity: **HIGH** (hang/corruption on a common mkfs path with
llbitmap)
**Step 8.4: RISK-BENEFIT RATIO**
Record:
- **Benefit: high** — stops underflow/wrong bitmap updates on a real I/O
pattern
- **Risk: very low** — ~7–10 lines, local to clone accounting, start/end
remain paired
- **Ratio: strongly favors backport**
===============================================================
PHASE 9: FINAL SYNTHESIS
===============================================================
**Step 9.1: COMPILE THE EVIDENCE**
**FOR:**
- Real bug: 0-sector WRITE accounting into llbitmap underflow
- Common trigger (mkfs.ext4 flush/FUA), Tested-by
- Buggy path exists in this 6.18.53 tree (`md_bitmap_start`, llbitmap)
- Small, obviously correct, no new API
- Series patches 6–9 already here; this is the matching empty-range fix
- MD RAID data-integrity relevant
**AGAINST:**
- Part of a larger reshape series (but this hunk is standalone)
- `md_free_cloned_bio` needs the same `sectors` guard (small extra hunk)
- Context differs slightly (`bi_private` vs `container_of`)
**UNRESOLVED:**
- Did not run mkfs.ext4 against this build
- `b4 dig -c` had no mainline SHA in this clone; thread confirmed via
other archives
- gnuweeb stable search was noisy, no dedicated nomination thread
**Step 9.2: STABLE RULES CHECKLIST**
1. Obviously correct and tested? **PASS** — paired start/end skip +
Tested-by
2. Real bug affecting users? **PASS** — mkfs.ext4 0-sector WRITE +
llbitmap wrap
3. Important (crash/corruption/hang)? **PASS** — HIGH (underflow walk /
bitmap corruption)
4. Small and contained? **PASS** — one file, ~9 lines
5. No new features/APIs? **PASS**
6. Can apply to the local tree? **PASS** — logic matches; minor extra
hunk on `md_free_cloned_bio`
**Step 9.3: EXCEPTION CATEGORIES**
Record: Not an exception category. Ordinary important bug fix.
**Step 9.4: DECISION**
This tree already moved bitmap accounting into `md_clone_bio` and has
llbitmap. Empty WRITEs still call `start_write(sectors == 0)`, which
underflows llbitmap. The patch is the right, small fix and belongs on
6.18.y.
Verification:
- [Phase 1] Tags: Tested-by Mykola Marzhan; Link msgid
20260802195038.164272-11 (v5 10/29); author Yu Kuai; no Fixes/Cc-
stable
- [Phase 2] Diff: `md.c` only; skip empty start; gate end on `sectors`;
init `sectors = 0`; require `bio_sectors` before start
- [Phase 2] Confirmed `llbitmap_start_write` uses `offset + sectors - 1`
at md-llbitmap.c:1079
- [Phase 3] `git merge-base --is-ancestor cd5fc65338181 HEAD` → 0
(upper-layer bitmap in tree)
- [Phase 3] llbitmap file present; intro `5ab829f1971dc` in history
- [Phase 3] Series 6–9 already on this branch (`73881ff7`, `d81822fd`,
`930cc4f1`, `c55aa6c1`)
- [Phase 3] No `skip bitmap accounting` commit in local `git log --
drivers/md/`
- [Phase 4] Cover letter: empty bitmap ranges as accounting
prerequisite; v5 rebased so patch 10 does not need `prepare_range`
- [Phase 4] lore.kernel.org 403; recovered via gnuweeb + openwall + IU
- [Phase 4] No NAK in fetched cover/patch
- [Phase 5] Callers: raid0/1/5/10/linear `md_account_bio`; userspace
block I/O
- [Phase 5] Legacy `bitmap_start_write` is `while (sectors)` — llbitmap
is the underflow
- [Phase 6] `git describe HEAD` = p-6.18-1-g03b85e06ca9bf; `make
kernelversion` = 6.18.53
- [Phase 6] Current `md_bitmap_start` has no empty return;
`md_clone_bio` has no `bio_sectors` guard
- [Phase 6] Backport: add same `sectors` check to `md_free_cloned_bio`
(raid5 reshape abort)
- [Phase 8] Failure: unsigned wrap → huge llbitmap walk; severity HIGH
- UNVERIFIED: live mkfs.ext4 reproduction; `b4 dig` match by commit SHA
**YES**
drivers/md/md.c | 9 +++++++--
1 file changed, 7 insertions(+), 2 deletions(-)
diff --git a/drivers/md/md.c b/drivers/md/md.c
index 03d328506b285..ce3bd127e3bb3 100644
--- a/drivers/md/md.c
+++ b/drivers/md/md.c
@@ -9266,6 +9266,8 @@ static void md_bitmap_start(struct mddev *mddev,
mddev->pers->bitmap_sector(mddev, &md_io_clone->offset,
&md_io_clone->sectors);
+ if (!md_io_clone->sectors)
+ return;
fn(mddev, md_io_clone->offset, md_io_clone->sectors);
}
@@ -9284,7 +9286,8 @@ static void md_end_clone_io(struct bio *bio)
struct bio *orig_bio = md_io_clone->orig_bio;
struct mddev *mddev = md_io_clone->mddev;
- if (bio_data_dir(orig_bio) == WRITE && md_bitmap_enabled(mddev, false))
+ if (bio_data_dir(orig_bio) == WRITE && md_io_clone->sectors &&
+ md_bitmap_enabled(mddev, false))
md_bitmap_end(mddev, md_io_clone);
if (bio->bi_status && !orig_bio->bi_status)
@@ -9308,12 +9311,14 @@ static void md_clone_bio(struct mddev *mddev, struct bio **bio)
md_io_clone = container_of(clone, struct md_io_clone, bio_clone);
md_io_clone->orig_bio = *bio;
md_io_clone->mddev = mddev;
+ md_io_clone->sectors = 0;
if (blk_queue_io_stat(bdev->bd_disk->queue))
md_io_clone->start_time = bio_start_io_acct(*bio);
else
md_io_clone->start_time = 0;
- if (bio_data_dir(*bio) == WRITE && md_bitmap_enabled(mddev, false)) {
+ if (bio_data_dir(*bio) == WRITE && bio_sectors(*bio) &&
+ md_bitmap_enabled(mddev, false)) {
md_io_clone->offset = (*bio)->bi_iter.bi_sector;
md_io_clone->sectors = bio_sectors(*bio);
md_io_clone->rw = op_stat_group(bio_op(*bio));
--
2.53.0
^ permalink raw reply related [flat|nested] 6+ messages in thread* Re: [PATCH AUTOSEL 6.18] md: skip bitmap accounting for empty write ranges
2026-10-19 13:49 ` [PATCH AUTOSEL 6.18] md: skip bitmap accounting for empty write ranges Sasha Levin
@ 2026-10-19 15:34 ` sashiko-bot
0 siblings, 0 replies; 6+ messages in thread
From: sashiko-bot @ 2026-10-19 15:34 UTC (permalink / raw)
To: Sasha Levin; +Cc: linux-raid, yukuai
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [High] md: missing sectors check in md_free_cloned_bio() before md_bitmap_end()
--
commit 6836aa7ecbc70ba01261dda2960c8f2c78bf3f55
Author: Yu Kuai <yukuai@fygo.io>
md: skip bitmap accounting for empty write ranges
This commit skips md bitmap start_write() and end_write() accounting when a
write bio has zero sectors or when personality bitmap mapping turns the bio
into an empty bitmap range.
Link: https://patch.msgid.link/20260802195038.164272-11-yukuai@kernel.org
> diff --git a/drivers/md/md.c b/drivers/md/md.c
> index 03d328506b285..ce3bd127e3bb3 100644
> --- a/drivers/md/md.c
> +++ b/drivers/md/md.c
> @@ -9266,6 +9266,8 @@ static void md_bitmap_start(struct mddev *mddev,
> mddev->pers->bitmap_sector(mddev, &md_io_clone->offset,
> &md_io_clone->sectors);
>
> + if (!md_io_clone->sectors)
> + return;
> fn(mddev, md_io_clone->offset, md_io_clone->sectors);
> }
>
> @@ -9284,7 +9286,8 @@ static void md_end_clone_io(struct bio *bio)
> struct bio *orig_bio = md_io_clone->orig_bio;
> struct mddev *mddev = md_io_clone->mddev;
>
> - if (bio_data_dir(orig_bio) == WRITE && md_bitmap_enabled(mddev, false))
> + if (bio_data_dir(orig_bio) == WRITE && md_io_clone->sectors &&
> + md_bitmap_enabled(mddev, false))
> md_bitmap_end(mddev, md_io_clone);
>
> if (bio->bi_status && !orig_bio->bi_status)
> @@ -9308,12 +9311,14 @@ static void md_clone_bio(struct mddev *mddev, struct bio **bio)
> md_io_clone = container_of(clone, struct md_io_clone, bio_clone);
> md_io_clone->orig_bio = *bio;
> md_io_clone->mddev = mddev;
> + md_io_clone->sectors = 0;
> if (blk_queue_io_stat(bdev->bd_disk->queue))
> md_io_clone->start_time = bio_start_io_acct(*bio);
> else
> md_io_clone->start_time = 0;
>
> - if (bio_data_dir(*bio) == WRITE && md_bitmap_enabled(mddev, false)) {
> + if (bio_data_dir(*bio) == WRITE && bio_sectors(*bio) &&
> + md_bitmap_enabled(mddev, false)) {
> md_io_clone->offset = (*bio)->bi_iter.bi_sector;
> md_io_clone->sectors = bio_sectors(*bio);
> md_io_clone->rw = op_stat_group(bio_op(*bio));
[Severity: High]
Should md_free_cloned_bio() in drivers/md/md.c also check
md_io_clone->sectors before calling md_bitmap_end(), just like
md_end_clone_io() does here?
In drivers/md/md.c, md_free_cloned_bio() still has:
if (bio_data_dir(orig_bio) == WRITE && md_bitmap_enabled(mddev, false))
md_bitmap_end(mddev, md_io_clone);
In raid5_make_request() -> md_account_bio() -> md_clone_bio(), the bio is
cloned before calling make_stripe_request(). If make_stripe_request()
returns STRIPE_WAIT_RESHAPE when a reshape is interrupted,
raid5_make_request() aborts the cloned bio via md_free_cloned_bio():
if (res == STRIPE_WAIT_RESHAPE) {
md_free_cloned_bio(bi);
return false;
}
With this patch, there are two cases where md_bitmap_start() is skipped
with md_io_clone->sectors == 0:
1. When bio_sectors(*bio) == 0 (for example, a zero-sector REQ_OP_WRITE |
REQ_FUA bio, or a zero-sector REQ_OP_WRITE | REQ_PREFLUSH bio falling
through log_handle_flush_request() with -EAGAIN), md_clone_bio() sets
md_io_clone->sectors = 0 and skips initializing md_io_clone->offset and
md_io_clone->rw (leaving stale contents from the mddev->io_clone_set
mempool) and skips calling md_bitmap_start().
2. When bio_sectors(*bio) != 0, but mddev->pers->bitmap_sector()
(raid5_bitmap_sector()) maps the range to md_io_clone->sectors == 0,
md_bitmap_start() returns early without calling start_write() or
start_discard().
If make_stripe_request() then returns STRIPE_WAIT_RESHAPE,
md_free_cloned_bio() will unconditionally call md_bitmap_end(mddev,
md_io_clone) with md_io_clone->sectors == 0 (and uninitialized offset and
rw in the first case). For llbitmap, llbitmap_end_write() and
llbitmap_end_discard() compute:
unsigned long end = (offset + sectors - 1) >> llbitmap->chunkshift;
When sectors == 0, if offset == 0 this underflows to ULONG_MAX and walks
llbitmap->pctl[] out of bounds, or if offset > 0 it calls
llbitmap_release_barrier() to drop page barrier references on
pctl->active that were never raised.
--
Sashiko AI review · https://sashiko.dev/#/patchset/20261019135441.1420968-307-sashal@kernel.org?part=1
^ permalink raw reply [flat|nested] 6+ messages in thread
* [PATCH AUTOSEL 6.18-5.10] md/raid5: reject zero-sector reshape chunks
[not found] <20261019135441.1420968-1-sashal@kernel.org>
2026-10-19 13:46 ` [PATCH AUTOSEL 6.18-5.10] md: add cond_resched() to md_do_sync()'s skip path Sasha Levin
2026-10-19 13:49 ` [PATCH AUTOSEL 6.18] md: skip bitmap accounting for empty write ranges Sasha Levin
@ 2026-10-19 13:50 ` Sasha Levin
2026-10-19 13:52 ` [PATCH AUTOSEL 6.18] md/raid5: complete discard bios while reshape is active Sasha Levin
3 siblings, 0 replies; 6+ messages in thread
From: Sasha Levin @ 2026-10-19 13:50 UTC (permalink / raw)
To: patches, stable
Cc: Yu Kuai, Mykola Marzhan, Sasha Levin, song, linux-raid,
linux-kernel
From: Yu Kuai <yukuai@fygo.io>
[ Upstream commit a41bb2ee1aca486853e84920565e50c15f86fa5d ]
Sashiko reported that RAID5 can accept a reshape chunk size that becomes
zero sectors. chunk_size_store() stores the sysfs byte value as n >> 9, so
writing a value below 512 bytes sets mddev->new_chunk_sectors to zero.
RAID5 then accepted that pending reshape geometry and raid5_start_reshape()
installed it into conf->chunk_sectors, letting reshape code divide by zero.
Reject zero-sector chunks both in check_reshape(), where normal sysfs
requests are validated, and in raid5_start_reshape(), so assembly/resume
paths also cannot install zero chunk geometry.
Test script: in QEMU, create a plain three-disk RAID5 array with 64K
chunks, write/read back a small pattern, write 1 to
/sys/block/md0/md/chunk_size, add a fourth disk, and run mdadm --grow
--raid-devices=4 --backup-file=... . The script scans dmesg for divide
error/Oops/KASAN signatures.
Bad kernel, eb29914412c3:
echo 1 > /sys/block/md0/md/chunk_size
mdadm --grow /dev/md0 --raid-devices=4 --backup-file=/root/md0-grow.bak
Oops: divide error: 0000 [#1] SMP KASAN NOPTI
RIP: raid5_get_active_stripe+0x863/0xc10
Call Trace:
raid5_sync_request
md_do_sync
md_thread
Kernel panic - not syncing: Fatal exception
Fixed kernel: echo 1 > /sys/block/md0/md/chunk_size bash: echo: write
error: Invalid argument chunk_write_rc=1 grow_rc=skipped RESULT:
REJECTED_ZERO_CHUNK_NO_OOPS
Tested-by: Mykola Marzhan <mykola@meshstor.io>
Link: https://patch.msgid.link/20260802195038.164272-5-yukuai@kernel.org
Signed-off-by: Yu Kuai <yukuai@fygo.io>
Signed-off-by: Sasha Levin <sashal@kernel.org>
---
LLM Generated explanations, may be completely bogus:
This is a **5-line RAID5 input-validation fix** for a divide-by-zero
panic during reshape. The local tree is **6.18.53** (`pending-6.18`);
the buggy path is present and the patch applies.
===============================================================
PHASE 1: COMMIT MESSAGE FORENSICS
===============================================================
Step 1.1: PARSE THE SUBJECT LINE
Record: **[md/raid5] [reject] reject zero-sector reshape chunks**
Step 1.2: PARSE ALL COMMIT MESSAGE TAGS
Record:
- **Tested-by:** Mykola Marzhan `<mykola@meshstor.io>` (independent QEMU
reproduction)
- **Link:**
`https://patch.msgid.link/20260802195038.164272-5-yukuai@kernel.org`
(v5 04/29)
- **Signed-off-by:** Yu Kuai `<yukuai@fygo.io>` (MD RAID co-maintainer)
- No **Fixes:**, **Reported-by:**, **Cc: stable**, **Reviewed-by**, or
**Acked-by** on the committed message (absence of Fixes/Cc:stable is
expected for this review)
- Body names **Sashiko** as the reporter (test harness, not syzbot)
- Bad-kernel SHA cited: `eb29914412c3`
Step 1.3: ANALYZE THE COMMIT BODY TEXT
Record:
- **Bug:** `chunk_size_store()` does `n >> 9`. A sysfs write below 512
bytes stores `mddev->new_chunk_sectors = 0`. RAID5 accepts that
geometry; `raid5_start_reshape()` copies it into
`conf->chunk_sectors`.
- **Symptom:** `Oops: divide error` in `raid5_get_active_stripe`, call
chain `raid5_sync_request` → `md_do_sync` → `md_thread`, then **kernel
panic**.
- **Trigger:** RAID5 array, `echo 1 > /sys/block/md0/md/chunk_size`,
then `mdadm --grow --raid-devices=4`.
- **Fix:** return `-EINVAL` in `check_reshape()` and
`raid5_start_reshape()` when `new_chunk_sectors` is zero.
- **Fixed result:** sysfs write returns `Invalid argument`; grow is
skipped; no oops.
Step 1.4: DETECT HIDDEN BUG FIXES
Record: Not hidden. Explicit panic fix with a reproduction and a
before/after result.
===============================================================
PHASE 2: DIFF ANALYSIS - LINE BY LINE
===============================================================
Step 2.1: INVENTORY THE CHANGES
Record:
- **File:** `drivers/md/raid5.c` only (**+5 / −0**)
- **Functions:** `check_reshape()`, `raid5_start_reshape()`
- **Scope:** single-file surgical validation
Step 2.2: UNDERSTAND THE CODE FLOW CHANGE
Record:
- **Hunk 1 (`check_reshape`):** after `has_failed()`, add `if
(!mddev->new_chunk_sectors) return -EINVAL`. Sysfs/`mdadm` reshape
checks that previously accepted 0 now fail. `chunk_size_store()` then
restores `new_chunk_sectors` to the live chunk size.
- **Hunk 2 (`raid5_start_reshape`):** after `MD_RECOVERY_RUNNING` busy-
check, same reject. Covers start-reshape, assembly, and resume so zero
geometry cannot be installed into `conf->chunk_sectors`.
Step 2.3: IDENTIFY THE BUG MECHANISM
Record: **[Logic / correctness + divide-by-zero]**
`raid5_check_reshape()` only validates chunk size when `new_chunk > 0`,
so **0 skips the power-of-two / PAGE_SIZE checks**. `check_reshape()`
then treats `0 != current chunk` as a real reshape.
`raid5_start_reshape()` does `conf->chunk_sectors =
mddev->new_chunk_sectors`. `stripe_set_idx()` / `raid5_compute_sector()`
then `sector_div(..., conf->chunk_sectors)` with divisor 0.
Step 2.4: ASSESS THE FIX QUALITY
Record: Obviously correct. Two `-EINVAL` guards, no API/locking/data-
structure change. Regression risk is rejecting a reshape that previously
panics. Normal grow does not use chunk size 0: `setup_conf()` already
requires a non-zero chunk, and running arrays keep `new_chunk_sectors ==
chunk_sectors`.
===============================================================
PHASE 3: GIT HISTORY INVESTIGATION
===============================================================
Step 3.1: BLAME THE CHANGED LINES
Record:
- Zero-chunk reject in **`setup_conf()`** since **Andre Noll, 2009**
(`664e7c413f1e9`) — array *start* is safe.
- `raid5_check_reshape()` `if (new_chunk > 0)` hole since **Neil Brown,
2009** (`b3546035277847`).
- `chunk_size_store()` `n >> 9` since **2009**.
- `conf->chunk_sectors = mddev->new_chunk_sectors` in
`raid5_start_reshape()` since **2009** (`09c9e5fa1b93ad`).
- Bug is long-standing, not new 6.18 code.
Step 3.2: FOLLOW THE FIXES: TAG
Record: No Fixes: tag. Introducer is the 2009 `n >> 9` store plus the
`new_chunk > 0` skip, both in this tree.
Step 3.3: CHECK FILE HISTORY FOR RELATED CHANGES
Record:
- This tree **already has** series patch 05: `02c10581866d0 md/raid5:
round bitmap stripes with sector division` (upstream `17ea021ae7498`).
- This commit (series 04, `a41bb2ee1aca4`) is **not** an ancestor of
HEAD.
- Cover letter: first five patches are **standalone** RAID5/llbitmap
correctness fixes. 04 does not depend on 05; 05 being present does not
replace 04.
Step 3.4: CHECK THE AUTHOR'S OTHER COMMITS
Record: Yu Kuai is listed in MAINTAINERS as **SOFTWARE RAID** co-
maintainer with Song Liu. Frequent `drivers/md/raid5.c` contributor.
Weight: high.
Step 3.5: CHECK FOR DEPENDENT/PREREQUISITE COMMITS
Record: None. Adds two comparisons on existing
`mddev->new_chunk_sectors`. `git apply --check` of `a41bb2ee1aca4`
**succeeded** (hunks at 8445 and 8490, offset −103).
===============================================================
PHASE 4: MAILING LIST AND EXTERNAL RESEARCH
===============================================================
Step 4.1: FIND THE ORIGINAL PATCH DISCUSSION
Record:
- **b4 dig -c a41bb2ee1aca4:** `[PATCH v5 04/29] md/raid5: reject zero-
sector reshape chunks` —
`https://patch.msgid.link/20260802195038.164272-5-yukuai@kernel.org`
- Series: **v5 of 29**, “md: improve lockless bitmap reshape support”
- This check was **added in v3** (“Add patch 4 to reject zero-sector
RAID5 reshape chunks”); v4/v5 keep the same diff.
- Cover letter: *“The first five patches fix standalone llbitmap and
RAID5 correctness issues that are independent of reshape.”*
- No NAK found on patchew/LKML/openwall mirrors. lore.kernel.org fetch
returned **403**; discussion taken from b4, patchew, LKML IU, and
openwall.
Step 4.2: CHECK WHO REVIEWED THE PATCH
Record: **b4 dig -w** To/Cc: Song Liu, Li Nan, Xiao Ni, linux-raid,
linux-kernel, Mykola Marzhan, Su Yue. Subsystem maintainers were on the
original mail. No Reviewed-by on this specific commit; series v1 had
Reviewed-by from Su Yue on other patches.
Step 4.3: SEARCH FOR THE BUG REPORT
Record: Reproduced in QEMU with an oops/panic log. **Tested-by** Mykola
Marzhan. Not syzbot. Trigger is crafted (write `1` then grow) but the
oops is real.
Step 4.4: CHECK FOR RELATED PATCHES AND SERIES
Record: 29-patch llbitmap reshape series. **This patch is standalone
(04/29).** Later patches add llbitmap reshape; they are not required for
this reject.
Step 4.5: CHECK STABLE MAILING LIST HISTORY
Record: No stable@ thread found for this subject. UNVERIFIED beyond web
search (lore.kernel.org/stable also 403).
===============================================================
PHASE 5: CODE SEMANTIC ANALYSIS
===============================================================
Step 5.1: IDENTIFY KEY FUNCTIONS IN THE DIFF
Record: `check_reshape()`, `raid5_start_reshape()`. Callers of the
first: `raid5_check_reshape()`, `raid6_check_reshape()` (raid4/5/6
`pers->check_reshape`).
Step 5.2: TRACE CALLERS
Record:
- `check_reshape` via sysfs: `chunk_size_store()`, `layout_store()`,
`raid_disks_store` → `update_raid_disks()`, `action_store`
(“reshape”), plus ioctl/`md_check_recovery`.
- `raid5_start_reshape`: `pers->start_reshape` from
action/ioctl/assembly when a reshape starts or is resumed.
- Context: md thread / reconfig, not IRQ.
Step 5.3: TRACE CALLEES
Record: After the new checks, existing `check_stripe_cache()`,
`has_failed()`, `resize_chunks()`, and geometry install. The dangerous
callee is later `sector_div` on `conf->chunk_sectors`.
Step 5.4: FOLLOW THE CALL CHAIN
Record (verified in this tree):
1. `chunk_size_store()` (`drivers/md/md.c:4492`) `new_chunk_sectors = n
>> 9` (write `1` → 0).
2. `raid5_check_reshape()` (`8793`) skips validation because `new_chunk
> 0` is false (`8805`).
3. `check_reshape()` (`8436`) has **no** zero reject; `0 !=
chunk_sectors` so it does not take the “nothing to do” return.
4. `mdadm --grow` → `raid5_start_reshape()` (`8533`)
`conf->chunk_sectors = mddev->new_chunk_sectors` (**0**).
5. Reshape sync: `raid5_sync_request` → `raid5_get_active_stripe` →
`init_stripe` → `stripe_set_idx` (`3576`) **`sector_div(stripe,
sectors_per_chunk)` with 0** → divide error.
Userspace-reachable via sysfs + mdadm; `chunk_size` is `S_IWUSR` (root).
Step 5.5: SEARCH FOR SIMILAR PATTERNS
Record: `setup_conf()` already rejects `!new_chunk_sectors` (`7524`).
Reshape is the missing twin of that start-time check. `new_chunk_sectors
= 0` elsewhere is array teardown (`md.c` reset), not a live RAID5
reshape sentinel.
===============================================================
PHASE 6: CROSS-REFERENCING AGAINST THE LOCAL TREE
===============================================================
Step 6.1: DOES THE BUGGY CODE EXIST IN THIS TREE?
Record: **Yes.** Tree is **v6.18.53 / pending-6.18** (`git describe`:
`p-6.18-1-g03b85e06ca9bf`). `a41bb2ee1aca4` is **not** an ancestor of
HEAD. Hole is at `check_reshape()` `8436` and `raid5_start_reshape()`
`8480`; store is `md.c:4492`.
Step 6.2: CHECK FOR BACKPORT COMPLICATIONS
Record: **Clean apply** (`git apply --check` exit 0, offset −103 only).
Insertion points match: after `has_failed()` and after
`MD_RECOVERY_RUNNING`.
Step 6.3: CHECK IF RELATED FIXES ARE ALREADY HERE
Record: No `zero-sector reshape` commit in this tree. Sibling 05 is
present; it does not reject zero chunks. `setup_conf()` zero check does
not cover reshape.
===============================================================
PHASE 7: SUBSYSTEM AND MAINTAINER CONTEXT
===============================================================
Step 7.1: IDENTIFY THE SUBSYSTEM AND ITS CRITICALITY
Record: **md/raid5 (software RAID)** — **IMPORTANT**. RAID5 reshape is a
production path (grow/shrink/layout change).
Step 7.2: ASSESS SUBSYSTEM ACTIVITY
Record: Actively maintained (`raid5.c` still getting lock/bitmap/discard
fixes). This hole is old (2009), not a new-feature bug.
===============================================================
PHASE 8: IMPACT AND RISK ASSESSMENT
===============================================================
Step 8.1: DETERMINE WHO IS AFFECTED
Record: **Driver-specific:** RAID4/5/6 users who reshape (or resume a
reshape) after a sub-512 `chunk_size` sysfs write, or with a superblock
`new_chunk` that becomes 0.
Step 8.2: DETERMINE THE TRIGGER CONDITIONS
Record: Not every boot. Needs a running RAID5/6, privileged sysfs write
`< 512` to `chunk_size`, then reshape (or resume with zero new-chunk in
metadata). Unprivileged users normally cannot write that sysfs file.
Still a real panic for admins, scripts, and test/fuzz tools.
Step 8.3: DETERMINE THE FAILURE MODE SEVERITY
Record: **Kernel oops + panic (divide-by-zero).** **CRITICAL.**
Step 8.4: CALCULATE RISK-BENEFIT RATIO
Record: **Benefit: high** (stops a proven panic). **Risk: very low** (5
lines, error-path `-EINVAL` only). Ratio strongly favors backport.
===============================================================
PHASE 9: FINAL SYNTHESIS
===============================================================
Step 9.1: COMPILE THE EVIDENCE
**FOR:**
- Proven divide-by-zero panic with call trace and QEMU repro.
- Same hole exists in this 6.18.53 tree (`n >> 9`, `if (new_chunk > 0)`
skip, unconditional install in `raid5_start_reshape`).
- 5-line, obviously correct guards; apply-check clean.
- Standalone per author cover letter; no new APIs.
- MD co-maintainer author; Tested-by from a third party.
- `setup_conf()` already rejects 0; reshape was the gap.
**AGAINST:**
- Privileged, somewhat artificial trigger (`echo 1` then grow).
- Nested in a 29-patch series (mitigated: explicitly standalone).
- No Fixes:/Cc:stable (expected here; not a negative).
**UNRESOLVED:**
- Full lore.kernel.org HTML was 403; used b4 + mirrors.
- No stable@ discussion found.
Step 9.2: APPLY THE STABLE RULES CHECKLIST
1. Obviously correct and tested? **PASS** — two guards; Tested-by;
before/after log.
2. Fixes a real bug that affects users? **PASS** — oops/panic on RAID5
reshape.
3. Important issue? **PASS** — **CRITICAL** kernel panic.
4. Small and contained? **PASS** — +5 lines, one file.
5. No new features or APIs? **PASS**.
6. Can apply to the local tree? **PASS** — apply-check succeeded; bug
present.
Step 9.3: CHECK FOR EXCEPTION CATEGORIES
Record: Not an ID/quirk/DT/docs exception. It is a normal stability fix.
Step 9.4: MAKE YOUR DECISION
Backport to this 6.18.53 tree. It closes a long-standing validation hole
that panics the kernel, with negligible regression risk.
===============================================================
What the commit solves for this tree
===============================================================
On 6.18.53, writing a sub-512 value to `/sys/block/mdX/md/chunk_size`
stores zero sectors. RAID5 reshape validation treats 0 as “no chunk
change” in `raid5_check_reshape()`, then accepts it in
`check_reshape()`, and `raid5_start_reshape()` installs 0 into
`conf->chunk_sectors`. Reshape then divides by zero in
`stripe_set_idx()` / `raid5_compute_sector()` and panics.
The patch rejects that geometry at the two points that can install it.
That matches `setup_conf()`’s existing start-time check and does not
change normal `mdadm --grow` (which keeps the current non-zero chunk).
Risk vs benefit: five lines of `-EINVAL` versus a documented panic.
Apply is clean. Series membership is not a blocker; the cover letter
calls this patch standalone, and this tree already took the following
patch (05) without this one.
===============================================================
Verification
===============================================================
- **[Phase 1]** Parsed tags: Tested-by Mykola Marzhan; Link msgid
`20260802195038.164272-5`; SoB Yu Kuai; no Fixes/Cc:stable.
Subject/body describe divide-by-zero panic.
- **[Phase 2]** Diff: +5 in `check_reshape` and `raid5_start_reshape`
only. Confirmed `raid5_start_reshape` assigns `conf->chunk_sectors =
mddev->new_chunk_sectors` at `raid5.c:8533`.
- **[Phase 3]** `git merge-base --is-ancestor a41bb2ee1aca4 HEAD` → not
in tree (exit 1). Blame: `n >> 9` and `new_chunk > 0` from 2009.
Author is MD co-maintainer (`MAINTAINERS` SOFTWARE RAID).
- **[Phase 3]** `git apply --check` of `a41bb2ee1aca4` → success, offset
−103.
- **[Phase 3]** Sibling 05 `02c10581866d0` is already in HEAD; 04 is
not. Cover letter: first five patches standalone.
- **[Phase 4]** `b4 dig -c a41bb2ee1aca4` → v5 04/29, msgid.link above.
`b4 dig -w` → Song Liu, linux-raid, testers. Patch added in v3; v5
matches committed diff. lore.kernel.org WebFetch 403.
- **[Phase 4]** patchew/LKML/openwall: same patch text; Mykola Tested-by
on the series; no NAK found.
- **[Phase 5]** Call path verified in this tree: `chunk_size_store`
(`md.c:4492`) → `raid5_check_reshape` (`if (new_chunk > 0)` at 8805) →
`check_reshape` (no zero check) → `raid5_start_reshape` install →
`stripe_set_idx` `sector_div` at 3576. `setup_conf` already rejects 0
at 7524.
- **[Phase 6]** `git describe HEAD` = `p-6.18-1-g03b85e06ca9bf`; `make
kernelversion` = **6.18.53**; branch `pending-6.18`. Buggy code
present; no existing zero-sector reshape fix.
- **[Phase 7]** md/raid5, IMPORTANT, actively maintained.
- **[Phase 8]** Failure mode: divide error oops + panic. Severity
CRITICAL. Trigger: privileged sysfs + reshape. Benefit high, risk very
low.
- **UNVERIFIED:** Full lore.kernel.org thread HTML (HTTP 403); no
stable@ hit beyond web search.
**YES**
drivers/md/raid5.c | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/drivers/md/raid5.c b/drivers/md/raid5.c
index b7da2b83e1938..56d7e05e94ce6 100644
--- a/drivers/md/raid5.c
+++ b/drivers/md/raid5.c
@@ -8437,6 +8437,8 @@ static int check_reshape(struct mddev *mddev)
return 0; /* nothing to do */
if (has_failed(conf))
return -EINVAL;
+ if (!mddev->new_chunk_sectors)
+ return -EINVAL;
if (mddev->delta_disks < 0 && mddev->reshape_position == MaxSector) {
/* We might be able to shrink, but the devices must
* be made bigger first.
@@ -8480,6 +8482,9 @@ static int raid5_start_reshape(struct mddev *mddev)
if (test_bit(MD_RECOVERY_RUNNING, &mddev->recovery))
return -EBUSY;
+ if (!mddev->new_chunk_sectors)
+ return -EINVAL;
+
if (!check_stripe_cache(mddev))
return -ENOSPC;
--
2.53.0
^ permalink raw reply related [flat|nested] 6+ messages in thread* [PATCH AUTOSEL 6.18] md/raid5: complete discard bios while reshape is active
[not found] <20261019135441.1420968-1-sashal@kernel.org>
` (2 preceding siblings ...)
2026-10-19 13:50 ` [PATCH AUTOSEL 6.18-5.10] md/raid5: reject zero-sector reshape chunks Sasha Levin
@ 2026-10-19 13:52 ` Sasha Levin
2026-10-19 15:25 ` sashiko-bot
3 siblings, 1 reply; 6+ messages in thread
From: Sasha Levin @ 2026-10-19 13:52 UTC (permalink / raw)
To: patches, stable
Cc: Genjian Zhang, Yu Kuai, Sasha Levin, song, linux-raid,
linux-kernel
From: Genjian Zhang <zhanggenjian@kylinos.cn>
[ Upstream commit 85764f475f3b3abd956bf8eaeaa643b367676cf4 ]
make_discard_request() returns without completing the bio when reshape
is in progress. Discard callers block in submit_bio_wait()
waiting for a completion that never arrives. The caller hangs in
uninterruptible sleep, and this does not resolve when reshape finishes.
Complete the bio with BLK_STS_AGAIN so userspace can retry after reshape,
consistent with the existing policy of not processing discard during
reshape.
Tested on a loop-backed RAID5 array during mdadm --grow: without this
patch, blkdiscard hangs in bio_await() and remains in uninterruptible
sleep after md reports "reshape done"; with this patch it returns
-EAGAIN instead.
Signed-off-by: Genjian Zhang <zhanggenjian@kylinos.cn>
Reviewed-by: Yu Kuai <yukuai@fygo.io>
Link: https://patch.msgid.link/20260711161326.962336-1-zhanggenjian@126.com
Signed-off-by: Yu Kuai <yukuai@fygo.io>
Signed-off-by: Sasha Levin <sashal@kernel.org>
---
LLM Generated explanations, may be completely bogus:
This tree is **6.1.188**. The reshape discard hang is present here; the
fix is a small completion of an otherwise leaked bio.
===============================================================
PHASE 1: COMMIT MESSAGE FORENSICS
===============================================================
Step 1.1: PARSE THE SUBJECT LINE
Record: `[md/raid5] [complete] complete discard bios while reshape is
active`
Step 1.2: PARSE ALL COMMIT MESSAGE TAGS
Record:
- Signed-off-by: Genjian Zhang <zhanggenjian@kylinos.cn> (author)
- Reviewed-by: Yu Kuai <yukuai@fygo.io> (applied the patch to md-7.3)
- Link:
https://patch.msgid.link/20260711161326.962336-1-zhanggenjian@126.com
- Signed-off-by: Yu Kuai <yukuai@fygo.io> (maintainer tree)
- No Fixes: tag (expected)
- No Cc: stable (expected)
- No Reported-by / Tested-by / Acked-by
- Ignore pipeline SOBs
Step 1.3: ANALYZE THE COMMIT BODY TEXT
Record:
- Bug: `make_discard_request()` returns without `bio_endio()` when
`mddev->reshape_position != MaxSector`.
- Symptom: discard callers (`submit_bio_wait()` / `blkdiscard`) sleep in
D-state forever. Hang survives reshape completion.
- Root cause: early return that skips completion; the bio is never
finished.
- Fix: complete with `BLK_STS_AGAIN` so userspace gets `-EAGAIN` and can
retry after reshape.
- Author tested loop-backed RAID5 during `mdadm --grow`.
Step 1.4: DETECT HIDDEN BUG FIXES
Record: Not hidden. This is an explicit hang fix (bio never completed).
===============================================================
PHASE 2: DIFF ANALYSIS - LINE BY LINE
===============================================================
Step 2.1: INVENTORY THE CHANGES
Record:
- File: `drivers/md/raid5.c` only
- Scope: surgical, one function (`make_discard_request`)
- Candidate form: early `return` → `goto complete_again`; success-path
`bio_endio` gets an explicit `return`; new label calls
`bio_endio_status(bi, BLK_STS_AGAIN)`
- Mailing-list v1 (what Yu Kuai applied): 3 insertions / 1 deletion,
inline `bio_endio_status` before `return`. Same behavior.
Step 2.2: UNDERSTAND THE CODE FLOW CHANGE
Record:
- Before: reshape active → return, bio left outstanding.
- After: reshape active → complete bio with `BLK_STS_AGAIN`.
- Path: discard submit during reshape (admin `blkdiscard` / `fstrim` /
FS discard).
- Success path unchanged (`bio_endio(bi)` still runs).
Step 2.3: IDENTIFY THE BUG MECHANISM
Record:
- Category: logic / resource-completion bug → uninterruptible hang
- Mechanism: bio remaining count never reaches zero; `submit_bio_wait()`
sits in `wait_for_completion_io()`.
- Fix: complete the bio with a retryable status instead of dropping it.
Step 2.4: ASSESS THE FIX QUALITY
Record:
- Obviously correct: every bio must be completed; this path did not.
- Minimal: a few lines, one function.
- Regression: callers see `-EAGAIN` instead of hanging.
`blkdev_issue_discard()` maps `-EOPNOTSUPP` to success, so
`BLK_STS_NOTSUPP` would lie; `BLK_STS_AGAIN` is the right retry
signal. Header comment says `BLK_STS_AGAIN` is for NOWAIT; maintainer
still accepted this, and the author showed `NOTSUPP` would look like a
successful discard.
- Red flags: none on the hang fix itself. Helper `bio_endio_status()` is
missing in 6.1 (backport uses existing `bio->bi_status` +
`bio_endio()`).
===============================================================
PHASE 3: GIT HISTORY INVESTIGATION
===============================================================
Step 3.1: BLAME THE CHANGED LINES
Record:
- Introducer: `620125f2bf8ff0` “MD: raid5 trim support” (Shaohua Li,
2012-10-11).
- The skip-without-complete has been in tree since RAID5 discard was
added.
- Present in this 6.1 checkout at `drivers/md/raid5.c:5809-5811`.
Step 3.2: FOLLOW THE FIXES: TAG
Record: No Fixes: tag. Introducer confirmed via blame + `git merge-base
--is-ancestor 620125f2bf8ff0 HEAD`.
Step 3.3: CHECK FILE HISTORY FOR RELATED CHANGES
Record:
- Recent `raid5.c` work is other hang/lockup fixes, not this discard
skip.
- `git log --grep='complete discard'` / `'discard bios while reshape'`
on `drivers/md/`: not in this tree.
- Standalone patch (not `N/M`).
Step 3.4: CHECK THE AUTHOR'S OTHER COMMITS
Record:
- Genjian Zhang: not an MD regular in this tree.
- Yu Kuai: frequent MD contributor here (`md/raid5: account discard IO`,
`fix IO hang when array is broken with IO inflight`, etc.) and applied
this to md-7.3.
Step 3.5: CHECK FOR DEPENDENT/PREREQUISITE COMMITS
Record:
- Functional dependency: none. Completing the bio does not need later
RAID5 refactoring.
- `bio_endio_status()` is absent in 6.1; equivalent is already used
(`bio_wouldblock_error()`, `bio->bi_status = ...; bio_endio()`).
- `raid5_discard_limits()` is context from later mainline, not used by
the fix. Not in 6.1.
- Applies standalone with a 2-line helper substitution.
===============================================================
PHASE 4: MAILING LIST AND EXTERNAL RESEARCH
===============================================================
Step 4.1: FIND THE ORIGINAL PATCH DISCUSSION
Record:
- Lore/msgid: `20260711161326.962336-1-zhanggenjian@126.com`
- `b4 mbox`: 6-message thread, standalone v1 (no v2).
- Recipients: Song Liu, Yu Kuai, linux-raid, linux-kernel.
- Yu Kuai 2026-07-19: Reviewed-by; leave REQ_NOWAIT hang (nowait being
removed in mainline).
- Yu Kuai 2026-07-31: “Rebase and applied to md-7.3”.
- Author: no v2 for REQ_NOWAIT.
- No NAK. No reviewer `Cc: stable`.
- Sashiko bot: (1) REQ_NOWAIT also leaks the bio (pre-existing); (2)
`BLK_STS_AGAIN` on blocking bios.
- Author: NOTSUPP is wrong (discard is supported, just not during
reshape); `blkdev_issue_discard()` treats `-EOPNOTSUPP` as success, so
NOTSUPP would report success with no discard; `BLK_STS_AGAIN` matches
raid10’s discard-during-reshape `-EAGAIN` policy.
Step 4.2: CHECK WHO REVIEWED THE PATCH
Record: MD maintainers CC’d (Song Liu, Yu Kuai). Yu Kuai reviewed and
applied. Song Liu did not comment in the thread.
Step 4.3: SEARCH FOR THE BUG REPORT
Record: No bugzilla/syzbot. Author reproduced with `blkdiscard` during
`mdadm --grow` on loop RAID5. Hang remained after “reshape done”.
Step 4.4: CHECK FOR RELATED PATCHES AND SERIES
Record: Standalone. REQ_NOWAIT leak left unfixed on maintainer request
(mainline nowait removal). In 6.1 that WARN_ON_ONCE return without
`bio_endio` is still present; it is a separate, rarer path.
Step 4.5: CHECK STABLE MAILING LIST HISTORY
Record: No stable-list discussion found for this subject. Absence of
`Cc: stable` is expected for this review pipeline.
===============================================================
PHASE 5: CODE SEMANTIC ANALYSIS
===============================================================
Step 5.1: IDENTIFY KEY FUNCTIONS IN THE DIFF
Record: `make_discard_request()` only.
Step 5.2: TRACE CALLERS
Record:
- Only caller: `raid5_make_request()` when `bio_op(bi) ==
REQ_OP_DISCARD` (`drivers/md/raid5.c:6175-6178`).
- After the call: `md_write_end(mddev)` always runs, so write accounting
stays balanced if the bio is completed with error.
- Context: MD make_request from `submit_bio` / `submit_bio_wait`.
Step 5.3: TRACE CALLEES
Record: On the buggy skip path, no stripe work, no `bio_inc_remaining`,
no bitmap. Completing the original bio is sufficient. Success path still
ends with `bio_endio(bi)` after stripe setup.
Step 5.4: FOLLOW THE CALL CHAIN
Record:
```
blkdiscard / FITRIM / FS discard
→ blkdev_issue_discard() / sb_issue_discard()
→ submit_bio_wait() [wait_for_completion_io, uninterruptible]
→ raid5_make_request()
→ make_discard_request()
→ return without bio_endio [BUG]
```
Reachable from userspace on a RAID5/6 array with discard enabled. Needs
admin (BLKDISCARD / fstrim), not an unprivileged syscall gadget.
Step 5.5: SEARCH FOR SIMILAR PATTERNS
Record:
- Same file still has REQ_NOWAIT `WARN_ON_ONCE` then `return` without
completing (6.1-only leftover).
- RAID10 already returns `-EAGAIN` for discard during reshape
(`raid10_handle_discard`, `drivers/md/raid10.c:1653-1654`) and
completes NOWAIT discards with `bio_wouldblock_error()`.
- `raid5_make_request` already uses `bio_wouldblock_error()` for NOWAIT
vs reshape on normal I/O (`:6193-6201`). Discard skipped that pattern.
No semcode MCP in this environment; callers/callees from `rg` + reading
the functions.
===============================================================
PHASE 6: CROSS-REFERENCING AGAINST THE LOCAL TREE
===============================================================
Step 6.1: DOES THE BUGGY CODE EXIST IN THIS TREE?
Record: Yes.
```5809:5811:drivers/md/raid5.c
if (mddev->reshape_position != MaxSector)
/* Skip discard while reshape is happening */
return;
```
- Tree: `p-6.1-11-g10bc6eb33d95f`, `make kernelversion` → 6.1.188.
- Bug since 2012 (`620125f2bf8ff0`), ancestor of HEAD.
Step 6.2: CHECK FOR BACKPORT COMPLICATIONS
Record: Needs a small backport, not a rework.
- No `bio_endio_status()` in 6.1 → `bi->bi_status = BLK_STS_AGAIN;
bio_endio(bi);` (or `bio_wouldblock_error(bi)`).
- No `raid5_discard_limits()` (later context only).
- 6.1 still has the REQ_NOWAIT warning above the reshape check; leave it
(out of scope of this commit).
- Expected difficulty: minor context/helper adjustment.
Step 6.3: CHECK IF RELATED FIXES ARE ALREADY HERE
Record: This hang is not fixed. Discard still disabled by default
(`devices_handle_discard_safely = false`), but the hang exists whenever
that param is set and discard is issued during reshape.
===============================================================
PHASE 7: SUBSYSTEM AND MAINTAINER CONTEXT
===============================================================
Step 7.1: IDENTIFY THE SUBSYSTEM AND ITS CRITICALITY
Record: MD RAID5/6 (`drivers/md/raid5.c`). IMPORTANT: not core VFS/mm,
but widely used for storage. RAID5 reshape (`mdadm --grow`) is a
supported production operation.
Step 7.2: ASSESS SUBSYSTEM ACTIVITY
Record: Active in this tree (recent hang, lockup, discard accounting
fixes). Yu Kuai is a regular MD committer here. Official maintainer in
MAINTAINERS: Song Liu.
===============================================================
PHASE 8: IMPACT AND RISK ASSESSMENT
===============================================================
Step 8.1: DETERMINE WHO IS AFFECTED
Record: RAID5/6 users who enabled discard
(`raid456.devices_handle_discard_safely=Y`) and run discard
(`blkdiscard`, `fstrim`, mount-time/online discard) while reshape is
active. Config- and workload-specific, not universal.
Step 8.2: DETERMINE THE TRIGGER CONDITIONS
Record:
- Trigger: discard bio while `reshape_position != MaxSector`.
- How common: reshape is infrequent; overlap with `fstrim.timer` / admin
trim is realistic on SSD RAID.
- Unprivileged: typically no (needs write on the device /
CAP_SYS_ADMIN). Still a production hang for admins and FS discard from
`fstrim`/ext4/xfs/f2fs (those use `blkdev_issue_discard` →
`submit_bio_wait`).
Step 8.3: DETERMINE THE FAILURE MODE SEVERITY
Record:
- Failure: uninterruptible sleep (`wait_for_completion_io`) that does
**not** clear when reshape finishes. Process is unkillable; hung-task
warnings; reboot to recover.
- Severity: **CRITICAL** for the affected path (hang), even though the
feature is opt-in.
Step 8.4: CALCULATE RISK-BENEFIT RATIO
Record:
- Benefit: high for RAID5/6 + discard + reshape (unkillable hang →
`-EAGAIN`).
- Risk: very low (few lines, error-only path, no new API, no
locking/data-structure change). Worst case: discard fails with
`-EAGAIN` instead of hanging.
- Ratio: strongly favors backport.
===============================================================
PHASE 9: FINAL SYNTHESIS
===============================================================
Step 9.1: COMPILE THE EVIDENCE
FOR:
- Real, tested uninterruptible hang; does not self-resolve after
reshape.
- Small, obviously correct: complete a bio that was leaked.
- Buggy code present in this 6.1 tree since 2012.
- Maintainer Reviewed-by and applied (Yu Kuai, md-7.3).
- `BLK_STS_AGAIN` avoids the `-EOPNOTSUPP`→success lie in
`blkdev_issue_discard()`.
- Matches “complete with retryable error” rather than silent drop.
- Callers (`blkdev_issue_discard`, FITRIM, `sb_issue_discard`) all wait
on completion.
AGAINST:
- Discard off by default (`devices_handle_discard_safely`).
- Exact mainline helper `bio_endio_status()` missing (trivial substitute
exists).
- `BLK_STS_AGAIN` is documented as NOWAIT-oriented; maintainer accepted
it anyway.
- No `Cc: stable` (not a negative for this process).
UNRESOLVED:
- Could not use `b4 dig -c <sha>` (commit not in this checkout). Thread
recovered via msgid/`b4 mbox`.
- No stable-list thread found.
Step 9.2: APPLY THE STABLE RULES CHECKLIST
1. Obviously correct and tested? **PASS** — missing `bio_endio` is the
hang; author tested `blkdiscard` vs reshape; Yu Kuai reviewed.
2. Fixes a real bug that affects users? **PASS** — D-state hang on
discard during reshape.
3. Important issue? **PASS** — unkillable hang / hung task (CRITICAL on
that path).
4. Small and contained? **PASS** — one function, a few lines.
5. No new features or APIs? **PASS**.
6. Can apply to the local tree? **PASS** with a trivial backport
(`bio_endio_status` → set `bi_status` + `bio_endio`).
Step 9.3: CHECK FOR EXCEPTION CATEGORIES
Record: Not an ID/quirk/DT/docs exception. Ordinary hang fix.
Step 9.4: MAKE YOUR DECISION
Backport to this 6.1 tree. The hang is in this code, the fix is
surgical, and the only adjustment is how to set bio status without
`bio_endio_status()`.
Verification:
- [Phase 1] Subject/tags/body from the candidate message and lore mbox;
hang + `-EAGAIN` policy confirmed.
- [Phase 2] Diff: reshape early-return without completion → `bio_endio`
with `BLK_STS_AGAIN`.
- [Phase 3] `git blame` `raid5.c:5809-5811` → `620125f2bf8ff0` (2012);
`git merge-base --is-ancestor` true; fix not in this tree.
- [Phase 3] `git log --grep` on `drivers/md/` for this subject: empty.
- [Phase 4] `b4 mbox` msgid
`20260711161326.962336-1-zhanggenjian@126.com`: 6 msgs; Yu Kuai
Reviewed-by + applied md-7.3; author rationale for AGAIN vs NOTSUPP.
- [Phase 4] Openwall/LKML mirrors match the patch; lore.kernel.org fetch
403 (Anubis); thread obtained via b4.
- [Phase 5] `rg make_discard_request`: only `raid5_make_request`.
`submit_bio_wait` uses `wait_for_completion_io`.
`blkdev_issue_discard` treats only `-EOPNOTSUPP` as 0.
- [Phase 5] RAID10 already returns `-EAGAIN` for discard during reshape
(`raid10.c:1653-1654`).
- [Phase 6] `git describe HEAD` → `p-6.1-11-g10bc6eb33d95f`; `make
kernelversion` → 6.1.188. Buggy return present. `bio_endio_status` /
`raid5_discard_limits` absent.
- [Phase 6] Equivalent completion: `include/linux/bio.h`
`bio_wouldblock_error()` / `BLK_STS_AGAIN`.
- [Phase 7] MAINTAINERS: Song Liu / linux-raid; Yu Kuai active on
`drivers/md/raid5.c` in this tree.
- [Phase 8] Failure mode: D-state hang until reboot; discard opt-in via
`devices_handle_discard_safely`.
- UNVERIFIED: whether some later mainline commit renamed the helper-only
form to the `goto complete_again` form (functionally equivalent).
The hang is real in 6.1, the fix is a few lines, and a missing helper is
not a reason to leave an unkillable discard stall in place.
**YES**
drivers/md/raid5.c | 8 ++++++--
1 file changed, 6 insertions(+), 2 deletions(-)
diff --git a/drivers/md/raid5.c b/drivers/md/raid5.c
index 2817cd7d197e9..b7da2b83e1938 100644
--- a/drivers/md/raid5.c
+++ b/drivers/md/raid5.c
@@ -5707,8 +5707,7 @@ static void make_discard_request(struct mddev *mddev, struct bio *bi)
int stripe_sectors;
if (mddev->reshape_position != MaxSector)
- /* Skip discard while reshape is happening */
- return;
+ goto complete_again;
stripe_sectors = conf->chunk_sectors *
(conf->raid_disks - conf->max_degraded);
@@ -5786,6 +5785,11 @@ static void make_discard_request(struct mddev *mddev, struct bio *bi)
}
bio_endio(bi);
+ return;
+
+complete_again:
+ /* Skip discard while reshape is happening */
+ bio_endio_status(bi, BLK_STS_AGAIN);
}
static bool ahead_of_reshape(struct mddev *mddev, sector_t sector,
--
2.53.0
^ permalink raw reply related [flat|nested] 6+ messages in thread* Re: [PATCH AUTOSEL 6.18] md/raid5: complete discard bios while reshape is active
2026-10-19 13:52 ` [PATCH AUTOSEL 6.18] md/raid5: complete discard bios while reshape is active Sasha Levin
@ 2026-10-19 15:25 ` sashiko-bot
0 siblings, 0 replies; 6+ messages in thread
From: sashiko-bot @ 2026-10-19 15:25 UTC (permalink / raw)
To: Sasha Levin; +Cc: linux-raid, yukuai
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Medium] md/raid5: make_discard_request() returns BLK_STS_AGAIN without REQ_NOWAIT
--
commit 2fc0a245a9d15532fa0b76f75c094edac4ec73ed
Author: Genjian Zhang <zhanggenjian@kylinos.cn>
md/raid5: complete discard bios while reshape is active
This commit completes discard bios in make_discard_request() with
BLK_STS_AGAIN when reshape is active instead of returning without
calling bio_endio().
Link: https://patch.msgid.link/20260711161326.962336-1-zhanggenjian@126.com
> diff --git a/drivers/md/raid5.c b/drivers/md/raid5.c
> index 2817cd7d197e9..b7da2b83e1938 100644
> --- a/drivers/md/raid5.c
> +++ b/drivers/md/raid5.c
> @@ -5707,8 +5707,7 @@ static void make_discard_request(struct mddev *mddev, struct bio *bi)
> int stripe_sectors;
>
> if (mddev->reshape_position != MaxSector)
> - /* Skip discard while reshape is happening */
> - return;
> + goto complete_again;
>
> stripe_sectors = conf->chunk_sectors *
> (conf->raid_disks - conf->max_degraded);
> @@ -5786,6 +5785,11 @@ static void make_discard_request(struct mddev *mddev, struct bio *bi)
> }
>
> bio_endio(bi);
> + return;
> +
> +complete_again:
> + /* Skip discard while reshape is happening */
> + bio_endio_status(bi, BLK_STS_AGAIN);
> }
[Severity: Medium]
Is it valid to complete synchronous discard bios with BLK_STS_AGAIN when
REQ_NOWAIT is not set?
In include/linux/blk_types.h, BLK_STS_AGAIN is documented as only valid
when RQF_NOWAIT is set:
/*
* BLK_STS_AGAIN should only be returned if RQF_NOWAIT is set
* and the bio would block (cf bio_wouldblock_error())
*/
#define BLK_STS_AGAIN ((__force blk_status_t)12)
When a mounted filesystem or fstrim issues a synchronous discard via
sb_issue_discard() -> blkdev_issue_discard() -> submit_bio_wait() while
RAID5 reshape is active (mddev->reshape_position != MaxSector),
blkdev_issue_discard() only masks -EOPNOTSUPP and propagates -EAGAIN to
the caller:
if (!ret && bio) {
ret = submit_bio_wait(bio);
if (ret == -EOPNOTSUPP)
ret = 0;
bio_put(bio);
}
In fs/ext4/mballoc.c:ext4_mb_clear_bb(), when freeing blocks with the
discard mount option enabled (in no-journal or data=writeback mode),
only -EOPNOTSUPP is ignored:
if (test_opt(sb, DISCARD)) {
err = ext4_issue_discard(sb, block_group, bit,
count_clusters);
if (err == -EOPNOTSUPP)
err = 0;
if (err)
ext4_msg(sb, KERN_WARNING, "discard request in"
" group:%u block:%d count:%lu failed"
" with %d", block_group, bit, count,
err);
}
When err is -EAGAIN, ext4_mb_clear_bb() skips freeing cross-group
overflow blocks and passes -EAGAIN to ext4_std_error(sb, err), marking
the filesystem in error and triggering a read-only remount or panic.
Similarly, in fs/gfs2/rgrp.c:gfs2_rgrp_send_discards(), an -EAGAIN
return from sb_issue_discard() jumps to fail: and sets
sdp->sd_args.ar_discard = 0, permanently disabling discards on the
mounted filesystem even after reshape completes.
Since make_discard_request() already completes skipped sub-stripe
advisory discards with bio_endio(bi) (BLK_STS_OK):
if (first_stripe >= last_stripe) {
bio_endio(bi);
return;
}
should discard bios skipped during reshape also be completed with
bio_endio(bi) (or BLK_STS_NOTSUPP) when REQ_NOWAIT is not set?
>
> static bool ahead_of_reshape(struct mddev *mddev, sector_t sector,
--
Sashiko AI review · https://sashiko.dev/#/patchset/20261019135441.1420968-447-sashal@kernel.org?part=1
^ permalink raw reply [flat|nested] 6+ messages in thread