Linux EXT4 FS development
 help / color / mirror / Atom feed
* Re: [PATCH v3 5/5] ext4: fix possible null-ptr-deref in mbt_kunit_exit()
From: Ritesh Harjani @ 2026-03-14 12:36 UTC (permalink / raw)
  To: Ye Bin, tytso, adilger.kernel, linux-ext4; +Cc: jack
In-Reply-To: <20260314074903.1314851-6-yebin@huaweicloud.com>

Ye Bin <yebin@huaweicloud.com> writes:

> From: Ye Bin <yebin10@huawei.com>
>
> There's issue as follows:
>     # test_new_blocks_simple: failed to initialize: -12
> KASAN: null-ptr-deref in range [0x0000000000000638-0x000000000000063f]
> Tainted: [E]=UNSIGNED_MODULE, [N]=TEST
> RIP: 0010:mbt_kunit_exit+0x5e/0x3e0 [ext4_test]
> Call Trace:
>  <TASK>
>  kunit_try_run_case_cleanup+0xbc/0x100 [kunit]
>  kunit_generic_run_threadfn_adapter+0x89/0x100 [kunit]
>  kthread+0x408/0x540
>  ret_from_fork+0xa76/0xdf0
>  ret_from_fork_asm+0x1a/0x30
>
> If mbt_kunit_init() init testcase failed will lead to null-ptr-deref.
> So add test if 'sb' is inited success in mbt_kunit_exit().
>

LGTM.
Reviewed-by: Ritesh Harjani (IBM) <ritesh.list@gmail.com>


> Fixes: 7c9fa399a369 ("ext4: add first unit test for ext4_mb_new_blocks_simple in mballoc")
> Signed-off-by: Ye Bin <yebin10@huawei.com>
> ---
>  fs/ext4/mballoc-test.c | 6 +++++-
>  1 file changed, 5 insertions(+), 1 deletion(-)
>
> diff --git a/fs/ext4/mballoc-test.c b/fs/ext4/mballoc-test.c
> index c75b91ae0cf0..90ed505fa4b1 100644
> --- a/fs/ext4/mballoc-test.c
> +++ b/fs/ext4/mballoc-test.c
> @@ -362,7 +362,6 @@ static int mbt_kunit_init(struct kunit *test)
>  		return ret;
>  	}
>  
> -	test->priv = sb;
>  	kunit_activate_static_stub(test,
>  				   ext4_read_block_bitmap_nowait,
>  				   ext4_read_block_bitmap_nowait_stub);
> @@ -383,6 +382,8 @@ static int mbt_kunit_init(struct kunit *test)
>  		return -ENOMEM;
>  	}
>  
> +	test->priv = sb;
> +
>  	return 0;
>  }
>  
> @@ -390,6 +391,9 @@ static void mbt_kunit_exit(struct kunit *test)
>  {
>  	struct super_block *sb = (struct super_block *)test->priv;
>  
> +	if (!sb)
> +		return;
> +
>  	mbt_mb_release(sb);
>  	mbt_ctx_release(sb);
>  	mbt_ext4_free_super_block(sb);
> -- 
> 2.34.1

^ permalink raw reply

* Re: [PATCH v3 4/5] ext4: fix possible null-ptr-deref in extents_kunit_exit()
From: Ritesh Harjani @ 2026-03-14 12:35 UTC (permalink / raw)
  To: Ye Bin, tytso, adilger.kernel, linux-ext4; +Cc: jack
In-Reply-To: <20260314074903.1314851-5-yebin@huaweicloud.com>

Ye Bin <yebin@huaweicloud.com> writes:

> From: Ye Bin <yebin10@huawei.com>
>
> There's issue as follows:
> KASAN: null-ptr-deref in range [0x00000000000002c0-0x00000000000002c7]
> Tainted: [E]=UNSIGNED_MODULE, [N]=TEST
> RIP: 0010:extents_kunit_exit+0x2e/0xc0 [ext4_test]
> Call Trace:
>  <TASK>
>  kunit_try_run_case_cleanup+0xbc/0x100 [kunit]
>  kunit_generic_run_threadfn_adapter+0x89/0x100 [kunit]
>  kthread+0x408/0x540
>  ret_from_fork+0xa76/0xdf0
>  ret_from_fork_asm+0x1a/0x30
>
> Above issue happens as extents_kunit_init() init testcase failed.
> So test if testcase is inited success.
>

LGTM.
Reviewed-by: Ritesh Harjani (IBM) <ritesh.list@gmail.com>


> Fixes: cb1e0c1d1fad ("ext4: kunit tests for extent splitting and conversion")
> Signed-off-by: Ye Bin <yebin10@huawei.com>
> ---
>  fs/ext4/extents-test.c | 7 +++++--
>  1 file changed, 5 insertions(+), 2 deletions(-)
>
> diff --git a/fs/ext4/extents-test.c b/fs/ext4/extents-test.c
> index 543236a31e13..6ae72986ca9c 100644
> --- a/fs/ext4/extents-test.c
> +++ b/fs/ext4/extents-test.c
> @@ -142,9 +142,12 @@ static struct file_system_type ext_fs_type = {
>  
>  static void extents_kunit_exit(struct kunit *test)
>  {
> -	struct super_block *sb = k_ctx.k_ei->vfs_inode.i_sb;
> -	struct ext4_sb_info *sbi = sb->s_fs_info;
> +	struct ext4_sb_info *sbi;
>  
> +	if (!k_ctx.k_ei)
> +		return;
> +
> +	sbi = k_ctx.k_ei->vfs_inode.i_sb->s_fs_info;
>  	ext4_es_unregister_shrinker(sbi);
>  	deactivate_super(sbi->s_sb);
>  	kfree(sbi);
> -- 
> 2.34.1

^ permalink raw reply

* Re: [PATCH v3 3/5] ext4: fix the error handling process in extents_kunit_init).
From: Ritesh Harjani @ 2026-03-14 12:29 UTC (permalink / raw)
  To: Ye Bin, tytso, adilger.kernel, linux-ext4; +Cc: jack
In-Reply-To: <20260314074903.1314851-4-yebin@huaweicloud.com>

Ye Bin <yebin@huaweicloud.com> writes:

> From: Ye Bin <yebin10@huawei.com>
>
> The error processing in extents_kunit_init() is improper, causing
> resource leakage.
> Reconstruct the error handling process to prevent potential resource
> leaks
>

Minor nit.

> Fixes: cb1e0c1d1fad ("ext4: kunit tests for extent splitting and conversion")
> Signed-off-by: Ye Bin <yebin10@huawei.com>
> ---
>  fs/ext4/extents-test.c | 44 ++++++++++++++++++++++++++++++------------
>  1 file changed, 32 insertions(+), 12 deletions(-)
>
> diff --git a/fs/ext4/extents-test.c b/fs/ext4/extents-test.c
> index 3d4663d99eb1..543236a31e13 100644
> --- a/fs/ext4/extents-test.c
> +++ b/fs/ext4/extents-test.c
> @@ -225,33 +225,37 @@ static int extents_kunit_init(struct kunit *test)
>  		(struct kunit_ext_test_param *)(test->param_value);
>  	int err;
>  
> -	sb = sget(&ext_fs_type, NULL, ext_set, 0, NULL);
> -	if (IS_ERR(sb))
> -		return PTR_ERR(sb);
> -
> -	sb->s_blocksize = 4096;
> -	sb->s_blocksize_bits = 12;
> -
>  	sbi = kzalloc_obj(struct ext4_sb_info);
>  	if (sbi == NULL)
>  		return -ENOMEM;
>  
> +	sb = sget(&ext_fs_type, NULL, ext_set, 0, NULL);
> +	if (IS_ERR(sb)) {
> +		kfree(sbi);
> +		return PTR_ERR(sb);
> +	}
> +
>  	sbi->s_sb = sb;
>  	sb->s_fs_info = sbi;
>  
> +	sb->s_blocksize = 4096;
> +	sb->s_blocksize_bits = 12;
> +
>  	if (!param || !param->disable_zeroout)
>  		sbi->s_extent_max_zeroout_kb = 32;
>  
>  	/* setup the mock inode */
>  	k_ctx.k_ei = kzalloc_obj(struct ext4_inode_info);
> -	if (k_ctx.k_ei == NULL)
> -		return -ENOMEM;
> +	if (k_ctx.k_ei == NULL) {
> +		err = -ENOMEM;
> +		goto out_deactivate;
> +	}
>  	ei = k_ctx.k_ei;
>  	inode = &ei->vfs_inode;
>  
>  	err = ext4_es_register_shrinker(sbi);
>  	if (err)
> -		return err;
> +		goto out_deactivate;
>  
>  	ext4_es_init_tree(&ei->i_es_tree);
>  	rwlock_init(&ei->i_es_lock);
> @@ -267,8 +271,10 @@ static int extents_kunit_init(struct kunit *test)
>  	inode->i_sb = sb;
>  
>  	k_ctx.k_data = kzalloc(EXT_DATA_LEN * 4096, GFP_KERNEL);
> -	if (k_ctx.k_data == NULL)
> -		return -ENOMEM;
> +	if (k_ctx.k_data == NULL) {
> +		err = -ENOMEM;
> +		goto out_deactivate;
> +	}
>  
>  	/*
>  	 * set the data area to a junk value
> @@ -313,6 +319,20 @@ static int extents_kunit_init(struct kunit *test)
>  	up_write(&sb->s_umount);
>  
>  	return 0;
> +
> +out_deactivate:
> +	kfree(k_ctx.k_ei);
> +	k_ctx.k_ei = NULL;
> +
> +	kfree(k_ctx.k_data);
> +	k_ctx.k_data = NULL;
> +
> +	if (sbi->s_es_shrinker)
> +		ext4_es_unregister_shrinker(sbi);

I don't think this extra check is necessary.
ext4_es_unregister_shrinker() already has checks in place.

Reviewed-by: Ritesh Harjani (IBM) <ritesh.list@gmail.com>

> +	deactivate_locked_super(sb);
> +	kfree(sbi);
> +
> +	return err;
>  }
>  
>  /*
> -- 
> 2.34.1

^ permalink raw reply

* Re: [PATCH v3 2/5] ext4: fix miss unlock 'sb->s_umount' in extents_kunit_init()
From: Ritesh Harjani @ 2026-03-14 12:21 UTC (permalink / raw)
  To: Ye Bin, tytso, adilger.kernel, linux-ext4; +Cc: jack
In-Reply-To: <20260314074903.1314851-3-yebin@huaweicloud.com>

Ye Bin <yebin@huaweicloud.com> writes:

> From: Ye Bin <yebin10@huawei.com>
>
> There's warning as follows when do ext4 kunit test:
> WARNING: kunit_try_catch/15923 still has locks held!
> 7.0.0-rc3-next-20260309-00028-g73f965a1bbb1-dirty #281 Tainted: G            E    N
> 1 lock held by kunit_try_catch/15923:
>  #0: ffff888139f860e0 (&type->s_umount_key#70/1){+.+.}-{4:4}, at: alloc_super.constprop.0+0x172/0xa90
> Call Trace:
>  <TASK>
>  dump_stack_lvl+0x180/0x1b0
>  debug_check_no_locks_held+0xc8/0xd0
>  do_exit+0x1502/0x2b20
>  kthread+0x3a9/0x540
>  ret_from_fork+0xa76/0xdf0
>  ret_from_fork_asm+0x1a/0x30
>
> As sget() will return 'sb' which holds 's->s_umount' lock. However,
> "extents-test" miss unlock this lock.
> So unlock 's->s_umount' in the end of extents_kunit_init().
>

Agreed.

LGTM.
Reviewed-by: Ritesh Harjani (IBM) <ritesh.list@gmail.com>


> Fixes: cb1e0c1d1fad ("ext4: kunit tests for extent splitting and conversion")
> Signed-off-by: Ye Bin <yebin10@huawei.com>
> ---
>  fs/ext4/extents-test.c | 2 ++
>  1 file changed, 2 insertions(+)
>
> diff --git a/fs/ext4/extents-test.c b/fs/ext4/extents-test.c
> index e3d23e3cda87..3d4663d99eb1 100644
> --- a/fs/ext4/extents-test.c
> +++ b/fs/ext4/extents-test.c
> @@ -310,6 +310,8 @@ static int extents_kunit_init(struct kunit *test)
>  	kunit_activate_static_stub(test, ext4_ext_zeroout, ext4_ext_zeroout_stub);
>  	kunit_activate_static_stub(test, ext4_issue_zeroout,
>  				   ext4_issue_zeroout_stub);
> +	up_write(&sb->s_umount);
> +
>  	return 0;
>  }
>  
> -- 
> 2.34.1

^ permalink raw reply

* Re: [PATCH v3 1/5] ext4: call deactivate_super() in extents_kunit_exit()
From: Ritesh Harjani @ 2026-03-14 12:19 UTC (permalink / raw)
  To: Ye Bin, tytso, adilger.kernel, linux-ext4; +Cc: jack
In-Reply-To: <20260314074903.1314851-2-yebin@huaweicloud.com>

Ye Bin <yebin@huaweicloud.com> writes:

> From: Ye Bin <yebin10@huawei.com>
>
> Call deactivate_super() is called in extents_kunit_exit() to cleanup
> the file system resource.

yes, since _init() routine call sget(), we should call
deactivate_super() in _exit() routine.

LGTM.
Reviewed-by: Ritesh Harjani (IBM) <ritesh.list@gmail.com>


>
> Fixes: cb1e0c1d1fad ("ext4: kunit tests for extent splitting and conversion")
> Signed-off-by: Ye Bin <yebin10@huawei.com>
> ---
>  fs/ext4/extents-test.c | 1 +
>  1 file changed, 1 insertion(+)
>
> diff --git a/fs/ext4/extents-test.c b/fs/ext4/extents-test.c
> index 5496b2c8e2cd..e3d23e3cda87 100644
> --- a/fs/ext4/extents-test.c
> +++ b/fs/ext4/extents-test.c
> @@ -146,6 +146,7 @@ static void extents_kunit_exit(struct kunit *test)
>  	struct ext4_sb_info *sbi = sb->s_fs_info;
>  
>  	ext4_es_unregister_shrinker(sbi);
> +	deactivate_super(sbi->s_sb);
>  	kfree(sbi);
>  	kfree(k_ctx.k_ei);
>  	kfree(k_ctx.k_data);
> -- 
> 2.34.1

^ permalink raw reply

* Re: [PATCH v3 0/5] Fix some issues about ext4-test
From: Ritesh Harjani @ 2026-03-14 12:04 UTC (permalink / raw)
  To: Ye Bin; +Cc: jack, tytso, adilger.kernel, linux-ext4
In-Reply-To: <20260314074903.1314851-1-yebin@huaweicloud.com>

Ye Bin <yebin@huaweicloud.com> writes:

> From: Ye Bin <yebin10@huawei.com>
>
> Diff v3 vs v2:
> 1. Remove three patches from this patchset:
>   ext4: fix mballoc-test.c is not compiled when EXT4_KUNIT_TESTS=M
>   ext4: introduce EXPORT_SYMBOL_FOR_EXT4_TEST() helper
>   ext4: fix extents-test.c is not compiled when EXT4_KUNIT_TESTS=M
> 2. Fix 'sbi' leak as no "sbi->s_sb->s_op->put_super()" registered.
>

...and that this patch series is based on top of [1]
[1]: https://lore.kernel.org/linux-ext4/5bb9041471dab8ce870c191c19cbe4df57473be8.1772381213.git.ritesh.list@gmail.com/

I think you also missed to take few RBs from previous version. But never
mind, thanks for sending the fixes :)

-ritesh

> Diff v2 vs v1:
> 1. Fix compile warning when disable EXT4_KUNIT_TESTS for patch[1][3];
> 2. Remove reviewed-by tag for patch[1];
>
> Patch [1]-[2]:
> Decoupled mballoc-test and extents-test from ext4. Patch [1] does not
> have any changes compared to the previously released version, so the
> reviewed-by is added.
> Patch [3-7]:
> Bugfix for extents-test.c.
> Patch [8]:
> Bugfix for mballoc-test.c.
>
> Ye Bin (5):
>   ext4: call deactivate_super() in extents_kunit_exit()
>   ext4: fix miss unlock 'sb->s_umount' in extents_kunit_init()
>   ext4: fix the error handling process in extents_kunit_init).
>   ext4: fix possible null-ptr-deref in extents_kunit_exit()
>   ext4: fix possible null-ptr-deref in mbt_kunit_exit()
>
>  fs/ext4/extents-test.c | 54 +++++++++++++++++++++++++++++++-----------
>  fs/ext4/mballoc-test.c |  6 ++++-
>  2 files changed, 45 insertions(+), 15 deletions(-)
>
> -- 
> 2.34.1

^ permalink raw reply

* Re: [PATCH] ext4: avoid allocate block from corrupted group in ext4_mb_find_by_goal()
From: Ritesh Harjani @ 2026-03-14  8:39 UTC (permalink / raw)
  To: Ye Bin, tytso, adilger.kernel, linux-ext4; +Cc: jack
In-Reply-To: <20260302134619.3145520-1-yebin@huaweicloud.com>

Ye Bin <yebin@huaweicloud.com> writes:

> From: Ye Bin <yebin10@huawei.com>
>
> There's issue as follows:
> ...
> EXT4-fs (mmcblk0p1): Delayed block allocation failed for inode 206 at logical offset 0 with max blocks 1 with error 117
> EXT4-fs (mmcblk0p1): This should not happen!! Data will be lost
>
> EXT4-fs (mmcblk0p1): Delayed block allocation failed for inode 206 at logical offset 0 with max blocks 1 with error 117
> EXT4-fs (mmcblk0p1): This should not happen!! Data will be lost
>
> EXT4-fs (mmcblk0p1): Delayed block allocation failed for inode 206 at logical offset 0 with max blocks 1 with error 117
> EXT4-fs (mmcblk0p1): This should not happen!! Data will be lost
>
> EXT4-fs (mmcblk0p1): Delayed block allocation failed for inode 206 at logical offset 0 with max blocks 1 with error 117
> EXT4-fs (mmcblk0p1): This should not happen!! Data will be lost
>
> EXT4-fs (mmcblk0p1): Delayed block allocation failed for inode 2243 at logical offset 0 with max blocks 1 with error 117
> EXT4-fs (mmcblk0p1): This should not happen!! Data will be lost
>
> EXT4-fs (mmcblk0p1): Delayed block allocation failed for inode 2239 at logical offset 0 with max blocks 1 with error 117
> EXT4-fs (mmcblk0p1): This should not happen!! Data will be lost
>
> EXT4-fs (mmcblk0p1): error count since last fsck: 1
> EXT4-fs (mmcblk0p1): initial error at time 1765597433: ext4_mb_generate_buddy:760
> EXT4-fs (mmcblk0p1): last error at time 1765597433: ext4_mb_generate_buddy:760
> ...
>
> According to the log analysis, blocks are always requested from the
> corrupted block group. This may happen as follows:
> ext4_mb_find_by_goal
>   ext4_mb_load_buddy
>    ext4_mb_load_buddy_gfp
>      ext4_mb_init_cache
>       ext4_read_block_bitmap_nowait
>       ext4_wait_block_bitmap
>        ext4_validate_block_bitmap
>         if (!grp || EXT4_MB_GRP_BBITMAP_CORRUPT(grp))
>          return -EFSCORRUPTED; // There's no logs.
>  if (err)
>   return err;  // Will return error
> ext4_lock_group(ac->ac_sb, group);
>   if (unlikely(EXT4_MB_GRP_BBITMAP_CORRUPT(e4b->bd_info))) // Unreachable
>    goto out;
>
> After commit 9008a58e5dce ("ext4: make the bitmap read routines return
> real error codes") merged, Commit 163a203ddb36 ("ext4: mark block group
> as corrupt on block bitmap error") is no real solution for allocating
> blocks from corrupted block groups. This is because if
> 'EXT4_MB_GRP_BBITMAP_CORRUPT(e4b->bd_info)' is true, then
> 'ext4_mb_load_buddy()' may return an error. This means that the block
> allocation will fail.
> Therefore, check block group if corrupted when ext4_mb_load_buddy()
> returns error.
>
> Fixes: 163a203ddb36 ("ext4: mark block group as corrupt on block bitmap error")
> Fixes: 9008a58e5dce ("ext4: make the bitmap read routines return real error codes")
> Signed-off-by: Ye Bin <yebin10@huawei.com>
> ---
>  fs/ext4/mballoc.c | 6 +++++-
>  1 file changed, 5 insertions(+), 1 deletion(-)
>
> diff --git a/fs/ext4/mballoc.c b/fs/ext4/mballoc.c
> index e2341489f4d0..ffa6886de8a3 100644
> --- a/fs/ext4/mballoc.c
> +++ b/fs/ext4/mballoc.c
> @@ -2443,8 +2443,12 @@ int ext4_mb_find_by_goal(struct ext4_allocation_context *ac,
>  		return 0;
>  
>  	err = ext4_mb_load_buddy(ac->ac_sb, group, e4b);
> -	if (err)
> +	if (err) {
> +		if (EXT4_MB_GRP_BBITMAP_CORRUPT(e4b->bd_info) &&
> +		    !(ac->ac_flags & EXT4_MB_HINT_GOAL_ONLY))
> +			return 0;
>  		return err;
> +	}


So, if we had to load the buddy info and if the group's block bitmap was
marked as corrupted, then we always return error, instead of
seaching for free blocks in other block groups (even for non-goal-only
allocations).

This patch fixes that path..
Nice catch! Was this happening as part of some xfstests?


Feel free to add:
Reviewed-by: Ritesh Harjani (IBM) <ritesh.list@gmail.com>

^ permalink raw reply

* [Bug report][xfstests ext4/024 crash on ext4&fscrypt] kernel BUG at lib/list_debug.c:32!
From: Zorro Lang @ 2026-03-14  8:09 UTC (permalink / raw)
  To: linux-ext4, linux-fscrypt; +Cc: Eric Biggers

Hi,

I'm currently running regression tests across various filesystems on linux
v7.0-rc3+ (HEAD=399af66228cfd7df79dc360810b6b673000f8090) before releasing
new fstests version. During my testing, I hit a crash several times with
ext4 on aarch64 (didn't hit it on other arches).

The console output showed a kernel warning about crypto/crypto_engine.c at
first:
  [  858.822866] WARNING: crypto/crypto_engine.c:55 at crypto_finalize_request+0x2bc/0x368 [crypto_engine], CPU#1: 15820000.crypto/587

then showed a bug at lib/list_debug.c and bad non-executable memory access:
  [  858.834134] kernel BUG at lib/list_debug.c:32!
  ...
  [  859.423056] Unable to handle kernel execute from non-executable memory at virtual address ffff80009c6a68b0
  ...

at last, it hit below warning when tried to make kdump:
  [  860.748125] Some CPUs may be stale, kdump will be unreliable.
  [  860.748294] WARNING: arch/arm64/kernel/machine_kexec.c:174 at machine_kexec+0x60/0x3c0, CPU#7: kworker/u50:4/65459

About more details please refer to the completed console output [1].

Thanks,
Zorro

[1]
[  855.079746] run fstests ext4/024 at 2026-03-13 18:47:51 
[  857.441234] EXT4-fs (nvme0n1p1): mounted filesystem 667e4072-1e6f-4c22-bb65-0155564e97b5 r/w with ordered data mode. Quota mode: none. 
[  857.482774] xfs_io (pid 71261) is setting deprecated v1 encryption policy; recommend upgrading to v2. 
[  857.537679] EXT4-fs (nvme0n1p1): unmounting filesystem 667e4072-1e6f-4c22-bb65-0155564e97b5. 
[  858.013188] EXT4-fs (nvme0n1p1): mounted filesystem f0ce627b-e729-4e24-be1c-311bc20e925c r/w with ordered data mode. Quota mode: none. 
[  858.767578] fscrypt: AES-256-CBC-CTS using implementation "cts-cbc-aes-ce" 
[  858.768276] fscrypt: AES-256-XTS using implementation "xts-aes-tegra" 
[  858.822156] fscrypt (nvme0n1p1, inode 1835010): Encryption failed for data unit 0: -115 
[  858.822525] ------------[ cut here ]------------ 
[  858.822825] ext4_bio_write_folio: ret = -115 
[  858.822866] WARNING: crypto/crypto_engine.c:55 at crypto_finalize_request+0x2bc/0x368 [crypto_engine], CPU#1: 15820000.crypto/587 
[  858.823370] fscrypt (nvme0n1p1, inode 1835010): Encryption failed for data unit 0: -115 
[  858.823433] Modules linked in: ext4 
[  858.823838] ext4_bio_write_folio: ret = -115 
[  858.823881]  mbcache jbd2 bnep btusb btrtl rtw88_8822ce rtw88_8822c vfat btintel rtw88_pci btbcm rtw88_core btmtk fat bluetooth mac80211 crc16 cfg80211 tegra194_cpufreq arm_dsu_pmu at24 rfkill tegra_bpmp_thermal fuse loop xfs ina3221 ucsi_ccg tegra_se crypto_engine tegra_drm drm_dp_aux_bus drm_display_helper cec aquantia nvme_tcp 
[  858.824424]  8-page vmalloc region starting at 0xffff80009c6a0000 allocated at copy_process+0x264/0x3e58 
[  858.824571]  mmc_block rpmb_core crc_itu_t nvme nvme_fabrics 
[  858.825681] list_add corruption. prev->next should be next (ffff0000d73a2d00), but was 0000000000000000. (prev=ffff80009c6a6950). 
[  858.826033]  xhci_tegra 
[  858.826910] ------------[ cut here ]------------ 
[  858.831597]  lm90 
[  858.834134] kernel BUG at lib/list_debug.c:32! 
[  858.838686]  nvme_core 
[  858.840859] Internal error: Oops - BUG: 00000000f2000800 [#1]  SMP 
[  858.845072]  nvme_keyring 
[  858.847438] Modules linked in: 
[  858.853734]  dwmac_tegra 
[  858.856358]  ext4 
[  858.859509]  stmmac_platform 
[  858.862134]  mbcache 
[  858.864233]  nvme_auth 
[  858.867122]  jbd2 
[  858.869484]  ghash_ce 
[  858.871847]  bnep 
[  858.873946]  stmmac 
[  858.876307]  btusb 
[  858.878409]  hkdf 
[  858.880335]  btrtl 
[  858.882435]  gpio_keys 
[  858.884534]  rtw88_8822ce 
[  858.886547]  pwm_fan 
[  858.888908]  rtw88_8822c 
[  858.891534]  sdhci_tegra 
[  858.893896]  vfat 
[  858.896520]  sdhci_pltfm 
[  858.899058]  btintel 
[  858.900983]  rtc_tegra 
[  858.903608]  rtw88_pci 
[  858.905884]  sdhci 
[  858.908070]  btbcm 
[  858.910434]  pcs_xpcs 
[  858.912533]  rtw88_core 
[  858.914635]  cqhci 
[  858.916996]  btmtk 
[  858.919622]  i2c_tegra_bpmp 
[  858.921634]  fat 
[  858.923732]  phy_tegra_xusb 
[  858.926534]  bluetooth 
[  858.928284]  host1x 
[  858.930997]  mac80211 
[  858.933359]  tegra186_gpc_dma 
[  858.935283]  crc16 
[  858.937646]  mmc_core 
[  858.940621]  cfg80211 
[  858.942634]  spi_tegra114 
[  858.944822]  tegra194_cpufreq 
[  858.947183]  ramoops 
[  858.949546]  arm_dsu_pmu 
[  858.952608]  i2c_tegra 
[  858.954970]  at24 
[  858.957597]  reed_solomon 
[  858.959783]  rfkill 
[  858.961797]  sunrpc 
[  858.964335]  tegra_bpmp_thermal 
[  858.966258]  dm_mirror 
[  858.968272]  fuse 
[  858.971247]  dm_region_hash 
[  858.973609]  loop 
[  858.975708]  dm_log 
[  858.978508]  xfs 
[  858.980522]  dm_mod 
[  858.982533]  ina3221 
[  858.984197]  i2c_dev 
[  858.986210]  ucsi_ccg 
[  858.988484]  nfnetlink 
[  858.990759]  tegra_se 
[  858.993121]  
[  858.995396]  crypto_engine 
[  858.997767] CPU: 1 UID: 0 PID: 587 Comm: 15820000.crypto Kdump: loaded Tainted: G        W           7.0.0-rc3+ #1 PREEMPT(full)  
[  858.999334]  tegra_drm 
[  859.002135] Tainted: [W]=WARN 
[  859.013507]  drm_dp_aux_bus 
[  859.015872] Hardware name: NVIDIA NVIDIA Jetson AGX Orin Developer Kit/Jetson, BIOS 36.5.0-gcid-41890718 08/27/2025 
[  859.018845]  drm_display_helper 
[  859.021560] pstate: 40400009 (nZcv daif +PAN -UAO -TCO -DIT -SSBS BTYPE=--) 
[  859.031797]  cec 
[  859.034947] pc : crypto_finalize_request+0x2bc/0x368 [crypto_engine] 
[  859.041859]  aquantia 
[  859.043609] lr : crypto_finalize_request+0x13c/0x368 [crypto_engine] 
[  859.049996]  nvme_tcp 
[  859.052359] sp : ffff800085ff7b80 
[  859.058484]  mmc_block 
[  859.060845] x29: ffff800085ff7b80 
[  859.064083]  rpmb_core 
[  859.066446]  x28: ffff000105a5f508 
[  859.069684]  crc_itu_t 
[  859.072046]  x27: ffff000105a5f530 
[  859.075459]  nvme 
[  859.077821]  
[  859.081146]  nvme_fabrics 
[  859.083246] x26: ffff80009c6a6960 
[  859.084647]  xhci_tegra 
[  859.087184]  x25: ffff0000d1243010 
[  859.090597]  lm90 
[  859.093221]  x24: ffff80009c6a6930 
[  859.096633]  nvme_core 
[  859.098733]  
[  859.102146]  nvme_keyring 
[  859.104508] x23: 0000000000000000 
[  859.106083]  dwmac_tegra 
[  859.108621]  x22: ffff0000d2f831c0 
[  859.112034]  stmmac_platform 
[  859.114658]  x21: 0000000000000000 
[  859.117810]  nvme_auth 
[  859.120696]  
[  859.124109]  ghash_ce 
[  859.126296] x20: ffff80009c6a6930 
[  859.127783]  stmmac 
[  859.130059]  x19: ffff0000d73a2c80 
[  859.133472]  hkdf 
[  859.135483]  x18: 0000000000000000 
[  859.138896]  gpio_keys 
[  859.140996]  
[  859.144409]  pwm_fan 
[  859.146771] x17: ffffc8ca055808bc 
[  859.148346]  sdhci_tegra 
[  859.150446]  x16: ffffc8ca067031e8 
[  859.153859]  sdhci_pltfm 
[  859.156395]  x15: ffffc8ca0462109c 
[  859.159633]  rtc_tegra 
[  859.162259]  
[  859.165496]  sdhci 
[  859.167859] x14: ffffc8ca046206e4 
[  859.169435]  pcs_xpcs 
[  859.171447]  x13: ffffc8ca03b4e4f0 
[  859.174860]  cqhci 
[  859.177221]  x12: ffff60001ae74598 
[  859.180459]  i2c_tegra_bpmp 
[  859.182558]  
[  859.185972]  phy_tegra_xusb 
[  859.188859] x11: 1fffe0001ae74597 
[  859.190433]  host1x 
[  859.193320]  x10: ffff60001ae74597 
[  859.196559]  tegra186_gpc_dma 
[  859.198658]  x9 : ffffc8ca0670324c 
[  859.201985]  mmc_core 
[  859.205134]  
[  859.208371]  spi_tegra114 
[  859.210733] x8 : ffff800085ff79f0 
[  859.212307]  ramoops 
[  859.214845]  x7 : 0000000000000000 
[  859.218084]  i2c_tegra 
[  859.220359]  x6 : ffff800085ff7b10 
[  859.223684]  reed_solomon 
[  859.226046]  
[  859.229371]  sunrpc 
[  859.231996] x5 : ffff800085ff7a50 
[  859.233571]  dm_mirror 
[  859.235584]  x4 : 1fffe0001a5f0639 
[  859.238909]  dm_region_hash 
[  859.241184]  x3 : 1fffe0001a5f0639 
[  859.244508]  dm_log 
[  859.247396]  
[  859.250722]  dm_mod 
[  859.252820] x2 : 0000000000000000 
[  859.254309]  i2c_dev 
[  859.256320]  x1 : 0000000000000003 
[  859.259646]  nfnetlink 
[  859.261920]  x0 : 0000000000000000 
[  859.265334]  
[  859.267696]  
[  859.270940] CPU: 7 UID: 0 PID: 65459 Comm: kworker/u50:4 Kdump: loaded Tainted: G        W           7.0.0-rc3+ #1 PREEMPT(full)  
[  859.272510] Call trace: 
[  859.274085] Tainted: [W]=WARN 
[  859.285634]  crypto_finalize_request+0x2bc/0x368 [crypto_engine] (P) 
[  859.288084] Hardware name: NVIDIA NVIDIA Jetson AGX Orin Developer Kit/Jetson, BIOS 36.5.0-gcid-41890718 08/27/2025 
[  859.291234]  crypto_finalize_skcipher_request+0x1c/0x30 [crypto_engine] 
[  859.297799] Workqueue: writeback wb_workfn 
[  859.308120]  tegra_aes_do_one_req+0x5ec/0xe40 [tegra_se] 
[  859.314859]  (flush-259:0) 
[  859.319058]  crypto_pump_requests.constprop.0+0x230/0x478 [crypto_engine] 
[  859.324484]  
[  859.327283]  crypto_pump_work+0x1c/0x38 [crypto_engine] 
[  859.333937] pstate: 604000c9 (nZCv daIF +PAN -UAO -TCO -DIT -SSBS BTYPE=--) 
[  859.335508]  kthread_worker_fn+0x344/0xdd0 
[  859.340583] pc : __list_add_valid_or_report+0x104/0x180 
[  859.347671]  kthread+0x2ec/0x390 
[  859.351785] lr : __list_add_valid_or_report+0x104/0x180 
[  859.356859]  ret_from_fork+0x10/0x20 
[  859.360096] sp : ffff80009c6a66d0 
[  859.365172] irq event stamp: 174 
[  859.368846] x29: ffff80009c6a66d0 
[  859.372258] hardirqs last  enabled at (173): [<ffffc8ca067032a8>] _raw_spin_unlock_irqrestore+0xc0/0x160 
[  859.375671]  x28: 0000000000000000 
[  859.379082] hardirqs last disabled at (174): [<ffffc8ca066db330>] el1_brk64+0x20/0x58 
[  859.388534]  x27: ffff80009c6a6870 
[  859.391771] softirqs last  enabled at (0): [<ffffc8ca03cd770c>] copy_process+0x115c/0x3e58 
[  859.399470]  
[  859.402796] softirqs last disabled at (0): [<0000000000000000>] 0x0 
[  859.411196] x26: ffff80009c6a68f0 
[  859.412771] ---[ end trace 0000000000000000 ]--- 
[  859.418896]  x25: fffffdffea00ed80 
[  859.423056] Unable to handle kernel execute from non-executable memory at virtual address ffff80009c6a68b0 
[  859.426860]  x24: 00000000ffffff8d 
[  859.426865] x23: ffff0000d73a2d08 x22: 1ffff000138d4d2a x21: ffff80009c6a6950 
[  859.426872] x20: ffff80009c6a6950 x19: ffff0000d73a2d00 x18: ffff0000e7d07ee0 
[  859.426877] x17: 0000000000000000 x16: 0000000000000000 x15: 00000000006f7470 
[  859.426883] x14: 0000000000000000 x13: 736369726261665f x12: ffff6001a8588ae3 
[  859.426889] x11: 1fffe001a8588ae2 x10: ffff6001a8588ae2 x9 : ffffc8ca03eb2250 
[  859.426895] x8 : 00009ffe57a7751e x7 : ffff000d42c45713 x6 : 0000000000000001 
[  859.426901] x5 : ffff000d42c45710 x4 : 1fffe0002a0f2001 x3 : 0000000000000000 
[  859.426907] x2 : 0000000000000000 x1 : ffff000150790000 x0 : 0000000000000075 
[  859.426913] Call trace: 
[  859.426915]  __list_add_valid_or_report+0x104/0x180 (P) 
[  859.426921]  crypto_enqueue_request+0xbc/0x230 
[  859.426930]  crypto_transfer_request.constprop.0+0x6c/0x120 [crypto_engine] 
[  859.430320] KASAN: probably user-memory-access in range [0x00000004e3534580-0x00000004e3534587] 
[  859.439808]  crypto_transfer_skcipher_request_to_engine+0x1c/0x38 [crypto_engine] 
[  859.439816]  tegra_aes_crypt+0x17c/0x308 [tegra_se] 
[  859.443046] Mem abort info: 
[  859.450307]  tegra_aes_encrypt+0x1c/0x28 [tegra_se] 
[  859.450316]  crypto_skcipher_encrypt+0xe0/0x158 
[  859.457490]   ESR = 0x000000008600000f 
[  859.464835]  fscrypt_crypt_data_unit+0x21c/0x2d0 
[  859.472193]   EC = 0x21: IABT (current EL), IL = 32 bits 
[  859.479359]  fscrypt_encrypt_pagecache_blocks+0x1e8/0x350 
[  859.479366]  ext4_bio_write_folio+0x8e4/0x1128 [ext4] 
[  859.486713]   SET = 0, FnV = 0 
[  859.493884]  mpage_submit_folio+0x158/0x1f0 [ext4] 
[  859.501241]   EA = 0, S1PTW = 0 
[  859.503859]  mpage_process_page_bufs+0x1c0/0x560 [ext4] 
[  859.509117]   FSC = 0x0f: level 3 permission fault 
[  859.513571]  mpage_prepare_extent_to_map+0x954/0xf10 [ext4] 
[  859.520492] swapper pgtable: 4k pages, 48-bit VAs, pgdp=0000000afefb4000 
[  859.529234]  ext4_do_writepages+0x720/0x1558 [ext4] 
[  859.536683] [ffff80009c6a68b0] pgd=10000001084d7403 
[  859.541484]  ext4_writepages+0x198/0x2d0 [ext4] 
[  859.544372] , p4d=10000001084d7403 
[  859.549184]  do_writepages+0x204/0x4c0 
[  859.553734] , pud=10000001084dd403 
[  859.557672]  __writeback_single_inode+0xf4/0x980 
[  859.557680]  writeback_sb_inodes+0x598/0xe38 
[  859.562222] , pmd=1000000138ba0403 
[  859.567557]  wb_writeback+0x17c/0xb68 
[  859.567564]  wb_do_writeback+0x228/0x8e0 
[  859.572899] , pte=00e800011ef66f03 
[  859.578145]  wb_workfn+0x74/0x1b0 
[  859.578151]  process_one_work+0x774/0x12d0 
[  859.581297]  
[  859.586197]  worker_thread+0x434/0xca0 
[  859.586204]  kthread+0x2ec/0x390 
[  859.676873]  ret_from_fork+0x10/0x20 
[  859.679392] Code: aa1303e1 aa1403e3 91050000 97b22a98 (d4210000)  
[  859.685612] SMP: stopping secondary CPUs 
��ERROR:   Unexpected affinity info state. Unhandled Exception from EL0 
x0             = 0xUnhandled Exception in EL3. 
x30            = 0x00000000500009e0 
x0             = 0x0000000000000000 
x1             = 0x00000000500175f8 
x2             = 0x0000000000000000 
x3             = 0x000000005000123c 
x4             = 0x0000000050014f43 
x5             = 0x0000000030cd183b 
x6             = 0x0000000050014f46 
x7             = 0x0000000000000000 
x8             = 0x0000000000000000 
x9             = 0x00000000500001c4 
x10            = 0x0000000050001920 
x11            = 0x0000000000000000 
x12            = 0x0000000000000000 
x13            = 0x0000000000000000 
x14            = 0x0000000000000000 
x15            = 0x0000000000000000 
x16            = 0x0000000000000000 
x17            = 0x0000000050000ca8 
x18            = 0x0000000000000001 
x19            = 0x0000000050000154 
x20            = 0x000000000000000b 
x21            = 0x0000000000000000 
x22            = 0x0000000000000000 
x23            = 0x0000000000000000 
x24            = 0x0000000000000000 
x25            = 0x0000000000000000 
x26            = 0x0000000000000000 
x27            = 0x0000000000000000 
x28            = 0x0000000000000000 
x29            = 0x000000005001adf0 
scr_el3        = 0x0000000000030638 
sctlr_el3      = 0x00000000b0cd183f 
cptr_el3       = 0x0000000000000000 
tcr_el3        = 0x0000000080823518 
daif           = 0x00000000000003c0 
mair_el3       = 0x00000000004404ff 
spsr_el3       = 0x00000000800002cd 
elr_el3        = 0x000000005000123c 
ttbr0_el3      = 0x0000000050025581 
esr_el3        = 0x0000000096000007 
far_el3        = 0x0000000000000000 
spsr_el1       = 0x0000000000000000 
elr_el1        = 0x0000000000000000 
spsr_abt       = 0x0000000000000000 
spsr_und       = 0x0000000000000000 
spsr_irq       = 0x0000000000000000 
spsr_fiq       = 0x0000000000000000 
sctlr_el1      = 0x0000000030d50838 
actlr_el1      = 0x0000000000000000 
cpacr_el1      = 0x0000000000000000 
csselr_el1     = 0x0000000000000000 
sp_el1         = 0x0000000000000000 
esr_el1        = 0x0000000000000000 
ttbr0_el1      = 0x0000000000000000 
ttbr1_el1      = 0x0000000000000000 
mair_el1       = 0x44e048e000098aa4 
amair_el1      = 0x0000000000000000 
tcr_el1        = 0x0000000000000000 
tpidr_el1      = 0x0000000000000000 
tpidr_el0      = 0x0000000000000000 
tpidrro_el0    = 0x0000000000000000 
par_el1        = 0x0000000000000800 
mpidr_el1      = 0x0000000081020300 
afsr0_el1      = 0x0000000000000000 
afsr1_el1      = 0x0000000000000000 
contextidr_el1 = 0x0000000000000000 
vbar_el1       = 0x0000000000000000 
cntp_ctl_el0   = 0x0000000000000000 
cntp_cval_el0  = 0x3f96188777cf8b0f 
cntv_ctl_el0   = 0x0000000000000000 
cntv_cval_el0  = 0x0bec5777baf3e8fa 
cntkctl_el1    = 0x0000000000000002 
sp_el0         = 0x000000005001ade0 
isr_el1        = 0x0000000000000000 
cpuectlr_el1   = 0xa000400b40543000 
gicd_ispendr regs (Offsets 0x200 - 0x278) 
 Offset:			value 
0000000000000200:		0x0000000000000000 
0000000000000204:		0x0000000000000000 
0000000000000208:		0x0000000000000000 
000000000000020c:		0x0000000000000000 
0000000000000210:		0x0000000000000000 
0000000000000214:		0x0000000000000000 
0000000000000218:		0x0000000000000000 
000000000000021c:		0x0000000000000000 
0000000000000220:		0x0000000000000000 
0000000000000224:		0x0000000000000000 
0000000000000228:		0x0000000000000000 
000000000000022c:		0x0000000000000000 
0000000000000230:		0x0000000000000000 
0000000000000234:		0x0000000000000001 
0000000000000238:		0x0000000000000000 
000000000000023c:		0x0000000000000000 
0000000000000240:		0x0000000000000000 
0000000000000244:		0x0000000000000000 
0000000000000248:		0x0000000000000000 
000000000000024c:		0x0000000000000000 
0000000000000250:		0x0000000000000000 
0000000000000254:		0x0000000000000000 
0000000000000258:		0x0000000000000000 
000000000000025c:		0x0000000000000000 
0000000000000260:		0x0000000000000000 
0000000000000264:		0x0000000000000000 
0000000000000268:		0x0000000000000000 
000000000000026c:		0x0000000000000000 
0000000000000270:		0x0000000000000000 
0000000000000274:		0x0000000000000000 
0000000000000278:		0x0000000000000000 
000000000000027c:		0x0000000000000000 �[  860.746815] SMP: failed to stop secondary CPUs 1 
[  860.747870] Starting crashdump kernel... 
[  860.747995] ------------[ cut here ]------------ 
[  860.748125] Some CPUs may be stale, kdump will be unreliable. 
[  860.748294] WARNING: arch/arm64/kernel/machine_kexec.c:174 at machine_kexec+0x60/0x3c0, CPU#7: kworker/u50:4/65459 
[  860.748600] Modules linked in: ext4 mbcache jbd2 bnep btusb btrtl rtw88_8822ce rtw88_8822c vfat btintel rtw88_pci btbcm rtw88_core btmtk fat bluetooth mac80211 crc16 cfg80211 tegra194_cpufreq arm_dsu_pmu at24 rfkill tegra_bpmp_thermal fuse loop xfs ina3221 ucsi_ccg tegra_se crypto_engine tegra_drm drm_dp_aux_bus drm_display_helper cec aquantia nvme_tcp mmc_block rpmb_core crc_itu_t nvme nvme_fabrics xhci_tegra lm90 nvme_core nvme_keyring dwmac_tegra stmmac_platform nvme_auth ghash_ce stmmac hkdf gpio_keys pwm_fan sdhci_tegra sdhci_pltfm rtc_tegra sdhci pcs_xpcs cqhci i2c_tegra_bpmp phy_tegra_xusb host1x tegra186_gpc_dma mmc_core spi_tegra114 ramoops i2c_tegra reed_solomon sunrpc dm_mirror dm_region_hash dm_log dm_mod i2c_dev nfnetlink 
[  860.752483] CPU: 7 UID: 0 PID: 65459 Comm: kworker/u50:4 Kdump: loaded Tainted: G        W           7.0.0-rc3+ #1 PREEMPT(full)  
[  860.761541] Tainted: [W]=WARN 
[  860.764682] Hardware name: NVIDIA NVIDIA Jetson AGX Orin Developer Kit/Jetson, BIOS 36.5.0-gcid-41890718 08/27/2025 
[  860.775191] Workqueue: writeback wb_workfn (flush-259:0) 
[  860.780699] pstate: 604003c9 (nZCv DAIF +PAN -UAO -TCO -DIT -SSBS BTYPE=--) 
[  860.787784] pc : machine_kexec+0x60/0x3c0 
[  860.791981] lr : machine_kexec+0x60/0x3c0 
[  860.796182] sp : ffff80009c6a6210 
[  860.799595] x29: ffff80009c6a6210 x28: ffff000150790000 x27: ffff80009c6a6870 
[  860.806946] x26: ffff80009c6a68f0 x25: ffffc8ca0b839280 x24: ffffc8ca0b839000 
[  860.814298] x23: ffff80009c6a6290 x22: ffffc8ca0b839200 x21: 1ffff000138d4c4a 
[  860.821560] x20: ffff000f8e369000 x19: ffffc8ca06759040 x18: ffff0000e7d07ee0 
[  860.828821] x17: 0000000000000000 x16: 0000000000000000 x15: 0772076e07750720 
[  860.835998] x14: 076507620720076c x13: fffffffffff20120 x12: ffff6001a8588ae3 
[  860.843348] x11: 1fffe001a8588ae2 x10: ffff6001a8588ae2 x9 : ffffc8ca03eb2250 
[  860.850521] x8 : 00009ffe57a7751e x7 : ffff000d42c45713 x6 : 0000000000000001 
[  860.857786] x5 : ffff000d42c45710 x4 : 1fffe0002a0f2001 x3 : dfff800000000000 
[  860.864784] x2 : 0000000000000000 x1 : 0000000000000000 x0 : ffff000150790000 
[  860.872047] Call trace: 
[  860.874495]  machine_kexec+0x60/0x3c0 (P) 
[  860.878696]  __crash_kexec+0x188/0x250 
[  860.882458]  crash_kexec+0x3c/0x58 
[  860.885870]  die+0x120/0x218 
[  860.888581]  bug_brk_handler+0x8c/0x1a0 
[  860.892519]  call_el1_break_hook+0x74/0xd8 
[  860.896546]  do_el1_brk64+0x2c/0x60 
[  860.900220]  el1_brk64+0x38/0x58 
[  860.903456]  el1h_64_sync_handler+0x6c/0xb0 
[  860.907658]  el1h_64_sync+0x80/0x88 
[  860.911157]  __list_add_valid_or_report+0x104/0x180 (P) 
[  860.916234]  crypto_enqueue_request+0xbc/0x230 
[  860.920695]  crypto_transfer_request.constprop.0+0x6c/0x120 [crypto_engine] 
[  860.927609]  crypto_transfer_skcipher_request_to_engine+0x1c/0x38 [crypto_engine] 
[  860.935049]  tegra_aes_crypt+0x17c/0x308 [tegra_se] 
[  860.939682]  tegra_aes_encrypt+0x1c/0x28 [tegra_se] 
[  860.944496]  crypto_skcipher_encrypt+0xe0/0x158 
[  860.949045]  fscrypt_crypt_data_unit+0x21c/0x2d0 
[  860.953771]  fscrypt_encrypt_pagecache_blocks+0x1e8/0x350 
[  860.959195]  ext4_bio_write_folio+0x8e4/0x1128 [ext4] 
[  860.964361]  mpage_submit_folio+0x158/0x1f0 [ext4] 
[  860.968997]  mpage_process_page_bufs+0x1c0/0x560 [ext4] 
[  860.974246]  mpage_prepare_extent_to_map+0x954/0xf10 [ext4] 
[  860.979847]  ext4_do_writepages+0x720/0x1558 [ext4] 
[  860.984833]  ext4_writepages+0x198/0x2d0 [ext4] 
[  860.989558]  do_writepages+0x204/0x4c0 
[  860.993408]  __writeback_single_inode+0xf4/0x980 
[  860.998132]  writeback_sb_inodes+0x598/0xe38 
[  861.002508]  wb_writeback+0x17c/0xb68 
[  861.006094]  wb_do_writeback+0x228/0x8e0 
[  861.010033]  wb_workfn+0x74/0x1b0 
[  861.013357]  process_one_work+0x774/0x12d0 
[  861.017382]  worker_thread+0x434/0xca0 
[  861.021233]  kthread+0x2ec/0x390 
[  861.024645]  ret_from_fork+0x10/0x20 
[  861.028233] irq event stamp: 83680 
[  861.031469] hardirqs last  enabled at (83679): [<ffffc8ca06702760>] _raw_spin_unlock_irq+0x38/0xb0 
[  861.040486] hardirqs last disabled at (83680): [<ffffc8ca06702a08>] _raw_spin_lock_irqsave+0x40/0x100 
[  861.049762] softirqs last  enabled at (83500): [<ffffc8ca03b4fa68>] put_cpu_fpsimd_context+0x18/0x60 
[  861.058688] softirqs last disabled at (83498): [<ffffc8ca03b4fa08>] get_cpu_fpsimd_context+0x18/0x60 
[  861.067613] ---[ end trace 0000000000000000 ]--- 
[  861.072160] Bye! 
[-- MARK -- Fri Mar 13 22:50:00 2026] 


^ permalink raw reply

* [PATCH v2 2/3] ext4: fix mballoc-test.c is not compiled when EXT4_KUNIT_TESTS=M
From: Ye Bin @ 2026-03-14  7:52 UTC (permalink / raw)
  To: tytso, adilger.kernel, linux-ext4; +Cc: jack
In-Reply-To: <20260314075258.1317579-1-yebin@huaweicloud.com>

From: Ye Bin <yebin10@huawei.com>

Now, only EXT4_KUNIT_TESTS=Y testcase will be compiled in 'mballoc.c'.
To solve this issue, the ext4 test code needs to be decoupled. The ext4
test module is compiled into a separate module.

Reported-by: ChenXiaoSong <chenxiaosong@kylinos.cn>
Closes: https://patchwork.kernel.org/project/cifs-client/patch/20260118091313.1988168-2-chenxiaosong.chenxiaosong@linux.dev/
Fixes: 7c9fa399a369 ("ext4: add first unit test for ext4_mb_new_blocks_simple in mballoc")
Signed-off-by: Ye Bin <yebin10@huawei.com>
---
 fs/ext4/Makefile       |   4 +-
 fs/ext4/mballoc-test.c |  81 ++++++++++++++++----------------
 fs/ext4/mballoc.c      | 102 +++++++++++++++++++++++++++++++++++++++--
 fs/ext4/mballoc.h      |  30 ++++++++++++
 4 files changed, 172 insertions(+), 45 deletions(-)

diff --git a/fs/ext4/Makefile b/fs/ext4/Makefile
index 72206a292676..d836c3fe311b 100644
--- a/fs/ext4/Makefile
+++ b/fs/ext4/Makefile
@@ -14,7 +14,7 @@ ext4-y	:= balloc.o bitmap.o block_validity.o dir.o ext4_jbd2.o extents.o \
 
 ext4-$(CONFIG_EXT4_FS_POSIX_ACL)	+= acl.o
 ext4-$(CONFIG_EXT4_FS_SECURITY)		+= xattr_security.o
-ext4-inode-test-objs			+= inode-test.o
-obj-$(CONFIG_EXT4_KUNIT_TESTS)		+= ext4-inode-test.o
+ext4-test-objs				+= inode-test.o mballoc-test.o
+obj-$(CONFIG_EXT4_KUNIT_TESTS)		+= ext4-test.o
 ext4-$(CONFIG_FS_VERITY)		+= verity.o
 ext4-$(CONFIG_FS_ENCRYPTION)		+= crypto.o
diff --git a/fs/ext4/mballoc-test.c b/fs/ext4/mballoc-test.c
index b9f22e3a8d5c..c75b91ae0cf0 100644
--- a/fs/ext4/mballoc-test.c
+++ b/fs/ext4/mballoc-test.c
@@ -8,6 +8,7 @@
 #include <linux/random.h>
 
 #include "ext4.h"
+#include "mballoc.h"
 
 struct mbt_grp_ctx {
 	struct buffer_head bitmap_bh;
@@ -336,7 +337,7 @@ ext4_mb_mark_context_stub(handle_t *handle, struct super_block *sb, bool state,
 	if (state)
 		mb_set_bits(bitmap_bh->b_data, blkoff, len);
 	else
-		mb_clear_bits(bitmap_bh->b_data, blkoff, len);
+		mb_clear_bits_test(bitmap_bh->b_data, blkoff, len);
 
 	return 0;
 }
@@ -413,14 +414,14 @@ static void test_new_blocks_simple(struct kunit *test)
 
 	/* get block at goal */
 	ar.goal = ext4_group_first_block_no(sb, goal_group);
-	found = ext4_mb_new_blocks_simple(&ar, &err);
+	found = ext4_mb_new_blocks_simple_test(&ar, &err);
 	KUNIT_ASSERT_EQ_MSG(test, ar.goal, found,
 		"failed to alloc block at goal, expected %llu found %llu",
 		ar.goal, found);
 
 	/* get block after goal in goal group */
 	ar.goal = ext4_group_first_block_no(sb, goal_group);
-	found = ext4_mb_new_blocks_simple(&ar, &err);
+	found = ext4_mb_new_blocks_simple_test(&ar, &err);
 	KUNIT_ASSERT_EQ_MSG(test, ar.goal + EXT4_C2B(sbi, 1), found,
 		"failed to alloc block after goal in goal group, expected %llu found %llu",
 		ar.goal + 1, found);
@@ -428,7 +429,7 @@ static void test_new_blocks_simple(struct kunit *test)
 	/* get block after goal group */
 	mbt_ctx_mark_used(sb, goal_group, 0, EXT4_CLUSTERS_PER_GROUP(sb));
 	ar.goal = ext4_group_first_block_no(sb, goal_group);
-	found = ext4_mb_new_blocks_simple(&ar, &err);
+	found = ext4_mb_new_blocks_simple_test(&ar, &err);
 	KUNIT_ASSERT_EQ_MSG(test,
 		ext4_group_first_block_no(sb, goal_group + 1), found,
 		"failed to alloc block after goal group, expected %llu found %llu",
@@ -438,7 +439,7 @@ static void test_new_blocks_simple(struct kunit *test)
 	for (i = goal_group; i < ext4_get_groups_count(sb); i++)
 		mbt_ctx_mark_used(sb, i, 0, EXT4_CLUSTERS_PER_GROUP(sb));
 	ar.goal = ext4_group_first_block_no(sb, goal_group);
-	found = ext4_mb_new_blocks_simple(&ar, &err);
+	found = ext4_mb_new_blocks_simple_test(&ar, &err);
 	KUNIT_ASSERT_EQ_MSG(test,
 		ext4_group_first_block_no(sb, 0) + EXT4_C2B(sbi, 1), found,
 		"failed to alloc block before goal group, expected %llu found %llu",
@@ -448,7 +449,7 @@ static void test_new_blocks_simple(struct kunit *test)
 	for (i = 0; i < ext4_get_groups_count(sb); i++)
 		mbt_ctx_mark_used(sb, i, 0, EXT4_CLUSTERS_PER_GROUP(sb));
 	ar.goal = ext4_group_first_block_no(sb, goal_group);
-	found = ext4_mb_new_blocks_simple(&ar, &err);
+	found = ext4_mb_new_blocks_simple_test(&ar, &err);
 	KUNIT_ASSERT_NE_MSG(test, err, 0,
 		"unexpectedly get block when no block is available");
 }
@@ -492,16 +493,16 @@ validate_free_blocks_simple(struct kunit *test, struct super_block *sb,
 			continue;
 
 		bitmap = mbt_ctx_bitmap(sb, i);
-		bit = mb_find_next_zero_bit(bitmap, max, 0);
+		bit = mb_find_next_zero_bit_test(bitmap, max, 0);
 		KUNIT_ASSERT_EQ_MSG(test, bit, max,
 				    "free block on unexpected group %d", i);
 	}
 
 	bitmap = mbt_ctx_bitmap(sb, goal_group);
-	bit = mb_find_next_zero_bit(bitmap, max, 0);
+	bit = mb_find_next_zero_bit_test(bitmap, max, 0);
 	KUNIT_ASSERT_EQ(test, bit, start);
 
-	bit = mb_find_next_bit(bitmap, max, bit + 1);
+	bit = mb_find_next_bit_test(bitmap, max, bit + 1);
 	KUNIT_ASSERT_EQ(test, bit, start + len);
 }
 
@@ -524,7 +525,7 @@ test_free_blocks_simple_range(struct kunit *test, ext4_group_t goal_group,
 
 	block = ext4_group_first_block_no(sb, goal_group) +
 		EXT4_C2B(sbi, start);
-	ext4_free_blocks_simple(inode, block, len);
+	ext4_free_blocks_simple_test(inode, block, len);
 	validate_free_blocks_simple(test, sb, goal_group, start, len);
 	mbt_ctx_mark_used(sb, goal_group, 0, EXT4_CLUSTERS_PER_GROUP(sb));
 }
@@ -566,15 +567,15 @@ test_mark_diskspace_used_range(struct kunit *test,
 
 	bitmap = mbt_ctx_bitmap(sb, TEST_GOAL_GROUP);
 	memset(bitmap, 0, sb->s_blocksize);
-	ret = ext4_mb_mark_diskspace_used(ac, NULL);
+	ret = ext4_mb_mark_diskspace_used_test(ac, NULL);
 	KUNIT_ASSERT_EQ(test, ret, 0);
 
 	max = EXT4_CLUSTERS_PER_GROUP(sb);
-	i = mb_find_next_bit(bitmap, max, 0);
+	i = mb_find_next_bit_test(bitmap, max, 0);
 	KUNIT_ASSERT_EQ(test, i, start);
-	i = mb_find_next_zero_bit(bitmap, max, i + 1);
+	i = mb_find_next_zero_bit_test(bitmap, max, i + 1);
 	KUNIT_ASSERT_EQ(test, i, start + len);
-	i = mb_find_next_bit(bitmap, max, i + 1);
+	i = mb_find_next_bit_test(bitmap, max, i + 1);
 	KUNIT_ASSERT_EQ(test, max, i);
 }
 
@@ -617,54 +618,54 @@ static void mbt_generate_buddy(struct super_block *sb, void *buddy,
 	max = EXT4_CLUSTERS_PER_GROUP(sb);
 	bb_h = buddy + sbi->s_mb_offsets[1];
 
-	off = mb_find_next_zero_bit(bb, max, 0);
+	off = mb_find_next_zero_bit_test(bb, max, 0);
 	grp->bb_first_free = off;
 	while (off < max) {
 		grp->bb_counters[0]++;
 		grp->bb_free++;
 
-		if (!(off & 1) && !mb_test_bit(off + 1, bb)) {
+		if (!(off & 1) && !mb_test_bit_test(off + 1, bb)) {
 			grp->bb_free++;
 			grp->bb_counters[0]--;
-			mb_clear_bit(off >> 1, bb_h);
+			mb_clear_bit_test(off >> 1, bb_h);
 			grp->bb_counters[1]++;
 			grp->bb_largest_free_order = 1;
 			off++;
 		}
 
-		off = mb_find_next_zero_bit(bb, max, off + 1);
+		off = mb_find_next_zero_bit_test(bb, max, off + 1);
 	}
 
 	for (order = 1; order < MB_NUM_ORDERS(sb) - 1; order++) {
 		bb = buddy + sbi->s_mb_offsets[order];
 		bb_h = buddy + sbi->s_mb_offsets[order + 1];
 		max = max >> 1;
-		off = mb_find_next_zero_bit(bb, max, 0);
+		off = mb_find_next_zero_bit_test(bb, max, 0);
 
 		while (off < max) {
-			if (!(off & 1) && !mb_test_bit(off + 1, bb)) {
+			if (!(off & 1) && !mb_test_bit_test(off + 1, bb)) {
 				mb_set_bits(bb, off, 2);
 				grp->bb_counters[order] -= 2;
-				mb_clear_bit(off >> 1, bb_h);
+				mb_clear_bit_test(off >> 1, bb_h);
 				grp->bb_counters[order + 1]++;
 				grp->bb_largest_free_order = order + 1;
 				off++;
 			}
 
-			off = mb_find_next_zero_bit(bb, max, off + 1);
+			off = mb_find_next_zero_bit_test(bb, max, off + 1);
 		}
 	}
 
 	max = EXT4_CLUSTERS_PER_GROUP(sb);
-	off = mb_find_next_zero_bit(bitmap, max, 0);
+	off = mb_find_next_zero_bit_test(bitmap, max, 0);
 	while (off < max) {
 		grp->bb_fragments++;
 
-		off = mb_find_next_bit(bitmap, max, off + 1);
+		off = mb_find_next_bit_test(bitmap, max, off + 1);
 		if (off + 1 >= max)
 			break;
 
-		off = mb_find_next_zero_bit(bitmap, max, off + 1);
+		off = mb_find_next_zero_bit_test(bitmap, max, off + 1);
 	}
 }
 
@@ -706,7 +707,7 @@ do_test_generate_buddy(struct kunit *test, struct super_block *sb, void *bitmap,
 	/* needed by validation in ext4_mb_generate_buddy */
 	ext4_grp->bb_free = mbt_grp->bb_free;
 	memset(ext4_buddy, 0xff, sb->s_blocksize);
-	ext4_mb_generate_buddy(sb, ext4_buddy, bitmap, TEST_GOAL_GROUP,
+	ext4_mb_generate_buddy_test(sb, ext4_buddy, bitmap, TEST_GOAL_GROUP,
 			       ext4_grp);
 
 	KUNIT_ASSERT_EQ(test, memcmp(mbt_buddy, ext4_buddy, sb->s_blocksize),
@@ -760,7 +761,7 @@ test_mb_mark_used_range(struct kunit *test, struct ext4_buddy *e4b,
 	ex.fe_group = TEST_GOAL_GROUP;
 
 	ext4_lock_group(sb, TEST_GOAL_GROUP);
-	mb_mark_used(e4b, &ex);
+	mb_mark_used_test(e4b, &ex);
 	ext4_unlock_group(sb, TEST_GOAL_GROUP);
 
 	mb_set_bits(bitmap, start, len);
@@ -769,7 +770,7 @@ test_mb_mark_used_range(struct kunit *test, struct ext4_buddy *e4b,
 	memset(buddy, 0xff, sb->s_blocksize);
 	for (i = 0; i < MB_NUM_ORDERS(sb); i++)
 		grp->bb_counters[i] = 0;
-	ext4_mb_generate_buddy(sb, buddy, bitmap, 0, grp);
+	ext4_mb_generate_buddy_test(sb, buddy, bitmap, 0, grp);
 
 	KUNIT_ASSERT_EQ(test, memcmp(buddy, e4b->bd_buddy, sb->s_blocksize),
 			0);
@@ -798,7 +799,7 @@ static void test_mb_mark_used(struct kunit *test)
 				bb_counters[MB_NUM_ORDERS(sb)]), GFP_KERNEL);
 	KUNIT_ASSERT_NOT_ERR_OR_NULL(test, grp);
 
-	ret = ext4_mb_load_buddy(sb, TEST_GOAL_GROUP, &e4b);
+	ret = ext4_mb_load_buddy_test(sb, TEST_GOAL_GROUP, &e4b);
 	KUNIT_ASSERT_EQ(test, ret, 0);
 
 	grp->bb_free = EXT4_CLUSTERS_PER_GROUP(sb);
@@ -809,7 +810,7 @@ static void test_mb_mark_used(struct kunit *test)
 		test_mb_mark_used_range(test, &e4b, ranges[i].start,
 					ranges[i].len, bitmap, buddy, grp);
 
-	ext4_mb_unload_buddy(&e4b);
+	ext4_mb_unload_buddy_test(&e4b);
 }
 
 static void
@@ -825,16 +826,16 @@ test_mb_free_blocks_range(struct kunit *test, struct ext4_buddy *e4b,
 		return;
 
 	ext4_lock_group(sb, e4b->bd_group);
-	mb_free_blocks(NULL, e4b, start, len);
+	mb_free_blocks_test(NULL, e4b, start, len);
 	ext4_unlock_group(sb, e4b->bd_group);
 
-	mb_clear_bits(bitmap, start, len);
+	mb_clear_bits_test(bitmap, start, len);
 	/* bypass bb_free validatoin in ext4_mb_generate_buddy */
 	grp->bb_free += len;
 	memset(buddy, 0xff, sb->s_blocksize);
 	for (i = 0; i < MB_NUM_ORDERS(sb); i++)
 		grp->bb_counters[i] = 0;
-	ext4_mb_generate_buddy(sb, buddy, bitmap, 0, grp);
+	ext4_mb_generate_buddy_test(sb, buddy, bitmap, 0, grp);
 
 	KUNIT_ASSERT_EQ(test, memcmp(buddy, e4b->bd_buddy, sb->s_blocksize),
 			0);
@@ -865,7 +866,7 @@ static void test_mb_free_blocks(struct kunit *test)
 				bb_counters[MB_NUM_ORDERS(sb)]), GFP_KERNEL);
 	KUNIT_ASSERT_NOT_ERR_OR_NULL(test, grp);
 
-	ret = ext4_mb_load_buddy(sb, TEST_GOAL_GROUP, &e4b);
+	ret = ext4_mb_load_buddy_test(sb, TEST_GOAL_GROUP, &e4b);
 	KUNIT_ASSERT_EQ(test, ret, 0);
 
 	ex.fe_start = 0;
@@ -873,7 +874,7 @@ static void test_mb_free_blocks(struct kunit *test)
 	ex.fe_group = TEST_GOAL_GROUP;
 
 	ext4_lock_group(sb, TEST_GOAL_GROUP);
-	mb_mark_used(&e4b, &ex);
+	mb_mark_used_test(&e4b, &ex);
 	ext4_unlock_group(sb, TEST_GOAL_GROUP);
 
 	grp->bb_free = 0;
@@ -886,7 +887,7 @@ static void test_mb_free_blocks(struct kunit *test)
 		test_mb_free_blocks_range(test, &e4b, ranges[i].start,
 					  ranges[i].len, bitmap, buddy, grp);
 
-	ext4_mb_unload_buddy(&e4b);
+	ext4_mb_unload_buddy_test(&e4b);
 }
 
 #define COUNT_FOR_ESTIMATE 100000
@@ -904,7 +905,7 @@ static void test_mb_mark_used_cost(struct kunit *test)
 	if (sb->s_blocksize > PAGE_SIZE)
 		kunit_skip(test, "blocksize exceeds pagesize");
 
-	ret = ext4_mb_load_buddy(sb, TEST_GOAL_GROUP, &e4b);
+	ret = ext4_mb_load_buddy_test(sb, TEST_GOAL_GROUP, &e4b);
 	KUNIT_ASSERT_EQ(test, ret, 0);
 
 	ex.fe_group = TEST_GOAL_GROUP;
@@ -918,7 +919,7 @@ static void test_mb_mark_used_cost(struct kunit *test)
 			ex.fe_start = ranges[i].start;
 			ex.fe_len = ranges[i].len;
 			ext4_lock_group(sb, TEST_GOAL_GROUP);
-			mb_mark_used(&e4b, &ex);
+			mb_mark_used_test(&e4b, &ex);
 			ext4_unlock_group(sb, TEST_GOAL_GROUP);
 		}
 		end = jiffies;
@@ -929,14 +930,14 @@ static void test_mb_mark_used_cost(struct kunit *test)
 				continue;
 
 			ext4_lock_group(sb, TEST_GOAL_GROUP);
-			mb_free_blocks(NULL, &e4b, ranges[i].start,
+			mb_free_blocks_test(NULL, &e4b, ranges[i].start,
 				       ranges[i].len);
 			ext4_unlock_group(sb, TEST_GOAL_GROUP);
 		}
 	}
 
 	kunit_info(test, "costed jiffies %lu\n", all);
-	ext4_mb_unload_buddy(&e4b);
+	ext4_mb_unload_buddy_test(&e4b);
 }
 
 static const struct mbt_ext4_block_layout mbt_test_layouts[] = {
diff --git a/fs/ext4/mballoc.c b/fs/ext4/mballoc.c
index ad24340bb75c..90cf038652bc 100644
--- a/fs/ext4/mballoc.c
+++ b/fs/ext4/mballoc.c
@@ -4088,7 +4088,7 @@ void ext4_exit_mballoc(void)
 
 #define EXT4_MB_BITMAP_MARKED_CHECK 0x0001
 #define EXT4_MB_SYNC_UPDATE 0x0002
-static int
+int
 ext4_mb_mark_context(handle_t *handle, struct super_block *sb, bool state,
 		     ext4_group_t group, ext4_grpblk_t blkoff,
 		     ext4_grpblk_t len, int flags, ext4_grpblk_t *ret_changed)
@@ -7192,6 +7192,102 @@ ext4_mballoc_query_range(
 	return error;
 }
 
-#ifdef CONFIG_EXT4_KUNIT_TESTS
-#include "mballoc-test.c"
+#if IS_ENABLED(CONFIG_EXT4_KUNIT_TESTS)
+void mb_clear_bits_test(void *bm, int cur, int len)
+{
+	 mb_clear_bits(bm, cur, len);
+}
+EXPORT_SYMBOL_FOR_EXT4_TEST(mb_clear_bits_test);
+
+ext4_fsblk_t
+ext4_mb_new_blocks_simple_test(struct ext4_allocation_request *ar,
+			       int *errp)
+{
+	return ext4_mb_new_blocks_simple(ar, errp);
+}
+EXPORT_SYMBOL_FOR_EXT4_TEST(ext4_mb_new_blocks_simple_test);
+
+int mb_find_next_zero_bit_test(void *addr, int max, int start)
+{
+	return mb_find_next_zero_bit(addr, max, start);
+}
+EXPORT_SYMBOL_FOR_EXT4_TEST(mb_find_next_zero_bit_test);
+
+int mb_find_next_bit_test(void *addr, int max, int start)
+{
+	return mb_find_next_bit(addr, max, start);
+}
+EXPORT_SYMBOL_FOR_EXT4_TEST(mb_find_next_bit_test);
+
+void mb_clear_bit_test(int bit, void *addr)
+{
+	mb_clear_bit(bit, addr);
+}
+EXPORT_SYMBOL_FOR_EXT4_TEST(mb_clear_bit_test);
+
+int mb_test_bit_test(int bit, void *addr)
+{
+	return mb_test_bit(bit, addr);
+}
+EXPORT_SYMBOL_FOR_EXT4_TEST(mb_test_bit_test);
+
+int ext4_mb_mark_diskspace_used_test(struct ext4_allocation_context *ac,
+				     handle_t *handle)
+{
+	return ext4_mb_mark_diskspace_used(ac, handle);
+}
+EXPORT_SYMBOL_FOR_EXT4_TEST(ext4_mb_mark_diskspace_used_test);
+
+int mb_mark_used_test(struct ext4_buddy *e4b, struct ext4_free_extent *ex)
+{
+	return mb_mark_used(e4b, ex);
+}
+EXPORT_SYMBOL_FOR_EXT4_TEST(mb_mark_used_test);
+
+void ext4_mb_generate_buddy_test(struct super_block *sb, void *buddy,
+				 void *bitmap, ext4_group_t group,
+				 struct ext4_group_info *grp)
+{
+	ext4_mb_generate_buddy(sb, buddy, bitmap, group, grp);
+}
+EXPORT_SYMBOL_FOR_EXT4_TEST(ext4_mb_generate_buddy_test);
+
+int ext4_mb_load_buddy_test(struct super_block *sb, ext4_group_t group,
+			    struct ext4_buddy *e4b)
+{
+	return ext4_mb_load_buddy(sb, group, e4b);
+}
+EXPORT_SYMBOL_FOR_EXT4_TEST(ext4_mb_load_buddy_test);
+
+void ext4_mb_unload_buddy_test(struct ext4_buddy *e4b)
+{
+	ext4_mb_unload_buddy(e4b);
+}
+EXPORT_SYMBOL_FOR_EXT4_TEST(ext4_mb_unload_buddy_test);
+
+void mb_free_blocks_test(struct inode *inode, struct ext4_buddy *e4b,
+			 int first, int count)
+{
+	mb_free_blocks(inode, e4b, first, count);
+}
+EXPORT_SYMBOL_FOR_EXT4_TEST(mb_free_blocks_test);
+
+void ext4_free_blocks_simple_test(struct inode *inode, ext4_fsblk_t block,
+				  unsigned long count)
+{
+	return ext4_free_blocks_simple(inode, block, count);
+}
+EXPORT_SYMBOL_FOR_EXT4_TEST(ext4_free_blocks_simple_test);
+
+EXPORT_SYMBOL_FOR_EXT4_TEST(ext4_wait_block_bitmap);
+EXPORT_SYMBOL_FOR_EXT4_TEST(ext4_mb_init);
+EXPORT_SYMBOL_FOR_EXT4_TEST(ext4_get_group_desc);
+EXPORT_SYMBOL_FOR_EXT4_TEST(ext4_count_free_clusters);
+EXPORT_SYMBOL_FOR_EXT4_TEST(ext4_get_group_info);
+EXPORT_SYMBOL_FOR_EXT4_TEST(ext4_free_group_clusters_set);
+EXPORT_SYMBOL_FOR_EXT4_TEST(ext4_mb_release);
+EXPORT_SYMBOL_FOR_EXT4_TEST(ext4_read_block_bitmap_nowait);
+EXPORT_SYMBOL_FOR_EXT4_TEST(mb_set_bits);
+EXPORT_SYMBOL_FOR_EXT4_TEST(ext4_fc_init_inode);
+EXPORT_SYMBOL_FOR_EXT4_TEST(ext4_mb_mark_context);
 #endif
diff --git a/fs/ext4/mballoc.h b/fs/ext4/mballoc.h
index 15a049f05d04..39333ce72cbd 100644
--- a/fs/ext4/mballoc.h
+++ b/fs/ext4/mballoc.h
@@ -270,4 +270,34 @@ ext4_mballoc_query_range(
 	ext4_mballoc_query_range_fn	formatter,
 	void				*priv);
 
+extern int ext4_mb_mark_context(handle_t *handle,
+		struct super_block *sb, bool state,
+		ext4_group_t group, ext4_grpblk_t blkoff,
+		ext4_grpblk_t len, int flags,
+		ext4_grpblk_t *ret_changed);
+#if IS_ENABLED(CONFIG_EXT4_KUNIT_TESTS)
+extern void mb_clear_bits_test(void *bm, int cur, int len);
+extern ext4_fsblk_t
+ext4_mb_new_blocks_simple_test(struct ext4_allocation_request *ar,
+			       int *errp);
+extern int mb_find_next_zero_bit_test(void *addr, int max, int start);
+extern int mb_find_next_bit_test(void *addr, int max, int start);
+extern void mb_clear_bit_test(int bit, void *addr);
+extern int mb_test_bit_test(int bit, void *addr);
+extern int
+ext4_mb_mark_diskspace_used_test(struct ext4_allocation_context *ac,
+				 handle_t *handle);
+extern int mb_mark_used_test(struct ext4_buddy *e4b,
+			     struct ext4_free_extent *ex);
+extern void ext4_mb_generate_buddy_test(struct super_block *sb,
+		void *buddy, void *bitmap, ext4_group_t group,
+		struct ext4_group_info *grp);
+extern int ext4_mb_load_buddy_test(struct super_block *sb,
+		ext4_group_t group, struct ext4_buddy *e4b);
+extern void ext4_mb_unload_buddy_test(struct ext4_buddy *e4b);
+extern void mb_free_blocks_test(struct inode *inode,
+		struct ext4_buddy *e4b, int first, int count);
+extern void ext4_free_blocks_simple_test(struct inode *inode,
+		ext4_fsblk_t block, unsigned long count);
+#endif
 #endif
-- 
2.34.1


^ permalink raw reply related

* [PATCH v2 3/3] ext4: fix extents-test.c is not compiled when EXT4_KUNIT_TESTS=M
From: Ye Bin @ 2026-03-14  7:52 UTC (permalink / raw)
  To: tytso, adilger.kernel, linux-ext4; +Cc: jack
In-Reply-To: <20260314075258.1317579-1-yebin@huaweicloud.com>

From: Ye Bin <yebin10@huawei.com>

Now, only EXT4_KUNIT_TESTS=Y testcase will be compiled in 'extents.c'.
To solve this issue, the ext4 test code needs to be decoupled. The
'extents-test' module is compiled into 'ext4-test' module.

Fixes: cb1e0c1d1fad ("ext4: kunit tests for extent splitting and conversion")
Signed-off-by: Ye Bin <yebin10@huawei.com>
---
 fs/ext4/Makefile       |  3 ++-
 fs/ext4/ext4_extents.h | 12 ++++++++++++
 fs/ext4/extents-test.c |  8 ++++----
 fs/ext4/extents.c      | 39 +++++++++++++++++++++++++++++++++------
 4 files changed, 51 insertions(+), 11 deletions(-)

diff --git a/fs/ext4/Makefile b/fs/ext4/Makefile
index d836c3fe311b..3baee4e7c1cf 100644
--- a/fs/ext4/Makefile
+++ b/fs/ext4/Makefile
@@ -14,7 +14,8 @@ ext4-y	:= balloc.o bitmap.o block_validity.o dir.o ext4_jbd2.o extents.o \
 
 ext4-$(CONFIG_EXT4_FS_POSIX_ACL)	+= acl.o
 ext4-$(CONFIG_EXT4_FS_SECURITY)		+= xattr_security.o
-ext4-test-objs				+= inode-test.o mballoc-test.o
+ext4-test-objs				+= inode-test.o mballoc-test.o \
+					   extents-test.o
 obj-$(CONFIG_EXT4_KUNIT_TESTS)		+= ext4-test.o
 ext4-$(CONFIG_FS_VERITY)		+= verity.o
 ext4-$(CONFIG_FS_ENCRYPTION)		+= crypto.o
diff --git a/fs/ext4/ext4_extents.h b/fs/ext4/ext4_extents.h
index c484125d963f..ebaf7cc42430 100644
--- a/fs/ext4/ext4_extents.h
+++ b/fs/ext4/ext4_extents.h
@@ -264,5 +264,17 @@ static inline void ext4_idx_store_pblock(struct ext4_extent_idx *ix,
 				     0xffff);
 }
 
+extern int __ext4_ext_dirty(const char *where, unsigned int line,
+			    handle_t *handle, struct inode *inode,
+			    struct ext4_ext_path *path);
+extern int ext4_ext_zeroout(struct inode *inode, struct ext4_extent *ex);
+#if IS_ENABLED(CONFIG_EXT4_KUNIT_TESTS)
+extern int ext4_ext_space_root_idx_test(struct inode *inode, int check);
+extern struct ext4_ext_path *ext4_split_convert_extents_test(
+				handle_t *handle, struct inode *inode,
+				struct ext4_map_blocks *map,
+				struct ext4_ext_path *path,
+				int flags, unsigned int *allocated);
+#endif
 #endif /* _EXT4_EXTENTS */
 
diff --git a/fs/ext4/extents-test.c b/fs/ext4/extents-test.c
index a6b3e6b592a5..5496b2c8e2cd 100644
--- a/fs/ext4/extents-test.c
+++ b/fs/ext4/extents-test.c
@@ -282,8 +282,8 @@ static int extents_kunit_init(struct kunit *test)
 	eh->eh_depth = 0;
 	eh->eh_entries = cpu_to_le16(1);
 	eh->eh_magic = EXT4_EXT_MAGIC;
-	eh->eh_max =
-		cpu_to_le16(ext4_ext_space_root_idx(&k_ctx.k_ei->vfs_inode, 0));
+	eh->eh_max = cpu_to_le16(ext4_ext_space_root_idx_test(
+					&k_ctx.k_ei->vfs_inode, 0));
 	eh->eh_generation = 0;
 
 	/*
@@ -386,8 +386,8 @@ static void test_split_convert(struct kunit *test)
 
 	switch (param->type) {
 	case TEST_SPLIT_CONVERT:
-		path = ext4_split_convert_extents(NULL, inode, &map, path,
-						  param->split_flags, NULL);
+		path = ext4_split_convert_extents_test(NULL, inode, &map,
+					path, param->split_flags, NULL);
 		break;
 	case TEST_CREATE_BLOCKS:
 		ext4_map_create_blocks_helper(test, inode, &map, param->split_flags);
diff --git a/fs/ext4/extents.c b/fs/ext4/extents.c
index 042e1555a674..5ca90f356c92 100644
--- a/fs/ext4/extents.c
+++ b/fs/ext4/extents.c
@@ -184,9 +184,9 @@ static int ext4_ext_get_access(handle_t *handle, struct inode *inode,
  *  - ENOMEM
  *  - EIO
  */
-static int __ext4_ext_dirty(const char *where, unsigned int line,
-			    handle_t *handle, struct inode *inode,
-			    struct ext4_ext_path *path)
+int __ext4_ext_dirty(const char *where, unsigned int line,
+		     handle_t *handle, struct inode *inode,
+		     struct ext4_ext_path *path)
 {
 	int err;
 
@@ -3144,7 +3144,7 @@ static void ext4_zeroout_es(struct inode *inode, struct ext4_extent *ex)
 }
 
 /* FIXME!! we need to try to merge to left or right after zero-out  */
-static int ext4_ext_zeroout(struct inode *inode, struct ext4_extent *ex)
+int ext4_ext_zeroout(struct inode *inode, struct ext4_extent *ex)
 {
 	ext4_fsblk_t ee_pblock;
 	unsigned int ee_len;
@@ -6238,6 +6238,33 @@ int ext4_ext_clear_bb(struct inode *inode)
 	return 0;
 }
 
-#ifdef CONFIG_EXT4_KUNIT_TESTS
-#include "extents-test.c"
+#if IS_ENABLED(CONFIG_EXT4_KUNIT_TESTS)
+int ext4_ext_space_root_idx_test(struct inode *inode, int check)
+{
+	return ext4_ext_space_root_idx(inode, check);
+}
+EXPORT_SYMBOL_FOR_EXT4_TEST(ext4_ext_space_root_idx_test);
+
+struct ext4_ext_path *ext4_split_convert_extents_test(handle_t *handle,
+			struct inode *inode, struct ext4_map_blocks *map,
+			struct ext4_ext_path *path, int flags,
+			unsigned int *allocated)
+{
+	return ext4_split_convert_extents(handle, inode, map, path,
+					  flags, allocated);
+}
+EXPORT_SYMBOL_FOR_EXT4_TEST(ext4_split_convert_extents_test);
+
+EXPORT_SYMBOL_FOR_EXT4_TEST(__ext4_ext_dirty);
+EXPORT_SYMBOL_FOR_EXT4_TEST(ext4_ext_zeroout);
+EXPORT_SYMBOL_FOR_EXT4_TEST(ext4_es_register_shrinker);
+EXPORT_SYMBOL_FOR_EXT4_TEST(ext4_es_unregister_shrinker);
+EXPORT_SYMBOL_FOR_EXT4_TEST(ext4_map_create_blocks);
+EXPORT_SYMBOL_FOR_EXT4_TEST(ext4_es_init_tree);
+EXPORT_SYMBOL_FOR_EXT4_TEST(ext4_es_lookup_extent);
+EXPORT_SYMBOL_FOR_EXT4_TEST(ext4_es_insert_extent);
+EXPORT_SYMBOL_FOR_EXT4_TEST(ext4_ext_insert_extent);
+EXPORT_SYMBOL_FOR_EXT4_TEST(ext4_find_extent);
+EXPORT_SYMBOL_FOR_EXT4_TEST(ext4_issue_zeroout);
+EXPORT_SYMBOL_FOR_EXT4_TEST(ext4_map_query_blocks);
 #endif
-- 
2.34.1


^ permalink raw reply related

* [PATCH v2 0/3] decoupling the ext4 test module
From: Ye Bin @ 2026-03-14  7:52 UTC (permalink / raw)
  To: tytso, adilger.kernel, linux-ext4; +Cc: jack

From: Ye Bin <yebin10@huawei.com>

This patchset is split out from the "Fix some issues about ext4-test"
patchset. It decouples mballoc-test.c and extents-test.c from the
ext4 module, resolving the issue where these two tests could not be run
when EXT4_KUNIT_TESTS is set to M.

Ye Bin (3):
  ext4: introduce EXPORT_SYMBOL_FOR_EXT4_TEST() helper
  ext4: fix mballoc-test.c is not compiled when EXT4_KUNIT_TESTS=M
  ext4: fix extents-test.c is not compiled when EXT4_KUNIT_TESTS=M

 fs/ext4/Makefile       |   5 +-
 fs/ext4/ext4.h         |   5 ++
 fs/ext4/ext4_extents.h |  12 +++++
 fs/ext4/extents-test.c |   8 ++--
 fs/ext4/extents.c      |  39 +++++++++++++---
 fs/ext4/mballoc-test.c |  81 ++++++++++++++++----------------
 fs/ext4/mballoc.c      | 102 +++++++++++++++++++++++++++++++++++++++--
 fs/ext4/mballoc.h      |  30 ++++++++++++
 8 files changed, 227 insertions(+), 55 deletions(-)

-- 
2.34.1


^ permalink raw reply

* [PATCH v2 1/3] ext4: introduce EXPORT_SYMBOL_FOR_EXT4_TEST() helper
From: Ye Bin @ 2026-03-14  7:52 UTC (permalink / raw)
  To: tytso, adilger.kernel, linux-ext4; +Cc: jack
In-Reply-To: <20260314075258.1317579-1-yebin@huaweicloud.com>

From: Ye Bin <yebin10@huawei.com>

Introduce EXPORT_SYMBOL_FOR_EXT4_TEST() helper for kuint test.

Signed-off-by: Ye Bin <yebin10@huawei.com>
---
 fs/ext4/ext4.h | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h
index 9552d154ea8d..b60b334b7693 100644
--- a/fs/ext4/ext4.h
+++ b/fs/ext4/ext4.h
@@ -3945,6 +3945,11 @@ static inline bool ext4_inode_can_atomic_write(struct inode *inode)
 extern int ext4_block_write_begin(handle_t *handle, struct folio *folio,
 				  loff_t pos, unsigned len,
 				  get_block_t *get_block);
+
+#if IS_ENABLED(CONFIG_EXT4_KUNIT_TESTS)
+#define EXPORT_SYMBOL_FOR_EXT4_TEST(sym) \
+	EXPORT_SYMBOL_FOR_MODULES(sym, "ext4-test")
+#endif
 #endif	/* __KERNEL__ */
 
 #endif	/* _EXT4_H */
-- 
2.34.1


^ permalink raw reply related

* [PATCH v3 5/5] ext4: fix possible null-ptr-deref in mbt_kunit_exit()
From: Ye Bin @ 2026-03-14  7:49 UTC (permalink / raw)
  To: tytso, adilger.kernel, linux-ext4; +Cc: jack
In-Reply-To: <20260314074903.1314851-1-yebin@huaweicloud.com>

From: Ye Bin <yebin10@huawei.com>

There's issue as follows:
    # test_new_blocks_simple: failed to initialize: -12
KASAN: null-ptr-deref in range [0x0000000000000638-0x000000000000063f]
Tainted: [E]=UNSIGNED_MODULE, [N]=TEST
RIP: 0010:mbt_kunit_exit+0x5e/0x3e0 [ext4_test]
Call Trace:
 <TASK>
 kunit_try_run_case_cleanup+0xbc/0x100 [kunit]
 kunit_generic_run_threadfn_adapter+0x89/0x100 [kunit]
 kthread+0x408/0x540
 ret_from_fork+0xa76/0xdf0
 ret_from_fork_asm+0x1a/0x30

If mbt_kunit_init() init testcase failed will lead to null-ptr-deref.
So add test if 'sb' is inited success in mbt_kunit_exit().

Fixes: 7c9fa399a369 ("ext4: add first unit test for ext4_mb_new_blocks_simple in mballoc")
Signed-off-by: Ye Bin <yebin10@huawei.com>
---
 fs/ext4/mballoc-test.c | 6 +++++-
 1 file changed, 5 insertions(+), 1 deletion(-)

diff --git a/fs/ext4/mballoc-test.c b/fs/ext4/mballoc-test.c
index c75b91ae0cf0..90ed505fa4b1 100644
--- a/fs/ext4/mballoc-test.c
+++ b/fs/ext4/mballoc-test.c
@@ -362,7 +362,6 @@ static int mbt_kunit_init(struct kunit *test)
 		return ret;
 	}
 
-	test->priv = sb;
 	kunit_activate_static_stub(test,
 				   ext4_read_block_bitmap_nowait,
 				   ext4_read_block_bitmap_nowait_stub);
@@ -383,6 +382,8 @@ static int mbt_kunit_init(struct kunit *test)
 		return -ENOMEM;
 	}
 
+	test->priv = sb;
+
 	return 0;
 }
 
@@ -390,6 +391,9 @@ static void mbt_kunit_exit(struct kunit *test)
 {
 	struct super_block *sb = (struct super_block *)test->priv;
 
+	if (!sb)
+		return;
+
 	mbt_mb_release(sb);
 	mbt_ctx_release(sb);
 	mbt_ext4_free_super_block(sb);
-- 
2.34.1


^ permalink raw reply related

* [PATCH v3 3/5] ext4: fix the error handling process in extents_kunit_init).
From: Ye Bin @ 2026-03-14  7:49 UTC (permalink / raw)
  To: tytso, adilger.kernel, linux-ext4; +Cc: jack
In-Reply-To: <20260314074903.1314851-1-yebin@huaweicloud.com>

From: Ye Bin <yebin10@huawei.com>

The error processing in extents_kunit_init() is improper, causing
resource leakage.
Reconstruct the error handling process to prevent potential resource
leaks

