From: Chao Yu via Linux-f2fs-devel <linux-f2fs-devel@lists.sourceforge.net>
To: jaegeuk@kernel.org, linux-f2fs-devel@lists.sourceforge.net
Cc: Xiaole He <hexiaole1994@126.com>
Subject: Re: [f2fs-dev] [PATCH v2] f2fs: fix age extent cache insertion skip on counter overflow
Date: Wed, 5 Nov 2025 16:12:03 +0800 [thread overview]
Message-ID: <ea199c99-7f5f-4381-a4ae-9527792c3122@kernel.org> (raw)
In-Reply-To: <20251027092341.5011-1-hexiaole1994@126.com>
Jaegeuk,
Just in case, you may missed this patch.
On 10/27/25 17:23, Xiaole He wrote:
> The age extent cache uses last_blocks (derived from
> allocated_data_blocks) to determine data age. However, there's a
> conflict between the deletion
> marker (last_blocks=0) and legitimate last_blocks=0 cases when
> allocated_data_blocks overflows to 0 after reaching ULLONG_MAX.
>
> In this case, valid extents are incorrectly skipped due to the
> "if (!tei->last_blocks)" check in __update_extent_tree_range().
>
> This patch fixes the issue by:
> 1. Reserving ULLONG_MAX as an invalid/deletion marker
> 2. Limiting allocated_data_blocks to range [0, ULLONG_MAX-1]
> 3. Using F2FS_EXTENT_AGE_INVALID for deletion scenarios
> 4. Adjusting overflow age calculation from ULLONG_MAX to (ULLONG_MAX-1)
>
> Reproducer (using a patched kernel with allocated_data_blocks
> initialized to ULLONG_MAX - 3 for quick testing):
>
> Step 1: Mount and check initial state
> # dd if=/dev/zero of=/tmp/test.img bs=1M count=100
> # mkfs.f2fs -f /tmp/test.img
> # mkdir -p /mnt/f2fs_test
> # mount -t f2fs -o loop,age_extent_cache /tmp/test.img /mnt/f2fs_test
> # cat /sys/kernel/debug/f2fs/status | grep -A 4 "Block Age"
> Allocated Data Blocks: 18446744073709551612 # ULLONG_MAX - 3
> Inner Struct Count: tree: 1(0), node: 0
>
> Step 2: Create files and write data to trigger overflow
> # touch /mnt/f2fs_test/{1,2,3,4}.txt; sync
> # cat /sys/kernel/debug/f2fs/status | grep -A 4 "Block Age"
> Allocated Data Blocks: 18446744073709551613 # ULLONG_MAX - 2
> Inner Struct Count: tree: 5(0), node: 1
>
> # dd if=/dev/urandom of=/mnt/f2fs_test/1.txt bs=4K count=1; sync
> # cat /sys/kernel/debug/f2fs/status | grep -A 4 "Block Age"
> Allocated Data Blocks: 18446744073709551614 # ULLONG_MAX - 1
> Inner Struct Count: tree: 5(0), node: 2
>
> # dd if=/dev/urandom of=/mnt/f2fs_test/2.txt bs=4K count=1; sync
> # cat /sys/kernel/debug/f2fs/status | grep -A 4 "Block Age"
> Allocated Data Blocks: 18446744073709551615 # ULLONG_MAX
> Inner Struct Count: tree: 5(0), node: 3
>
> # dd if=/dev/urandom of=/mnt/f2fs_test/3.txt bs=4K count=1; sync
> # cat /sys/kernel/debug/f2fs/status | grep -A 4 "Block Age"
> Allocated Data Blocks: 0 # Counter overflowed!
> Inner Struct Count: tree: 5(0), node: 4
>
> Step 3: Trigger the bug - next write should create node but gets skipped
> # dd if=/dev/urandom of=/mnt/f2fs_test/4.txt bs=4K count=1; sync
> # cat /sys/kernel/debug/f2fs/status | grep -A 4 "Block Age"
> Allocated Data Blocks: 1
> Inner Struct Count: tree: 5(0), node: 4
>
> Expected: node: 5 (new extent node for 4.txt)
> Actual: node: 4 (extent insertion was incorrectly skipped due to
> last_blocks = allocated_data_blocks = 0 in __get_new_block_age)
>
> After this fix, the extent node is correctly inserted and node count
> becomes 5 as expected.
>
> Fixes: 71644dff4811 ("f2fs: add block_age-based extent cache")
> Cc: stable@kernel.org
> Signed-off-by: Xiaole He <hexiaole1994@126.com>
> ---
> Changes in v2:
> - Added Fixes tag and Cc stable
> - Updated reproducer to use 'grep -A 4' for better output clarity
> ---
> fs/f2fs/extent_cache.c | 5 +++--
> fs/f2fs/f2fs.h | 6 ++++++
> fs/f2fs/segment.c | 9 +++++++--
> 3 files changed, 16 insertions(+), 4 deletions(-)
>
> diff --git a/fs/f2fs/extent_cache.c b/fs/f2fs/extent_cache.c
> index 33e09c453c70..0ed84cc065a7 100644
> --- a/fs/f2fs/extent_cache.c
> +++ b/fs/f2fs/extent_cache.c
> @@ -808,7 +808,7 @@ static void __update_extent_tree_range(struct inode *inode,
> }
> goto out_read_extent_cache;
> update_age_extent_cache:
> - if (!tei->last_blocks)
> + if (tei->last_blocks == F2FS_EXTENT_AGE_INVALID)
> goto out_read_extent_cache;
>
> __set_extent_info(&ei, fofs, len, 0, false,
> @@ -912,7 +912,7 @@ static int __get_new_block_age(struct inode *inode, struct extent_info *ei,
> cur_age = cur_blocks - tei.last_blocks;
> else
> /* allocated_data_blocks overflow */
> - cur_age = ULLONG_MAX - tei.last_blocks + cur_blocks;
> + cur_age = (ULLONG_MAX - 1) - tei.last_blocks + cur_blocks;
>
> if (tei.age)
> ei->age = __calculate_block_age(sbi, cur_age, tei.age);
> @@ -1114,6 +1114,7 @@ void f2fs_update_age_extent_cache_range(struct dnode_of_data *dn,
> struct extent_info ei = {
> .fofs = fofs,
> .len = len,
> + .last_blocks = F2FS_EXTENT_AGE_INVALID,
> };
>
> if (!__may_extent_tree(dn->inode, EX_BLOCK_AGE))
> diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h
> index 5b4e9548a231..fa3c676adc30 100644
> --- a/fs/f2fs/f2fs.h
> +++ b/fs/f2fs/f2fs.h
> @@ -707,6 +707,12 @@ enum extent_type {
> NR_EXTENT_CACHES,
> };
>
> +/*
> + * Reserved value to mark invalid age extents, hence valid block range
> + * from 0 to ULLONG_MAX-1
> + */
> +#define F2FS_EXTENT_AGE_INVALID ULLONG_MAX
> +
> struct extent_info {
> unsigned int fofs; /* start offset in a file */
> unsigned int len; /* length of the extent */
> diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c
> index b45eace879d7..a473cd1fb37d 100644
> --- a/fs/f2fs/segment.c
> +++ b/fs/f2fs/segment.c
> @@ -3863,8 +3863,13 @@ int f2fs_allocate_data_block(struct f2fs_sb_info *sbi, struct folio *folio,
> locate_dirty_segment(sbi, GET_SEGNO(sbi, old_blkaddr));
> locate_dirty_segment(sbi, GET_SEGNO(sbi, *new_blkaddr));
>
> - if (IS_DATASEG(curseg->seg_type))
> - atomic64_inc(&sbi->allocated_data_blocks);
> + if (IS_DATASEG(curseg->seg_type)) {
> + unsigned long long new_val;
> +
> + new_val = atomic64_inc_return(&sbi->allocated_data_blocks);
> + if (unlikely(new_val == ULLONG_MAX))
> + atomic64_set(&sbi->allocated_data_blocks, 0);
> + }
>
> up_write(&sit_i->sentry_lock);
>
_______________________________________________
Linux-f2fs-devel mailing list
Linux-f2fs-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/linux-f2fs-devel
next prev parent reply other threads:[~2025-11-05 8:12 UTC|newest]
Thread overview: 6+ messages / expand[flat|nested] mbox.gz Atom feed top
2025-10-23 3:54 [f2fs-dev] [PATCH v1] f2fs: fix age extent cache insertion skip on counter overflow Xiaole He
2025-10-27 8:04 ` Chao Yu via Linux-f2fs-devel
2025-10-27 9:23 ` [f2fs-dev] [PATCH v2] " Xiaole He
2025-10-27 10:55 ` Chao Yu via Linux-f2fs-devel
2025-11-05 8:12 ` Chao Yu via Linux-f2fs-devel [this message]
2025-11-11 22:50 ` patchwork-bot+f2fs--- via Linux-f2fs-devel
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=ea199c99-7f5f-4381-a4ae-9527792c3122@kernel.org \
--to=linux-f2fs-devel@lists.sourceforge.net \
--cc=chao@kernel.org \
--cc=hexiaole1994@126.com \
--cc=jaegeuk@kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).