* Re: [PATCH V3 3/3] nvme-multipath: enable PCI P2PDMA for multipath devices
From: Nitesh Shetty @ 2026-04-17 10:42 UTC (permalink / raw)
To: Chaitanya Kulkarni
Cc: song, yukuai, linan122, kbusch, axboe, hch, sagi, linux-raid,
linux-nvme, kmodukuri
In-Reply-To: <20260416212633.72650-4-kch@nvidia.com>
[-- Attachment #1: Type: text/plain, Size: 1498 bytes --]
On 16/04/26 02:26PM, Chaitanya Kulkarni wrote:
>From: Kiran Kumar Modukuri <kmodukuri@nvidia.com>
>
>NVMe multipath does not expose BLK_FEAT_PCI_P2PDMA on the head disk
>even when all underlying controllers support it.
>
>Set BLK_FEAT_PCI_P2PDMA unconditionally in nvme_mpath_alloc_disk()
>alongside the other features. nvme_update_ns_info_block() already
>calls queue_limits_stack_bdev() to stack each path's limits onto the
>head disk, which routes through blk_stack_limits(). The core now
>clears BLK_FEAT_PCI_P2PDMA automatically if any path (e.g., FC) does
>not support it, consistent with how BLK_FEAT_NOWAIT and BLK_FEAT_POLL
>are handled.
>
>Signed-off-by: Kiran Kumar Modukuri <kmodukuri@nvidia.com>
>Signed-off-by: Chaitanya Kulkarni <kch@nvidia.com>
>---
> drivers/nvme/host/multipath.c | 2 +-
> 1 file changed, 1 insertion(+), 1 deletion(-)
>
>diff --git a/drivers/nvme/host/multipath.c b/drivers/nvme/host/multipath.c
>index ba00f0b72b85..957e39c4795d 100644
>--- a/drivers/nvme/host/multipath.c
>+++ b/drivers/nvme/host/multipath.c
>@@ -734,7 +734,7 @@ int nvme_mpath_alloc_disk(struct nvme_ctrl *ctrl, struct nvme_ns_head *head)
> blk_set_stacking_limits(&lim);
> lim.dma_alignment = 3;
> lim.features |= BLK_FEAT_IO_STAT | BLK_FEAT_NOWAIT |
>- BLK_FEAT_POLL | BLK_FEAT_ATOMIC_WRITES;
>+ BLK_FEAT_POLL | BLK_FEAT_ATOMIC_WRITES | BLK_FEAT_PCI_P2PDMA;
> if (head->ids.csi == NVME_CSI_ZNS)
> lim.features |= BLK_FEAT_ZONED;
Reviewed-by: Nitesh Shetty <nj.shetty@samsung.com>
[-- Attachment #2: Type: text/plain, Size: 0 bytes --]
^ permalink raw reply
* Re: [PATCH v2] raid6: arm64: add SVE optimized implementation for syndrome generation
From: Ard Biesheuvel @ 2026-04-17 14:43 UTC (permalink / raw)
To: Robin Murphy, Demian Shulhan
Cc: Christoph Hellwig, Mark Rutland, Song Liu, Yu Kuai, Will Deacon,
Catalin Marinas, Mark Brown, linux-arm-kernel, Li Nan, linux-raid,
linux-kernel
In-Reply-To: <8db4defe-8b5e-4cc3-880b-72d46510b034@arm.com>
On Thu, 16 Apr 2026, at 18:26, Robin Murphy wrote:
> On 16/04/2026 3:59 pm, Demian Shulhan wrote:
>> Hi Ard!
...
>>> OK, so the takeaway here is that SVE is only worth the hassle if the
>>> vector length is at least 256 bits. This is not entirely surprising,
>>> but given that Graviton4 went back to 128 bit vectors from 256, I
>>> wonder what the future expectation is here.
>>
>> I agree. The results from the SnapRAID tests are not as impressive as
>> I hoped, and the fact that Neoverse-V2 went back to 128-bit is a red
>> flag. It suggests that wide SVE registers might not be a priority in
>> future architecture versions.
>
> If you look at the Neoverse V1 software optimisation guide[1], the SVE
> instructions generally have half the throughput of their ASIMD
> equivalents (i.e. presumably the vector pipes are still only 128 bits
> wide and SVE is just using them in pairs), so indeed the total
> instruction count is largely meaningless - IPC might be somewhat more
> relevant, but I'd say the only performance number that's really
> meaningful is the end-to-end MB/s measure of how fast the function
> implementation as a whole can process data.
On arm64, kernel mode NEON is mostly used to gain access to AES and SHA
instructions, and only to a lesser degree to speed up ordinary
arithmetic, and so XOR is somewhat of an outlier here.
Given that Neoverse V1 apparently already carves up ordinary arithmetic
performed on 256-bit vectors and operates on 128 bits at a time, I am
rather skeptical that we're likely to see any SVE implementations of the
crypto extensions soon that are meaningfully faster, given that these
are presumably much costlier to implement in terms of gate count, and
therefore likely to be split up even on SVE implementations that can
perform ordinary arithmetic on 256+ bit vectors in a single cycle. Note
that even the arm64 SIMD accelerated CRC implementations rely heavily on
64x64->128 polynomial multiplication.
IOW, before we consider kernel mode SVE, I'd like to see some benchmarks
for other algorithms too.
> It's probably also worth checking whether the current NEON routines
> themselves are actually optimal for modern big CPUs - things have
> moved on quite a bit since Cortex-A57 (whose ASIMD performance could
> also be described as "esoteric" at the best of times...)
>
Some of those crypto routines could definitely be made faster, but it
highly depends on the context whether that actually helps: for instance,
there was a proposal a while ago to incorporate the AES-GCM code from
the OpenSSL project (authored by ARM) but at the time, it slightly
regressed the ~1500 byte case and only gave a substantial improvement
for much larger block sizes, which aren't that common in the kernel for
this particular algorithm.
IOW, any contributions that improve the existing code (or outright
replace it with something faster, for all I care) are highly
appreciated, but they should be motivated by benchmarks that reflect
the use cases that we actually consider important for the algorithm
in question.
^ permalink raw reply
* Re: [PATCH v2] raid6: arm64: add SVE optimized implementation for syndrome generation
From: Mark Brown @ 2026-04-17 15:36 UTC (permalink / raw)
To: Ard Biesheuvel
Cc: Robin Murphy, Demian Shulhan, Christoph Hellwig, Mark Rutland,
Song Liu, Yu Kuai, Will Deacon, Catalin Marinas, linux-arm-kernel,
Li Nan, linux-raid, linux-kernel
In-Reply-To: <c9362db6-1fef-4e70-9525-29b2936f4887@app.fastmail.com>
[-- Attachment #1: Type: text/plain, Size: 1411 bytes --]
On Fri, Apr 17, 2026 at 04:43:06PM +0200, Ard Biesheuvel wrote:
> On arm64, kernel mode NEON is mostly used to gain access to AES and SHA
> instructions, and only to a lesser degree to speed up ordinary
> arithmetic, and so XOR is somewhat of an outlier here.
> Given that Neoverse V1 apparently already carves up ordinary arithmetic
> performed on 256-bit vectors and operates on 128 bits at a time, I am
> rather skeptical that we're likely to see any SVE implementations of the
> crypto extensions soon that are meaningfully faster, given that these
> are presumably much costlier to implement in terms of gate count, and
> therefore likely to be split up even on SVE implementations that can
> perform ordinary arithmetic on 256+ bit vectors in a single cycle. Note
> that even the arm64 SIMD accelerated CRC implementations rely heavily on
> 64x64->128 polynomial multiplication.
I'd not be surprised to see something that delivers useful benefits
using SVE at some point.
> IOW, before we consider kernel mode SVE, I'd like to see some benchmarks
> for other algorithms too.
Definitely, it needs a solid win to merge anything. I do want to get
back to the situation where we've got out of tree infrastructure patches
so that people working on algorithms have something to base their work
on (and see the overheads using SVE incurs) but unless theres's a
practical user they should stay out of tree.
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]
^ permalink raw reply
* Re: [RFC PATCH 1/2] kernel/notifier: replace single-linked list with double-linked list for reverse traversal
From: Song Chen @ 2026-04-19 0:07 UTC (permalink / raw)
To: Petr Mladek
Cc: rafael, lenb, mturquette, sboyd, viresh.kumar, agk, snitzer,
mpatocka, bmarzins, song, yukuai, linan122, jason.wessel, danielt,
dianders, horms, davem, edumazet, kuba, pabeni, paulmck, frederic,
mcgrof, petr.pavlu, da.gomez, samitolvanen, atomlin, jpoimboe,
jikos, mbenes, joe.lawrence, rostedt, mhiramat, mark.rutland,
mathieu.desnoyers, linux-modules, linux-kernel,
linux-trace-kernel, linux-acpi, linux-clk, linux-pm,
live-patching, dm-devel, linux-raid, kgdb-bugreport, netdev
In-Reply-To: <aeC7ByGA5MHBcGQR@pathway.suse.cz>
Hi,
On 4/16/26 18:33, Petr Mladek wrote:
> On Wed 2026-04-15 15:01:37, chensong_2000@189.cn wrote:
>> From: Song Chen <chensong_2000@189.cn>
>>
>> The current notifier chain implementation uses a single-linked list
>> (struct notifier_block *next), which only supports forward traversal
>> in priority order. This makes it difficult to handle cleanup/teardown
>> scenarios that require notifiers to be called in reverse priority order.
>>
>> A concrete example is the ordering dependency between ftrace and
>> livepatch during module load/unload. see the detail here [1].
>>
>> This patch replaces the single-linked list in struct notifier_block
>> with a struct list_head, converting the notifier chain into a
>> doubly-linked list sorted in descending priority order. Based on
>> this, a new function notifier_call_chain_reverse() is introduced,
>> which traverses the chain in reverse (ascending priority order).
>> The corresponding blocking_notifier_call_chain_reverse() is also
>> added as the locking wrapper for blocking notifier chains.
>>
>> The internal notifier_call_chain_robust() is updated to use
>> notifier_call_chain_reverse() for rollback: on error, it records
>> the failing notifier (last_nb) and the count of successfully called
>> notifiers (nr), then rolls back exactly those nr-1 notifiers in
>> reverse order starting from last_nb's predecessor, without needing
>> to know the total length of the chain.
>>
>> With this change, subsystems with symmetric setup/teardown ordering
>> requirements can register a single notifier_block with one priority
>> value, and rely on blocking_notifier_call_chain() for forward
>> traversal and blocking_notifier_call_chain_reverse() for reverse
>> traversal, without needing hard-coded call sequences or separate
>> notifier registrations for each direction.
>>
>> [1]:https://lore.kernel.org/all
>> /alpine.LNX.2.00.1602172216491.22700@cbobk.fhfr.pm/
>>
>> --- a/include/linux/notifier.h
>> +++ b/include/linux/notifier.h
>> @@ -53,41 +53,41 @@ typedef int (*notifier_fn_t)(struct notifier_block *nb,
> [...]
>> struct notifier_block {
>> notifier_fn_t notifier_call;
>> - struct notifier_block __rcu *next;
>> + struct list_head __rcu entry;
>> int priority;
>> };
> [...]
>> #define ATOMIC_INIT_NOTIFIER_HEAD(name) do { \
>> spin_lock_init(&(name)->lock); \
>> - (name)->head = NULL; \
>> + INIT_LIST_HEAD(&(name)->head); \
>
> I would expect the RCU variant here, aka INIT_LIST_HEAD_RCU().
I'm not familiar with list rcu, but i will look into it and give it a try.
>
>> --- a/kernel/notifier.c
>> +++ b/kernel/notifier.c
>> @@ -14,39 +14,47 @@
>> * are layered on top of these, with appropriate locking added.
>> */
>>
>> -static int notifier_chain_register(struct notifier_block **nl,
>> +static int notifier_chain_register(struct list_head *nl,
>> struct notifier_block *n,
>> bool unique_priority)
>> {
>> - while ((*nl) != NULL) {
>> - if (unlikely((*nl) == n)) {
>> + struct notifier_block *cur;
>> +
>> + list_for_each_entry(cur, nl, entry) {
>> + if (unlikely(cur == n)) {
>> WARN(1, "notifier callback %ps already registered",
>> n->notifier_call);
>> return -EEXIST;
>> }
>> - if (n->priority > (*nl)->priority)
>> - break;
>> - if (n->priority == (*nl)->priority && unique_priority)
>> +
>> + if (n->priority == cur->priority && unique_priority)
>> return -EBUSY;
>> - nl = &((*nl)->next);
>> +
>> + if (n->priority > cur->priority) {
>> + list_add_tail(&n->entry, &cur->entry);
>> + goto out;
>> + }
>> }
>> - n->next = *nl;
>> - rcu_assign_pointer(*nl, n);
>> +
>> + list_add_tail(&n->entry, nl);
>
> I would expect list_add_tail_rcu() here.
>
>> @@ -59,25 +67,25 @@ static int notifier_chain_unregister(struct notifier_block **nl,
>> * value of this parameter is -1.
>> * @nr_calls: Records the number of notifications sent. Don't care
>> * value of this field is NULL.
>> + * @last_nb: Records the last called notifier block for rolling back
>> * Return: notifier_call_chain returns the value returned by the
>> * last notifier function called.
>> */
>> -static int notifier_call_chain(struct notifier_block **nl,
>> +static int notifier_call_chain(struct list_head *nl,
>> unsigned long val, void *v,
>> - int nr_to_call, int *nr_calls)
>> + int nr_to_call, int *nr_calls,
>> + struct notifier_block **last_nb)
>> {
>> int ret = NOTIFY_DONE;
>> - struct notifier_block *nb, *next_nb;
>> -
>> - nb = rcu_dereference_raw(*nl);
>> + struct notifier_block *nb;
>>
>> - while (nb && nr_to_call) {
>> - next_nb = rcu_dereference_raw(nb->next);
>> + if (!nr_to_call)
>> + return ret;
>>
>> + list_for_each_entry(nb, nl, entry) {
>
> I would expect the RCU variant here, aka list_for_each_rcu()
>
> These are just two random examples which I found by a quick look.
>
> I guess that the notifier API is very old and it does not use all
> the RCU API features which allow to track safety when
> CONFIG_PROVE_RCU and CONFIG_PROVE_RCU_LIST are enabled.
>
> It actually might be worth to audit the code and make it right.
>
>> #ifdef CONFIG_DEBUG_NOTIFIERS
>> if (unlikely(!func_ptr_is_kernel_text(nb->notifier_call))) {
>> WARN(1, "Invalid notifier called!");
>> - nb = next_nb;
>> continue;
>> }
>> #endif
>
> That said, I am not sure if the ftrace/livepatching handlers are
> the right motivation for this. Especially when I see the
> complexity of the 2nd patch [*]
>
> After thinking more about it. I am not even sure that the ftrace and
> livepatching callbacks are good candidates for generic notifiers.
> They are too special. It is not only about ordering them against
> each other. But it is also about ordering them against other
> notifiers. The ftrace/livepatching callbacks must be the first/last
> during module load/release.
>
> [*] The 2nd patch is not archived by lore for some reason.
> I have found only a review but it gives a good picture, see
> https://lore.kernel.org/all/1191caf5-6a61-4622-a15e-854d3701f4fc@suse.com/
>
> Best Regards,
> Petr
>
Thanks.
BR
Song
^ permalink raw reply
* Re: [RFC PATCH 1/2] kernel/notifier: replace single-linked list with double-linked list for reverse traversal
From: Song Chen @ 2026-04-19 0:21 UTC (permalink / raw)
To: David Laight
Cc: rafael, lenb, mturquette, sboyd, viresh.kumar, agk, snitzer,
mpatocka, bmarzins, song, yukuai, linan122, jason.wessel, danielt,
dianders, horms, davem, edumazet, kuba, pabeni, paulmck, frederic,
mcgrof, petr.pavlu, da.gomez, samitolvanen, atomlin, jpoimboe,
jikos, mbenes, pmladek, joe.lawrence, rostedt, mhiramat,
mark.rutland, mathieu.desnoyers, linux-modules, linux-kernel,
linux-trace-kernel, linux-acpi, linux-clk, linux-pm,
live-patching, dm-devel, linux-raid, kgdb-bugreport, netdev
In-Reply-To: <20260416133004.07bd2886@pumpkin>
Hi,
On 4/16/26 20:30, David Laight wrote:
> On Wed, 15 Apr 2026 15:01:37 +0800
> chensong_2000@189.cn wrote:
>
>> From: Song Chen <chensong_2000@189.cn>
>>
>> The current notifier chain implementation uses a single-linked list
>> (struct notifier_block *next), which only supports forward traversal
>> in priority order. This makes it difficult to handle cleanup/teardown
>> scenarios that require notifiers to be called in reverse priority order.
>
> If it is only cleanup/teardown then the list can be order-reversed
> as part of that process at the same time as the list is deleted.
>
> David
>
>
>
Sorry, i don't follow, the notifiers in the list are deleted when
calling notifier_chain_unregister, other than that, they are traversed
forward and backward.
Song
^ permalink raw reply
* [PATCH 00/19] md: support llbitmap reshape for raid10 and raid5
From: Yu Kuai @ 2026-04-19 3:09 UTC (permalink / raw)
To: linux-raid; +Cc: linux-kernel, Li Nan, Yu Kuai, Cheng Cheng
llbitmap currently tracks one live bitmap geometry, but reshape needs old and
new mapping to coexist until reshape completion. That breaks normal bitmap
accounting in three places:
- bios that cross reshape_position cannot be accounted as one bitmap range
- checkpointed dirty state must move forward as reshape advances
- interrupted reshape must resume with the same llbitmap interpretation after
reassembly
The main pieces are:
- add bitmap-side range preparation so md core uses one bitmap API
- split reshape-crossing bios in raid10/raid5 before bitmap accounting
- let llbitmap stage target geometry and remap checkpointed bits in one bitmap
- teach raid10/raid5 to provide exact old/new mapping and drive llbitmap
reshape progress
Validation
Runtime:
- custom QEMU llbitmap reshape matrix passed for raid10 and raid5:
- expanded/changed bit-state checks with query_bit
- cross-boundary writes during reshape
- heavier raid5 write path that previously warned/crashed
- degraded stop/assemble data verification after reshape
- interrupted reshape stop/assemble/continue for raid10 and raid5
- mdadm test 02r5grow passed in disk mode
The temporary query_bit validation hook is intentionally kept out of this
submission series.
Yu Kuai (19):
md: add exact bitmap mapping and reshape hooks
md: add helper to split bios at reshape offset
md/md-llbitmap: track bitmap sync_size explicitly
md/md-llbitmap: allocate page controls independently
md/md-llbitmap: grow the page cache in place for reshape
md/md-llbitmap: track target reshape geometry fields
md/md-llbitmap: finish reshape geometry
md/md-llbitmap: refuse reshape while llbitmap still needs sync
md/md-llbitmap: add reshape range mapping helpers
md/md-llbitmap: don't skip reshape ranges from bitmap state
md/md-llbitmap: remap checkpointed bits as reshape progresses
md/md-llbitmap: clamp state-machine walks to tracked bits
md/raid10: reject llbitmap chunk shrink during reshape
md/raid10: wire llbitmap reshape lifecycle
md/raid10: split reshape bios before bitmap accounting
md/raid5: add exact old and new llbitmap mapping helpers
md/raid5: reject llbitmap chunk shrink during reshape
md/raid5: wire llbitmap reshape lifecycle
md/raid5: split reshape bios before bitmap accounting
drivers/md/md-bitmap.c | 8 +
drivers/md/md-bitmap.h | 7 +
drivers/md/md-llbitmap.c | 602 +++++++++++++++++++++++++++++++++++---
drivers/md/md.c | 51 +++
drivers/md/md.h | 8 +
drivers/md/raid10.c | 44 ++-
drivers/md/raid5.c | 114 ++++++--
7 files changed, 760 insertions(+), 74 deletions(-)
--
2.51.0
^ permalink raw reply
* [PATCH] md: add exact bitmap mapping and reshape hooks
From: Yu Kuai @ 2026-04-19 3:09 UTC (permalink / raw)
To: linux-raid; +Cc: linux-kernel, Li Nan, Yu Kuai, Cheng Cheng
In-Reply-To: <20260419030942.824195-1-yukuai@fnnas.com>
Add bitmap mapping and reshape hooks needed by llbitmap reshape
support without teaching md core to account a single bio against
multiple bitmap ranges.
This also adds the old/new bitmap geometry helpers used by
personalities to describe reshape mapping to llbitmap.
Signed-off-by: Yu Kuai <yukuai@fnnas.com>
---
drivers/md/md-bitmap.c | 8 ++++++++
drivers/md/md-bitmap.h | 8 ++++++++
drivers/md/md-llbitmap.c | 8 ++++++++
drivers/md/md.c | 12 ++++++++----
drivers/md/md.h | 4 ++++
5 files changed, 36 insertions(+), 4 deletions(-)
diff --git a/drivers/md/md-bitmap.c b/drivers/md/md-bitmap.c
index 83378c033c72..69b3f5fc6bc0 100644
--- a/drivers/md/md-bitmap.c
+++ b/drivers/md/md-bitmap.c
@@ -1728,6 +1728,13 @@ static void bitmap_start_write(struct mddev *mddev, sector_t offset,
}
}
+static void bitmap_prepare_range(struct mddev *mddev, sector_t *offset,
+ unsigned long *sectors)
+{
+ if (mddev->pers->bitmap_sector)
+ mddev->pers->bitmap_sector(mddev, offset, sectors);
+}
+
static void bitmap_end_write(struct mddev *mddev, sector_t offset,
unsigned long sectors)
{
@@ -2987,6 +2994,7 @@ static struct bitmap_operations bitmap_ops = {
.flush = bitmap_flush,
.write_all = bitmap_write_all,
.dirty_bits = bitmap_dirty_bits,
+ .prepare_range = bitmap_prepare_range,
.unplug = bitmap_unplug,
.daemon_work = bitmap_daemon_work,
diff --git a/drivers/md/md-bitmap.h b/drivers/md/md-bitmap.h
index b42a28fa83a0..8edc7218a1f4 100644
--- a/drivers/md/md-bitmap.h
+++ b/drivers/md/md-bitmap.h
@@ -93,6 +93,14 @@ struct bitmap_operations {
void (*write_all)(struct mddev *mddev);
void (*dirty_bits)(struct mddev *mddev, unsigned long s,
unsigned long e);
+ /* Prepare a range for this bitmap implementation. */
+ void (*prepare_range)(struct mddev *mddev,
+ sector_t *offset,
+ unsigned long *sectors);
+ void (*reshape_finish)(struct mddev *mddev);
+ int (*reshape_can_start)(struct mddev *mddev);
+ void (*reshape_mark)(struct mddev *mddev, sector_t old_pos,
+ sector_t new_pos);
void (*unplug)(struct mddev *mddev, bool sync);
void (*daemon_work)(struct mddev *mddev);
diff --git a/drivers/md/md-llbitmap.c b/drivers/md/md-llbitmap.c
index cdfecaca216b..62b8f3efa9f5 100644
--- a/drivers/md/md-llbitmap.c
+++ b/drivers/md/md-llbitmap.c
@@ -1061,6 +1061,13 @@ static void llbitmap_destroy(struct mddev *mddev)
mutex_unlock(&mddev->bitmap_info.mutex);
}
+static void llbitmap_prepare_range(struct mddev *mddev, sector_t *offset,
+ unsigned long *sectors)
+{
+ if (mddev->pers->bitmap_sector)
+ mddev->pers->bitmap_sector(mddev, offset, sectors);
+}
+
static void llbitmap_start_write(struct mddev *mddev, sector_t offset,
unsigned long sectors)
{
@@ -1596,6 +1603,7 @@ static struct bitmap_operations llbitmap_ops = {
.update_sb = llbitmap_update_sb,
.get_stats = llbitmap_get_stats,
.dirty_bits = llbitmap_dirty_bits,
+ .prepare_range = llbitmap_prepare_range,
.write_all = llbitmap_write_all,
.group = &md_llbitmap_group,
diff --git a/drivers/md/md.c b/drivers/md/md.c
index c9597c6bd341..5b001e3e4cec 100644
--- a/drivers/md/md.c
+++ b/drivers/md/md.c
@@ -9301,6 +9301,12 @@ void md_submit_discard_bio(struct mddev *mddev, struct md_rdev *rdev,
}
EXPORT_SYMBOL_GPL(md_submit_discard_bio);
+static void md_bitmap_prepare_range(struct mddev *mddev, sector_t *offset,
+ unsigned long *sectors)
+{
+ mddev->bitmap_ops->prepare_range(mddev, offset, sectors);
+}
+
static void md_bitmap_start(struct mddev *mddev,
struct md_io_clone *md_io_clone)
{
@@ -9308,10 +9314,8 @@ static void md_bitmap_start(struct mddev *mddev,
mddev->bitmap_ops->start_discard :
mddev->bitmap_ops->start_write;
- if (mddev->pers->bitmap_sector)
- mddev->pers->bitmap_sector(mddev, &md_io_clone->offset,
- &md_io_clone->sectors);
-
+ md_bitmap_prepare_range(mddev, &md_io_clone->offset,
+ &md_io_clone->sectors);
fn(mddev, md_io_clone->offset, md_io_clone->sectors);
}
diff --git a/drivers/md/md.h b/drivers/md/md.h
index ac84289664cd..82a36bdaeaaa 100644
--- a/drivers/md/md.h
+++ b/drivers/md/md.h
@@ -793,6 +793,10 @@ struct md_personality
/* convert io ranges from array to bitmap */
void (*bitmap_sector)(struct mddev *mddev, sector_t *offset,
unsigned long *sectors);
+ void (*bitmap_sector_map)(struct mddev *mddev, sector_t *offset,
+ unsigned long *sectors, bool previous);
+ sector_t (*bitmap_sync_size)(struct mddev *mddev, bool previous);
+ sector_t (*bitmap_array_sectors)(struct mddev *mddev, bool previous);
};
struct md_sysfs_entry {
--
2.51.0
^ permalink raw reply related
* [PATCH] md: add helper to split bios at reshape offset
From: Yu Kuai @ 2026-04-19 3:09 UTC (permalink / raw)
To: linux-raid; +Cc: linux-kernel, Li Nan, Yu Kuai, Cheng Cheng
In-Reply-To: <20260419030942.824195-1-yukuai@fnnas.com>
Add mddev_bio_split_at_reshape_offset() so personalities can share
reshape-offset bio splitting instead of open-coding the same helper in
multiple places.
Signed-off-by: Yu Kuai <yukuai@fnnas.com>
---
drivers/md/md.c | 39 +++++++++++++++++++++++++++++++++++++++
drivers/md/md.h | 4 ++++
2 files changed, 43 insertions(+)
diff --git a/drivers/md/md.c b/drivers/md/md.c
index 5b001e3e4cec..d222e365141b 100644
--- a/drivers/md/md.c
+++ b/drivers/md/md.c
@@ -9301,6 +9301,45 @@ void md_submit_discard_bio(struct mddev *mddev, struct md_rdev *rdev,
}
EXPORT_SYMBOL_GPL(md_submit_discard_bio);
+struct bio *mddev_bio_split_at_reshape_offset(struct mddev *mddev,
+ struct bio *bio,
+ unsigned int *max_sectors,
+ struct bio_set *bs)
+{
+ sector_t boundary;
+ sector_t start;
+ sector_t end;
+ unsigned int split_sectors;
+
+ split_sectors = bio_sectors(bio);
+ if (max_sectors && *max_sectors && *max_sectors < split_sectors)
+ split_sectors = *max_sectors;
+
+ if (!test_bit(MD_RECOVERY_RESHAPE, &mddev->recovery))
+ goto split;
+
+ boundary = mddev->reshape_position;
+ start = bio->bi_iter.bi_sector;
+ end = bio_end_sector(bio);
+ if (start >= boundary || end <= boundary)
+ goto split;
+
+ if (boundary - start < split_sectors)
+ split_sectors = boundary - start;
+
+split:
+ if (max_sectors)
+ *max_sectors = split_sectors;
+ if (split_sectors < bio_sectors(bio)) {
+ bio = bio_submit_split_bioset(bio, split_sectors, bs);
+ if (bio)
+ bio->bi_opf |= REQ_NOMERGE;
+ }
+
+ return bio;
+}
+EXPORT_SYMBOL_GPL(mddev_bio_split_at_reshape_offset);
+
static void md_bitmap_prepare_range(struct mddev *mddev, sector_t *offset,
unsigned long *sectors)
{
diff --git a/drivers/md/md.h b/drivers/md/md.h
index 82a36bdaeaaa..7247eeca5aa4 100644
--- a/drivers/md/md.h
+++ b/drivers/md/md.h
@@ -920,6 +920,10 @@ extern void md_error(struct mddev *mddev, struct md_rdev *rdev);
extern void md_finish_reshape(struct mddev *mddev);
void md_submit_discard_bio(struct mddev *mddev, struct md_rdev *rdev,
struct bio *bio, sector_t start, sector_t size);
+struct bio *mddev_bio_split_at_reshape_offset(struct mddev *mddev,
+ struct bio *bio,
+ unsigned int *max_sectors,
+ struct bio_set *bs);
void md_account_bio(struct mddev *mddev, struct bio **bio);
void md_free_cloned_bio(struct bio *bio);
--
2.51.0
^ permalink raw reply related
* [PATCH] md/md-llbitmap: track bitmap sync_size explicitly
From: Yu Kuai @ 2026-04-19 3:09 UTC (permalink / raw)
To: linux-raid; +Cc: linux-kernel, Li Nan, Yu Kuai, Cheng Cheng
In-Reply-To: <20260419030942.824195-1-yukuai@fnnas.com>
Track llbitmap's own sync_size instead of always using
mddev->resync_max_sectors directly.
This is the minimal bookkeeping needed before llbitmap can track old
and new reshape geometry independently.
Signed-off-by: Yu Kuai <yukuai@fnnas.com>
---
drivers/md/md-llbitmap.c | 14 ++++++++++++--
1 file changed, 12 insertions(+), 2 deletions(-)
diff --git a/drivers/md/md-llbitmap.c b/drivers/md/md-llbitmap.c
index 62b8f3efa9f5..547b1317df43 100644
--- a/drivers/md/md-llbitmap.c
+++ b/drivers/md/md-llbitmap.c
@@ -267,6 +267,8 @@ struct llbitmap {
unsigned long chunksize;
/* total number of chunks */
unsigned long chunks;
+ /* total number of sectors tracked by current bitmap geometry */
+ sector_t sync_size;
unsigned long last_end_sync;
/*
* time in seconds that dirty bits will be cleared if the page is not
@@ -791,6 +793,7 @@ static int llbitmap_init(struct llbitmap *llbitmap)
llbitmap->chunkshift = ffz(~chunksize);
llbitmap->chunksize = chunksize;
llbitmap->chunks = chunks;
+ llbitmap->sync_size = blocks;
mddev->bitmap_info.daemon_sleep = DEFAULT_DAEMON_SLEEP;
ret = llbitmap_cache_pages(llbitmap);
@@ -811,6 +814,7 @@ static int llbitmap_read_sb(struct llbitmap *llbitmap)
unsigned long daemon_sleep;
unsigned long chunksize;
unsigned long events;
+ sector_t sync_size;
struct page *sb_page;
bitmap_super_t *sb;
int ret = -EINVAL;
@@ -860,6 +864,9 @@ static int llbitmap_read_sb(struct llbitmap *llbitmap)
goto out_put_page;
}
+ sync_size = le64_to_cpu(sb->sync_size);
+ if (!sync_size)
+ sync_size = mddev->resync_max_sectors;
chunksize = le32_to_cpu(sb->chunksize);
if (!is_power_of_2(chunksize)) {
pr_err("md/llbitmap: %s: chunksize not a power of 2",
@@ -895,8 +902,9 @@ static int llbitmap_read_sb(struct llbitmap *llbitmap)
llbitmap->barrier_idle = DEFAULT_BARRIER_IDLE;
llbitmap->chunksize = chunksize;
- llbitmap->chunks = DIV_ROUND_UP_SECTOR_T(mddev->resync_max_sectors, chunksize);
+ llbitmap->chunks = DIV_ROUND_UP_SECTOR_T(sync_size, chunksize);
llbitmap->chunkshift = ffz(~chunksize);
+ llbitmap->sync_size = sync_size;
ret = llbitmap_cache_pages(llbitmap);
out_put_page:
@@ -1026,6 +1034,7 @@ static int llbitmap_resize(struct mddev *mddev, sector_t blocks, int chunksize)
llbitmap->chunkshift = ffz(~chunksize);
llbitmap->chunksize = chunksize;
llbitmap->chunks = chunks;
+ llbitmap->sync_size = blocks;
return 0;
}
@@ -1384,7 +1393,7 @@ static void llbitmap_update_sb(void *data)
sb->events = cpu_to_le64(mddev->events);
sb->state = cpu_to_le32(llbitmap->flags);
sb->chunksize = cpu_to_le32(llbitmap->chunksize);
- sb->sync_size = cpu_to_le64(mddev->resync_max_sectors);
+ sb->sync_size = cpu_to_le64(llbitmap->sync_size);
sb->events_cleared = cpu_to_le64(llbitmap->events_cleared);
sb->sectors_reserved = cpu_to_le32(mddev->bitmap_info.space);
sb->daemon_sleep = cpu_to_le32(mddev->bitmap_info.daemon_sleep);
@@ -1402,6 +1411,7 @@ static int llbitmap_get_stats(void *data, struct md_bitmap_stats *stats)
stats->missing_pages = 0;
stats->pages = llbitmap->nr_pages;
stats->file_pages = llbitmap->nr_pages;
+ stats->sync_size = llbitmap->sync_size;
stats->behind_writes = atomic_read(&llbitmap->behind_writes);
stats->behind_wait = wq_has_sleeper(&llbitmap->behind_wait);
--
2.51.0
^ permalink raw reply related
* [PATCH] md/md-llbitmap: allocate page controls independently
From: Yu Kuai @ 2026-04-19 3:09 UTC (permalink / raw)
To: linux-raid; +Cc: linux-kernel, Li Nan, Yu Kuai, Cheng Cheng
In-Reply-To: <20260419030942.824195-1-yukuai@fnnas.com>
Allocate one llbitmap page-control object at a time and free each
object through the same model.
Let llbitmap_read_page() return a zeroed page without reading disk when
the page index is beyond the current bitmap size, so page-control
allocation no longer needs a separate read_existing flag.
This keeps the llbitmap page-control lifetime self-consistent and
prepares the page-cache code for later in-place growth.
Signed-off-by: Yu Kuai <yukuai@fnnas.com>
---
drivers/md/md-llbitmap.c | 99 +++++++++++++++++++++++++---------------
1 file changed, 62 insertions(+), 37 deletions(-)
diff --git a/drivers/md/md-llbitmap.c b/drivers/md/md-llbitmap.c
index 547b1317df43..a411041fd122 100644
--- a/drivers/md/md-llbitmap.c
+++ b/drivers/md/md-llbitmap.c
@@ -443,13 +443,19 @@ static void llbitmap_write(struct llbitmap *llbitmap, enum llbitmap_state state,
llbitmap_set_page_dirty(llbitmap, idx, bit);
}
+static unsigned int llbitmap_used_pages(struct llbitmap *llbitmap,
+ unsigned long chunks)
+{
+ return DIV_ROUND_UP(chunks + BITMAP_DATA_OFFSET, PAGE_SIZE);
+}
+
static struct page *llbitmap_read_page(struct llbitmap *llbitmap, int idx)
{
struct mddev *mddev = llbitmap->mddev;
struct page *page = NULL;
struct md_rdev *rdev;
- if (llbitmap->pctl && llbitmap->pctl[idx])
+ if (llbitmap->pctl && idx < llbitmap->nr_pages && llbitmap->pctl[idx])
page = llbitmap->pctl[idx]->page;
if (page)
return page;
@@ -457,6 +463,8 @@ static struct page *llbitmap_read_page(struct llbitmap *llbitmap, int idx)
page = alloc_page(GFP_KERNEL | __GFP_ZERO);
if (!page)
return ERR_PTR(-ENOMEM);
+ if (idx >= llbitmap_used_pages(llbitmap, llbitmap->chunks))
+ return page;
rdev_for_each(rdev, mddev) {
sector_t sector;
@@ -527,61 +535,78 @@ static void llbitmap_free_pages(struct llbitmap *llbitmap)
for (i = 0; i < llbitmap->nr_pages; i++) {
struct llbitmap_page_ctl *pctl = llbitmap->pctl[i];
- if (!pctl || !pctl->page)
- break;
-
- __free_page(pctl->page);
+ if (!pctl)
+ continue;
+ if (pctl->page)
+ __free_page(pctl->page);
percpu_ref_exit(&pctl->active);
+ kfree(pctl);
}
- kfree(llbitmap->pctl[0]);
kfree(llbitmap->pctl);
llbitmap->pctl = NULL;
}
-static int llbitmap_cache_pages(struct llbitmap *llbitmap)
+static struct llbitmap_page_ctl *
+llbitmap_alloc_page_ctl(struct llbitmap *llbitmap, int idx)
{
struct llbitmap_page_ctl *pctl;
- unsigned int nr_pages = DIV_ROUND_UP(llbitmap->chunks +
- BITMAP_DATA_OFFSET, PAGE_SIZE);
+ struct page *page;
unsigned int size = struct_size(pctl, dirty, BITS_TO_LONGS(
llbitmap->blocks_per_page));
- int i;
-
- llbitmap->pctl = kmalloc_array(nr_pages, sizeof(void *),
- GFP_KERNEL | __GFP_ZERO);
- if (!llbitmap->pctl)
- return -ENOMEM;
size = round_up(size, cache_line_size());
- pctl = kmalloc_array(nr_pages, size, GFP_KERNEL | __GFP_ZERO);
- if (!pctl) {
- kfree(llbitmap->pctl);
- return -ENOMEM;
+ pctl = kzalloc(size, GFP_KERNEL);
+ if (!pctl)
+ return ERR_PTR(-ENOMEM);
+
+ page = llbitmap_read_page(llbitmap, idx);
+
+ if (IS_ERR(page)) {
+ kfree(pctl);
+ return ERR_CAST(page);
}
- llbitmap->nr_pages = nr_pages;
+ if (percpu_ref_init(&pctl->active, active_release,
+ PERCPU_REF_ALLOW_REINIT, GFP_KERNEL)) {
+ __free_page(page);
+ kfree(pctl);
+ return ERR_PTR(-ENOMEM);
+ }
- for (i = 0; i < nr_pages; i++, pctl = (void *)pctl + size) {
- struct page *page = llbitmap_read_page(llbitmap, i);
+ pctl->page = page;
+ pctl->state = page_address(page);
+ init_waitqueue_head(&pctl->wait);
+ return pctl;
+}
- llbitmap->pctl[i] = pctl;
+static unsigned int llbitmap_reserved_pages(struct llbitmap *llbitmap)
+{
+ return DIV_ROUND_UP(llbitmap->mddev->bitmap_info.space << SECTOR_SHIFT,
+ PAGE_SIZE);
+}
- if (IS_ERR(page)) {
- llbitmap_free_pages(llbitmap);
- return PTR_ERR(page);
- }
+static int llbitmap_alloc_pages(struct llbitmap *llbitmap)
+{
+ unsigned int used_pages = llbitmap_used_pages(llbitmap, llbitmap->chunks);
+ unsigned int nr_pages = max(used_pages, llbitmap_reserved_pages(llbitmap));
+ int i;
+
+ llbitmap->pctl = kcalloc(nr_pages, sizeof(*llbitmap->pctl), GFP_KERNEL);
+ if (!llbitmap->pctl)
+ return -ENOMEM;
- if (percpu_ref_init(&pctl->active, active_release,
- PERCPU_REF_ALLOW_REINIT, GFP_KERNEL)) {
- __free_page(page);
+ llbitmap->nr_pages = nr_pages;
+
+ for (i = 0; i < nr_pages; i++) {
+ llbitmap->pctl[i] = llbitmap_alloc_page_ctl(llbitmap, i);
+ if (IS_ERR(llbitmap->pctl[i])) {
+ int ret = PTR_ERR(llbitmap->pctl[i]);
+
+ llbitmap->pctl[i] = NULL;
llbitmap_free_pages(llbitmap);
- return -ENOMEM;
+ return ret;
}
-
- pctl->page = page;
- pctl->state = page_address(page);
- init_waitqueue_head(&pctl->wait);
}
return 0;
@@ -796,7 +821,7 @@ static int llbitmap_init(struct llbitmap *llbitmap)
llbitmap->sync_size = blocks;
mddev->bitmap_info.daemon_sleep = DEFAULT_DAEMON_SLEEP;
- ret = llbitmap_cache_pages(llbitmap);
+ ret = llbitmap_alloc_pages(llbitmap);
if (ret)
return ret;
@@ -905,7 +930,7 @@ static int llbitmap_read_sb(struct llbitmap *llbitmap)
llbitmap->chunks = DIV_ROUND_UP_SECTOR_T(sync_size, chunksize);
llbitmap->chunkshift = ffz(~chunksize);
llbitmap->sync_size = sync_size;
- ret = llbitmap_cache_pages(llbitmap);
+ ret = llbitmap_alloc_pages(llbitmap);
out_put_page:
__free_page(sb_page);
--
2.51.0
^ permalink raw reply related
* [PATCH] md/md-llbitmap: grow the page cache in place for reshape
From: Yu Kuai @ 2026-04-19 3:09 UTC (permalink / raw)
To: linux-raid; +Cc: linux-kernel, Li Nan, Yu Kuai, Cheng Cheng
In-Reply-To: <20260419030942.824195-1-yukuai@fnnas.com>
Use the page-control helpers to grow llbitmap's cached pages in place
for resize and later reshape preparation, instead of rebuilding the
whole cache.
Signed-off-by: Yu Kuai <yukuai@fnnas.com>
---
drivers/md/md-llbitmap.c | 139 +++++++++++++++++++++++++++++++++++----
1 file changed, 127 insertions(+), 12 deletions(-)
diff --git a/drivers/md/md-llbitmap.c b/drivers/md/md-llbitmap.c
index a411041fd122..841b64f0b4e6 100644
--- a/drivers/md/md-llbitmap.c
+++ b/drivers/md/md-llbitmap.c
@@ -350,6 +350,19 @@ static char state_machine[BitStateCount][BitmapActionCount] = {
};
static void __llbitmap_flush(struct mddev *mddev);
+static void llbitmap_flush(struct mddev *mddev);
+static void llbitmap_update_sb(void *data);
+
+static void llbitmap_resize_chunks(struct mddev *mddev, sector_t blocks,
+ unsigned long *chunksize,
+ unsigned long *chunks)
+{
+ *chunks = DIV_ROUND_UP_SECTOR_T(blocks, *chunksize);
+ while (*chunks > mddev->bitmap_info.space << SECTOR_SHIFT) {
+ *chunksize = *chunksize << 1;
+ *chunks = DIV_ROUND_UP_SECTOR_T(blocks, *chunksize);
+ }
+}
static enum llbitmap_state llbitmap_read(struct llbitmap *llbitmap, loff_t pos)
{
@@ -586,6 +599,48 @@ static unsigned int llbitmap_reserved_pages(struct llbitmap *llbitmap)
PAGE_SIZE);
}
+static int llbitmap_expand_pages(struct llbitmap *llbitmap,
+ unsigned long chunks)
+{
+ struct llbitmap_page_ctl **pctl;
+ unsigned int old_nr_pages = llbitmap->nr_pages;
+ unsigned int nr_pages = llbitmap_used_pages(llbitmap, chunks);
+ int i;
+ int ret;
+
+ if (nr_pages <= old_nr_pages)
+ return 0;
+
+ pctl = kcalloc(nr_pages, sizeof(*pctl), GFP_KERNEL);
+ if (!pctl)
+ return -ENOMEM;
+
+ if (llbitmap->pctl)
+ memcpy(pctl, llbitmap->pctl,
+ array_size(old_nr_pages, sizeof(*pctl)));
+
+ for (i = old_nr_pages; i < nr_pages; i++) {
+ pctl[i] = llbitmap_alloc_page_ctl(llbitmap, i);
+ if (IS_ERR(pctl[i]))
+ goto err_alloc_ptr;
+ }
+
+ kfree(llbitmap->pctl);
+ llbitmap->pctl = pctl;
+ llbitmap->nr_pages = nr_pages;
+ return 0;
+
+err_alloc_ptr:
+ ret = PTR_ERR(pctl[i]);
+ for (i--; i >= (int)old_nr_pages; i--) {
+ __free_page(pctl[i]->page);
+ percpu_ref_exit(&pctl[i]->active);
+ kfree(pctl[i]);
+ }
+ kfree(pctl);
+ return ret;
+}
+
static int llbitmap_alloc_pages(struct llbitmap *llbitmap)
{
unsigned int used_pages = llbitmap_used_pages(llbitmap, llbitmap->chunks);
@@ -612,6 +667,34 @@ static int llbitmap_alloc_pages(struct llbitmap *llbitmap)
return 0;
}
+static void llbitmap_mark_range(struct llbitmap *llbitmap,
+ unsigned long start,
+ unsigned long end,
+ enum llbitmap_state state)
+{
+ while (start <= end) {
+ llbitmap_write(llbitmap, state, start);
+ start++;
+ }
+}
+
+static int llbitmap_prepare_resize(struct llbitmap *llbitmap,
+ unsigned long old_chunks,
+ unsigned long new_chunks,
+ unsigned long cache_chunks)
+{
+ int ret;
+
+ llbitmap_flush(llbitmap->mddev);
+ ret = llbitmap_expand_pages(llbitmap, cache_chunks);
+ if (ret)
+ return ret;
+ if (new_chunks > old_chunks)
+ llbitmap_mark_range(llbitmap, old_chunks, new_chunks - 1,
+ BitUnwritten);
+ return 0;
+}
+
static void llbitmap_init_state(struct llbitmap *llbitmap)
{
enum llbitmap_state state = BitUnwritten;
@@ -899,10 +982,10 @@ static int llbitmap_read_sb(struct llbitmap *llbitmap)
goto out_put_page;
}
- if (chunksize < DIV_ROUND_UP_SECTOR_T(mddev->resync_max_sectors,
+ if (chunksize < DIV_ROUND_UP_SECTOR_T(sync_size,
mddev->bitmap_info.space << SECTOR_SHIFT)) {
pr_err("md/llbitmap: %s: chunksize too small %lu < %llu / %lu",
- mdname(mddev), chunksize, mddev->resync_max_sectors,
+ mdname(mddev), chunksize, sync_size,
mddev->bitmap_info.space);
goto out_put_page;
}
@@ -1044,24 +1127,56 @@ static int llbitmap_create(struct mddev *mddev)
static int llbitmap_resize(struct mddev *mddev, sector_t blocks, int chunksize)
{
struct llbitmap *llbitmap = mddev->bitmap;
+ sector_t old_blocks = llbitmap->sync_size;
+ unsigned long old_chunks = llbitmap->chunks;
unsigned long chunks;
+ unsigned long cache_chunks;
+ int ret = 0;
+ unsigned long bitmap_chunksize;
+ bool reshape;
if (chunksize == 0)
chunksize = llbitmap->chunksize;
- /* If there is enough space, leave the chunksize unchanged. */
- chunks = DIV_ROUND_UP_SECTOR_T(blocks, chunksize);
- while (chunks > mddev->bitmap_info.space << SECTOR_SHIFT) {
- chunksize = chunksize << 1;
- chunks = DIV_ROUND_UP_SECTOR_T(blocks, chunksize);
- }
+ bitmap_chunksize = chunksize;
+ llbitmap_resize_chunks(mddev, blocks, &bitmap_chunksize, &chunks);
- llbitmap->chunkshift = ffz(~chunksize);
- llbitmap->chunksize = chunksize;
- llbitmap->chunks = chunks;
- llbitmap->sync_size = blocks;
+ reshape = mddev->delta_disks || mddev->new_level != mddev->level ||
+ mddev->new_layout != mddev->layout ||
+ mddev->new_chunk_sectors != mddev->chunk_sectors;
+ if (!reshape && bitmap_chunksize != llbitmap->chunksize)
+ return -EOPNOTSUPP;
+ if (blocks == old_blocks && chunks == llbitmap->chunks)
+ return 0;
+
+ mutex_lock(&mddev->bitmap_info.mutex);
+ cache_chunks = reshape ? max(old_chunks, chunks) : chunks;
+ ret = llbitmap_prepare_resize(llbitmap, old_chunks, chunks, cache_chunks);
+ if (ret)
+ goto out;
+
+ if (reshape) {
+ llbitmap->reshape_sync_size = blocks;
+ llbitmap->reshape_chunksize = bitmap_chunksize;
+ llbitmap->reshape_chunks = chunks;
+ llbitmap->chunks = max(old_chunks, chunks);
+ } else {
+ if (blocks < old_blocks && chunks < old_chunks)
+ llbitmap_mark_range(llbitmap, chunks, old_chunks - 1,
+ BitUnwritten);
+ mddev->bitmap_info.chunksize = bitmap_chunksize;
+ llbitmap->chunks = chunks;
+ llbitmap->sync_size = blocks;
+ llbitmap_update_sb(llbitmap);
+ }
+ __llbitmap_flush(mddev);
+ mutex_unlock(&mddev->bitmap_info.mutex);
return 0;
+
+out:
+ mutex_unlock(&mddev->bitmap_info.mutex);
+ return ret;
}
static int llbitmap_load(struct mddev *mddev)
--
2.51.0
^ permalink raw reply related
* [PATCH] md/md-llbitmap: track target reshape geometry fields
From: Yu Kuai @ 2026-04-19 3:09 UTC (permalink / raw)
To: linux-raid; +Cc: linux-kernel, Li Nan, Yu Kuai, Cheng Cheng
In-Reply-To: <20260419030942.824195-1-yukuai@fnnas.com>
Track llbitmap bookkeeping for the target reshape geometry while keeping
a single live bitmap instance.
Add the reshape geometry fields, refresh helper, and update the load and
resize paths to keep the target geometry in sync.
Signed-off-by: Yu Kuai <yukuai@fnnas.com>
---
drivers/md/md-llbitmap.c | 38 ++++++++++++++++++++++++++++++++++++++
1 file changed, 38 insertions(+)
diff --git a/drivers/md/md-llbitmap.c b/drivers/md/md-llbitmap.c
index 841b64f0b4e6..cfd97022b283 100644
--- a/drivers/md/md-llbitmap.c
+++ b/drivers/md/md-llbitmap.c
@@ -269,6 +269,9 @@ struct llbitmap {
unsigned long chunks;
/* total number of sectors tracked by current bitmap geometry */
sector_t sync_size;
+ unsigned long reshape_chunksize;
+ unsigned long reshape_chunks;
+ sector_t reshape_sync_size;
unsigned long last_end_sync;
/*
* time in seconds that dirty bits will be cleared if the page is not
@@ -364,6 +367,38 @@ static void llbitmap_resize_chunks(struct mddev *mddev, sector_t blocks,
}
}
+static bool llbitmap_reshaping(struct llbitmap *llbitmap)
+{
+ return llbitmap->mddev->reshape_position != MaxSector;
+}
+
+static sector_t llbitmap_personality_sync_size(struct llbitmap *llbitmap,
+ bool previous)
+{
+ struct mddev *mddev = llbitmap->mddev;
+
+ if (!llbitmap_reshaping(llbitmap) || !mddev->private || !mddev->pers ||
+ !mddev->pers->bitmap_sync_size)
+ return llbitmap->sync_size;
+ return mddev->pers->bitmap_sync_size(mddev, previous);
+}
+
+static void llbitmap_refresh_reshape(struct llbitmap *llbitmap)
+{
+ unsigned long old_chunks = DIV_ROUND_UP_SECTOR_T(llbitmap->sync_size,
+ llbitmap->chunksize);
+ sector_t blocks = llbitmap_personality_sync_size(llbitmap, false);
+ unsigned long chunksize = llbitmap->chunksize;
+ unsigned long chunks = DIV_ROUND_UP_SECTOR_T(blocks, chunksize);
+
+ llbitmap->reshape_sync_size = blocks;
+ llbitmap->reshape_chunksize = chunksize;
+ llbitmap->reshape_chunks = chunks;
+ llbitmap_resize_chunks(llbitmap->mddev, blocks, &llbitmap->reshape_chunksize,
+ &llbitmap->reshape_chunks);
+ llbitmap->chunks = max(old_chunks, llbitmap->reshape_chunks);
+}
+
static enum llbitmap_state llbitmap_read(struct llbitmap *llbitmap, loff_t pos)
{
unsigned int idx;
@@ -902,6 +937,7 @@ static int llbitmap_init(struct llbitmap *llbitmap)
llbitmap->chunksize = chunksize;
llbitmap->chunks = chunks;
llbitmap->sync_size = blocks;
+ llbitmap_refresh_reshape(llbitmap);
mddev->bitmap_info.daemon_sleep = DEFAULT_DAEMON_SLEEP;
ret = llbitmap_alloc_pages(llbitmap);
@@ -1013,6 +1049,7 @@ static int llbitmap_read_sb(struct llbitmap *llbitmap)
llbitmap->chunks = DIV_ROUND_UP_SECTOR_T(sync_size, chunksize);
llbitmap->chunkshift = ffz(~chunksize);
llbitmap->sync_size = sync_size;
+ llbitmap_refresh_reshape(llbitmap);
ret = llbitmap_alloc_pages(llbitmap);
out_put_page:
@@ -1168,6 +1205,7 @@ static int llbitmap_resize(struct mddev *mddev, sector_t blocks, int chunksize)
mddev->bitmap_info.chunksize = bitmap_chunksize;
llbitmap->chunks = chunks;
llbitmap->sync_size = blocks;
+ llbitmap_refresh_reshape(llbitmap);
llbitmap_update_sb(llbitmap);
}
__llbitmap_flush(mddev);
--
2.51.0
^ permalink raw reply related
* [PATCH] md/md-llbitmap: finish reshape geometry
From: Yu Kuai @ 2026-04-19 3:09 UTC (permalink / raw)
To: linux-raid; +Cc: linux-kernel, Li Nan, Yu Kuai, Cheng Cheng
In-Reply-To: <20260419030942.824195-1-yukuai@fnnas.com>
Commit the staged llbitmap geometry when reshape finishes.\n\nThe reshape staging itself is handled through llbitmap_resize(), so only\nthe finish step remains in this patch.\n\nSigned-off-by: Yu Kuai <yukuai@fnnas.com>
---
drivers/md/md-llbitmap.c | 25 +++++++++++++++++++++++++
1 file changed, 25 insertions(+)
diff --git a/drivers/md/md-llbitmap.c b/drivers/md/md-llbitmap.c
index cfd97022b283..17a505ec65d6 100644
--- a/drivers/md/md-llbitmap.c
+++ b/drivers/md/md-llbitmap.c
@@ -1537,6 +1537,30 @@ static void llbitmap_dirty_bits(struct mddev *mddev, unsigned long s,
llbitmap_state_machine(mddev->bitmap, s, e, BitmapActionStartwrite);
}
+static void llbitmap_reshape_finish(struct mddev *mddev)
+{
+ struct llbitmap *llbitmap = mddev->bitmap;
+
+ if (mddev->pers->quiesce)
+ mddev->pers->quiesce(mddev, 1);
+
+ mutex_lock(&mddev->bitmap_info.mutex);
+ llbitmap_flush(mddev);
+
+ llbitmap->chunksize = llbitmap->reshape_chunksize;
+ llbitmap->chunkshift = ffz(~llbitmap->chunksize);
+ llbitmap->chunks = llbitmap->reshape_chunks;
+ llbitmap->sync_size = llbitmap->reshape_sync_size;
+ llbitmap_refresh_reshape(llbitmap);
+ mddev->bitmap_info.chunksize = llbitmap->chunksize;
+ llbitmap_update_sb(llbitmap);
+ __llbitmap_flush(mddev);
+ mutex_unlock(&mddev->bitmap_info.mutex);
+
+ if (mddev->pers->quiesce)
+ mddev->pers->quiesce(mddev, 0);
+}
+
static void llbitmap_write_sb(struct llbitmap *llbitmap)
{
int nr_blocks = DIV_ROUND_UP(BITMAP_DATA_OFFSET, llbitmap->io_size);
@@ -1792,6 +1816,7 @@ static struct bitmap_operations llbitmap_ops = {
.get_stats = llbitmap_get_stats,
.dirty_bits = llbitmap_dirty_bits,
.prepare_range = llbitmap_prepare_range,
+ .reshape_finish = llbitmap_reshape_finish,
.write_all = llbitmap_write_all,
.group = &md_llbitmap_group,
--
2.51.0
^ permalink raw reply related
* [PATCH] md/md-llbitmap: refuse reshape while llbitmap still needs sync
From: Yu Kuai @ 2026-04-19 3:09 UTC (permalink / raw)
To: linux-raid; +Cc: linux-kernel, Li Nan, Yu Kuai, Cheng Cheng
In-Reply-To: <20260419030942.824195-1-yukuai@fnnas.com>
Reject reshape when llbitmap still contains NeedSync or Syncing bits.
This keeps reshape from starting until the current llbitmap state has
been reconciled.
Signed-off-by: Yu Kuai <yukuai@fnnas.com>
---
drivers/md/md-llbitmap.c | 24 ++++++++++++++++++++++++
1 file changed, 24 insertions(+)
diff --git a/drivers/md/md-llbitmap.c b/drivers/md/md-llbitmap.c
index 17a505ec65d6..88169eeda4b5 100644
--- a/drivers/md/md-llbitmap.c
+++ b/drivers/md/md-llbitmap.c
@@ -1537,6 +1537,29 @@ static void llbitmap_dirty_bits(struct mddev *mddev, unsigned long s,
llbitmap_state_machine(mddev->bitmap, s, e, BitmapActionStartwrite);
}
+static int llbitmap_reshape_can_start(struct mddev *mddev)
+{
+ struct llbitmap *llbitmap = mddev->bitmap;
+ unsigned long chunk;
+ int ret = 0;
+
+ if (!llbitmap)
+ return 0;
+
+ mutex_lock(&mddev->bitmap_info.mutex);
+ for (chunk = 0; chunk < llbitmap->chunks; chunk++) {
+ enum llbitmap_state state = llbitmap_read(llbitmap, chunk);
+
+ if (state == BitNeedSync || state == BitSyncing) {
+ ret = -EBUSY;
+ break;
+ }
+ }
+ mutex_unlock(&mddev->bitmap_info.mutex);
+
+ return ret;
+}
+
static void llbitmap_reshape_finish(struct mddev *mddev)
{
struct llbitmap *llbitmap = mddev->bitmap;
@@ -1817,6 +1840,7 @@ static struct bitmap_operations llbitmap_ops = {
.dirty_bits = llbitmap_dirty_bits,
.prepare_range = llbitmap_prepare_range,
.reshape_finish = llbitmap_reshape_finish,
+ .reshape_can_start = llbitmap_reshape_can_start,
.write_all = llbitmap_write_all,
.group = &md_llbitmap_group,
--
2.51.0
^ permalink raw reply related
* [PATCH] md/md-llbitmap: add reshape range mapping helpers
From: Yu Kuai @ 2026-04-19 3:09 UTC (permalink / raw)
To: linux-raid; +Cc: linux-kernel, Li Nan, Yu Kuai, Cheng Cheng
In-Reply-To: <20260419030942.824195-1-yukuai@fnnas.com>
Teach llbitmap to choose old versus new geometry during reshape and to
encode exact bitmap ranges for the active geometry.
This is the mapping groundwork for checkpoint remapping.
Signed-off-by: Yu Kuai <yukuai@fnnas.com>
---
drivers/md/md-llbitmap.c | 96 ++++++++++++++++++++++++++++++++++++++--
1 file changed, 92 insertions(+), 4 deletions(-)
diff --git a/drivers/md/md-llbitmap.c b/drivers/md/md-llbitmap.c
index 88169eeda4b5..fd1dc86afc69 100644
--- a/drivers/md/md-llbitmap.c
+++ b/drivers/md/md-llbitmap.c
@@ -9,6 +9,7 @@
#include <linux/sched.h>
#include <linux/list.h>
#include <linux/file.h>
+#include <linux/math64.h>
#include <linux/seq_file.h>
#include <trace/events/block.h>
@@ -383,6 +384,16 @@ static sector_t llbitmap_personality_sync_size(struct llbitmap *llbitmap,
return mddev->pers->bitmap_sync_size(mddev, previous);
}
+static sector_t llbitmap_logical_size(struct llbitmap *llbitmap, bool previous)
+{
+ struct mddev *mddev = llbitmap->mddev;
+
+ if (!llbitmap_reshaping(llbitmap) || !mddev->private || !mddev->pers ||
+ !mddev->pers->bitmap_array_sectors)
+ return llbitmap_personality_sync_size(llbitmap, previous);
+ return mddev->pers->bitmap_array_sectors(mddev, previous);
+}
+
static void llbitmap_refresh_reshape(struct llbitmap *llbitmap)
{
unsigned long old_chunks = DIV_ROUND_UP_SECTOR_T(llbitmap->sync_size,
@@ -399,6 +410,52 @@ static void llbitmap_refresh_reshape(struct llbitmap *llbitmap)
llbitmap->chunks = max(old_chunks, llbitmap->reshape_chunks);
}
+static void llbitmap_map_layout(struct llbitmap *llbitmap, sector_t *offset,
+ unsigned long *sectors, bool previous)
+{
+ sector_t limit = llbitmap_logical_size(llbitmap, previous);
+ sector_t start = *offset;
+ sector_t end = start + *sectors;
+
+ if (start >= limit) {
+ *sectors = 0;
+ return;
+ }
+ if (end > limit)
+ end = limit;
+
+ *offset = start;
+ *sectors = end - start;
+ if (!*sectors)
+ return;
+
+ if (llbitmap->mddev->pers->bitmap_sector_map)
+ llbitmap->mddev->pers->bitmap_sector_map(llbitmap->mddev, offset,
+ sectors, previous);
+ else if (!previous && llbitmap->mddev->pers->bitmap_sector)
+ llbitmap->mddev->pers->bitmap_sector(llbitmap->mddev, offset,
+ sectors);
+}
+
+static void llbitmap_encode_range(struct llbitmap *llbitmap, sector_t *offset,
+ unsigned long *sectors, bool previous)
+{
+ unsigned long chunksize = previous ? llbitmap->chunksize :
+ llbitmap->reshape_chunksize;
+ u64 start;
+ u64 end;
+
+ if (!*sectors) {
+ *offset = 0;
+ return;
+ }
+
+ start = div64_u64(*offset, chunksize);
+ end = div64_u64(*offset + *sectors - 1, chunksize);
+ *offset = (sector_t)start << llbitmap->chunkshift;
+ *sectors = (end - start + 1) << llbitmap->chunkshift;
+}
+
static enum llbitmap_state llbitmap_read(struct llbitmap *llbitmap, loff_t pos)
{
unsigned int idx;
@@ -1248,11 +1305,32 @@ static void llbitmap_destroy(struct mddev *mddev)
mutex_unlock(&mddev->bitmap_info.mutex);
}
+static bool llbitmap_map_previous(struct llbitmap *llbitmap, sector_t offset,
+ unsigned long sectors)
+{
+ struct mddev *mddev = llbitmap->mddev;
+ sector_t boundary = mddev->reshape_position;
+
+ if (!llbitmap_reshaping(llbitmap))
+ return false;
+
+ WARN_ON_ONCE(sectors && offset < boundary && offset + sectors > boundary);
+
+ return mddev->reshape_backwards ? offset < boundary : offset >= boundary;
+}
+
static void llbitmap_prepare_range(struct mddev *mddev, sector_t *offset,
unsigned long *sectors)
{
- if (mddev->pers->bitmap_sector)
- mddev->pers->bitmap_sector(mddev, offset, sectors);
+ struct llbitmap *llbitmap = mddev->bitmap;
+ bool previous;
+
+ if (!llbitmap)
+ return;
+
+ previous = llbitmap_map_previous(llbitmap, *offset, *sectors);
+ llbitmap_map_layout(llbitmap, offset, sectors, previous);
+ llbitmap_encode_range(llbitmap, offset, sectors, previous);
}
static void llbitmap_start_write(struct mddev *mddev, sector_t offset,
@@ -1421,7 +1499,11 @@ static bool llbitmap_blocks_synced(struct mddev *mddev, sector_t offset)
{
struct llbitmap *llbitmap = mddev->bitmap;
unsigned long p = offset >> llbitmap->chunkshift;
- enum llbitmap_state c = llbitmap_read(llbitmap, p);
+ enum llbitmap_state c;
+
+ if (p >= llbitmap->chunks)
+ return false;
+ c = llbitmap_read(llbitmap, p);
return c == BitClean || c == BitDirty;
}
@@ -1431,7 +1513,11 @@ static sector_t llbitmap_skip_sync_blocks(struct mddev *mddev, sector_t offset)
struct llbitmap *llbitmap = mddev->bitmap;
unsigned long p = offset >> llbitmap->chunkshift;
int blocks = llbitmap->chunksize - (offset & (llbitmap->chunksize - 1));
- enum llbitmap_state c = llbitmap_read(llbitmap, p);
+ enum llbitmap_state c;
+
+ if (p >= llbitmap->chunks)
+ return 0;
+ c = llbitmap_read(llbitmap, p);
/* always skip unwritten blocks */
if (c == BitUnwritten)
@@ -1461,6 +1547,8 @@ static bool llbitmap_start_sync(struct mddev *mddev, sector_t offset,
* if md_do_sync() loop more times.
*/
*blocks = llbitmap->chunksize - (offset & (llbitmap->chunksize - 1));
+ if (p >= llbitmap->chunks)
+ return false;
return llbitmap_state_machine(llbitmap, p, p,
BitmapActionStartsync) == BitSyncing;
}
--
2.51.0
^ permalink raw reply related
* [PATCH] md/md-llbitmap: don't skip reshape ranges from bitmap state
From: Yu Kuai @ 2026-04-19 3:09 UTC (permalink / raw)
To: linux-raid; +Cc: linux-kernel, Li Nan, Yu Kuai, Cheng Cheng
In-Reply-To: <20260419030942.824195-1-yukuai@fnnas.com>
Reshape progress is tracked by array metadata rather than llbitmap.
Do not let llbitmap skip_sync_blocks() suppress reshape ranges based on
stale bitmap state before the corresponding checkpoint is persisted.
Signed-off-by: Yu Kuai <yukuai@fnnas.com>
---
drivers/md/md-llbitmap.c | 8 ++++++++
1 file changed, 8 insertions(+)
diff --git a/drivers/md/md-llbitmap.c b/drivers/md/md-llbitmap.c
index fd1dc86afc69..ad1b7a85914b 100644
--- a/drivers/md/md-llbitmap.c
+++ b/drivers/md/md-llbitmap.c
@@ -1519,6 +1519,14 @@ static sector_t llbitmap_skip_sync_blocks(struct mddev *mddev, sector_t offset)
return 0;
c = llbitmap_read(llbitmap, p);
+ /*
+ * Reshape progress is tracked by array metadata rather than llbitmap.
+ * Skipping reshape ranges from stale bitmap state can lose data after a
+ * restart before the corresponding bits are checkpointed to disk.
+ */
+ if (test_bit(MD_RECOVERY_RESHAPE, &mddev->recovery))
+ return 0;
+
/* always skip unwritten blocks */
if (c == BitUnwritten)
return blocks;
--
2.51.0
^ permalink raw reply related
* [PATCH] md/md-llbitmap: remap checkpointed bits as reshape progresses
From: Yu Kuai @ 2026-04-19 3:09 UTC (permalink / raw)
To: linux-raid; +Cc: linux-kernel, Li Nan, Yu Kuai, Cheng Cheng
In-Reply-To: <20260419030942.824195-1-yukuai@fnnas.com>
Merge checkpointed old llbitmap state forward as reshape_position
advances and record the checkpoint remap through reshape_mark().
Signed-off-by: Yu Kuai <yukuai@fnnas.com>
---
drivers/md/md-llbitmap.c | 172 +++++++++++++++++++++++++++++++++++++++
1 file changed, 172 insertions(+)
diff --git a/drivers/md/md-llbitmap.c b/drivers/md/md-llbitmap.c
index ad1b7a85914b..b3ff67779557 100644
--- a/drivers/md/md-llbitmap.c
+++ b/drivers/md/md-llbitmap.c
@@ -435,6 +435,14 @@ static void llbitmap_map_layout(struct llbitmap *llbitmap, sector_t *offset,
else if (!previous && llbitmap->mddev->pers->bitmap_sector)
llbitmap->mddev->pers->bitmap_sector(llbitmap->mddev, offset,
sectors);
+
+ limit = llbitmap_personality_sync_size(llbitmap, previous);
+ start = *offset;
+ end = start + *sectors;
+ if (start >= limit)
+ *sectors = 0;
+ else if (end > limit)
+ *sectors = limit - start;
}
static void llbitmap_encode_range(struct llbitmap *llbitmap, sector_t *offset,
@@ -787,6 +795,33 @@ static int llbitmap_prepare_resize(struct llbitmap *llbitmap,
return 0;
}
+static enum llbitmap_state
+llbitmap_rmerge_state(struct llbitmap *llbitmap,
+ enum llbitmap_state dst,
+ enum llbitmap_state src)
+{
+ bool level_456 = raid_is_456(llbitmap->mddev);
+
+ if (dst == BitNeedSync || dst == BitSyncing ||
+ src == BitNeedSync || src == BitSyncing)
+ return BitNeedSync;
+
+ if (dst == BitDirty || src == BitDirty)
+ return BitDirty;
+
+ /*
+ * Reshape generates valid target parity/data for both already-written
+ * and not-yet-written regions in the checkpointed range, so a mix of
+ * clean and unwritten still results in a clean destination bit.
+ */
+ if (level_456 && ((dst == BitClean && src == BitUnwritten) ||
+ (src == BitClean && dst == BitUnwritten)))
+ return BitClean;
+ if (dst == BitClean || src == BitClean)
+ return BitClean;
+ return BitUnwritten;
+}
+
static void llbitmap_init_state(struct llbitmap *llbitmap)
{
enum llbitmap_state state = BitUnwritten;
@@ -1656,6 +1691,120 @@ static int llbitmap_reshape_can_start(struct mddev *mddev)
return ret;
}
+struct llbitmap_reshape_range {
+ sector_t offset;
+ unsigned long sectors;
+ sector_t start;
+ sector_t end;
+};
+
+static enum llbitmap_state
+llbitmap_reshape_init_dst(struct llbitmap *llbitmap, unsigned long dst,
+ const struct llbitmap_reshape_range *new)
+{
+ u64 bit_start = (u64)dst * llbitmap->reshape_chunksize;
+ u64 bit_end = bit_start + llbitmap->reshape_chunksize;
+
+ if (!llbitmap->mddev->reshape_backwards)
+ return bit_start < new->offset ? llbitmap_read(llbitmap, dst) :
+ BitUnwritten;
+ return bit_end > new->end ? llbitmap_read(llbitmap, dst) : BitUnwritten;
+}
+
+static void llbitmap_reshape_dst_range(struct llbitmap *llbitmap,
+ unsigned long dst,
+ const struct llbitmap_reshape_range *new,
+ struct llbitmap_reshape_range *dst_range)
+{
+ sector_t dst_bit_start = (sector_t)dst * llbitmap->reshape_chunksize;
+
+ dst_range->start = max(dst_bit_start, new->offset);
+ dst_range->end = min(dst_bit_start + llbitmap->reshape_chunksize,
+ new->end);
+ dst_range->offset = dst_range->start;
+ dst_range->sectors = dst_range->end - dst_range->start;
+}
+
+static void llbitmap_reshape_map_range(struct llbitmap *llbitmap,
+ sector_t lo, sector_t hi,
+ bool previous,
+ struct llbitmap_reshape_range *range)
+{
+ range->offset = lo;
+ range->sectors = hi - lo;
+ llbitmap_map_layout(llbitmap, &range->offset, &range->sectors, previous);
+ range->start = range->offset;
+ range->end = range->offset + range->sectors;
+}
+
+static bool llbitmap_reshape_src_range(const struct llbitmap_reshape_range *old,
+ const struct llbitmap_reshape_range *new,
+ const struct llbitmap_reshape_range *dst,
+ struct llbitmap_reshape_range *src)
+{
+ if (!old->sectors)
+ return false;
+
+ src->start = old->offset +
+ mul_u64_u64_div_u64(dst->start - new->offset,
+ old->sectors, new->sectors);
+ src->end = old->offset +
+ mul_u64_u64_div_u64_roundup(dst->end - new->offset,
+ old->sectors, new->sectors);
+ if (src->end > old->end)
+ src->end = old->end;
+ src->offset = src->start;
+ src->sectors = src->end - src->start;
+
+ return src->sectors;
+}
+
+static enum llbitmap_state llbitmap_rmerge_src(struct llbitmap *llbitmap,
+ enum llbitmap_state state,
+ const struct llbitmap_reshape_range *src)
+{
+ unsigned long bit = div64_u64(src->start, llbitmap->chunksize);
+ unsigned long end = div64_u64(src->end - 1, llbitmap->chunksize);
+
+ while (bit <= end) {
+ enum llbitmap_state src_state = llbitmap_read(llbitmap, bit);
+
+ state = llbitmap_rmerge_state(llbitmap, state, src_state);
+ bit++;
+ }
+
+ return state;
+}
+
+static void llbitmap_reshape_merge(struct llbitmap *llbitmap,
+ const struct llbitmap_reshape_range *old,
+ const struct llbitmap_reshape_range *new)
+{
+ unsigned long dst_start;
+ unsigned long dst_end;
+ unsigned long dst;
+
+ if (!new->sectors)
+ return;
+
+ dst_start = div64_u64(new->offset, llbitmap->reshape_chunksize);
+ dst_end = div64_u64(new->end - 1, llbitmap->reshape_chunksize);
+
+ for (dst = dst_start; dst <= dst_end; dst++) {
+ struct llbitmap_reshape_range dst_range;
+ struct llbitmap_reshape_range src;
+ enum llbitmap_state state;
+
+ llbitmap_reshape_dst_range(llbitmap, dst, new, &dst_range);
+ state = llbitmap_reshape_init_dst(llbitmap, dst, new);
+ if (llbitmap_reshape_src_range(old, new, &dst_range, &src))
+ state = llbitmap_rmerge_src(llbitmap, state, &src);
+ else
+ state = llbitmap_rmerge_state(llbitmap, state, BitUnwritten);
+ llbitmap_write(llbitmap, state, dst);
+ }
+}
+
static void llbitmap_reshape_finish(struct mddev *mddev)
{
struct llbitmap *llbitmap = mddev->bitmap;
@@ -1680,6 +1829,28 @@ static void llbitmap_reshape_finish(struct mddev *mddev)
mddev->pers->quiesce(mddev, 0);
}
+static void llbitmap_reshape_mark(struct mddev *mddev, sector_t old_pos,
+ sector_t new_pos)
+{
+ struct llbitmap *llbitmap = mddev->bitmap;
+ sector_t lo;
+ sector_t hi;
+ struct llbitmap_reshape_range old;
+ struct llbitmap_reshape_range new;
+
+ if (!llbitmap || old_pos == new_pos)
+ return;
+
+ lo = min(old_pos, new_pos);
+ hi = max(old_pos, new_pos);
+ if (!hi)
+ return;
+
+ llbitmap_reshape_map_range(llbitmap, lo, hi, true, &old);
+ llbitmap_reshape_map_range(llbitmap, lo, hi, false, &new);
+ llbitmap_reshape_merge(llbitmap, &old, &new);
+}
+
static void llbitmap_write_sb(struct llbitmap *llbitmap)
{
int nr_blocks = DIV_ROUND_UP(BITMAP_DATA_OFFSET, llbitmap->io_size);
@@ -1937,6 +2108,7 @@ static struct bitmap_operations llbitmap_ops = {
.prepare_range = llbitmap_prepare_range,
.reshape_finish = llbitmap_reshape_finish,
.reshape_can_start = llbitmap_reshape_can_start,
+ .reshape_mark = llbitmap_reshape_mark,
.write_all = llbitmap_write_all,
.group = &md_llbitmap_group,
--
2.51.0
^ permalink raw reply related
* [PATCH] md/md-llbitmap: clamp state-machine walks to tracked bits
From: Yu Kuai @ 2026-04-19 3:09 UTC (permalink / raw)
To: linux-raid; +Cc: linux-kernel, Li Nan, Yu Kuai, Cheng Cheng
In-Reply-To: <20260419030942.824195-1-yukuai@fnnas.com>
llbitmap_state_machine() can be called with an end bit beyond
llbitmap->chunks. In particular, llbitmap_cond_end_sync() passes
sector >> chunkshift, and sector can reach the tracked boundary
exactly.
Clamp the state-machine range to llbitmap->chunks so it cannot walk
past the tracked bitmap.
Signed-off-by: Yu Kuai <yukuai@fnnas.com>
---
drivers/md/md-llbitmap.c | 5 ++++-
1 file changed, 4 insertions(+), 1 deletion(-)
diff --git a/drivers/md/md-llbitmap.c b/drivers/md/md-llbitmap.c
index b3ff67779557..6f81ccf3e884 100644
--- a/drivers/md/md-llbitmap.c
+++ b/drivers/md/md-llbitmap.c
@@ -853,7 +853,10 @@ static enum llbitmap_state llbitmap_state_machine(struct llbitmap *llbitmap,
llbitmap_init_state(llbitmap);
return BitNone;
}
-
+ if (start >= llbitmap->chunks)
+ return BitNone;
+ if (end >= llbitmap->chunks)
+ end = llbitmap->chunks - 1;
while (start <= end) {
enum llbitmap_state c = llbitmap_read(llbitmap, start);
--
2.51.0
^ permalink raw reply related
* [PATCH] md/raid10: reject llbitmap chunk shrink during reshape
From: Yu Kuai @ 2026-04-19 3:09 UTC (permalink / raw)
To: linux-raid; +Cc: linux-kernel, Li Nan, Yu Kuai, Cheng Cheng
In-Reply-To: <20260419030942.824195-1-yukuai@fnnas.com>
llbitmap reshape keeps one live bitmap and only supports growing the
tracked chunk geometry. Reject RAID10 reshape attempts that would shrink
the llbitmap chunk size.
Signed-off-by: Yu Kuai <yukuai@fnnas.com>
---
drivers/md/raid10.c | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/drivers/md/raid10.c b/drivers/md/raid10.c
index 4901ebe45c87..ad19257c79fb 100644
--- a/drivers/md/raid10.c
+++ b/drivers/md/raid10.c
@@ -4260,6 +4260,10 @@ static int raid10_check_reshape(struct mddev *mddev)
if (conf->geo.far_copies != 1 && !conf->geo.far_offset)
return -EINVAL;
+ if (mddev->bitmap_id == ID_LLBITMAP &&
+ mddev->new_chunk_sectors &&
+ mddev->new_chunk_sectors < mddev->chunk_sectors)
+ return -EOPNOTSUPP;
if (setup_geo(&geo, mddev, geo_start) != conf->copies)
/* mustn't change number of copies */
--
2.51.0
^ permalink raw reply related
* [PATCH] md/raid10: wire llbitmap reshape lifecycle
From: Yu Kuai @ 2026-04-19 3:09 UTC (permalink / raw)
To: linux-raid; +Cc: linux-kernel, Li Nan, Yu Kuai, Cheng Cheng
In-Reply-To: <20260419030942.824195-1-yukuai@fnnas.com>
Prepare llbitmap before RAID10 starts growing, checkpoint the bitmap
before advancing reshape_position, finish the llbitmap geometry update
when reshape completes, and export the old and new tracked sizes.
Signed-off-by: Yu Kuai <yukuai@fnnas.com>
---
drivers/md/raid10.c | 39 +++++++++++++++++++++++++++++++++++++++
1 file changed, 39 insertions(+)
diff --git a/drivers/md/raid10.c b/drivers/md/raid10.c
index ad19257c79fb..13e31d01ed0f 100644
--- a/drivers/md/raid10.c
+++ b/drivers/md/raid10.c
@@ -4370,6 +4370,12 @@ static int raid10_start_reshape(struct mddev *mddev)
if (test_bit(MD_RECOVERY_RUNNING, &mddev->recovery))
return -EBUSY;
+ if (md_bitmap_enabled(mddev, false) &&
+ mddev->bitmap_ops->reshape_can_start) {
+ ret = mddev->bitmap_ops->reshape_can_start(mddev);
+ if (ret)
+ return ret;
+ }
if (setup_geo(&new, mddev, geo_start) != conf->copies)
return -EINVAL;
@@ -4683,6 +4689,13 @@ static sector_t reshape_request(struct mddev *mddev, sector_t sector_nr,
time_after(jiffies, conf->reshape_checkpoint + 10*HZ)) {
/* Need to update reshape_position in metadata */
wait_barrier(conf, false);
+ if (md_bitmap_enabled(mddev, false) &&
+ mddev->bitmap_ops->reshape_mark &&
+ conf->reshape_safe != conf->reshape_progress) {
+ mddev->bitmap_ops->reshape_mark(mddev, conf->reshape_safe,
+ conf->reshape_progress);
+ mddev->bitmap_ops->unplug(mddev, true);
+ }
mddev->reshape_position = conf->reshape_progress;
if (mddev->reshape_backwards)
mddev->curr_resync_completed = raid10_size(mddev, 0, 0)
@@ -4881,9 +4894,19 @@ static void reshape_request_write(struct mddev *mddev, struct r10bio *r10_bio)
static void end_reshape(struct r10conf *conf)
{
+ struct mddev *mddev = conf->mddev;
+
if (test_bit(MD_RECOVERY_INTR, &conf->mddev->recovery))
return;
+ if (md_bitmap_enabled(mddev, false) &&
+ mddev->bitmap_ops->reshape_mark &&
+ conf->reshape_safe != conf->reshape_progress) {
+ mddev->bitmap_ops->reshape_mark(mddev, conf->reshape_safe,
+ conf->reshape_progress);
+ mddev->bitmap_ops->unplug(mddev, true);
+ }
+
spin_lock_irq(&conf->device_lock);
conf->prev = conf->geo;
md_finish_reshape(conf->mddev);
@@ -5015,10 +5038,15 @@ static void end_reshape_request(struct r10bio *r10_bio)
static void raid10_finish_reshape(struct mddev *mddev)
{
struct r10conf *conf = mddev->private;
+ bool llbitmap = mddev->bitmap_id == ID_LLBITMAP &&
+ md_bitmap_enabled(mddev, false);
if (test_bit(MD_RECOVERY_INTR, &mddev->recovery))
return;
+ if (llbitmap && mddev->bitmap_ops->reshape_finish)
+ mddev->bitmap_ops->reshape_finish(mddev);
+
if (mddev->delta_disks > 0) {
if (mddev->resync_offset > mddev->resync_max_sectors) {
mddev->resync_offset = mddev->resync_max_sectors;
@@ -5045,6 +5073,15 @@ static void raid10_finish_reshape(struct mddev *mddev)
mddev->reshape_backwards = 0;
}
+static sector_t raid10_bitmap_sync_size(struct mddev *mddev, bool previous)
+{
+ struct r10conf *conf = mddev->private;
+
+ if (previous)
+ return raid10_size(mddev, 0, 0);
+ return raid10_size(mddev, 0, conf->geo.raid_disks);
+}
+
static struct md_personality raid10_personality =
{
.head = {
@@ -5071,6 +5108,8 @@ static struct md_personality raid10_personality =
.start_reshape = raid10_start_reshape,
.finish_reshape = raid10_finish_reshape,
.update_reshape_pos = raid10_update_reshape_pos,
+ .bitmap_sync_size = raid10_bitmap_sync_size,
+ .bitmap_array_sectors = raid10_bitmap_sync_size,
};
static int __init raid10_init(void)
--
2.51.0
^ permalink raw reply related
* [PATCH] md/raid10: split reshape bios before bitmap accounting
From: Yu Kuai @ 2026-04-19 3:09 UTC (permalink / raw)
To: linux-raid; +Cc: linux-kernel, Li Nan, Yu Kuai, Cheng Cheng
In-Reply-To: <20260419030942.824195-1-yukuai@fnnas.com>
Use the shared mddev_bio_split_at_reshape_offset() helper so RAID10
submits only one-side bios to llbitmap during reshape.
Signed-off-by: Yu Kuai <yukuai@fnnas.com>
---
drivers/md/raid10.c | 7 ++++++-
1 file changed, 6 insertions(+), 1 deletion(-)
diff --git a/drivers/md/raid10.c b/drivers/md/raid10.c
index 13e31d01ed0f..b54cb1828a48 100644
--- a/drivers/md/raid10.c
+++ b/drivers/md/raid10.c
@@ -1891,6 +1891,12 @@ static bool raid10_make_request(struct mddev *mddev, struct bio *bio)
sectors = chunk_sects -
(bio->bi_iter.bi_sector &
(chunk_sects - 1));
+
+ bio = mddev_bio_split_at_reshape_offset(mddev, bio, §ors,
+ &conf->bio_split);
+ if (!bio)
+ return true;
+
__make_request(mddev, bio, sectors);
/* In case raid10d snuck in to freeze_array */
@@ -4264,7 +4270,6 @@ static int raid10_check_reshape(struct mddev *mddev)
mddev->new_chunk_sectors &&
mddev->new_chunk_sectors < mddev->chunk_sectors)
return -EOPNOTSUPP;
-
if (setup_geo(&geo, mddev, geo_start) != conf->copies)
/* mustn't change number of copies */
return -EINVAL;
--
2.51.0
^ permalink raw reply related
* [PATCH] md/raid5: add exact old and new llbitmap mapping helpers
From: Yu Kuai @ 2026-04-19 3:09 UTC (permalink / raw)
To: linux-raid; +Cc: linux-kernel, Li Nan, Yu Kuai, Cheng Cheng
In-Reply-To: <20260419030942.824195-1-yukuai@fnnas.com>
Teach RAID5 to export exact old and new llbitmap mappings and the
corresponding sync and array sizes for reshape-aware bitmap users.
Signed-off-by: Yu Kuai <yukuai@fnnas.com>
---
drivers/md/raid5.c | 70 ++++++++++++++++++++++++++++++++++------------
1 file changed, 52 insertions(+), 18 deletions(-)
diff --git a/drivers/md/raid5.c b/drivers/md/raid5.c
index 1f8360d4cdb7..0c58c175bad9 100644
--- a/drivers/md/raid5.c
+++ b/drivers/md/raid5.c
@@ -5898,25 +5898,43 @@ static enum reshape_loc get_reshape_loc(struct mddev *mddev,
return LOC_BEHIND_RESHAPE;
}
-static void raid5_bitmap_sector(struct mddev *mddev, sector_t *offset,
- unsigned long *sectors)
+static void raid5_bitmap_sector_map(struct mddev *mddev, sector_t *offset,
+ unsigned long *sectors,
+ bool previous)
{
struct r5conf *conf = mddev->private;
sector_t start = *offset;
sector_t end = start + *sectors;
- sector_t prev_start = start;
- sector_t prev_end = end;
int sectors_per_chunk;
- enum reshape_loc loc;
int dd_idx;
- sectors_per_chunk = conf->chunk_sectors *
- (conf->raid_disks - conf->max_degraded);
+ if (previous)
+ sectors_per_chunk = conf->prev_chunk_sectors *
+ (conf->previous_raid_disks - conf->max_degraded);
+ else
+ sectors_per_chunk = conf->chunk_sectors *
+ (conf->raid_disks - conf->max_degraded);
start = round_down(start, sectors_per_chunk);
end = round_up(end, sectors_per_chunk);
- start = raid5_compute_sector(conf, start, 0, &dd_idx, NULL);
- end = raid5_compute_sector(conf, end, 0, &dd_idx, NULL);
+ start = raid5_compute_sector(conf, start, previous, &dd_idx, NULL);
+ end = raid5_compute_sector(conf, end, previous, &dd_idx, NULL);
+ *offset = start;
+ *sectors = end - start;
+}
+
+static void raid5_bitmap_sector(struct mddev *mddev, sector_t *offset,
+ unsigned long *sectors)
+{
+ struct r5conf *conf = mddev->private;
+ sector_t start = *offset;
+ sector_t end = start + *sectors;
+ sector_t prev_start = start;
+ unsigned long prev_sectors = end - start;
+ enum reshape_loc loc;
+
+ raid5_bitmap_sector_map(mddev, &start, sectors, false);
+ end = start + *sectors;
/*
* For LOC_INSIDE_RESHAPE, this IO will wait for reshape to make
@@ -5925,17 +5943,10 @@ static void raid5_bitmap_sector(struct mddev *mddev, sector_t *offset,
loc = get_reshape_loc(mddev, conf, prev_start);
if (likely(loc != LOC_AHEAD_OF_RESHAPE)) {
*offset = start;
- *sectors = end - start;
return;
}
- sectors_per_chunk = conf->prev_chunk_sectors *
- (conf->previous_raid_disks - conf->max_degraded);
- prev_start = round_down(prev_start, sectors_per_chunk);
- prev_end = round_down(prev_end, sectors_per_chunk);
-
- prev_start = raid5_compute_sector(conf, prev_start, 1, &dd_idx, NULL);
- prev_end = raid5_compute_sector(conf, prev_end, 1, &dd_idx, NULL);
+ raid5_bitmap_sector_map(mddev, &prev_start, &prev_sectors, true);
/*
* for LOC_AHEAD_OF_RESHAPE, reshape can make progress before this IO
@@ -5943,7 +5954,7 @@ static void raid5_bitmap_sector(struct mddev *mddev, sector_t *offset,
* we set bits for both.
*/
*offset = min(start, prev_start);
- *sectors = max(end, prev_end) - *offset;
+ *sectors = max(end, prev_start + prev_sectors) - *offset;
}
static enum stripe_result make_stripe_request(struct mddev *mddev,
@@ -9002,6 +9013,20 @@ static void raid5_prepare_suspend(struct mddev *mddev)
wake_up(&conf->wait_for_reshape);
}
+static sector_t raid5_bitmap_sync_size(struct mddev *mddev, bool previous)
+{
+ return mddev->dev_sectors;
+}
+
+static sector_t raid5_bitmap_array_sectors(struct mddev *mddev, bool previous)
+{
+ struct r5conf *conf = mddev->private;
+
+ if (previous)
+ return raid5_size(mddev, 0, 0);
+ return raid5_size(mddev, mddev->dev_sectors, conf->raid_disks);
+}
+
static struct md_personality raid6_personality =
{
.head = {
@@ -9031,6 +9056,9 @@ static struct md_personality raid6_personality =
.change_consistency_policy = raid5_change_consistency_policy,
.prepare_suspend = raid5_prepare_suspend,
.bitmap_sector = raid5_bitmap_sector,
+ .bitmap_sector_map = raid5_bitmap_sector_map,
+ .bitmap_sync_size = raid5_bitmap_sync_size,
+ .bitmap_array_sectors = raid5_bitmap_array_sectors,
};
static struct md_personality raid5_personality =
{
@@ -9061,6 +9089,9 @@ static struct md_personality raid5_personality =
.change_consistency_policy = raid5_change_consistency_policy,
.prepare_suspend = raid5_prepare_suspend,
.bitmap_sector = raid5_bitmap_sector,
+ .bitmap_sector_map = raid5_bitmap_sector_map,
+ .bitmap_sync_size = raid5_bitmap_sync_size,
+ .bitmap_array_sectors = raid5_bitmap_array_sectors,
};
static struct md_personality raid4_personality =
@@ -9092,6 +9123,9 @@ static struct md_personality raid4_personality =
.change_consistency_policy = raid5_change_consistency_policy,
.prepare_suspend = raid5_prepare_suspend,
.bitmap_sector = raid5_bitmap_sector,
+ .bitmap_sector_map = raid5_bitmap_sector_map,
+ .bitmap_sync_size = raid5_bitmap_sync_size,
+ .bitmap_array_sectors = raid5_bitmap_array_sectors,
};
static int __init raid5_init(void)
--
2.51.0
^ permalink raw reply related
* [PATCH] md/raid5: reject llbitmap chunk shrink during reshape
From: Yu Kuai @ 2026-04-19 3:09 UTC (permalink / raw)
To: linux-raid; +Cc: linux-kernel, Li Nan, Yu Kuai, Cheng Cheng
In-Reply-To: <20260419030942.824195-1-yukuai@fnnas.com>
llbitmap reshape keeps one live bitmap and only supports growing the
tracked chunk geometry. Reject RAID5 reshape attempts that would shrink
the llbitmap chunk size.
Signed-off-by: Yu Kuai <yukuai@fnnas.com>
---
drivers/md/raid5.c | 3 +++
1 file changed, 3 insertions(+)
diff --git a/drivers/md/raid5.c b/drivers/md/raid5.c
index 0c58c175bad9..178283aa91a4 100644
--- a/drivers/md/raid5.c
+++ b/drivers/md/raid5.c
@@ -8459,6 +8459,9 @@ static int check_reshape(struct mddev *mddev)
if (!check_stripe_cache(mddev))
return -ENOSPC;
+ if (mddev->bitmap_id == ID_LLBITMAP &&
+ mddev->new_chunk_sectors < mddev->chunk_sectors)
+ return -EOPNOTSUPP;
if (mddev->new_chunk_sectors > mddev->chunk_sectors ||
mddev->delta_disks > 0)
if (resize_chunks(conf,
--
2.51.0
^ permalink raw reply related
* [PATCH] md/raid5: wire llbitmap reshape lifecycle
From: Yu Kuai @ 2026-04-19 3:09 UTC (permalink / raw)
To: linux-raid; +Cc: linux-kernel, Li Nan, Yu Kuai, Cheng Cheng
In-Reply-To: <20260419030942.824195-1-yukuai@fnnas.com>
Prepare llbitmap before RAID5 reshape starts, checkpoint the bitmap
before advancing reshape_position, and finish the llbitmap geometry
update when reshape completes.
Signed-off-by: Yu Kuai <yukuai@fnnas.com>
---
drivers/md/raid5.c | 37 +++++++++++++++++++++++++++++++++++++
1 file changed, 37 insertions(+)
diff --git a/drivers/md/raid5.c b/drivers/md/raid5.c
index 178283aa91a4..476f6fc5a97c 100644
--- a/drivers/md/raid5.c
+++ b/drivers/md/raid5.c
@@ -6378,6 +6378,13 @@ static sector_t reshape_request(struct mddev *mddev, sector_t sector_nr, int *sk
|| test_bit(MD_RECOVERY_INTR, &mddev->recovery));
if (atomic_read(&conf->reshape_stripes) != 0)
return 0;
+ if (md_bitmap_enabled(mddev, false) &&
+ mddev->bitmap_ops->reshape_mark &&
+ conf->reshape_safe != conf->reshape_progress) {
+ mddev->bitmap_ops->reshape_mark(mddev, conf->reshape_safe,
+ conf->reshape_progress);
+ mddev->bitmap_ops->unplug(mddev, true);
+ }
mddev->reshape_position = conf->reshape_progress;
mddev->curr_resync_completed = sector_nr;
if (!mddev->reshape_backwards)
@@ -6487,6 +6494,13 @@ static sector_t reshape_request(struct mddev *mddev, sector_t sector_nr, int *sk
|| test_bit(MD_RECOVERY_INTR, &mddev->recovery));
if (atomic_read(&conf->reshape_stripes) != 0)
goto ret;
+ if (md_bitmap_enabled(mddev, false) &&
+ mddev->bitmap_ops->reshape_mark &&
+ conf->reshape_safe != conf->reshape_progress) {
+ mddev->bitmap_ops->reshape_mark(mddev, conf->reshape_safe,
+ conf->reshape_progress);
+ mddev->bitmap_ops->unplug(mddev, true);
+ }
mddev->reshape_position = conf->reshape_progress;
mddev->curr_resync_completed = sector_nr;
if (!mddev->reshape_backwards)
@@ -8524,6 +8538,12 @@ static int raid5_start_reshape(struct mddev *mddev)
mdname(mddev));
return -EINVAL;
}
+ if (md_bitmap_enabled(mddev, false) &&
+ mddev->bitmap_id == ID_LLBITMAP) {
+ i = mddev->bitmap_ops->resize(mddev, mddev->dev_sectors, 0);
+ if (i)
+ return i;
+ }
atomic_set(&conf->reshape_stripes, 0);
spin_lock_irq(&conf->device_lock);
@@ -8608,10 +8628,19 @@ static int raid5_start_reshape(struct mddev *mddev)
*/
static void end_reshape(struct r5conf *conf)
{
+ struct mddev *mddev = conf->mddev;
if (!test_bit(MD_RECOVERY_INTR, &conf->mddev->recovery)) {
struct md_rdev *rdev;
+ if (md_bitmap_enabled(mddev, false) &&
+ mddev->bitmap_ops->reshape_mark &&
+ conf->reshape_safe != conf->reshape_progress) {
+ mddev->bitmap_ops->reshape_mark(mddev, conf->reshape_safe,
+ conf->reshape_progress);
+ mddev->bitmap_ops->unplug(mddev, true);
+ }
+
spin_lock_irq(&conf->device_lock);
conf->previous_raid_disks = conf->raid_disks;
md_finish_reshape(conf->mddev);
@@ -8638,8 +8667,16 @@ static void raid5_finish_reshape(struct mddev *mddev)
{
struct r5conf *conf = mddev->private;
struct md_rdev *rdev;
+ bool llbitmap = mddev->bitmap_id == ID_LLBITMAP &&
+ md_bitmap_enabled(mddev, false);
if (!test_bit(MD_RECOVERY_INTR, &mddev->recovery)) {
+ if (llbitmap && mddev->bitmap_ops->reshape_finish)
+ mddev->bitmap_ops->reshape_finish(mddev);
+ if (llbitmap) {
+ mddev->resync_offset = 0;
+ mddev->resync_max_sectors = mddev->dev_sectors;
+ }
if (mddev->delta_disks <= 0) {
int d;
--
2.51.0
^ permalink raw reply related
* [PATCH] md/raid5: split reshape bios before bitmap accounting
From: Yu Kuai @ 2026-04-19 3:09 UTC (permalink / raw)
To: linux-raid; +Cc: linux-kernel, Li Nan, Yu Kuai, Cheng Cheng
In-Reply-To: <20260419030942.824195-1-yukuai@fnnas.com>
Use the shared mddev_bio_split_at_reshape_offset() helper so RAID5
submits only one-side bios to llbitmap during reshape.
Signed-off-by: Yu Kuai <yukuai@fnnas.com>
---
drivers/md/raid5.c | 8 ++++++++
1 file changed, 8 insertions(+)
diff --git a/drivers/md/raid5.c b/drivers/md/raid5.c
index 476f6fc5a97c..7fa74c60c7d8 100644
--- a/drivers/md/raid5.c
+++ b/drivers/md/raid5.c
@@ -6134,6 +6134,14 @@ static bool raid5_make_request(struct mddev *mddev, struct bio * bi)
return true;
}
+ bi = mddev_bio_split_at_reshape_offset(mddev, bi, NULL,
+ &conf->bio_split);
+ if (!bi) {
+ if (rw == WRITE)
+ md_write_end(mddev);
+ return true;
+ }
+
logical_sector = bi->bi_iter.bi_sector & ~((sector_t)RAID5_STRIPE_SECTORS(conf)-1);
bi->bi_next = NULL;
--
2.51.0
^ permalink raw reply related
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox