From nobody Mon Jun 8 06:36:54 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-alma10-1.taild15c8.ts.net [100.103.45.18]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id A8CD14C77CB; Fri, 5 Jun 2026 09:15:58 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=100.103.45.18 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1780650959; cv=none; b=hemrEWx9LrzVLF6cZZNC5UPz6/0PZWn6CkpMWcb1DRbqjU139sgoyb0SUreXGHN3OTgTjKChrEGhzYUv41QPgnW67Emd2WpbrMGO7QZlU/OxWlotqta1ngfLKpVDDAOeG4qf+oUaj8eQ38LWafbEoqKoNUz9hHs2Ve1xGA6SIqs= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1780650959; c=relaxed/simple; bh=TYm639w866QzCt/sx2iQbtab1OJevzoO/4Sn9I/OQS0=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=bRyovQ48EDbTKa+P8xQpYAtd7vvNsJoF60uhzA1/CsupXpI8gV0anmlJSmXSb1yVDDy43OQq3OqOE/M99bKjKSjFMjlO1TqdCaxZJQgJC6QQKpJXgzVnOpe07oMkhLdMdGkzkg3IymZqaX/Jk3jO/Quu2VQIjjYjozRK33mxmmI= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=cnirCPLB; arc=none smtp.client-ip=100.103.45.18 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="cnirCPLB" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 5744E1F00898; Fri, 5 Jun 2026 09:15:56 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=kernel.org; s=k20260515; t=1780650958; bh=JHWrSdYQPOJymu1T2qGzdzhsSw9YfaWz1vKN+Ffc3GU=; h=From:To:Cc:Subject:Date:In-Reply-To:References; b=cnirCPLB4jn2WYbKQEN0D7KEuFtbl0ok9aZDT5CNsEM8PIHhuYE1UQ0nhEvjjD2th IlHuUF9e4zBZVA2i8iCZtjFEdXZSbQvwfOIPe+corkwDPLrSSUM8F7wA+mzmhiWSqr +ZP3IjQ0qBBeIctk7lXbz02lL17OM07+93rhg4goHsrqztDDF+XDjCtB9DY25xTu+s e59Z20hkjqWkGCAuSqXACy/D6e2njfWuKo5qWEjMe4WtN3HefgEwAA0sKU15Cwi484 5pgG1n+C+HYlxTBHC66vK7AqmlKTm3WAiL9iq4gdaXcG8bUDbBxlnG3aa3yDUOtLr9 4r/t2Kk+CpDMA== From: Yu Kuai To: Song Liu , Yu Kuai Cc: Li Nan , Xiao Ni , linux-raid@vger.kernel.org, linux-kernel@vger.kernel.org Subject: [PATCH] md/md-llbitmap: remap checkpointed bits as reshape progresses Date: Fri, 5 Jun 2026 17:15:19 +0800 Message-ID: <20260605091527.2463539-13-yukuai@kernel.org> X-Mailer: git-send-email 2.51.0 In-Reply-To: <20260605091527.2463539-1-yukuai@kernel.org> References: <20260605091527.2463539-1-yukuai@kernel.org> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" From: Yu Kuai Merge checkpointed old llbitmap state forward as reshape_position advances and record the checkpoint remap through reshape_mark(). Signed-off-by: Yu Kuai --- 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 67e63d81a824..6783f1b3ddf0 100644 --- a/drivers/md/md-llbitmap.c +++ b/drivers/md/md-llbitmap.c @@ -499,10 +499,18 @@ static void llbitmap_map_layout(struct llbitmap *llbi= tmap, sector_t *offset, 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); + + limit =3D llbitmap_personality_sync_size(llbitmap, previous); + start =3D *offset; + end =3D start + *sectors; + if (start >=3D limit) + *sectors =3D 0; + else if (end > limit) + *sectors =3D limit - start; } =20 static void llbitmap_encode_range(struct llbitmap *llbitmap, sector_t *off= set, unsigned long *sectors, bool previous) { @@ -903,10 +911,37 @@ static int llbitmap_prepare_resize(struct llbitmap *l= lbitmap, llbitmap_mark_range(llbitmap, old_chunks, new_chunks - 1, BitUnwritten); return 0; } =20 +static enum llbitmap_state +llbitmap_rmerge_state(struct llbitmap *llbitmap, + enum llbitmap_state dst, + enum llbitmap_state src) +{ + bool level_456 =3D raid_is_456(llbitmap->mddev); + + if (dst =3D=3D BitNeedSync || dst =3D=3D BitSyncing || + src =3D=3D BitNeedSync || src =3D=3D BitSyncing) + return BitNeedSync; + + if (dst =3D=3D BitDirty || src =3D=3D 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 =3D=3D BitClean && src =3D=3D BitUnwritten) || + (src =3D=3D BitClean && dst =3D=3D BitUnwritten))) + return BitClean; + if (dst =3D=3D BitClean || src =3D=3D BitClean) + return BitClean; + return BitUnwritten; +} + static void llbitmap_init_state(struct llbitmap *llbitmap) { struct mddev *mddev =3D llbitmap->mddev; enum llbitmap_state state =3D BitUnwritten; unsigned long i; @@ -1796,10 +1831,124 @@ static int llbitmap_reshape_can_start(struct mddev= *mddev) mutex_unlock(&mddev->bitmap_info.mutex); =20 return ret; } =20 +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 =3D (u64)dst * llbitmap->reshape_chunksize; + u64 bit_end =3D 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 =3D (sector_t)dst * llbitmap->reshape_chunksize; + + dst_range->start =3D max(dst_bit_start, new->offset); + dst_range->end =3D min(dst_bit_start + llbitmap->reshape_chunksize, + new->end); + dst_range->offset =3D dst_range->start; + dst_range->sectors =3D 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 =3D lo; + range->sectors =3D hi - lo; + llbitmap_map_layout(llbitmap, &range->offset, &range->sectors, previous); + range->start =3D range->offset; + range->end =3D 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 =3D old->offset + + mul_u64_u64_div_u64(dst->start - new->offset, + old->sectors, new->sectors); + src->end =3D old->offset + + mul_u64_u64_div_u64_roundup(dst->end - new->offset, + old->sectors, new->sectors); + if (src->end > old->end) + src->end =3D old->end; + src->offset =3D src->start; + src->sectors =3D 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 =3D div64_u64(src->start, llbitmap->chunksize); + unsigned long end =3D div64_u64(src->end - 1, llbitmap->chunksize); + + while (bit <=3D end) { + enum llbitmap_state src_state =3D llbitmap_read(llbitmap, bit); + + state =3D 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 =3D div64_u64(new->offset, llbitmap->reshape_chunksize); + dst_end =3D div64_u64(new->end - 1, llbitmap->reshape_chunksize); + + for (dst =3D dst_start; dst <=3D 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 =3D llbitmap_reshape_init_dst(llbitmap, dst, new); + if (llbitmap_reshape_src_range(old, new, &dst_range, &src)) + state =3D llbitmap_rmerge_src(llbitmap, state, &src); + else + state =3D llbitmap_rmerge_state(llbitmap, state, BitUnwritten); + llbitmap_write(llbitmap, state, dst); + } +} + static void llbitmap_reshape_finish(struct mddev *mddev) { struct llbitmap *llbitmap =3D mddev->bitmap; =20 if (mddev->pers->quiesce) @@ -1820,10 +1969,32 @@ static void llbitmap_reshape_finish(struct mddev *m= ddev) =20 if (mddev->pers->quiesce) mddev->pers->quiesce(mddev, 0); } =20 +static void llbitmap_reshape_mark(struct mddev *mddev, sector_t old_pos, + sector_t new_pos) +{ + struct llbitmap *llbitmap =3D mddev->bitmap; + sector_t lo; + sector_t hi; + struct llbitmap_reshape_range old; + struct llbitmap_reshape_range new; + + if (!llbitmap || old_pos =3D=3D new_pos) + return; + + lo =3D min(old_pos, new_pos); + hi =3D 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 =3D DIV_ROUND_UP(BITMAP_DATA_OFFSET, llbitmap->io_size); =20 bitmap_fill(llbitmap->pctl[0]->dirty, nr_blocks); @@ -2116,10 +2287,11 @@ static struct bitmap_operations llbitmap_ops =3D { .get_stats =3D llbitmap_get_stats, .dirty_bits =3D llbitmap_dirty_bits, .prepare_range =3D llbitmap_prepare_range, .reshape_finish =3D llbitmap_reshape_finish, .reshape_can_start =3D llbitmap_reshape_can_start, + .reshape_mark =3D llbitmap_reshape_mark, .write_all =3D llbitmap_write_all, =20 .groups =3D md_llbitmap_groups, }; =20 --=20 2.51.0