Fixes: cb1e0c1d1fad ("ext4: kunit tests for extent splitting and conversion")
Signed-off-by: Ye Bin <yebin10@huawei.com>
---
 fs/ext4/extents-test.c | 44 ++++++++++++++++++++++++++++++------------
 1 file changed, 32 insertions(+), 12 deletions(-)

diff --git a/fs/ext4/extents-test.c b/fs/ext4/extents-test.c
index 3d4663d99eb1..543236a31e13 100644
--- a/fs/ext4/extents-test.c
+++ b/fs/ext4/extents-test.c
@@ -225,33 +225,37 @@ static int extents_kunit_init(struct kunit *test)
 		(struct kunit_ext_test_param *)(test->param_value);
 	int err;
 
-	sb = sget(&ext_fs_type, NULL, ext_set, 0, NULL);
-	if (IS_ERR(sb))
-		return PTR_ERR(sb);
-
-	sb->s_blocksize = 4096;
-	sb->s_blocksize_bits = 12;
-
 	sbi = kzalloc_obj(struct ext4_sb_info);
 	if (sbi == NULL)
 		return -ENOMEM;
 
+	sb = sget(&ext_fs_type, NULL, ext_set, 0, NULL);
+	if (IS_ERR(sb)) {
+		kfree(sbi);
+		return PTR_ERR(sb);
+	}
+
 	sbi->s_sb = sb;
 	sb->s_fs_info = sbi;
 
+	sb->s_blocksize = 4096;
+	sb->s_blocksize_bits = 12;
+
 	if (!param || !param->disable_zeroout)
 		sbi->s_extent_max_zeroout_kb = 32;
 
 	/* setup the mock inode */
 	k_ctx.k_ei = kzalloc_obj(struct ext4_inode_info);
-	if (k_ctx.k_ei == NULL)
-		return -ENOMEM;
+	if (k_ctx.k_ei == NULL) {
+		err = -ENOMEM;
+		goto out_deactivate;
+	}
 	ei = k_ctx.k_ei;
 	inode = &ei->vfs_inode;
 
 	err = ext4_es_register_shrinker(sbi);
 	if (err)
-		return err;
+		goto out_deactivate;
 
 	ext4_es_init_tree(&ei->i_es_tree);
 	rwlock_init(&ei->i_es_lock);
@@ -267,8 +271,10 @@ static int extents_kunit_init(struct kunit *test)
 	inode->i_sb = sb;
 
 	k_ctx.k_data = kzalloc(EXT_DATA_LEN * 4096, GFP_KERNEL);
-	if (k_ctx.k_data == NULL)
-		return -ENOMEM;
+	if (k_ctx.k_data == NULL) {
+		err = -ENOMEM;
+		goto out_deactivate;
+	}
 
 	/*
 	 * set the data area to a junk value
@@ -313,6 +319,20 @@ static int extents_kunit_init(struct kunit *test)
 	up_write(&sb->s_umount);
 
 	return 0;
+
+out_deactivate:
+	kfree(k_ctx.k_ei);
+	k_ctx.k_ei = NULL;
+
+	kfree(k_ctx.k_data);
+	k_ctx.k_data = NULL;
+
+	if (sbi->s_es_shrinker)
+		ext4_es_unregister_shrinker(sbi);
+	deactivate_locked_super(sb);
+	kfree(sbi);
+
+	return err;
 }
 
 /*
-- 
2.34.1


^ permalink raw reply related

* [PATCH v3 2/5] ext4: fix miss unlock 'sb->s_umount' in extents_kunit_init()
From: Ye Bin @ 2026-03-14  7:49 UTC (permalink / raw)
  To: tytso, adilger.kernel, linux-ext4; +Cc: jack
In-Reply-To: <20260314074903.1314851-1-yebin@huaweicloud.com>

From: Ye Bin <yebin10@huawei.com>

There's warning as follows when do ext4 kunit test:
WARNING: kunit_try_catch/15923 still has locks held!
7.0.0-rc3-next-20260309-00028-g73f965a1bbb1-dirty #281 Tainted: G            E    N
1 lock held by kunit_try_catch/15923:
 #0: ffff888139f860e0 (&type->s_umount_key#70/1){+.+.}-{4:4}, at: alloc_super.constprop.0+0x172/0xa90
Call Trace:
 <TASK>
 dump_stack_lvl+0x180/0x1b0
 debug_check_no_locks_held+0xc8/0xd0
 do_exit+0x1502/0x2b20
 kthread+0x3a9/0x540
 ret_from_fork+0xa76/0xdf0
 ret_from_fork_asm+0x1a/0x30

As sget() will return 'sb' which holds 's->s_umount' lock. However,
"extents-test" miss unlock this lock.
So unlock 's->s_umount' in the end of extents_kunit_init().

Fixes: cb1e0c1d1fad ("ext4: kunit tests for extent splitting and conversion")
Signed-off-by: Ye Bin <yebin10@huawei.com>
---
 fs/ext4/extents-test.c | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/fs/ext4/extents-test.c b/fs/ext4/extents-test.c
index e3d23e3cda87..3d4663d99eb1 100644
--- a/fs/ext4/extents-test.c
+++ b/fs/ext4/extents-test.c
@@ -310,6 +310,8 @@ static int extents_kunit_init(struct kunit *test)
 	kunit_activate_static_stub(test, ext4_ext_zeroout, ext4_ext_zeroout_stub);
 	kunit_activate_static_stub(test, ext4_issue_zeroout,
 				   ext4_issue_zeroout_stub);
+	up_write(&sb->s_umount);
+
 	return 0;
 }
 
-- 
2.34.1


^ permalink raw reply related

* [PATCH v3 4/5] ext4: fix possible null-ptr-deref in extents_kunit_exit()
From: Ye Bin @ 2026-03-14  7:49 UTC (permalink / raw)
  To: tytso, adilger.kernel, linux-ext4; +Cc: jack
In-Reply-To: <20260314074903.1314851-1-yebin@huaweicloud.com>

From: Ye Bin <yebin10@huawei.com>

There's issue as follows:
KASAN: null-ptr-deref in range [0x00000000000002c0-0x00000000000002c7]
Tainted: [E]=UNSIGNED_MODULE, [N]=TEST
RIP: 0010:extents_kunit_exit+0x2e/0xc0 [ext4_test]
Call Trace:
 <TASK>
 kunit_try_run_case_cleanup+0xbc/0x100 [kunit]
 kunit_generic_run_threadfn_adapter+0x89/0x100 [kunit]
 kthread+0x408/0x540
 ret_from_fork+0xa76/0xdf0
 ret_from_fork_asm+0x1a/0x30

Above issue happens as extents_kunit_init() init testcase failed.
So test if testcase is inited success.

Fixes: cb1e0c1d1fad ("ext4: kunit tests for extent splitting and conversion")
Signed-off-by: Ye Bin <yebin10@huawei.com>
---
 fs/ext4/extents-test.c | 7 +++++--
 1 file changed, 5 insertions(+), 2 deletions(-)

diff --git a/fs/ext4/extents-test.c b/fs/ext4/extents-test.c
index 543236a31e13..6ae72986ca9c 100644
--- a/fs/ext4/extents-test.c
+++ b/fs/ext4/extents-test.c
@@ -142,9 +142,12 @@ static struct file_system_type ext_fs_type = {
 
 static void extents_kunit_exit(struct kunit *test)
 {
-	struct super_block *sb = k_ctx.k_ei->vfs_inode.i_sb;
-	struct ext4_sb_info *sbi = sb->s_fs_info;
+	struct ext4_sb_info *sbi;
 
+	if (!k_ctx.k_ei)
+		return;
+
+	sbi = k_ctx.k_ei->vfs_inode.i_sb->s_fs_info;
 	ext4_es_unregister_shrinker(sbi);
 	deactivate_super(sbi->s_sb);
 	kfree(sbi);
-- 
2.34.1


^ permalink raw reply related

* [PATCH v3 0/5] Fix some issues about ext4-test
From: Ye Bin @ 2026-03-14  7:48 UTC (permalink / raw)
  To: tytso, adilger.kernel, linux-ext4; +Cc: jack

From: Ye Bin <yebin10@huawei.com>

Diff v3 vs v2:
1. Remove three patches from this patchset:
  ext4: fix mballoc-test.c is not compiled when EXT4_KUNIT_TESTS=M
  ext4: introduce EXPORT_SYMBOL_FOR_EXT4_TEST() helper
  ext4: fix extents-test.c is not compiled when EXT4_KUNIT_TESTS=M
2. Fix 'sbi' leak as no "sbi->s_sb->s_op->put_super()" registered.

Diff v2 vs v1:
1. Fix compile warning when disable EXT4_KUNIT_TESTS for patch[1][3];
2. Remove reviewed-by tag for patch[1];

Patch [1]-[2]:
Decoupled mballoc-test and extents-test from ext4. Patch [1] does not
have any changes compared to the previously released version, so the
reviewed-by is added.
Patch [3-7]:
Bugfix for extents-test.c.
Patch [8]:
Bugfix for mballoc-test.c.

Ye Bin (5):
  ext4: call deactivate_super() in extents_kunit_exit()
  ext4: fix miss unlock 'sb->s_umount' in extents_kunit_init()
  ext4: fix the error handling process in extents_kunit_init).
  ext4: fix possible null-ptr-deref in extents_kunit_exit()
  ext4: fix possible null-ptr-deref in mbt_kunit_exit()

 fs/ext4/extents-test.c | 54 +++++++++++++++++++++++++++++++-----------
 fs/ext4/mballoc-test.c |  6 ++++-
 2 files changed, 45 insertions(+), 15 deletions(-)

-- 
2.34.1


^ permalink raw reply

* [PATCH v3 1/5] ext4: call deactivate_super() in extents_kunit_exit()
From: Ye Bin @ 2026-03-14  7:48 UTC (permalink / raw)
  To: tytso, adilger.kernel, linux-ext4; +Cc: jack
In-Reply-To: <20260314074903.1314851-1-yebin@huaweicloud.com>

From: Ye Bin <yebin10@huawei.com>

Call deactivate_super() is called in extents_kunit_exit() to cleanup
the file system resource.

Fixes: cb1e0c1d1fad ("ext4: kunit tests for extent splitting and conversion")
Signed-off-by: Ye Bin <yebin10@huawei.com>
---
 fs/ext4/extents-test.c | 1 +
 1 file changed, 1 insertion(+)

diff --git a/fs/ext4/extents-test.c b/fs/ext4/extents-test.c
index 5496b2c8e2cd..e3d23e3cda87 100644
--- a/fs/ext4/extents-test.c
+++ b/fs/ext4/extents-test.c
@@ -146,6 +146,7 @@ static void extents_kunit_exit(struct kunit *test)
 	struct ext4_sb_info *sbi = sb->s_fs_info;
 
 	ext4_es_unregister_shrinker(sbi);
+	deactivate_super(sbi->s_sb);
 	kfree(sbi);
 	kfree(k_ctx.k_ei);
 	kfree(k_ctx.k_data);
-- 
2.34.1


^ permalink raw reply related

* Re: [RFC PATCH] ext4: handle wraparound when searching for blocks for indirect mapped blocks
From: Baokun Li @ 2026-03-14  7:41 UTC (permalink / raw)
  To: Theodore Tso; +Cc: Jan Kara, Ext4 Developers List, libaokun
In-Reply-To: <20260313143101.GA38016@macsyma-wired.lan>

On 3/13/26 10:31 PM, Theodore Tso wrote:

> On Fri, Mar 13, 2026 at 10:00:04AM +0800, Baokun Li wrote:
>> IIUC, ngroups/end here only depend on filesystem size and whether the inode
>> is extent-based, and both should stay unchanged during block allocation.
>> So doing the check once at the beginning should be sufficient. Am I missing
>> anything?
> The problem here is that case where we use
> ext4_get_allocation_groups_count as ngroups and end are different.  In
> some places we do this:
>
>         ext4_group_t ngroups = ext4_get_allocation_groups_count(ac);
>
> and in others we do this:
>
>         end = ext4_get_allocation_groups_count(ac);
>
> Now, if start is zero, then these two are equivalent.  But if start is
> not zero, but say, is 2**32 - 8, then where we use
> ext4_allocaiton_groups_count() as the last block group to search, then
> we only will search exactly 8 block groups, and if
> there are free blocks in the first 8 block groups, then the scanning
> function will fail.

ext4_mb_scan_groups_p2_aligned(), ext4_mb_scan_groups_goal_fast(), and
ext4_mb_scan_groups_best_avail() will all wrap around and retry
[0, 2^32 - 8) after failing to search [2^32 - 8, 2^32). So if there are
free blocks in the first 8 block groups, the allocation will not fail.

>
> Alternatively, if we just do the check at the beginning, then 2**32 -
> 8 is a valid starting point, but if we just search forward by ngroups,
> then we may end up returning a block group which won't work for
> indirect mapped inodes.

Here, ngroups refers to the number of block groups available for
allocation, i.e. the range [0, ngroups), rather than implying that
ngroups groups are scanned on each pass. In general, we first scan the
[start, ngroups) portion of the xarrays, and then the [0, start) portion.

For indirect-mapped inodes, ext4_get_allocation_groups_count() returns
s_blockfile_groups. The end of those groups should not exceed 2^32 blocks;
otherwise, the s_blockfile_groups logic itself would be broken.

> Hence, in *every* function where we call
> ext4_get_allocaiton_groups_count(), if the goal is to search all block
> groups that are valid for indirect mapped inodes, and start might be
> greater than 0, we *have* to handle wraparound.
>
> Does that make sense?
>
Thanks for the detailed explanation, but I’m afraid I still don’t
quite follow.

Take `ext4_mb_scan_groups_p2_aligned` as an example:

ext4_mb_scan_groups
  ngroups = ext4_get_allocation_groups_count(ac); // safe
  start = ac->ac_g_ex.fe_group; // may be greater than ngroups
  ext4_mb_scan_groups_p2_aligned()
    start = group; // may be greater than ngroups
    end = ext4_get_allocation_groups_count(ac); // == ngroups
wrap_around:
    ext4_mb_scan_groups_xa_range(start, end)
      ngroups = ext4_get_allocation_groups_count(ac); // == end
        // `start` may exceed `end`, but `end` is always within bounds.
        // So we only need to make sure the `start` computed by
        // ext4_mb_scan_groups() is less than `ngroups`.
    // Forward scan failed; let the caller handle wrap-around.
    if (start) {
        end = start; // `end` may be invalid if `start` is invalid.
        start = 0;
        goto wrap_around;
    }

So it looks like the later start/end values only go wrong if the initial
start is already >= ngroups.

Did I miss something here?


Thanks,
Baokun


^ permalink raw reply

* Re: [PATCH -next 6/8] ext4: fix the error handling process in extents_kunit_init).
From: yebin (H) @ 2026-03-14  2:21 UTC (permalink / raw)
  To: Ritesh Harjani, Ye Bin, tytso, adilger.kernel, linux-ext4; +Cc: jack
In-Reply-To: <sea4sz73.ritesh.list@gmail.com>



On 2026/3/13 20:15, Ritesh Harjani wrote:
> Ye Bin <yebin@huaweicloud.com> writes:
>
>> From: Ye Bin <yebin10@huawei.com>
>>
>> The error processing in extents_kunit_init() is improper, causing
>> resource leakage.
>> Reconstruct the error handling process to prevent potential resource
>> leaks
>>
>> Fixes: cb1e0c1d1fad ("ext4: kunit tests for extent splitting and conversion")
>> Signed-off-by: Ye Bin <yebin10@huawei.com>
>> ---
>>   fs/ext4/extents-test.c | 41 +++++++++++++++++++++++++++++------------
>>   1 file changed, 29 insertions(+), 12 deletions(-)
>>
>> diff --git a/fs/ext4/extents-test.c b/fs/ext4/extents-test.c
>> index 70d84c0a18e2..7e2796f72d45 100644
>> --- a/fs/ext4/extents-test.c
>> +++ b/fs/ext4/extents-test.c
>> @@ -222,33 +222,37 @@ static int extents_kunit_init(struct kunit *test)
>>   		(struct kunit_ext_test_param *)(test->param_value);
>>   	int err;
>>
>> -	sb = sget(&ext_fs_type, NULL, ext_set, 0, NULL);
>> -	if (IS_ERR(sb))
>> -		return PTR_ERR(sb);
>> -
>> -	sb->s_blocksize = 4096;
>> -	sb->s_blocksize_bits = 12;
>> -
>>   	sbi = kzalloc_obj(struct ext4_sb_info);
>>   	if (sbi == NULL)
>>   		return -ENOMEM;
>>
>> +	sb = sget(&ext_fs_type, NULL, ext_set, 0, NULL);
>> +	if (IS_ERR(sb)) {
>> +		kfree(sbi);
>> +		return PTR_ERR(sb);
>> +	}
>> +
>>   	sbi->s_sb = sb;
>>   	sb->s_fs_info = sbi;
>>
>> +	sb->s_blocksize = 4096;
>> +	sb->s_blocksize_bits = 12;
>> +
>>   	if (!param || !param->disable_zeroout)
>>   		sbi->s_extent_max_zeroout_kb = 32;
>>
>>   	/* setup the mock inode */
>>   	k_ctx.k_ei = kzalloc_obj(struct ext4_inode_info);
>> -	if (k_ctx.k_ei == NULL)
>> -		return -ENOMEM;
>> +	if (k_ctx.k_ei == NULL) {
>> +		err = -ENOMEM;
>> +		goto out_deactivate;
>> +	}
>>   	ei = k_ctx.k_ei;
>>   	inode = &ei->vfs_inode;
>>
>>   	err = ext4_es_register_shrinker(sbi);
>>   	if (err)
>> -		return err;
>> +		goto out_deactivate;
>>
>>   	ext4_es_init_tree(&ei->i_es_tree);
>>   	rwlock_init(&ei->i_es_lock);
>> @@ -264,8 +268,10 @@ static int extents_kunit_init(struct kunit *test)
>>   	inode->i_sb = sb;
>>
>>   	k_ctx.k_data = kzalloc(EXT_DATA_LEN * 4096, GFP_KERNEL);
>> -	if (k_ctx.k_data == NULL)
>> -		return -ENOMEM;
>> +	if (k_ctx.k_data == NULL) {
>> +		err = -ENOMEM;
>> +		goto out_deactivate;
>> +	}
>>
>>   	/*
>>   	 * set the data area to a junk value
>> @@ -310,6 +316,17 @@ static int extents_kunit_init(struct kunit *test)
>>   	up_write(&sb->s_umount);
>>
>>   	return 0;
>> +
>> +out_deactivate:
>> +	kfree(k_ctx.k_ei);
>> +	k_ctx.k_ei = NULL;
>> +
>> +	kfree(k_ctx.k_data);
>> +	k_ctx.k_data = NULL;
>> +
>> +	deactivate_locked_super(sb);
>
> So I guess the right thing to do for now is, what Ojaswin also mentioned
> [1].  Let's go with patch [2], as is.. and we can fix all the remaining
> issues as part of this series. With that in mind, this patch series
> needs to be applid on top of [2]. So that means.. our error handling
> should also properly take care of unregister shrinker and freeing sbi..
>
> [1]: https://lore.kernel.org/linux-ext4/abPh6GX0t22m628D@li-dc0c254c-257c-11b2-a85c-98b6c1322444.ibm.com/
> [2]: https://lore.kernel.org/linux-ext4/5bb9041471dab8ce870c191c19cbe4df57473be8.1772381213.git.ritesh.list@gmail.com/#t
>
> -ritesh
>
I will base on your patch and then create a separate patchset for the
fixes and send it.
>
>> +
>> +	return err;
>>   }
>>
>>   /*
>> --
>> 2.34.1
>
>
> .
>

^ permalink raw reply

* Re: [PATCH] ext4: fix bigalloc cluster arithmetic when s_first_data_block != 0
From: Theodore Tso @ 2026-03-14  1:29 UTC (permalink / raw)
  To: Helen Koike
  Cc: adilger.kernel, linux-ext4, linux-fsdevel, linux-kernel,
	kernel-dev, syzbot+b73703b873a33d8eb8f6
In-Reply-To: <20260313151835.1248953-1-koike@igalia.com>

On Fri, Mar 13, 2026 at 12:18:30PM -0300, Helen Koike wrote:
> On filesystems with bigalloc enabled and s_first_data_block != 0,

This is never supposed to happen; fsck already checks for this.  From
e2fsck/super.c:

	should_be = (sb->s_log_block_size == 0 &&
		     EXT2FS_CLUSTER_RATIO(fs) == 1) ? 1 : 0;
	if (sb->s_first_data_block != should_be) {
		pctx.blk = sb->s_first_data_block;
		pctx.blk2 = should_be;
		fix_problem(ctx, PR_0_FIRST_DATA_BLOCK, &pctx);
		ctx->flags |= E2F_FLAG_ABORT;
		return;
	}

You can also see evidence of this code path getting trigger by
clicking on "corrupt_fs" in the Syzkaller dashboard.

> Introduce four macros that subtract s_first_data_block before
> operating, matching the coordinate system used by mballoc:

This is *way* more complicated than it needs to be.  All you need to
do is just reject the mount if the file system is this insanely
corrupted.  For example:

	if (ext4_has_feature_bigalloc(sb) &&
	    es->s_first_data_block) {
		ext4_msg(sb, KERN_WARNING, "bad geometry: bigalloc "
			 "file system with non-zero first_data_block");
		return -EINVAL;
	}

> Regarding the issue reported by syzbot...

Yeah, this is why I generally don't pay that much attention to syzbot
reports that mount a corrupted file system.  Users are supposed to run
fsck on a file system before they blindly mount it.  If they don't, I
don't consider it a security vulnerability; it's just a stupid system
administrator trick, and he or she deserves everything that happens to
them.  Hence, such issues are not a security issue, but just a quality
of implementation issue.  I'll accept patches to addrese sorts of
things, but it doesn't rate a CVE or high priority processing.

Furthermore, any fix needs to as simple as possible, and avoids adding
extra overhead especially on hot paths.  In this particular case,
rejecting the mount is the simplest fix, since the file system
corruption is *easy* to detect.

Cheers,

					- Ted

^ permalink raw reply

* Re: [PATCH 02/61] btrfs: Prefer IS_ERR_OR_NULL over manual NULL check
From: David Sterba @ 2026-03-13 19:22 UTC (permalink / raw)
  To: Philipp Hahn
  Cc: amd-gfx, apparmor, bpf, ceph-devel, cocci, dm-devel, dri-devel,
	gfs2, intel-gfx, intel-wired-lan, iommu, kvm, linux-arm-kernel,
	linux-block, linux-bluetooth, linux-btrfs, linux-cifs, linux-clk,
	linux-erofs, linux-ext4, linux-fsdevel, linux-gpio, linux-hyperv,
	linux-input, linux-kernel, linux-leds, linux-media, linux-mips,
	linux-mm, linux-modules, linux-mtd, linux-nfs, linux-omap,
	linux-phy, linux-pm, linux-rockchip, linux-s390, linux-scsi,
	linux-sctp, linux-security-module, linux-sh, linux-sound,
	linux-stm32, linux-trace-kernel, linux-usb, linux-wireless,
	netdev, ntfs3, samba-technical, sched-ext, target-devel,
	tipc-discussion, v9fs, Chris Mason, David Sterba
In-Reply-To: <20260310-b4-is_err_or_null-v1-2-bd63b656022d@avm.de>

On Tue, Mar 10, 2026 at 12:48:28PM +0100, Philipp Hahn wrote:
> Prefer using IS_ERR_OR_NULL() over using IS_ERR() and a manual NULL
> check.
> 
> IS_ERR_OR_NULL() already uses likely(!ptr) internally. checkpatch does
> not like nesting it:
> > WARNING: nested (un)?likely() calls, IS_ERR_OR_NULL already uses
> > unlikely() internally
> Remove the explicit use of likely().
> 
> Change generated with coccinelle.
> 
> To: Chris Mason <clm@fb.com>
> To: David Sterba <dsterba@suse.com>
> Cc: linux-btrfs@vger.kernel.org
> Cc: linux-kernel@vger.kernel.org
> Signed-off-by: Philipp Hahn <phahn-oss@avm.de>

Added to for-next, we seem to be using IS_ERR_OR_NULL() already in a
few other places so this is makes sense for consistency. Thanks.

^ permalink raw reply

* [PATCH v2] ext4: fix partial cluster handling when s_first_data_block != 0
From: Helen Koike @ 2026-03-13 17:07 UTC (permalink / raw)
  To: tytso, adilger.kernel, linux-ext4, linux-fsdevel, linux-kernel,
	kernel-dev, koike, syzbot+b73703b873a33d8eb8f6

On filesystems with bigalloc enabled and s_first_data_block != 0,
mballoc defines clusters starting from s_first_data_block, so cluster
N covers blocks [s_first_data_block + N*s_cluster_ratio, ...).

EXT4_B2C/EXT4_C2B perform a plain bit-shift on the absolute block
number, ignoring s_first_data_block.

For instance, with cluster_ratio = 4 and s_first_data_block = 1:

This is how EXT4_B2C/EXT4_C2B view it:

  block:    0   1   2   3   4   5   6   7   8   9  10  11  12 ...
            |___________|   |___________|   |___________|
  cluster:       0               1               2

This is how mballoc views it:

  block:    0   1   2   3   4   5   6   7   8   9  10  11  12 ...
                |___________|   |___________|   |___________|
  cluster:           0               1               2

This causes the following issues:

1) In extents.c, partial->pclu is stored and compared using EXT4_B2C,
   then passed to ext4_free_blocks() via EXT4_C2B. The resulting
   block address is misaligned with mballoc's cluster boundaries,
   causing the wrong cluster to be freed.

2) In ext4_free_blocks(), EXT4_PBLK_COFF uses the same plain
   bit-mask, so the intra-cluster offset of the start block is
   computed incorrectly, misaligning the freed range.

Introduce three macros that subtract s_first_data_block before
operating, matching the coordinate system used by mballoc:

  EXT4_MB_B2C(sbi, blk)        block -> cluster number
  EXT4_MB_C2B(sbi, cluster)    cluster -> first block of cluster
  EXT4_MB_PBLK_COFF(s, blk)    intra-cluster offset of a block

Use EXT4_MB_B2C/EXT4_MB_C2B for all partial->pclu operations in
extents.c, and EXT4_MB_PBLK_COFF in the alignment prologue of
ext4_free_blocks().

Regarding the issue reported by syzbot:

  Context: s_first_data_block=1, cluster size 16 and block 145 contains
  a extents leaf for inode 15.
  When an extent is removed (block 145 went from 7 to 6 valid extent
  entries), ext4_ext_rm_leaf() sees the cluster partial->pclu (which is
  10) to be freed.

  Note that EXT4_C2B(partial->pclu=10) is 160, and EXT4_B2C(145) is 9
  (so the extents leaf is in a different cluster). Finally
  ext4_free_blocks(..,block=160, count=16..) is called, which shouldn't
  be a problem (it should not free block 145).

  Except that in  ext4_free_blocks() (and later in ext4_mb_clear_bb()
  and mb_free_blocks()) , block 160 resolves to group 0 bit 9 count 1
  (which frees block 145 containing extents leaf!!!!), causing block
  145 to be reused by other inodes while inode 15 still thinks that
  block 145 contains extents metadata, resulting later in the UAF we see
  by syzbot due to corrupted eh_entries read by inode 15.

  The issue doesn't reproduce with the current patch.

Test results:

> kvm-xfstests --kernel $BUILD_DIR/arch/x86/boot/bzImage smoke

ext4/4k: 7 tests, 1 skipped, 1113 seconds
  generic/475  Pass     185s
  generic/476  Pass     184s
  generic/521  Pass     182s
  generic/522  Pass     181s
  generic/642  Pass     185s
  generic/750  Pass     185s
  generic/751  Skipped  1s
Totals: 7 tests, 1 skipped, 0 failures, 0 errors, 1103s

> kvm-xfstests --kernel $BUILD_DIR/arch/x86/boot/bzImage -c 1k smoke

ext4/1k: 5 tests, 1 skipped, 755 seconds
  generic/521  Pass     183s
  generic/522  Pass     182s
  generic/642  Pass     186s
  generic/750  Pass     186s
  generic/751  Skipped  2s
Totals: 5 tests, 1 skipped, 0 failures, 0 errors, 739s

Signed-off-by: Helen Koike <koike@igalia.com>
Reported-by: syzbot+b73703b873a33d8eb8f6@syzkaller.appspotmail.com
Closes: https://syzkaller.appspot.com/bug?extid=b73703b873a33d8eb8f6

---

v2:
- Update commit title to reflect the partial cluster issue
- Minor updates in commit message
- Removed unnecessary EXT4_MB_LBLK_COFF macro
- Rewrite the macros to reuse preexisting macros
- Update parameter names and indentation to match current coding style
- Update comments before the new macros

v1: https://lore.kernel.org/all/20260313151835.1248953-1-koike@igalia.com/

---

Hi all,

I'm not very familiar with this subsystem or prior discussions around
this issue. I did some digging, but I couldn't find much related to it.
With that in mind, please let me know if this approach does not make
sense and/or if you have any pointers.

I also checked the thread below [1] to see whether the EXT4_NUM_* macros
should be used instead, but that seems to be addressing a different
context.

I appreciate any feedback.

[1] https://lore.kernel.org/all/20130221121545.GA30821@gmail.com/

Thanks,
Helen
---
 fs/ext4/ext4.h    |  9 +++++++++
 fs/ext4/extents.c | 20 ++++++++++----------
 fs/ext4/mballoc.c |  2 +-
 3 files changed, 20 insertions(+), 11 deletions(-)

diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h
index 7e8f66ba17f4..eb0cfcd1d97a 100644
--- a/fs/ext4/ext4.h
+++ b/fs/ext4/ext4.h
@@ -315,6 +315,12 @@ struct ext4_io_submit {
 #define EXT4_B2C(sbi, blk)	((blk) >> (sbi)->s_cluster_bits)
 /* Translate a cluster number to a block number */
 #define EXT4_C2B(sbi, cluster)	((cluster) << (sbi)->s_cluster_bits)
+/* Translate a block to cluster using mballoc's cluster definition */
+#define EXT4_MB_B2C(sbi, blk) (EXT4_B2C((sbi), (blk) - \
+		le32_to_cpu((sbi)->s_es->s_first_data_block)))
+/* Translate a cluster to block using mballoc's cluster definition */
+#define EXT4_MB_C2B(sbi, cluster) (EXT4_C2B((sbi), (cluster)) + \
+		le32_to_cpu((sbi)->s_es->s_first_data_block))
 /* Translate # of blks to # of clusters */
 #define EXT4_NUM_B2C(sbi, blks)	(((blks) + (sbi)->s_cluster_ratio - 1) >> \
 				 (sbi)->s_cluster_bits)
@@ -331,6 +337,9 @@ struct ext4_io_submit {
 				 ((ext4_fsblk_t) (s)->s_cluster_ratio - 1))
 #define EXT4_LBLK_COFF(s, lblk) ((lblk) &				\
 				 ((ext4_lblk_t) (s)->s_cluster_ratio - 1))
+/* Get the cluster offset using mballoc's cluster definition */
+#define EXT4_MB_PBLK_COFF(s, pblk) (EXT4_PBLK_COFF((s), (pblk) - \
+		le32_to_cpu((s)->s_es->s_first_data_block)))
 
 /*
  * Structure of a blocks group descriptor
diff --git a/fs/ext4/extents.c b/fs/ext4/extents.c
index 35703dce23a3..38d49f784c22 100644
--- a/fs/ext4/extents.c
+++ b/fs/ext4/extents.c
@@ -2493,13 +2493,13 @@ static int ext4_remove_blocks(handle_t *handle, struct inode *inode,
 	last_pblk = ext4_ext_pblock(ex) + ee_len - 1;
 
 	if (partial->state != initial &&
-	    partial->pclu != EXT4_B2C(sbi, last_pblk)) {
+	    partial->pclu != EXT4_MB_B2C(sbi, last_pblk)) {
 		if (partial->state == tofree) {
 			flags = get_default_free_blocks_flags(inode);
 			if (ext4_is_pending(inode, partial->lblk))
 				flags |= EXT4_FREE_BLOCKS_RERESERVE_CLUSTER;
 			ext4_free_blocks(handle, inode, NULL,
-					 EXT4_C2B(sbi, partial->pclu),
+					 EXT4_MB_C2B(sbi, partial->pclu),
 					 sbi->s_cluster_ratio, flags);
 			if (flags & EXT4_FREE_BLOCKS_RERESERVE_CLUSTER)
 				ext4_rereserve_cluster(inode, partial->lblk);
@@ -2545,7 +2545,7 @@ static int ext4_remove_blocks(handle_t *handle, struct inode *inode,
 	ext4_free_blocks(handle, inode, NULL, pblk, num, flags);
 
 	/* reset the partial cluster if we've freed past it */
