[PATCH 3/3] md/md-llbitmap: fail resize that needs more bitmap pages
From: Mykola Marzhan
Date: Sun Jul 19 2026 - 10:44:35 EST
llbitmap_resize() only updates chunkshift/chunksize/chunks. The
page cache backing the bitmap (pctl[] and nr_pages) is sized at
creation and never grown, so a grow whose chunk count crosses into a
never-allocated page leaves the data path indexing past the end of
pctl[]. And when the reserved bitmap space is too small for the new
size, the chunksize-doubling loop silently changes the
chunk-to-sector mapping without remapping any on-disk bit, so the
existing intent state describes the wrong sectors afterwards.
Fail such resizes before anything is mutated: -EINVAL for a resize
that needs a different chunksize, -ENOSPC for a grow that needs more
bitmap pages than were allocated at create time. Shrinks, grows
within the allocated pages, and assemble-time sizing keep working.
"mdadm --grow --size" past the boundary now fails with an error
instead of corrupting memory, and a raid10 disk-add reshape crossing
it fails cleanly in raid10_start_reshape(), which calls
bitmap_ops->resize() directly and reverts the geometry on error.
Grows within the last allocated page can still expose per-chunk
bytes that are stale (after a shrink) or never initialised (the
tail of the last page: llbitmap_init() covers only [0, chunks - 1]).
That window closes once the bitmap page cache can actually grow --
the subject of separate work under review.
Fixes: 5ab829f1971d ("md/md-llbitmap: introduce new lockless bitmap")
Cc: stable@xxxxxxxxxxxxxxx # v6.18+
Assisted-by: Claude-Code:claude-opus-4-8
Signed-off-by: Mykola Marzhan <mykola@xxxxxxxxxxx>
---
drivers/md/md-llbitmap.c | 26 ++++++++++++++++++++++++++
1 file changed, 26 insertions(+)
diff --git a/drivers/md/md-llbitmap.c b/drivers/md/md-llbitmap.c
index 5a4e2abaa757..e8f853e8461d 100644
--- a/drivers/md/md-llbitmap.c
+++ b/drivers/md/md-llbitmap.c
@@ -1150,6 +1150,32 @@ static int llbitmap_resize(struct mddev *mddev, sector_t blocks, int chunksize)
chunks = DIV_ROUND_UP_SECTOR_T(blocks, chunksize);
}
+ /*
+ * A changed chunksize would require remapping every on-disk bit:
+ * each chunk would cover a different sector range. Refuse rather
+ * than corrupt the existing intent record.
+ */
+ if (chunksize != llbitmap->chunksize) {
+ pr_warn("md/llbitmap: %s: cannot resize to %llu sectors: bitmap space (%lu sectors) requires chunksize %u, current is %lu\n",
+ mdname(mddev), (unsigned long long)blocks,
+ mddev->bitmap_info.space, (unsigned int)chunksize,
+ llbitmap->chunksize);
+ return -EINVAL;
+ }
+
+ /*
+ * pctl[]/nr_pages are sized at creation and not grown here; a
+ * grow past the last allocated page would index past pctl[].
+ */
+ if (DIV_ROUND_UP(chunks + BITMAP_DATA_OFFSET, PAGE_SIZE) >
+ llbitmap->nr_pages) {
+ pr_warn("md/llbitmap: %s: cannot grow to %lu chunks: needs %lu bitmap pages, only %u allocated; growing the bitmap is not yet supported\n",
+ mdname(mddev), chunks,
+ DIV_ROUND_UP(chunks + BITMAP_DATA_OFFSET, PAGE_SIZE),
+ llbitmap->nr_pages);
+ return -ENOSPC;
+ }
+
llbitmap->chunkshift = ffz(~chunksize);
llbitmap->chunksize = chunksize;
llbitmap->chunks = chunks;
--
2.43.0