-	if (partial->state != initial && partial->pclu != EXT4_B2C(sbi, pblk))
+	if (partial->state != initial && partial->pclu != EXT4_MB_B2C(sbi, pblk))
 		partial->state = initial;
 
 	/*
@@ -2560,7 +2560,7 @@ static int ext4_remove_blocks(handle_t *handle, struct inode *inode,
 	 */
 	if (EXT4_LBLK_COFF(sbi, from) && num == ee_len) {
 		if (partial->state == initial) {
-			partial->pclu = EXT4_B2C(sbi, pblk);
+			partial->pclu = EXT4_MB_B2C(sbi, pblk);
 			partial->lblk = from;
 			partial->state = tofree;
 		}
@@ -2651,7 +2651,7 @@ ext4_ext_rm_leaf(handle_t *handle, struct inode *inode,
 			 */
 			if (sbi->s_cluster_ratio > 1) {
 				pblk = ext4_ext_pblock(ex);
-				partial->pclu = EXT4_B2C(sbi, pblk);
+				partial->pclu = EXT4_MB_B2C(sbi, pblk);
 				partial->state = nofree;
 			}
 			ex--;
@@ -2766,13 +2766,13 @@ ext4_ext_rm_leaf(handle_t *handle, struct inode *inode,
 	 */
 	if (partial->state == tofree && ex >= EXT_FIRST_EXTENT(eh)) {
 		pblk = ext4_ext_pblock(ex) + ex_ee_len - 1;
-		if (partial->pclu != EXT4_B2C(sbi, pblk)) {
+		if (partial->pclu != EXT4_MB_B2C(sbi, pblk)) {
 			int flags = get_default_free_blocks_flags(inode);
 
 			if (ext4_is_pending(inode, partial->lblk))
 				flags |= EXT4_FREE_BLOCKS_RERESERVE_CLUSTER;
 			ext4_free_blocks(handle, inode, NULL,
-					 EXT4_C2B(sbi, partial->pclu),
+					 EXT4_MB_C2B(sbi, partial->pclu),
 					 sbi->s_cluster_ratio, flags);
 			if (flags & EXT4_FREE_BLOCKS_RERESERVE_CLUSTER)
 				ext4_rereserve_cluster(inode, partial->lblk);
@@ -2886,7 +2886,7 @@ int ext4_ext_remove_space(struct inode *inode, ext4_lblk_t start,
 			 */
 			if (sbi->s_cluster_ratio > 1) {
 				pblk = ext4_ext_pblock(ex) + end - ee_block + 1;
-				partial.pclu = EXT4_B2C(sbi, pblk);
+				partial.pclu = EXT4_MB_B2C(sbi, pblk);
 				partial.state = nofree;
 			}
 
@@ -2919,7 +2919,7 @@ int ext4_ext_remove_space(struct inode *inode, ext4_lblk_t start,
 			if (err < 0)
 				goto out;
 			if (pblk) {
-				partial.pclu = EXT4_B2C(sbi, pblk);
+				partial.pclu = EXT4_MB_B2C(sbi, pblk);
 				partial.state = nofree;
 			}
 		}
@@ -3041,7 +3041,7 @@ int ext4_ext_remove_space(struct inode *inode, ext4_lblk_t start,
 		if (ext4_is_pending(inode, partial.lblk))
 			flags |= EXT4_FREE_BLOCKS_RERESERVE_CLUSTER;
 		ext4_free_blocks(handle, inode, NULL,
-				 EXT4_C2B(sbi, partial.pclu),
+				 EXT4_MB_C2B(sbi, partial.pclu),
 				 sbi->s_cluster_ratio, flags);
 		if (flags & EXT4_FREE_BLOCKS_RERESERVE_CLUSTER)
 			ext4_rereserve_cluster(inode, partial.lblk);
diff --git a/fs/ext4/mballoc.c b/fs/ext4/mballoc.c
index 9c7881a4ea75..eb57265e3347 100644
--- a/fs/ext4/mballoc.c
+++ b/fs/ext4/mballoc.c
@@ -6360,7 +6360,7 @@ void ext4_free_blocks(handle_t *handle, struct inode *inode,
 	 * blocks at the beginning or the end unless we are explicitly
 	 * requested to avoid doing so.
 	 */
-	overflow = EXT4_PBLK_COFF(sbi, block);
+	overflow = EXT4_MB_PBLK_COFF(sbi, block);
 	if (overflow) {
 		if (flags & EXT4_FREE_BLOCKS_NOFREE_FIRST_CLUSTER) {
 			overflow = sbi->s_cluster_ratio - overflow;
-- 
2.53.0


^ permalink raw reply related

* Re: [PATCH] ext4: fix bigalloc cluster arithmetic when s_first_data_block != 0
From: Helen Koike @ 2026-03-13 15:28 UTC (permalink / raw)
  To: tytso, adilger.kernel, linux-ext4, linux-fsdevel, linux-kernel,
	kernel-dev, syzbot+b73703b873a33d8eb8f6
In-Reply-To: <20260313151835.1248953-1-koike@igalia.com>

Please ignore this patch, I just found about:

https://lore.kernel.org/all/1361433665-16880-1-git-send-email-lczerner@redhat.com/

I'll re-evaluate and submit v2 if it make sense.

Thanks
Helen

On 3/13/26 12:18 PM, Helen Koike wrote:
> On filesystems with bigalloc enabled and s_first_data_block != 0,
> mballoc defines clusters starting from s_first_data_block, so cluster
> N covers blocks [s_first_data_block + N*s_cluster_ratio, ...).
> 
> EXT4_B2C/EXT4_C2B perform a plain bit-shift on the absolute block
> number, ignoring s_first_data_block.
> 
> For instance, with cluster_ratio = 4 and s_first_data_block = 1:
> 
> This is how EXT4_B2C/EXT4_C2B view it:
> 
>    block:    0   1   2   3   4   5   6   7   8   9  10  11  12 ...
>              |___________|   |___________|   |___________|
>    cluster:       0               1               2
> 
> This is how mballoc views it:
> 
>    block:    0   1   2   3   4   5   6   7   8   9  10  11  12 ...
>                  |___________|   |___________|   |___________|
>    cluster:           0               1               2
> 
> This causes the following issues:
> 
> 1) In extents.c, partial->pclu is stored and compared using EXT4_B2C,
>     then passed to ext4_free_blocks() via EXT4_C2B. The resulting
>     block address is misaligned with mballoc's cluster boundaries,
>     causing the wrong cluster to be freed.
> 
> 2) In ext4_free_blocks(), EXT4_PBLK_COFF uses the same plain
>     bit-mask, so the intra-cluster offset of the start block is
>     computed incorrectly, misaligning the freed range.
> 
> Introduce four macros that subtract s_first_data_block before
> operating, matching the coordinate system used by mballoc:
> 
>    EXT4_MB_B2C(sbi, blk)        block -> cluster number
>    EXT4_MB_C2B(sbi, cluster)    cluster -> first block of cluster
>    EXT4_MB_PBLK_COFF(sbi, blk)  intra-cluster offset of a block
>    EXT4_MB_LBLK_COFF(sbi, n)    intra-cluster offset of a block count
> 
> Use EXT4_MB_B2C/EXT4_MB_C2B for all partial->pclu operations in
> extents.c, and EXT4_MB_PBLK_COFF/EXT4_MB_LBLK_COFF in the alignment
> prologue of ext4_free_blocks().
> 
> Regarding the issue reported by syzbot:
> 
>    Context: s_first_data_block=1, cluster size 16 and block 145 contains
>    a extents leaf for inode 15.
>    When an extent is removed (block 145 went from 7 to 6 valid extent
>    entries), ext4_ext_rm_leaf() sees the cluster partial->pclu (which is
>    10) to be freed.
> 
>    Note that EXT4_C2B(partial->pclu=10) is 160, and EXT4_B2C(145) is 9
>    (so the extents leaf is in a different cluster). Finally
>    ext4_free_blocks(..,block=160, count=16..) is called, which shouldn't
>    be a problem (it should not free block 145).
> 
>    Except that in  ext4_free_blocks() (and later in ext4_mb_clear_bb()
>    and mb_free_blocks()) , block 160 resolves to group 0 bit 9 count 1
>    (which frees block 145 containing extents leaf!!!!), causing block
>    145 to be reused by other inodes while inode 15 still thinks that
>    block 145 contains extents metadata, resulting later in the UAF we see
>    by syzbot.
> 
>    The issue doesn't reproduce with the current patch.
> 
> Test results:
> 
>> kvm-xfstests --kernel $BUILD_DIR/arch/x86/boot/bzImage smoke
> 
> ext4/4k: 7 tests, 1 skipped, 1113 seconds
>    generic/475  Pass     185s
>    generic/476  Pass     184s
>    generic/521  Pass     182s
>    generic/522  Pass     181s
>    generic/642  Pass     185s
>    generic/750  Pass     185s
>    generic/751  Skipped  1s
> Totals: 7 tests, 1 skipped, 0 failures, 0 errors, 1103s
> 
>> kvm-xfstests --kernel $BUILD_DIR/arch/x86/boot/bzImage -c 1k smoke
> 
> ext4/1k: 5 tests, 1 skipped, 755 seconds
>    generic/521  Pass     183s
>    generic/522  Pass     182s
>    generic/642  Pass     186s
>    generic/750  Pass     186s
>    generic/751  Skipped  2s
> Totals: 5 tests, 1 skipped, 0 failures, 0 errors, 739s
> 
> Signed-off-by: Helen Koike <koike@igalia.com>
> Reported-by: syzbot+b73703b873a33d8eb8f6@syzkaller.appspotmail.com
> Closes: https://syzkaller.appspot.com/bug?extid=b73703b873a33d8eb8f6
> 
> ---
> 
> Hi all,
> 
> I'm sorry if the commit message is too verbose. I'm not a file system
> developer, so I tried to be didactic for those like me.
> 
> I was also wondering if I should update the existing macros instead of
> creating new ones, but since I'm not very familiar with this subsystem
> and current discussions I decided to be safe and change only for the use
> case I could test.
> 
> If you think this line of solution make sense I could update the
> existing macros, please just let me know.
> 
> Thanks
> Helen
> 
> ---
>   fs/ext4/ext4.h    | 21 +++++++++++++++++++++
>   fs/ext4/extents.c | 20 ++++++++++----------
>   fs/ext4/mballoc.c |  4 ++--
>   3 files changed, 33 insertions(+), 12 deletions(-)
> 
> diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h
> index 7e8f66ba17f4..83fd6e53c003 100644
> --- a/fs/ext4/ext4.h
> +++ b/fs/ext4/ext4.h
> @@ -315,6 +315,17 @@ struct ext4_io_submit {
>   #define EXT4_B2C(sbi, blk)	((blk) >> (sbi)->s_cluster_bits)
>   /* Translate a cluster number to a block number */
>   #define EXT4_C2B(sbi, cluster)	((cluster) << (sbi)->s_cluster_bits)
> +/*
> + * Translate a physical block number to a cluster number using mballoc's
> + * cluster definition, which accounts for s_first_data_block.  Use these
> + * when tracking partial->pclu so that the cluster numbers are consistent
> + * with what ext4_get_group_no_and_offset() (and thus ext4_free_blocks())
> + * expects.
> + */
> +#define EXT4_MB_B2C(sbi, nr) \
> +	(((nr) - le32_to_cpu((sbi)->s_es->s_first_data_block)) >> (sbi)->s_cluster_bits)
> +#define EXT4_MB_C2B(sbi, cluster) \
> +	(((cluster) << (sbi)->s_cluster_bits) + le32_to_cpu((sbi)->s_es->s_first_data_block))
>   /* Translate # of blks to # of clusters */
>   #define EXT4_NUM_B2C(sbi, blks)	(((blks) + (sbi)->s_cluster_ratio - 1) >> \
>   				 (sbi)->s_cluster_bits)
> @@ -331,6 +342,16 @@ struct ext4_io_submit {
>   				 ((ext4_fsblk_t) (s)->s_cluster_ratio - 1))
>   #define EXT4_LBLK_COFF(s, lblk) ((lblk) &				\
>   				 ((ext4_lblk_t) (s)->s_cluster_ratio - 1))
> +/*
> + * Cluster-offset macros that account for s_first_data_block, consistent
> + * with mballoc's cluster numbering.  Use EXT4_MB_PBLK_COFF when aligning a
> + * physical block number and EXT4_MB_LBLK_COFF when aligning a block count.
> + */
> +#define EXT4_MB_PBLK_COFF(sbi, pblk) \
> +	(((pblk) - le32_to_cpu((sbi)->s_es->s_first_data_block)) & \
> +	 ((ext4_fsblk_t)(sbi)->s_cluster_ratio - 1))
> +#define EXT4_MB_LBLK_COFF(sbi, lblk) \
> +	((lblk) & ((ext4_lblk_t)(sbi)->s_cluster_ratio - 1))
>   
>   /*
>    * Structure of a blocks group descriptor
> diff --git a/fs/ext4/extents.c b/fs/ext4/extents.c
> index 35703dce23a3..38d49f784c22 100644
> --- a/fs/ext4/extents.c
> +++ b/fs/ext4/extents.c
> @@ -2493,13 +2493,13 @@ static int ext4_remove_blocks(handle_t *handle, struct inode *inode,
>   	last_pblk = ext4_ext_pblock(ex) + ee_len - 1;
>   
>   	if (partial->state != initial &&
> -	    partial->pclu != EXT4_B2C(sbi, last_pblk)) {
> +	    partial->pclu != EXT4_MB_B2C(sbi, last_pblk)) {
>   		if (partial->state == tofree) {
>   			flags = get_default_free_blocks_flags(inode);
>   			if (ext4_is_pending(inode, partial->lblk))
>   				flags |= EXT4_FREE_BLOCKS_RERESERVE_CLUSTER;
>   			ext4_free_blocks(handle, inode, NULL,
> -					 EXT4_C2B(sbi, partial->pclu),
> +					 EXT4_MB_C2B(sbi, partial->pclu),
>   					 sbi->s_cluster_ratio, flags);
>   			if (flags & EXT4_FREE_BLOCKS_RERESERVE_CLUSTER)
>   				ext4_rereserve_cluster(inode, partial->lblk);
> @@ -2545,7 +2545,7 @@ static int ext4_remove_blocks(handle_t *handle, struct inode *inode,
>   	ext4_free_blocks(handle, inode, NULL, pblk, num, flags);
>   
>   	/* reset the partial cluster if we've freed past it */
> -	if (partial->state != initial && partial->pclu != EXT4_B2C(sbi, pblk))
> +	if (partial->state != initial && partial->pclu != EXT4_MB_B2C(sbi, pblk))
>   		partial->state = initial;
>   
>   	/*
> @@ -2560,7 +2560,7 @@ static int ext4_remove_blocks(handle_t *handle, struct inode *inode,
>   	 */
>   	if (EXT4_LBLK_COFF(sbi, from) && num == ee_len) {
>   		if (partial->state == initial) {
> -			partial->pclu = EXT4_B2C(sbi, pblk);
> +			partial->pclu = EXT4_MB_B2C(sbi, pblk);
>   			partial->lblk = from;
>   			partial->state = tofree;
>   		}
> @@ -2651,7 +2651,7 @@ ext4_ext_rm_leaf(handle_t *handle, struct inode *inode,
>   			 */
>   			if (sbi->s_cluster_ratio > 1) {
>   				pblk = ext4_ext_pblock(ex);
> -				partial->pclu = EXT4_B2C(sbi, pblk);
> +				partial->pclu = EXT4_MB_B2C(sbi, pblk);
>   				partial->state = nofree;
>   			}
>   			ex--;
> @@ -2766,13 +2766,13 @@ ext4_ext_rm_leaf(handle_t *handle, struct inode *inode,
>   	 */
>   	if (partial->state == tofree && ex >= EXT_FIRST_EXTENT(eh)) {
>   		pblk = ext4_ext_pblock(ex) + ex_ee_len - 1;
> -		if (partial->pclu != EXT4_B2C(sbi, pblk)) {
> +		if (partial->pclu != EXT4_MB_B2C(sbi, pblk)) {
>   			int flags = get_default_free_blocks_flags(inode);
>   
>   			if (ext4_is_pending(inode, partial->lblk))
>   				flags |= EXT4_FREE_BLOCKS_RERESERVE_CLUSTER;
>   			ext4_free_blocks(handle, inode, NULL,
> -					 EXT4_C2B(sbi, partial->pclu),
> +					 EXT4_MB_C2B(sbi, partial->pclu),
>   					 sbi->s_cluster_ratio, flags);
>   			if (flags & EXT4_FREE_BLOCKS_RERESERVE_CLUSTER)
>   				ext4_rereserve_cluster(inode, partial->lblk);
> @@ -2886,7 +2886,7 @@ int ext4_ext_remove_space(struct inode *inode, ext4_lblk_t start,
>   			 */
>   			if (sbi->s_cluster_ratio > 1) {
>   				pblk = ext4_ext_pblock(ex) + end - ee_block + 1;
> -				partial.pclu = EXT4_B2C(sbi, pblk);
> +				partial.pclu = EXT4_MB_B2C(sbi, pblk);
>   				partial.state = nofree;
>   			}
>   
> @@ -2919,7 +2919,7 @@ int ext4_ext_remove_space(struct inode *inode, ext4_lblk_t start,
>   			if (err < 0)
>   				goto out;
>   			if (pblk) {
> -				partial.pclu = EXT4_B2C(sbi, pblk);
> +				partial.pclu = EXT4_MB_B2C(sbi, pblk);
>   				partial.state = nofree;
>   			}
>   		}
> @@ -3041,7 +3041,7 @@ int ext4_ext_remove_space(struct inode *inode, ext4_lblk_t start,
>   		if (ext4_is_pending(inode, partial.lblk))
>   			flags |= EXT4_FREE_BLOCKS_RERESERVE_CLUSTER;
>   		ext4_free_blocks(handle, inode, NULL,
> -				 EXT4_C2B(sbi, partial.pclu),
> +				 EXT4_MB_C2B(sbi, partial.pclu),
>   				 sbi->s_cluster_ratio, flags);
>   		if (flags & EXT4_FREE_BLOCKS_RERESERVE_CLUSTER)
>   			ext4_rereserve_cluster(inode, partial.lblk);
> diff --git a/fs/ext4/mballoc.c b/fs/ext4/mballoc.c
> index 9c7881a4ea75..5c7ad20e4e7a 100644
> --- a/fs/ext4/mballoc.c
> +++ b/fs/ext4/mballoc.c
> @@ -6360,7 +6360,7 @@ void ext4_free_blocks(handle_t *handle, struct inode *inode,
>   	 * blocks at the beginning or the end unless we are explicitly
>   	 * requested to avoid doing so.
>   	 */
> -	overflow = EXT4_PBLK_COFF(sbi, block);
> +	overflow = EXT4_MB_PBLK_COFF(sbi, block);
>   	if (overflow) {
>   		if (flags & EXT4_FREE_BLOCKS_NOFREE_FIRST_CLUSTER) {
>   			overflow = sbi->s_cluster_ratio - overflow;
> @@ -6376,7 +6376,7 @@ void ext4_free_blocks(handle_t *handle, struct inode *inode,
>   		/* The range changed so it's no longer validated */
>   		flags &= ~EXT4_FREE_BLOCKS_VALIDATED;
>   	}
> -	overflow = EXT4_LBLK_COFF(sbi, count);
> +	overflow = EXT4_MB_LBLK_COFF(sbi, count);
>   	if (overflow) {
>   		if (flags & EXT4_FREE_BLOCKS_NOFREE_LAST_CLUSTER) {
>   			if (count > overflow)


^ permalink raw reply

* [PATCH] ext4: fix bigalloc cluster arithmetic when s_first_data_block != 0
From: Helen Koike @ 2026-03-13 15:18 UTC (permalink / raw)
  To: tytso, adilger.kernel, linux-ext4, linux-fsdevel, linux-kernel,
	kernel-dev, koike, syzbot+b73703b873a33d8eb8f6

On filesystems with bigalloc enabled and s_first_data_block != 0,
mballoc defines clusters starting from s_first_data_block, so cluster
N covers blocks [s_first_data_block + N*s_cluster_ratio, ...).

EXT4_B2C/EXT4_C2B perform a plain bit-shift on the absolute block
number, ignoring s_first_data_block.

For instance, with cluster_ratio = 4 and s_first_data_block = 1:

This is how EXT4_B2C/EXT4_C2B view it:

  block:    0   1   2   3   4   5   6   7   8   9  10  11  12 ...
            |___________|   |___________|   |___________|
  cluster:       0               1               2

This is how mballoc views it:

  block:    0   1   2   3   4   5   6   7   8   9  10  11  12 ...
                |___________|   |___________|   |___________|
  cluster:           0               1               2

This causes the following issues:

1) In extents.c, partial->pclu is stored and compared using EXT4_B2C,
   then passed to ext4_free_blocks() via EXT4_C2B. The resulting
   block address is misaligned with mballoc's cluster boundaries,
   causing the wrong cluster to be freed.

2) In ext4_free_blocks(), EXT4_PBLK_COFF uses the same plain
   bit-mask, so the intra-cluster offset of the start block is
   computed incorrectly, misaligning the freed range.

Introduce four macros that subtract s_first_data_block before
operating, matching the coordinate system used by mballoc:

  EXT4_MB_B2C(sbi, blk)        block -> cluster number
  EXT4_MB_C2B(sbi, cluster)    cluster -> first block of cluster
  EXT4_MB_PBLK_COFF(sbi, blk)  intra-cluster offset of a block
  EXT4_MB_LBLK_COFF(sbi, n)    intra-cluster offset of a block count

Use EXT4_MB_B2C/EXT4_MB_C2B for all partial->pclu operations in
extents.c, and EXT4_MB_PBLK_COFF/EXT4_MB_LBLK_COFF in the alignment
prologue of ext4_free_blocks().

Regarding the issue reported by syzbot:

  Context: s_first_data_block=1, cluster size 16 and block 145 contains
  a extents leaf for inode 15.
  When an extent is removed (block 145 went from 7 to 6 valid extent
  entries), ext4_ext_rm_leaf() sees the cluster partial->pclu (which is
  10) to be freed.

  Note that EXT4_C2B(partial->pclu=10) is 160, and EXT4_B2C(145) is 9
  (so the extents leaf is in a different cluster). Finally
  ext4_free_blocks(..,block=160, count=16..) is called, which shouldn't
  be a problem (it should not free block 145).

  Except that in  ext4_free_blocks() (and later in ext4_mb_clear_bb()
  and mb_free_blocks()) , block 160 resolves to group 0 bit 9 count 1
  (which frees block 145 containing extents leaf!!!!), causing block
  145 to be reused by other inodes while inode 15 still thinks that
  block 145 contains extents metadata, resulting later in the UAF we see
  by syzbot.

  The issue doesn't reproduce with the current patch.

Test results:

> kvm-xfstests --kernel $BUILD_DIR/arch/x86/boot/bzImage smoke

ext4/4k: 7 tests, 1 skipped, 1113 seconds
  generic/475  Pass     185s
  generic/476  Pass     184s
  generic/521  Pass     182s
  generic/522  Pass     181s
  generic/642  Pass     185s
  generic/750  Pass     185s
  generic/751  Skipped  1s
Totals: 7 tests, 1 skipped, 0 failures, 0 errors, 1103s

> kvm-xfstests --kernel $BUILD_DIR/arch/x86/boot/bzImage -c 1k smoke

ext4/1k: 5 tests, 1 skipped, 755 seconds
  generic/521  Pass     183s
  generic/522  Pass     182s
  generic/642  Pass     186s
  generic/750  Pass     186s
  generic/751  Skipped  2s
Totals: 5 tests, 1 skipped, 0 failures, 0 errors, 739s

Signed-off-by: Helen Koike <koike@igalia.com>
Reported-by: syzbot+b73703b873a33d8eb8f6@syzkaller.appspotmail.com
Closes: https://syzkaller.appspot.com/bug?extid=b73703b873a33d8eb8f6

---

Hi all,

I'm sorry if the commit message is too verbose. I'm not a file system
developer, so I tried to be didactic for those like me.

I was also wondering if I should update the existing macros instead of
creating new ones, but since I'm not very familiar with this subsystem
and current discussions I decided to be safe and change only for the use
case I could test.

If you think this line of solution make sense I could update the
existing macros, please just let me know.

Thanks
Helen

---
 fs/ext4/ext4.h    | 21 +++++++++++++++++++++
 fs/ext4/extents.c | 20 ++++++++++----------
 fs/ext4/mballoc.c |  4 ++--
 3 files changed, 33 insertions(+), 12 deletions(-)

diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h
index 7e8f66ba17f4..83fd6e53c003 100644
--- a/fs/ext4/ext4.h
+++ b/fs/ext4/ext4.h
@@ -315,6 +315,17 @@ struct ext4_io_submit {
 #define EXT4_B2C(sbi, blk)	((blk) >> (sbi)->s_cluster_bits)
 /* Translate a cluster number to a block number */
 #define EXT4_C2B(sbi, cluster)	((cluster) << (sbi)->s_cluster_bits)
+/*
+ * Translate a physical block number to a cluster number using mballoc's
+ * cluster definition, which accounts for s_first_data_block.  Use these
+ * when tracking partial->pclu so that the cluster numbers are consistent
+ * with what ext4_get_group_no_and_offset() (and thus ext4_free_blocks())
+ * expects.
+ */
+#define EXT4_MB_B2C(sbi, nr) \
+	(((nr) - le32_to_cpu((sbi)->s_es->s_first_data_block)) >> (sbi)->s_cluster_bits)
+#define EXT4_MB_C2B(sbi, cluster) \
+	(((cluster) << (sbi)->s_cluster_bits) + le32_to_cpu((sbi)->s_es->s_first_data_block))
 /* Translate # of blks to # of clusters */
 #define EXT4_NUM_B2C(sbi, blks)	(((blks) + (sbi)->s_cluster_ratio - 1) >> \
 				 (sbi)->s_cluster_bits)
@@ -331,6 +342,16 @@ struct ext4_io_submit {
 				 ((ext4_fsblk_t) (s)->s_cluster_ratio - 1))
 #define EXT4_LBLK_COFF(s, lblk) ((lblk) &				\
 				 ((ext4_lblk_t) (s)->s_cluster_ratio - 1))
+/*
+ * Cluster-offset macros that account for s_first_data_block, consistent
+ * with mballoc's cluster numbering.  Use EXT4_MB_PBLK_COFF when aligning a
+ * physical block number and EXT4_MB_LBLK_COFF when aligning a block count.
+ */
+#define EXT4_MB_PBLK_COFF(sbi, pblk) \
+	(((pblk) - le32_to_cpu((sbi)->s_es->s_first_data_block)) & \
+	 ((ext4_fsblk_t)(sbi)->s_cluster_ratio - 1))
+#define EXT4_MB_LBLK_COFF(sbi, lblk) \
+	((lblk) & ((ext4_lblk_t)(sbi)->s_cluster_ratio - 1))
 
 /*
  * Structure of a blocks group descriptor
diff --git a/fs/ext4/extents.c b/fs/ext4/extents.c
index 35703dce23a3..38d49f784c22 100644
--- a/fs/ext4/extents.c
+++ b/fs/ext4/extents.c
@@ -2493,13 +2493,13 @@ static int ext4_remove_blocks(handle_t *handle, struct inode *inode,
 	last_pblk = ext4_ext_pblock(ex) + ee_len - 1;
 
 	if (partial->state != initial &&
-	    partial->pclu != EXT4_B2C(sbi, last_pblk)) {
+	    partial->pclu != EXT4_MB_B2C(sbi, last_pblk)) {
 		if (partial->state == tofree) {
 			flags = get_default_free_blocks_flags(inode);
 			if (ext4_is_pending(inode, partial->lblk))
 				flags |= EXT4_FREE_BLOCKS_RERESERVE_CLUSTER;
 			ext4_free_blocks(handle, inode, NULL,
-					 EXT4_C2B(sbi, partial->pclu),
+					 EXT4_MB_C2B(sbi, partial->pclu),
 					 sbi->s_cluster_ratio, flags);
 			if (flags & EXT4_FREE_BLOCKS_RERESERVE_CLUSTER)
 				ext4_rereserve_cluster(inode, partial->lblk);
@@ -2545,7 +2545,7 @@ static int ext4_remove_blocks(handle_t *handle, struct inode *inode,
 	ext4_free_blocks(handle, inode, NULL, pblk, num, flags);
 
 	/* reset the partial cluster if we've freed past it */
-	if (partial->state != initial && partial->pclu != EXT4_B2C(sbi, pblk))
+	if (partial->state != initial && partial->pclu != EXT4_MB_B2C(sbi, pblk))
 		partial->state = initial;
 
 	/*
@@ -2560,7 +2560,7 @@ static int ext4_remove_blocks(handle_t *handle, struct inode *inode,
 	 */
 	if (EXT4_LBLK_COFF(sbi, from) && num == ee_len) {
 		if (partial->state == initial) {
-			partial->pclu = EXT4_B2C(sbi, pblk);
+			partial->pclu = EXT4_MB_B2C(sbi, pblk);
 			partial->lblk = from;
 			partial->state = tofree;
 		}
@@ -2651,7 +2651,7 @@ ext4_ext_rm_leaf(handle_t *handle, struct inode *inode,
 			 */
 			if (sbi->s_cluster_ratio > 1) {
 				pblk = ext4_ext_pblock(ex);
-				partial->pclu = EXT4_B2C(sbi, pblk);
+				partial->pclu = EXT4_MB_B2C(sbi, pblk);
 				partial->state = nofree;
 			}
 			ex--;
@@ -2766,13 +2766,13 @@ ext4_ext_rm_leaf(handle_t *handle, struct inode *inode,
 	 */
 	if (partial->state == tofree && ex >= EXT_FIRST_EXTENT(eh)) {
 		pblk = ext4_ext_pblock(ex) + ex_ee_len - 1;
-		if (partial->pclu != EXT4_B2C(sbi, pblk)) {
+		if (partial->pclu != EXT4_MB_B2C(sbi, pblk)) {
 			int flags = get_default_free_blocks_flags(inode);
 
 			if (ext4_is_pending(inode, partial->lblk))
 				flags |= EXT4_FREE_BLOCKS_RERESERVE_CLUSTER;
 			ext4_free_blocks(handle, inode, NULL,
-					 EXT4_C2B(sbi, partial->pclu),
+					 EXT4_MB_C2B(sbi, partial->pclu),
 					 sbi->s_cluster_ratio, flags);
 			if (flags & EXT4_FREE_BLOCKS_RERESERVE_CLUSTER)
 				ext4_rereserve_cluster(inode, partial->lblk);
@@ -2886,7 +2886,7 @@ int ext4_ext_remove_space(struct inode *inode, ext4_lblk_t start,
 			 */
 			if (sbi->s_cluster_ratio > 1) {
 				pblk = ext4_ext_pblock(ex) + end - ee_block + 1;
-				partial.pclu = EXT4_B2C(sbi, pblk);
+				partial.pclu = EXT4_MB_B2C(sbi, pblk);
 				partial.state = nofree;
 			}
 
@@ -2919,7 +2919,7 @@ int ext4_ext_remove_space(struct inode *inode, ext4_lblk_t start,
 			if (err < 0)
 				goto out;
 			if (pblk) {
-				partial.pclu = EXT4_B2C(sbi, pblk);
+				partial.pclu = EXT4_MB_B2C(sbi, pblk);
 				partial.state = nofree;
 			}
 		}
@@ -3041,7 +3041,7 @@ int ext4_ext_remove_space(struct inode *inode, ext4_lblk_t start,
 		if (ext4_is_pending(inode, partial.lblk))
 			flags |= EXT4_FREE_BLOCKS_RERESERVE_CLUSTER;
 		ext4_free_blocks(handle, inode, NULL,
-				 EXT4_C2B(sbi, partial.pclu),
+				 EXT4_MB_C2B(sbi, partial.pclu),
 				 sbi->s_cluster_ratio, flags);
 		if (flags & EXT4_FREE_BLOCKS_RERESERVE_CLUSTER)
 			ext4_rereserve_cluster(inode, partial.lblk);
diff --git a/fs/ext4/mballoc.c b/fs/ext4/mballoc.c
index 9c7881a4ea75..5c7ad20e4e7a 100644
--- a/fs/ext4/mballoc.c
+++ b/fs/ext4/mballoc.c
@@ -6360,7 +6360,7 @@ void ext4_free_blocks(handle_t *handle, struct inode *inode,
 	 * blocks at the beginning or the end unless we are explicitly
 	 * requested to avoid doing so.
 	 */
-	overflow = EXT4_PBLK_COFF(sbi, block);
+	overflow = EXT4_MB_PBLK_COFF(sbi, block);
 	if (overflow) {
 		if (flags & EXT4_FREE_BLOCKS_NOFREE_FIRST_CLUSTER) {
 			overflow = sbi->s_cluster_ratio - overflow;
@@ -6376,7 +6376,7 @@ void ext4_free_blocks(handle_t *handle, struct inode *inode,
 		/* The range changed so it's no longer validated */
 		flags &= ~EXT4_FREE_BLOCKS_VALIDATED;
 	}
-	overflow = EXT4_LBLK_COFF(sbi, count);
+	overflow = EXT4_MB_LBLK_COFF(sbi, count);
 	if (overflow) {
 		if (flags & EXT4_FREE_BLOCKS_NOFREE_LAST_CLUSTER) {
 			if (count > overflow)
-- 
2.53.0


^ permalink raw reply related


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox