* [RFC PATCH 0/3] f2fs: reduce zoned LFS memory by sharing SIT valid maps
@ 2026-03-09 10:36 'wallentx
2026-03-09 10:36 ` [RFC PATCH 1/3] f2fs: prepare cur_valid_map for safe lockless access 'wallentx
` (2 more replies)
0 siblings, 3 replies; 6+ messages in thread
From: 'wallentx @ 2026-03-09 10:36 UTC (permalink / raw)
To: linux-f2fs-devel; +Cc: jaegeuk, chao, linux-kernel, wallentx
From: wallentx <william.allentx@gmail.com>
This series reduces SIT-related memory overhead on zoned F2FS.
For zoned devices, F2FS operates in LFS mode. In that configuration SSR
is not used, IPU is not allowed, and discard defaults to section
granularity. That leaves a large amount of per-segment SIT bitmap
storage representing segments that are trivially empty or trivially
full.
The core change lets empty and fully valid segments reuse shared
zero/full SIT valid maps, while active or partial segments keep private
maps.
The series is split as follows:
- [1/3] prepare lockless cur_valid_map readers for later pointer
replacement
- [2/3] introduce shared zero/full SIT valid maps for zoned LFS, keep
private maps only for active or partial segments, invalidate scanned
SIT metadata pages after mount-time rebuild, and reject
checkpoint=disable because its accounting model does not fit the
collapsed shared-SIT representation
- [3/3] add shared_sit_check/noshared_sit_check to make the remaining
CONFIG_F2FS_CHECK_FS mirror overhead optional without changing the
default behavior
On a test system with 43 HM-SMR zoned volumes (~550 TB total) and
CONFIG_F2FS_CHECK_FS=y, static F2FS memory on top of jaegeuk/f2fs dev
at 5f04e90eedd0 changed as follows:
- current behavior: 58.91 GiB
- patch 2: 27.70 GiB
- patch 2 + patch 3 with noshared_sit_check: 12.10 GiB
The same design has also been exercised for several months on a kernel
based on v6.18 on the same host. This series has also been built,
booted, and measured on top of jaegeuk/f2fs dev at 5f04e90eedd0.
Feedback is especially welcome on:
- the 3-way split
- the checkpoint=disable restriction in shared-SIT mode
- whether shared_sit_check/noshared_sit_check is the right interface
for the remaining CHECK_FS mirror cost
wallentx (3):
f2fs: prepare cur_valid_map for safe lockless access
f2fs: reduce zoned LFS memory by sharing SIT valid maps
f2fs: add mount option to disable shared SIT mirror checks
Documentation/filesystems/f2fs.rst | 11 +
fs/f2fs/checkpoint.c | 4 +-
fs/f2fs/debug.c | 21 +-
fs/f2fs/f2fs.h | 22 ++
fs/f2fs/gc.c | 29 +-
fs/f2fs/segment.c | 436 ++++++++++++++++++++++++-----
fs/f2fs/segment.h | 31 +-
fs/f2fs/super.c | 25 ++
fs/f2fs/sysfs.c | 8 +-
9 files changed, 494 insertions(+), 93 deletions(-)
--
2.53.0
^ permalink raw reply [flat|nested] 6+ messages in thread* [RFC PATCH 1/3] f2fs: prepare cur_valid_map for safe lockless access 2026-03-09 10:36 [RFC PATCH 0/3] f2fs: reduce zoned LFS memory by sharing SIT valid maps 'wallentx @ 2026-03-09 10:36 ` 'wallentx 2026-03-09 10:36 ` [RFC PATCH 2/3] f2fs: reduce zoned LFS memory by sharing SIT valid maps 'wallentx 2026-03-09 10:37 ` [RFC PATCH 3/3] f2fs: add mount option to disable shared SIT mirror checks 'wallentx 2 siblings, 0 replies; 6+ messages in thread From: 'wallentx @ 2026-03-09 10:36 UTC (permalink / raw) To: linux-f2fs-devel; +Cc: jaegeuk, chao, linux-kernel, wallentx From: wallentx <william.allentx@gmail.com> checkpoint validation and segment_bits_seq_show() can read cur_valid_map without taking the segment lock. A following patch will allow zoned LFS segments to switch between shared and private SIT valid maps, so those readers need to tolerate pointer publication safely. Prepare for that by reading cur_valid_map under RCU in the lockless paths and by publishing the initial map pointer with RCU semantics. This is preparatory and does not change the SIT layout or allocation policy yet. Signed-off-by: wallentx <william.allentx@gmail.com> --- fs/f2fs/checkpoint.c | 4 +++- fs/f2fs/segment.c | 3 ++- fs/f2fs/sysfs.c | 9 ++++++++- 3 files changed, 13 insertions(+), 3 deletions(-) diff --git a/fs/f2fs/checkpoint.c b/fs/f2fs/checkpoint.c index 6dd39b7de11a..7b196d9df0f7 100644 --- a/fs/f2fs/checkpoint.c +++ b/fs/f2fs/checkpoint.c @@ -354,7 +354,9 @@ static bool __is_bitmap_valid(struct f2fs_sb_info *sbi, block_t blkaddr, offset = GET_BLKOFF_FROM_SEG0(sbi, blkaddr); se = get_seg_entry(sbi, segno); - exist = f2fs_test_bit(offset, se->cur_valid_map); + rcu_read_lock(); + exist = f2fs_test_bit(offset, rcu_dereference(se->cur_valid_map)); + rcu_read_unlock(); /* skip data, if we already have an error in checkpoint. */ if (unlikely(f2fs_cp_error(sbi))) diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index 23faf6725632..c9cfc8f17698 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -4845,7 +4845,8 @@ static int build_sit_info(struct f2fs_sb_info *sbi) bitmap = sit_i->bitmap; for (start = 0; start < MAIN_SEGS(sbi); start++) { - sit_i->sentries[start].cur_valid_map = bitmap; + rcu_assign_pointer(sit_i->sentries[start].cur_valid_map, + bitmap); bitmap += SIT_VBLOCK_MAP_SIZE; sit_i->sentries[start].ckpt_valid_map = bitmap; diff --git a/fs/f2fs/sysfs.c b/fs/f2fs/sysfs.c index 969e06b65b04..9c79f7b63583 100644 --- a/fs/f2fs/sysfs.c +++ b/fs/f2fs/sysfs.c @@ -1782,11 +1782,18 @@ static int __maybe_unused segment_bits_seq_show(struct seq_file *seq, for (i = 0; i < total_segs; i++) { struct seg_entry *se = get_seg_entry(sbi, i); + unsigned char map[SIT_VBLOCK_MAP_SIZE]; seq_printf(seq, "%-10d", i); seq_printf(seq, "%d|%-3u|", se->type, se->valid_blocks); + + rcu_read_lock(); + memcpy(map, rcu_dereference(se->cur_valid_map), + SIT_VBLOCK_MAP_SIZE); + rcu_read_unlock(); + for (j = 0; j < SIT_VBLOCK_MAP_SIZE; j++) - seq_printf(seq, " %.2x", se->cur_valid_map[j]); + seq_printf(seq, " %.2x", map[j]); seq_printf(seq, "| %llx", se->mtime); seq_putc(seq, '\n'); } -- 2.53.0 ^ permalink raw reply related [flat|nested] 6+ messages in thread
* [RFC PATCH 2/3] f2fs: reduce zoned LFS memory by sharing SIT valid maps 2026-03-09 10:36 [RFC PATCH 0/3] f2fs: reduce zoned LFS memory by sharing SIT valid maps 'wallentx 2026-03-09 10:36 ` [RFC PATCH 1/3] f2fs: prepare cur_valid_map for safe lockless access 'wallentx @ 2026-03-09 10:36 ` 'wallentx 2026-03-14 5:08 ` Chao Yu 2026-03-09 10:37 ` [RFC PATCH 3/3] f2fs: add mount option to disable shared SIT mirror checks 'wallentx 2 siblings, 1 reply; 6+ messages in thread From: 'wallentx @ 2026-03-09 10:36 UTC (permalink / raw) To: linux-f2fs-devel; +Cc: jaegeuk, chao, linux-kernel, wallentx From: wallentx <william.allentx@gmail.com> For zoned devices, F2FS only allows LFS mode. In that configuration SSR is not used, IPU is not allowed, and discard defaults to section granularity. Even so, F2FS still allocates per-segment cur/ckpt valid maps for every segment, including the common cases where a segment is trivially empty or trivially full. Reduce that overhead by introducing shared zero/full SIT valid maps for zoned LFS. Empty inactive segments point at the shared zero map, full inactive segments point at the shared full map, and only active or partially valid segments keep private maps. Update SIT rebuild and runtime updates to move segments between shared and private maps safely, and retire replaced private maps with RCU. Also invalidate scanned SIT metadata pages after mount-time rebuild so META_MAPPING does not retain the full SIT scan, update memory reporting to reflect the new layout, and reject checkpoint=disable because its checkpoint-era validity accounting does not fit the collapsed shared-SIT representation. On a test system with 43 HM-SMR zoned volumes (~550 TB total), CONFIG_F2FS_CHECK_FS=y, and this patch applied on top of jaegeuk/f2fs dev at 5f04e90eedd0, static F2FS memory dropped from 58.91 GiB to 27.70 GiB. Signed-off-by: wallentx <william.allentx@gmail.com> --- fs/f2fs/debug.c | 20 ++- fs/f2fs/f2fs.h | 6 + fs/f2fs/segment.c | 338 ++++++++++++++++++++++++++++++++++++++++++---- fs/f2fs/segment.h | 3 + fs/f2fs/super.c | 4 + fs/f2fs/sysfs.c | 3 +- 6 files changed, 341 insertions(+), 33 deletions(-) diff --git a/fs/f2fs/debug.c b/fs/f2fs/debug.c index af88db8fdb71..d8bfdac5c1e4 100644 --- a/fs/f2fs/debug.c +++ b/fs/f2fs/debug.c @@ -319,9 +319,23 @@ static void update_mem_info(struct f2fs_sb_info *sbi) si->base_mem += sizeof(struct sit_info); si->base_mem += MAIN_SEGS(sbi) * sizeof(struct seg_entry); si->base_mem += f2fs_bitmap_size(MAIN_SEGS(sbi)); - si->base_mem += 2 * SIT_VBLOCK_MAP_SIZE * MAIN_SEGS(sbi); - si->base_mem += SIT_VBLOCK_MAP_SIZE * MAIN_SEGS(sbi); - si->base_mem += SIT_VBLOCK_MAP_SIZE; + + if (f2fs_use_shared_sit_map(sbi)) { + /* shared cur/ckpt maps (zero + full bitmaps) */ + si->base_mem += SIT_VBLOCK_MAP_SIZE * 2; + /* Approximate private bitmaps for active logs */ + si->base_mem += SIT_VBLOCK_MAP_SIZE * NR_CURSEG_TYPE; +#ifdef CONFIG_F2FS_CHECK_FS + si->base_mem += SIT_VBLOCK_MAP_SIZE * MAIN_SEGS(sbi); +#endif + if (f2fs_block_unit_discard(sbi)) + si->base_mem += SIT_VBLOCK_MAP_SIZE * MAIN_SEGS(sbi); + } else { + si->base_mem += 2 * SIT_VBLOCK_MAP_SIZE * MAIN_SEGS(sbi); + si->base_mem += SIT_VBLOCK_MAP_SIZE * MAIN_SEGS(sbi); + si->base_mem += SIT_VBLOCK_MAP_SIZE; + } + if (__is_large_section(sbi)) si->base_mem += MAIN_SECS(sbi) * sizeof(struct sec_entry); si->base_mem += __bitmap_size(sbi, SIT_BITMAP); diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index 8942b2a63cfd..11f3601ffd34 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -4914,6 +4914,12 @@ static inline bool f2fs_lfs_mode(struct f2fs_sb_info *sbi) return F2FS_OPTION(sbi).fs_mode == FS_MODE_LFS; } +/* Share SIT valid maps only for zoned LFS. */ +static inline bool f2fs_use_shared_sit_map(struct f2fs_sb_info *sbi) +{ + return f2fs_sb_has_blkzoned(sbi) && f2fs_lfs_mode(sbi); +} + static inline bool f2fs_is_sequential_zone_area(struct f2fs_sb_info *sbi, block_t blkaddr) { diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index c9cfc8f17698..0dab6b16ba56 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -31,6 +31,24 @@ static struct kmem_cache *discard_entry_slab; static struct kmem_cache *discard_cmd_slab; static struct kmem_cache *sit_entry_set_slab; static struct kmem_cache *revoke_entry_slab; +static struct kmem_cache *sit_bitmap_slab; + +struct f2fs_sit_bitmap { + struct rcu_head rcu; + unsigned char map[SIT_VBLOCK_MAP_SIZE]; +}; + +static void f2fs_free_sit_bitmap_rcu(struct rcu_head *rcu) +{ + struct f2fs_sit_bitmap *b = container_of(rcu, struct f2fs_sit_bitmap, rcu); + + kmem_cache_free(sit_bitmap_slab, b); +} + +static struct f2fs_sit_bitmap *f2fs_sit_bitmap_from_map(void *map) +{ + return container_of(map, struct f2fs_sit_bitmap, map); +} static unsigned long __reverse_ulong(unsigned char *str) { @@ -2444,6 +2462,31 @@ static int update_sit_entry_for_release(struct f2fs_sb_info *sbi, struct seg_ent f2fs_bug_on(sbi, GET_SEGNO(sbi, blkaddr) != GET_SEGNO(sbi, blkaddr + del_count - 1)); + if (f2fs_use_shared_sit_map(sbi)) { + struct sit_info *sit_i = SIT_I(sbi); + + if (se->cur_valid_map == sit_i->bitmap_full) { + struct f2fs_sit_bitmap *b = f2fs_kmem_cache_alloc(sit_bitmap_slab, + GFP_NOFS, true, sbi); + memset(b->map, 0xff, SIT_VBLOCK_MAP_SIZE); + rcu_assign_pointer(se->cur_valid_map, b->map); + se->ckpt_valid_map = b->map; +#ifdef CONFIG_F2FS_CHECK_FS + memcpy(se->cur_valid_map_mir, b->map, SIT_VBLOCK_MAP_SIZE); +#endif + } else if (se->cur_valid_map == sit_i->bitmap_zero) { + /* Should not happen, freeing empty segment */ + struct f2fs_sit_bitmap *b = f2fs_kmem_cache_alloc(sit_bitmap_slab, + GFP_NOFS, true, sbi); + memset(b->map, 0, SIT_VBLOCK_MAP_SIZE); + rcu_assign_pointer(se->cur_valid_map, b->map); + se->ckpt_valid_map = b->map; +#ifdef CONFIG_F2FS_CHECK_FS + memcpy(se->cur_valid_map_mir, b->map, SIT_VBLOCK_MAP_SIZE); +#endif + } + } + for (i = 0; i < del_count; i++) { exist = f2fs_test_and_clear_bit(offset + i, se->cur_valid_map); #ifdef CONFIG_F2FS_CHECK_FS @@ -2478,10 +2521,18 @@ static int update_sit_entry_for_release(struct f2fs_sb_info *sbi, struct seg_ent f2fs_test_and_clear_bit(offset + i, se->discard_map)) sbi->discard_blks++; - if (!f2fs_test_bit(offset + i, se->ckpt_valid_map)) { - se->ckpt_valid_blocks -= 1; - if (__is_large_section(sbi)) - get_sec_entry(sbi, segno)->ckpt_valid_blocks -= 1; + if (se->cur_valid_map != se->ckpt_valid_map) { + if (!f2fs_test_bit(offset + i, se->ckpt_valid_map)) { + se->ckpt_valid_blocks -= 1; + if (__is_large_section(sbi)) + get_sec_entry(sbi, segno)->ckpt_valid_blocks -= 1; + } + } else { + if (exist) { + se->ckpt_valid_blocks -= 1; + if (__is_large_section(sbi)) + get_sec_entry(sbi, segno)->ckpt_valid_blocks -= 1; + } } } @@ -2499,6 +2550,31 @@ static int update_sit_entry_for_alloc(struct f2fs_sb_info *sbi, struct seg_entry bool mir_exist; #endif + if (f2fs_use_shared_sit_map(sbi)) { + struct sit_info *sit_i = SIT_I(sbi); + + if (se->cur_valid_map == sit_i->bitmap_zero) { + struct f2fs_sit_bitmap *b = f2fs_kmem_cache_alloc(sit_bitmap_slab, + GFP_NOFS, true, sbi); + memset(b->map, 0, SIT_VBLOCK_MAP_SIZE); + rcu_assign_pointer(se->cur_valid_map, b->map); + se->ckpt_valid_map = b->map; +#ifdef CONFIG_F2FS_CHECK_FS + memcpy(se->cur_valid_map_mir, b->map, SIT_VBLOCK_MAP_SIZE); +#endif + } else if (se->cur_valid_map == sit_i->bitmap_full) { + /* Should not happen in LFS alloc, but for safety */ + struct f2fs_sit_bitmap *b = f2fs_kmem_cache_alloc(sit_bitmap_slab, + GFP_NOFS, true, sbi); + memset(b->map, 0xff, SIT_VBLOCK_MAP_SIZE); + rcu_assign_pointer(se->cur_valid_map, b->map); + se->ckpt_valid_map = b->map; +#ifdef CONFIG_F2FS_CHECK_FS + memcpy(se->cur_valid_map_mir, b->map, SIT_VBLOCK_MAP_SIZE); +#endif + } + } + exist = f2fs_test_and_set_bit(offset, se->cur_valid_map); #ifdef CONFIG_F2FS_CHECK_FS mir_exist = f2fs_test_and_set_bit(offset, @@ -2525,14 +2601,23 @@ static int update_sit_entry_for_alloc(struct f2fs_sb_info *sbi, struct seg_entry * or newly invalidated. */ if (!is_sbi_flag_set(sbi, SBI_CP_DISABLED)) { - if (!f2fs_test_and_set_bit(offset, se->ckpt_valid_map)) { - se->ckpt_valid_blocks++; - if (__is_large_section(sbi)) - get_sec_entry(sbi, segno)->ckpt_valid_blocks++; + if (se->cur_valid_map != se->ckpt_valid_map) { + if (!f2fs_test_and_set_bit(offset, se->ckpt_valid_map)) { + se->ckpt_valid_blocks++; + if (__is_large_section(sbi)) + get_sec_entry(sbi, segno)->ckpt_valid_blocks++; + } + } else { + if (!exist) { + se->ckpt_valid_blocks++; + if (__is_large_section(sbi)) + get_sec_entry(sbi, segno)->ckpt_valid_blocks++; + } } } - if (!f2fs_test_bit(offset, se->ckpt_valid_map)) { + if (se->cur_valid_map != se->ckpt_valid_map && + !f2fs_test_bit(offset, se->ckpt_valid_map)) { se->ckpt_valid_blocks += del; if (__is_large_section(sbi)) get_sec_entry(sbi, segno)->ckpt_valid_blocks += del; @@ -2582,6 +2667,40 @@ static void update_sit_entry(struct f2fs_sb_info *sbi, block_t blkaddr, int del) if (__is_large_section(sbi)) get_sec_entry(sbi, segno)->valid_blocks += del; + + if (f2fs_use_shared_sit_map(sbi)) { + struct sit_info *sit_i = SIT_I(sbi); + + if (new_vblocks == 0 && + se->cur_valid_map != sit_i->bitmap_zero) { + void *old_map = se->cur_valid_map; + + rcu_assign_pointer(se->cur_valid_map, sit_i->bitmap_zero); + se->ckpt_valid_map = sit_i->bitmap_zero; +#ifdef CONFIG_F2FS_CHECK_FS + memset(se->cur_valid_map_mir, 0, SIT_VBLOCK_MAP_SIZE); +#endif + if (old_map != sit_i->bitmap_zero && + old_map != sit_i->bitmap_full) { + call_rcu(&f2fs_sit_bitmap_from_map(old_map)->rcu, + f2fs_free_sit_bitmap_rcu); + } + } else if (new_vblocks == BLKS_PER_SEG(sbi) && + se->cur_valid_map != sit_i->bitmap_full) { + void *old_map = se->cur_valid_map; + + rcu_assign_pointer(se->cur_valid_map, sit_i->bitmap_full); + se->ckpt_valid_map = sit_i->bitmap_full; +#ifdef CONFIG_F2FS_CHECK_FS + memset(se->cur_valid_map_mir, 0xff, SIT_VBLOCK_MAP_SIZE); +#endif + if (old_map != sit_i->bitmap_zero && + old_map != sit_i->bitmap_full) { + call_rcu(&f2fs_sit_bitmap_from_map(old_map)->rcu, + f2fs_free_sit_bitmap_rcu); + } + } + } } void f2fs_invalidate_blocks(struct f2fs_sb_info *sbi, block_t addr, @@ -4812,6 +4931,7 @@ static int build_sit_info(struct f2fs_sb_info *sbi) char *src_bitmap, *bitmap; unsigned int bitmap_size, main_bitmap_size, sit_bitmap_size; unsigned int discard_map = f2fs_block_unit_discard(sbi) ? 1 : 0; + bool share_map = f2fs_use_shared_sit_map(sbi); /* allocate memory for SIT information */ sit_i = f2fs_kzalloc(sbi, sizeof(struct sit_info), GFP_KERNEL); @@ -4838,28 +4958,73 @@ static int build_sit_info(struct f2fs_sb_info *sbi) #else bitmap_size = MAIN_SEGS(sbi) * SIT_VBLOCK_MAP_SIZE * (2 + discard_map); #endif - sit_i->bitmap = f2fs_kvzalloc(sbi, bitmap_size, GFP_KERNEL); - if (!sit_i->bitmap) - return -ENOMEM; - - bitmap = sit_i->bitmap; - for (start = 0; start < MAIN_SEGS(sbi); start++) { - rcu_assign_pointer(sit_i->sentries[start].cur_valid_map, - bitmap); - bitmap += SIT_VBLOCK_MAP_SIZE; + if (share_map) { + sit_i->bitmap_zero = f2fs_kzalloc(sbi, SIT_VBLOCK_MAP_SIZE, GFP_KERNEL); + if (!sit_i->bitmap_zero) + return -ENOMEM; + sit_i->bitmap_full = f2fs_kzalloc(sbi, SIT_VBLOCK_MAP_SIZE, GFP_KERNEL); + if (!sit_i->bitmap_full) { + kfree(sit_i->bitmap_zero); + sit_i->bitmap_zero = NULL; + return -ENOMEM; + } + memset(sit_i->bitmap_full, 0xff, SIT_VBLOCK_MAP_SIZE); - sit_i->sentries[start].ckpt_valid_map = bitmap; - bitmap += SIT_VBLOCK_MAP_SIZE; + bitmap_size = MAIN_SEGS(sbi) * SIT_VBLOCK_MAP_SIZE * discard_map; +#ifdef CONFIG_F2FS_CHECK_FS + bitmap_size += MAIN_SEGS(sbi) * SIT_VBLOCK_MAP_SIZE; +#endif + if (bitmap_size) { + sit_i->bitmap = f2fs_kvzalloc(sbi, bitmap_size, GFP_KERNEL); + if (!sit_i->bitmap) { + kfree(sit_i->bitmap_full); + kfree(sit_i->bitmap_zero); + sit_i->bitmap_full = NULL; + sit_i->bitmap_zero = NULL; + return -ENOMEM; + } + bitmap = sit_i->bitmap; + } + for (start = 0; start < MAIN_SEGS(sbi); start++) { + rcu_assign_pointer(sit_i->sentries[start].cur_valid_map, + sit_i->bitmap_zero); + sit_i->sentries[start].ckpt_valid_map = + sit_i->bitmap_zero; #ifdef CONFIG_F2FS_CHECK_FS - sit_i->sentries[start].cur_valid_map_mir = bitmap; - bitmap += SIT_VBLOCK_MAP_SIZE; + sit_i->sentries[start].cur_valid_map_mir = + bitmap; + bitmap += SIT_VBLOCK_MAP_SIZE; #endif + if (discard_map) { + sit_i->sentries[start].discard_map = bitmap; + bitmap += SIT_VBLOCK_MAP_SIZE; + } + } + } else { + sit_i->bitmap = f2fs_kvzalloc(sbi, bitmap_size, GFP_KERNEL); + if (!sit_i->bitmap) + return -ENOMEM; + + bitmap = sit_i->bitmap; + + for (start = 0; start < MAIN_SEGS(sbi); start++) { + rcu_assign_pointer(sit_i->sentries[start].cur_valid_map, bitmap); + bitmap += SIT_VBLOCK_MAP_SIZE; - if (discard_map) { - sit_i->sentries[start].discard_map = bitmap; + sit_i->sentries[start].ckpt_valid_map = bitmap; bitmap += SIT_VBLOCK_MAP_SIZE; + +#ifdef CONFIG_F2FS_CHECK_FS + sit_i->sentries[start].cur_valid_map_mir = bitmap; + bitmap += SIT_VBLOCK_MAP_SIZE; +#endif + + if (discard_map) { + sit_i->sentries[start].discard_map = bitmap; + bitmap += SIT_VBLOCK_MAP_SIZE; + } } } @@ -5009,7 +5174,39 @@ static int build_sit_entries(struct f2fs_sb_info *sbi) err = check_block_count(sbi, start, &sit); if (err) return err; - seg_info_from_raw_sit(se, &sit); + + if (f2fs_use_shared_sit_map(sbi)) { + unsigned int vblocks = GET_SIT_VBLOCKS(&sit); + unsigned char *map = NULL; + bool is_active = is_curseg(sbi, start); + + if (vblocks == 0 && !is_active) { + map = sit_i->bitmap_zero; + } else if (vblocks == BLKS_PER_SEG(sbi) && !is_active) { + map = sit_i->bitmap_full; + } else { + struct f2fs_sit_bitmap *b = + f2fs_kmem_cache_alloc(sit_bitmap_slab, + GFP_KERNEL, + false, sbi); + if (!b) + return -ENOMEM; + map = b->map; + memcpy(map, sit.valid_map, SIT_VBLOCK_MAP_SIZE); + } + + se->valid_blocks = vblocks; + se->ckpt_valid_blocks = vblocks; + rcu_assign_pointer(se->cur_valid_map, map); + se->ckpt_valid_map = map; +#ifdef CONFIG_F2FS_CHECK_FS + memcpy(se->cur_valid_map_mir, map, SIT_VBLOCK_MAP_SIZE); +#endif + se->type = GET_SIT_TYPE(&sit); + se->mtime = le64_to_cpu(sit.mtime); + } else { + seg_info_from_raw_sit(se, &sit); + } if (se->type >= NR_PERSISTENT_LOG) { f2fs_err(sbi, "Invalid segment type: %u, segno: %u", @@ -5039,6 +5236,15 @@ static int build_sit_entries(struct f2fs_sb_info *sbi) get_sec_entry(sbi, start)->valid_blocks += se->valid_blocks; } + if (f2fs_use_shared_sit_map(sbi)) { + pgoff_t start_addr = sit_i->sit_base_addr + start_blk; + pgoff_t end_addr = start_addr + readed - 1; + pgoff_t alt_start_addr = start_addr + sit_i->sit_blocks; + pgoff_t alt_end_addr = alt_start_addr + readed - 1; + + invalidate_mapping_pages(META_MAPPING(sbi), start_addr, end_addr); + invalidate_mapping_pages(META_MAPPING(sbi), alt_start_addr, alt_end_addr); + } start_blk += readed; } while (start_blk < sit_blk_cnt); @@ -5065,7 +5271,52 @@ static int build_sit_entries(struct f2fs_sb_info *sbi) err = check_block_count(sbi, start, &sit); if (err) break; - seg_info_from_raw_sit(se, &sit); + + if (f2fs_use_shared_sit_map(sbi)) { + unsigned int vblocks = GET_SIT_VBLOCKS(&sit); + unsigned char *map = se->cur_valid_map; + bool is_active = is_curseg(sbi, start); + + if (vblocks == 0 && !is_active) { + if (map != sit_i->bitmap_zero && + map != sit_i->bitmap_full) + kmem_cache_free(sit_bitmap_slab, + f2fs_sit_bitmap_from_map(map)); + map = sit_i->bitmap_zero; + } else if (vblocks == BLKS_PER_SEG(sbi) && !is_active) { + if (map != sit_i->bitmap_zero && + map != sit_i->bitmap_full) + kmem_cache_free(sit_bitmap_slab, + f2fs_sit_bitmap_from_map(map)); + map = sit_i->bitmap_full; + } else { + if (map == sit_i->bitmap_zero || + map == sit_i->bitmap_full) { + struct f2fs_sit_bitmap *b = + f2fs_kmem_cache_alloc(sit_bitmap_slab, + GFP_KERNEL, + false, sbi); + if (!b) { + up_read(&curseg->journal_rwsem); + return -ENOMEM; + } + map = b->map; + } + memcpy(map, sit.valid_map, SIT_VBLOCK_MAP_SIZE); + } + + se->valid_blocks = vblocks; + se->ckpt_valid_blocks = vblocks; + rcu_assign_pointer(se->cur_valid_map, map); + se->ckpt_valid_map = map; +#ifdef CONFIG_F2FS_CHECK_FS + memcpy(se->cur_valid_map_mir, map, SIT_VBLOCK_MAP_SIZE); +#endif + se->type = GET_SIT_TYPE(&sit); + se->mtime = le64_to_cpu(sit.mtime); + } else { + seg_info_from_raw_sit(se, &sit); + } if (se->type >= NR_PERSISTENT_LOG) { f2fs_err(sbi, "Invalid segment type: %u, segno: %u", @@ -5844,8 +6095,30 @@ static void destroy_sit_info(struct f2fs_sb_info *sbi) if (!sit_i) return; - if (sit_i->sentries) - kvfree(sit_i->bitmap); + if (sit_i->sentries) { + if (f2fs_use_shared_sit_map(sbi)) { + unsigned int start; + + for (start = 0; start < MAIN_SEGS(sbi); start++) { + struct seg_entry *se = &sit_i->sentries[start]; + + if (se->cur_valid_map && + se->cur_valid_map != sit_i->bitmap_zero && + se->cur_valid_map != sit_i->bitmap_full) { + struct f2fs_sit_bitmap *b; + + b = f2fs_sit_bitmap_from_map(se->cur_valid_map); + kmem_cache_free(sit_bitmap_slab, b); + } + } + kfree(sit_i->bitmap_zero); + kfree(sit_i->bitmap_full); + if (sit_i->bitmap) + kvfree(sit_i->bitmap); + } else { + kvfree(sit_i->bitmap); + } + } kfree(sit_i->tmp_map); kvfree(sit_i->sentries); @@ -5898,8 +6171,16 @@ int __init f2fs_create_segment_manager_caches(void) sizeof(struct revoke_entry)); if (!revoke_entry_slab) goto destroy_sit_entry_set; + + sit_bitmap_slab = f2fs_kmem_cache_create("f2fs_sit_bitmap", + sizeof(struct f2fs_sit_bitmap)); + if (!sit_bitmap_slab) + goto destroy_revoke_entry; + return 0; +destroy_revoke_entry: + kmem_cache_destroy(revoke_entry_slab); destroy_sit_entry_set: kmem_cache_destroy(sit_entry_set_slab); destroy_discard_cmd: @@ -5916,4 +6197,5 @@ void f2fs_destroy_segment_manager_caches(void) kmem_cache_destroy(discard_cmd_slab); kmem_cache_destroy(discard_entry_slab); kmem_cache_destroy(revoke_entry_slab); + kmem_cache_destroy(sit_bitmap_slab); } diff --git a/fs/f2fs/segment.h b/fs/f2fs/segment.h index 068845660b0f..cb45cfa7a658 100644 --- a/fs/f2fs/segment.h +++ b/fs/f2fs/segment.h @@ -233,6 +233,9 @@ struct sit_info { unsigned long long dirty_max_mtime; /* rerange candidates in GC_AT */ unsigned int last_victim[MAX_GC_POLICY]; /* last victim segment # */ + + unsigned char *bitmap_zero; /* shared zero bitmap */ + unsigned char *bitmap_full; /* shared full bitmap */ }; struct free_segmap_info { diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c index 8774c60b4be4..83ce88ce12cb 100644 --- a/fs/f2fs/super.c +++ b/fs/f2fs/super.c @@ -2617,6 +2617,10 @@ static int f2fs_disable_checkpoint(struct f2fs_sb_info *sbi) f2fs_err(sbi, "checkpoint=disable on readonly fs"); return -EINVAL; } + if (f2fs_use_shared_sit_map(sbi)) { + f2fs_err(sbi, "checkpoint=disable is not supported in zoned shared SIT mode"); + return -EOPNOTSUPP; + } sbi->sb->s_flags |= SB_ACTIVE; /* check if we need more GC first */ diff --git a/fs/f2fs/sysfs.c b/fs/f2fs/sysfs.c index 9c79f7b63583..2baf349721c9 100644 --- a/fs/f2fs/sysfs.c +++ b/fs/f2fs/sysfs.c @@ -1788,8 +1788,7 @@ static int __maybe_unused segment_bits_seq_show(struct seq_file *seq, seq_printf(seq, "%d|%-3u|", se->type, se->valid_blocks); rcu_read_lock(); - memcpy(map, rcu_dereference(se->cur_valid_map), - SIT_VBLOCK_MAP_SIZE); + memcpy(map, rcu_dereference(se->cur_valid_map), SIT_VBLOCK_MAP_SIZE); rcu_read_unlock(); for (j = 0; j < SIT_VBLOCK_MAP_SIZE; j++) -- 2.53.0 ^ permalink raw reply related [flat|nested] 6+ messages in thread
* Re: [RFC PATCH 2/3] f2fs: reduce zoned LFS memory by sharing SIT valid maps 2026-03-09 10:36 ` [RFC PATCH 2/3] f2fs: reduce zoned LFS memory by sharing SIT valid maps 'wallentx @ 2026-03-14 5:08 ` Chao Yu 0 siblings, 0 replies; 6+ messages in thread From: Chao Yu @ 2026-03-14 5:08 UTC (permalink / raw) To: 'wallentx, linux-f2fs-devel; +Cc: chao, jaegeuk, linux-kernel Hi wallentx, Thanks for your contribution! On 2026/3/9 18:36, 'wallentx wrote: > From: wallentx <william.allentx@gmail.com> > > For zoned devices, F2FS only allows LFS mode. In that configuration SSR > is not used, IPU is not allowed, and discard defaults to section > granularity. Even so, F2FS still allocates per-segment cur/ckpt valid > maps for every segment, including the common cases where a segment is > trivially empty or trivially full. > > Reduce that overhead by introducing shared zero/full SIT valid maps for > zoned LFS. Empty inactive segments point at the shared zero map, full > inactive segments point at the shared full map, and only active or > partially valid segments keep private maps. Update SIT rebuild and > runtime updates to move segments between shared and private maps safely, > and retire replaced private maps with RCU. > > Also invalidate scanned SIT metadata pages after mount-time rebuild so > META_MAPPING does not retain the full SIT scan, update memory reporting > to reflect the new layout, and reject checkpoint=disable because its > checkpoint-era validity accounting does not fit the collapsed shared-SIT > representation. I think that will be a problem, since checkpoint=disable is used in Android product, we can not fail to apply mount option for shared sit map. Can we make this feature optional? Thanks, > > On a test system with 43 HM-SMR zoned volumes (~550 TB total), > CONFIG_F2FS_CHECK_FS=y, and this patch applied on top of jaegeuk/f2fs > dev at 5f04e90eedd0, static F2FS memory dropped from 58.91 GiB to > 27.70 GiB. > > Signed-off-by: wallentx <william.allentx@gmail.com> > --- > fs/f2fs/debug.c | 20 ++- > fs/f2fs/f2fs.h | 6 + > fs/f2fs/segment.c | 338 ++++++++++++++++++++++++++++++++++++++++++---- > fs/f2fs/segment.h | 3 + > fs/f2fs/super.c | 4 + > fs/f2fs/sysfs.c | 3 +- > 6 files changed, 341 insertions(+), 33 deletions(-) > > diff --git a/fs/f2fs/debug.c b/fs/f2fs/debug.c > index af88db8fdb71..d8bfdac5c1e4 100644 > --- a/fs/f2fs/debug.c > +++ b/fs/f2fs/debug.c > @@ -319,9 +319,23 @@ static void update_mem_info(struct f2fs_sb_info *sbi) > si->base_mem += sizeof(struct sit_info); > si->base_mem += MAIN_SEGS(sbi) * sizeof(struct seg_entry); > si->base_mem += f2fs_bitmap_size(MAIN_SEGS(sbi)); > - si->base_mem += 2 * SIT_VBLOCK_MAP_SIZE * MAIN_SEGS(sbi); > - si->base_mem += SIT_VBLOCK_MAP_SIZE * MAIN_SEGS(sbi); > - si->base_mem += SIT_VBLOCK_MAP_SIZE; > + > + if (f2fs_use_shared_sit_map(sbi)) { > + /* shared cur/ckpt maps (zero + full bitmaps) */ > + si->base_mem += SIT_VBLOCK_MAP_SIZE * 2; > + /* Approximate private bitmaps for active logs */ > + si->base_mem += SIT_VBLOCK_MAP_SIZE * NR_CURSEG_TYPE; > +#ifdef CONFIG_F2FS_CHECK_FS > + si->base_mem += SIT_VBLOCK_MAP_SIZE * MAIN_SEGS(sbi); > +#endif > + if (f2fs_block_unit_discard(sbi)) > + si->base_mem += SIT_VBLOCK_MAP_SIZE * MAIN_SEGS(sbi); > + } else { > + si->base_mem += 2 * SIT_VBLOCK_MAP_SIZE * MAIN_SEGS(sbi); > + si->base_mem += SIT_VBLOCK_MAP_SIZE * MAIN_SEGS(sbi); > + si->base_mem += SIT_VBLOCK_MAP_SIZE; > + } > + > if (__is_large_section(sbi)) > si->base_mem += MAIN_SECS(sbi) * sizeof(struct sec_entry); > si->base_mem += __bitmap_size(sbi, SIT_BITMAP); > diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h > index 8942b2a63cfd..11f3601ffd34 100644 > --- a/fs/f2fs/f2fs.h > +++ b/fs/f2fs/f2fs.h > @@ -4914,6 +4914,12 @@ static inline bool f2fs_lfs_mode(struct f2fs_sb_info *sbi) > return F2FS_OPTION(sbi).fs_mode == FS_MODE_LFS; > } > > +/* Share SIT valid maps only for zoned LFS. */ > +static inline bool f2fs_use_shared_sit_map(struct f2fs_sb_info *sbi) > +{ > + return f2fs_sb_has_blkzoned(sbi) && f2fs_lfs_mode(sbi); > +} > + > static inline bool f2fs_is_sequential_zone_area(struct f2fs_sb_info *sbi, > block_t blkaddr) > { > diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c > index c9cfc8f17698..0dab6b16ba56 100644 > --- a/fs/f2fs/segment.c > +++ b/fs/f2fs/segment.c > @@ -31,6 +31,24 @@ static struct kmem_cache *discard_entry_slab; > static struct kmem_cache *discard_cmd_slab; > static struct kmem_cache *sit_entry_set_slab; > static struct kmem_cache *revoke_entry_slab; > +static struct kmem_cache *sit_bitmap_slab; > + > +struct f2fs_sit_bitmap { > + struct rcu_head rcu; > + unsigned char map[SIT_VBLOCK_MAP_SIZE]; > +}; > + > +static void f2fs_free_sit_bitmap_rcu(struct rcu_head *rcu) > +{ > + struct f2fs_sit_bitmap *b = container_of(rcu, struct f2fs_sit_bitmap, rcu); > + > + kmem_cache_free(sit_bitmap_slab, b); > +} > + > +static struct f2fs_sit_bitmap *f2fs_sit_bitmap_from_map(void *map) > +{ > + return container_of(map, struct f2fs_sit_bitmap, map); > +} > > static unsigned long __reverse_ulong(unsigned char *str) > { > @@ -2444,6 +2462,31 @@ static int update_sit_entry_for_release(struct f2fs_sb_info *sbi, struct seg_ent > > f2fs_bug_on(sbi, GET_SEGNO(sbi, blkaddr) != GET_SEGNO(sbi, blkaddr + del_count - 1)); > > + if (f2fs_use_shared_sit_map(sbi)) { > + struct sit_info *sit_i = SIT_I(sbi); > + > + if (se->cur_valid_map == sit_i->bitmap_full) { > + struct f2fs_sit_bitmap *b = f2fs_kmem_cache_alloc(sit_bitmap_slab, > + GFP_NOFS, true, sbi); > + memset(b->map, 0xff, SIT_VBLOCK_MAP_SIZE); > + rcu_assign_pointer(se->cur_valid_map, b->map); > + se->ckpt_valid_map = b->map; > +#ifdef CONFIG_F2FS_CHECK_FS > + memcpy(se->cur_valid_map_mir, b->map, SIT_VBLOCK_MAP_SIZE); > +#endif > + } else if (se->cur_valid_map == sit_i->bitmap_zero) { > + /* Should not happen, freeing empty segment */ > + struct f2fs_sit_bitmap *b = f2fs_kmem_cache_alloc(sit_bitmap_slab, > + GFP_NOFS, true, sbi); > + memset(b->map, 0, SIT_VBLOCK_MAP_SIZE); > + rcu_assign_pointer(se->cur_valid_map, b->map); > + se->ckpt_valid_map = b->map; > +#ifdef CONFIG_F2FS_CHECK_FS > + memcpy(se->cur_valid_map_mir, b->map, SIT_VBLOCK_MAP_SIZE); > +#endif > + } > + } > + > for (i = 0; i < del_count; i++) { > exist = f2fs_test_and_clear_bit(offset + i, se->cur_valid_map); > #ifdef CONFIG_F2FS_CHECK_FS > @@ -2478,10 +2521,18 @@ static int update_sit_entry_for_release(struct f2fs_sb_info *sbi, struct seg_ent > f2fs_test_and_clear_bit(offset + i, se->discard_map)) > sbi->discard_blks++; > > - if (!f2fs_test_bit(offset + i, se->ckpt_valid_map)) { > - se->ckpt_valid_blocks -= 1; > - if (__is_large_section(sbi)) > - get_sec_entry(sbi, segno)->ckpt_valid_blocks -= 1; > + if (se->cur_valid_map != se->ckpt_valid_map) { > + if (!f2fs_test_bit(offset + i, se->ckpt_valid_map)) { > + se->ckpt_valid_blocks -= 1; > + if (__is_large_section(sbi)) > + get_sec_entry(sbi, segno)->ckpt_valid_blocks -= 1; > + } > + } else { > + if (exist) { > + se->ckpt_valid_blocks -= 1; > + if (__is_large_section(sbi)) > + get_sec_entry(sbi, segno)->ckpt_valid_blocks -= 1; > + } > } > } > > @@ -2499,6 +2550,31 @@ static int update_sit_entry_for_alloc(struct f2fs_sb_info *sbi, struct seg_entry > bool mir_exist; > #endif > > + if (f2fs_use_shared_sit_map(sbi)) { > + struct sit_info *sit_i = SIT_I(sbi); > + > + if (se->cur_valid_map == sit_i->bitmap_zero) { > + struct f2fs_sit_bitmap *b = f2fs_kmem_cache_alloc(sit_bitmap_slab, > + GFP_NOFS, true, sbi); > + memset(b->map, 0, SIT_VBLOCK_MAP_SIZE); > + rcu_assign_pointer(se->cur_valid_map, b->map); > + se->ckpt_valid_map = b->map; > +#ifdef CONFIG_F2FS_CHECK_FS > + memcpy(se->cur_valid_map_mir, b->map, SIT_VBLOCK_MAP_SIZE); > +#endif > + } else if (se->cur_valid_map == sit_i->bitmap_full) { > + /* Should not happen in LFS alloc, but for safety */ > + struct f2fs_sit_bitmap *b = f2fs_kmem_cache_alloc(sit_bitmap_slab, > + GFP_NOFS, true, sbi); > + memset(b->map, 0xff, SIT_VBLOCK_MAP_SIZE); > + rcu_assign_pointer(se->cur_valid_map, b->map); > + se->ckpt_valid_map = b->map; > +#ifdef CONFIG_F2FS_CHECK_FS > + memcpy(se->cur_valid_map_mir, b->map, SIT_VBLOCK_MAP_SIZE); > +#endif > + } > + } > + > exist = f2fs_test_and_set_bit(offset, se->cur_valid_map); > #ifdef CONFIG_F2FS_CHECK_FS > mir_exist = f2fs_test_and_set_bit(offset, > @@ -2525,14 +2601,23 @@ static int update_sit_entry_for_alloc(struct f2fs_sb_info *sbi, struct seg_entry > * or newly invalidated. > */ > if (!is_sbi_flag_set(sbi, SBI_CP_DISABLED)) { > - if (!f2fs_test_and_set_bit(offset, se->ckpt_valid_map)) { > - se->ckpt_valid_blocks++; > - if (__is_large_section(sbi)) > - get_sec_entry(sbi, segno)->ckpt_valid_blocks++; > + if (se->cur_valid_map != se->ckpt_valid_map) { > + if (!f2fs_test_and_set_bit(offset, se->ckpt_valid_map)) { > + se->ckpt_valid_blocks++; > + if (__is_large_section(sbi)) > + get_sec_entry(sbi, segno)->ckpt_valid_blocks++; > + } > + } else { > + if (!exist) { > + se->ckpt_valid_blocks++; > + if (__is_large_section(sbi)) > + get_sec_entry(sbi, segno)->ckpt_valid_blocks++; > + } > } > } > > - if (!f2fs_test_bit(offset, se->ckpt_valid_map)) { > + if (se->cur_valid_map != se->ckpt_valid_map && > + !f2fs_test_bit(offset, se->ckpt_valid_map)) { > se->ckpt_valid_blocks += del; > if (__is_large_section(sbi)) > get_sec_entry(sbi, segno)->ckpt_valid_blocks += del; > @@ -2582,6 +2667,40 @@ static void update_sit_entry(struct f2fs_sb_info *sbi, block_t blkaddr, int del) > > if (__is_large_section(sbi)) > get_sec_entry(sbi, segno)->valid_blocks += del; > + > + if (f2fs_use_shared_sit_map(sbi)) { > + struct sit_info *sit_i = SIT_I(sbi); > + > + if (new_vblocks == 0 && > + se->cur_valid_map != sit_i->bitmap_zero) { > + void *old_map = se->cur_valid_map; > + > + rcu_assign_pointer(se->cur_valid_map, sit_i->bitmap_zero); > + se->ckpt_valid_map = sit_i->bitmap_zero; > +#ifdef CONFIG_F2FS_CHECK_FS > + memset(se->cur_valid_map_mir, 0, SIT_VBLOCK_MAP_SIZE); > +#endif > + if (old_map != sit_i->bitmap_zero && > + old_map != sit_i->bitmap_full) { > + call_rcu(&f2fs_sit_bitmap_from_map(old_map)->rcu, > + f2fs_free_sit_bitmap_rcu); > + } > + } else if (new_vblocks == BLKS_PER_SEG(sbi) && > + se->cur_valid_map != sit_i->bitmap_full) { > + void *old_map = se->cur_valid_map; > + > + rcu_assign_pointer(se->cur_valid_map, sit_i->bitmap_full); > + se->ckpt_valid_map = sit_i->bitmap_full; > +#ifdef CONFIG_F2FS_CHECK_FS > + memset(se->cur_valid_map_mir, 0xff, SIT_VBLOCK_MAP_SIZE); > +#endif > + if (old_map != sit_i->bitmap_zero && > + old_map != sit_i->bitmap_full) { > + call_rcu(&f2fs_sit_bitmap_from_map(old_map)->rcu, > + f2fs_free_sit_bitmap_rcu); > + } > + } > + } > } > > void f2fs_invalidate_blocks(struct f2fs_sb_info *sbi, block_t addr, > @@ -4812,6 +4931,7 @@ static int build_sit_info(struct f2fs_sb_info *sbi) > char *src_bitmap, *bitmap; > unsigned int bitmap_size, main_bitmap_size, sit_bitmap_size; > unsigned int discard_map = f2fs_block_unit_discard(sbi) ? 1 : 0; > + bool share_map = f2fs_use_shared_sit_map(sbi); > > /* allocate memory for SIT information */ > sit_i = f2fs_kzalloc(sbi, sizeof(struct sit_info), GFP_KERNEL); > @@ -4838,28 +4958,73 @@ static int build_sit_info(struct f2fs_sb_info *sbi) > #else > bitmap_size = MAIN_SEGS(sbi) * SIT_VBLOCK_MAP_SIZE * (2 + discard_map); > #endif > - sit_i->bitmap = f2fs_kvzalloc(sbi, bitmap_size, GFP_KERNEL); > - if (!sit_i->bitmap) > - return -ENOMEM; > - > - bitmap = sit_i->bitmap; > > - for (start = 0; start < MAIN_SEGS(sbi); start++) { > - rcu_assign_pointer(sit_i->sentries[start].cur_valid_map, > - bitmap); > - bitmap += SIT_VBLOCK_MAP_SIZE; > + if (share_map) { > + sit_i->bitmap_zero = f2fs_kzalloc(sbi, SIT_VBLOCK_MAP_SIZE, GFP_KERNEL); > + if (!sit_i->bitmap_zero) > + return -ENOMEM; > + sit_i->bitmap_full = f2fs_kzalloc(sbi, SIT_VBLOCK_MAP_SIZE, GFP_KERNEL); > + if (!sit_i->bitmap_full) { > + kfree(sit_i->bitmap_zero); > + sit_i->bitmap_zero = NULL; > + return -ENOMEM; > + } > + memset(sit_i->bitmap_full, 0xff, SIT_VBLOCK_MAP_SIZE); > > - sit_i->sentries[start].ckpt_valid_map = bitmap; > - bitmap += SIT_VBLOCK_MAP_SIZE; > + bitmap_size = MAIN_SEGS(sbi) * SIT_VBLOCK_MAP_SIZE * discard_map; > +#ifdef CONFIG_F2FS_CHECK_FS > + bitmap_size += MAIN_SEGS(sbi) * SIT_VBLOCK_MAP_SIZE; > +#endif > + if (bitmap_size) { > + sit_i->bitmap = f2fs_kvzalloc(sbi, bitmap_size, GFP_KERNEL); > + if (!sit_i->bitmap) { > + kfree(sit_i->bitmap_full); > + kfree(sit_i->bitmap_zero); > + sit_i->bitmap_full = NULL; > + sit_i->bitmap_zero = NULL; > + return -ENOMEM; > + } > + bitmap = sit_i->bitmap; > + } > > + for (start = 0; start < MAIN_SEGS(sbi); start++) { > + rcu_assign_pointer(sit_i->sentries[start].cur_valid_map, > + sit_i->bitmap_zero); > + sit_i->sentries[start].ckpt_valid_map = > + sit_i->bitmap_zero; > #ifdef CONFIG_F2FS_CHECK_FS > - sit_i->sentries[start].cur_valid_map_mir = bitmap; > - bitmap += SIT_VBLOCK_MAP_SIZE; > + sit_i->sentries[start].cur_valid_map_mir = > + bitmap; > + bitmap += SIT_VBLOCK_MAP_SIZE; > #endif > + if (discard_map) { > + sit_i->sentries[start].discard_map = bitmap; > + bitmap += SIT_VBLOCK_MAP_SIZE; > + } > + } > + } else { > + sit_i->bitmap = f2fs_kvzalloc(sbi, bitmap_size, GFP_KERNEL); > + if (!sit_i->bitmap) > + return -ENOMEM; > + > + bitmap = sit_i->bitmap; > + > + for (start = 0; start < MAIN_SEGS(sbi); start++) { > + rcu_assign_pointer(sit_i->sentries[start].cur_valid_map, bitmap); > + bitmap += SIT_VBLOCK_MAP_SIZE; > > - if (discard_map) { > - sit_i->sentries[start].discard_map = bitmap; > + sit_i->sentries[start].ckpt_valid_map = bitmap; > bitmap += SIT_VBLOCK_MAP_SIZE; > + > +#ifdef CONFIG_F2FS_CHECK_FS > + sit_i->sentries[start].cur_valid_map_mir = bitmap; > + bitmap += SIT_VBLOCK_MAP_SIZE; > +#endif > + > + if (discard_map) { > + sit_i->sentries[start].discard_map = bitmap; > + bitmap += SIT_VBLOCK_MAP_SIZE; > + } > } > } > > @@ -5009,7 +5174,39 @@ static int build_sit_entries(struct f2fs_sb_info *sbi) > err = check_block_count(sbi, start, &sit); > if (err) > return err; > - seg_info_from_raw_sit(se, &sit); > + > + if (f2fs_use_shared_sit_map(sbi)) { > + unsigned int vblocks = GET_SIT_VBLOCKS(&sit); > + unsigned char *map = NULL; > + bool is_active = is_curseg(sbi, start); > + > + if (vblocks == 0 && !is_active) { > + map = sit_i->bitmap_zero; > + } else if (vblocks == BLKS_PER_SEG(sbi) && !is_active) { > + map = sit_i->bitmap_full; > + } else { > + struct f2fs_sit_bitmap *b = > + f2fs_kmem_cache_alloc(sit_bitmap_slab, > + GFP_KERNEL, > + false, sbi); > + if (!b) > + return -ENOMEM; > + map = b->map; > + memcpy(map, sit.valid_map, SIT_VBLOCK_MAP_SIZE); > + } > + > + se->valid_blocks = vblocks; > + se->ckpt_valid_blocks = vblocks; > + rcu_assign_pointer(se->cur_valid_map, map); > + se->ckpt_valid_map = map; > +#ifdef CONFIG_F2FS_CHECK_FS > + memcpy(se->cur_valid_map_mir, map, SIT_VBLOCK_MAP_SIZE); > +#endif > + se->type = GET_SIT_TYPE(&sit); > + se->mtime = le64_to_cpu(sit.mtime); > + } else { > + seg_info_from_raw_sit(se, &sit); > + } > > if (se->type >= NR_PERSISTENT_LOG) { > f2fs_err(sbi, "Invalid segment type: %u, segno: %u", > @@ -5039,6 +5236,15 @@ static int build_sit_entries(struct f2fs_sb_info *sbi) > get_sec_entry(sbi, start)->valid_blocks += > se->valid_blocks; > } > + if (f2fs_use_shared_sit_map(sbi)) { > + pgoff_t start_addr = sit_i->sit_base_addr + start_blk; > + pgoff_t end_addr = start_addr + readed - 1; > + pgoff_t alt_start_addr = start_addr + sit_i->sit_blocks; > + pgoff_t alt_end_addr = alt_start_addr + readed - 1; > + > + invalidate_mapping_pages(META_MAPPING(sbi), start_addr, end_addr); > + invalidate_mapping_pages(META_MAPPING(sbi), alt_start_addr, alt_end_addr); > + } > start_blk += readed; > } while (start_blk < sit_blk_cnt); > > @@ -5065,7 +5271,52 @@ static int build_sit_entries(struct f2fs_sb_info *sbi) > err = check_block_count(sbi, start, &sit); > if (err) > break; > - seg_info_from_raw_sit(se, &sit); > + > + if (f2fs_use_shared_sit_map(sbi)) { > + unsigned int vblocks = GET_SIT_VBLOCKS(&sit); > + unsigned char *map = se->cur_valid_map; > + bool is_active = is_curseg(sbi, start); > + > + if (vblocks == 0 && !is_active) { > + if (map != sit_i->bitmap_zero && > + map != sit_i->bitmap_full) > + kmem_cache_free(sit_bitmap_slab, > + f2fs_sit_bitmap_from_map(map)); > + map = sit_i->bitmap_zero; > + } else if (vblocks == BLKS_PER_SEG(sbi) && !is_active) { > + if (map != sit_i->bitmap_zero && > + map != sit_i->bitmap_full) > + kmem_cache_free(sit_bitmap_slab, > + f2fs_sit_bitmap_from_map(map)); > + map = sit_i->bitmap_full; > + } else { > + if (map == sit_i->bitmap_zero || > + map == sit_i->bitmap_full) { > + struct f2fs_sit_bitmap *b = > + f2fs_kmem_cache_alloc(sit_bitmap_slab, > + GFP_KERNEL, > + false, sbi); > + if (!b) { > + up_read(&curseg->journal_rwsem); > + return -ENOMEM; > + } > + map = b->map; > + } > + memcpy(map, sit.valid_map, SIT_VBLOCK_MAP_SIZE); > + } > + > + se->valid_blocks = vblocks; > + se->ckpt_valid_blocks = vblocks; > + rcu_assign_pointer(se->cur_valid_map, map); > + se->ckpt_valid_map = map; > +#ifdef CONFIG_F2FS_CHECK_FS > + memcpy(se->cur_valid_map_mir, map, SIT_VBLOCK_MAP_SIZE); > +#endif > + se->type = GET_SIT_TYPE(&sit); > + se->mtime = le64_to_cpu(sit.mtime); > + } else { > + seg_info_from_raw_sit(se, &sit); > + } > > if (se->type >= NR_PERSISTENT_LOG) { > f2fs_err(sbi, "Invalid segment type: %u, segno: %u", > @@ -5844,8 +6095,30 @@ static void destroy_sit_info(struct f2fs_sb_info *sbi) > if (!sit_i) > return; > > - if (sit_i->sentries) > - kvfree(sit_i->bitmap); > + if (sit_i->sentries) { > + if (f2fs_use_shared_sit_map(sbi)) { > + unsigned int start; > + > + for (start = 0; start < MAIN_SEGS(sbi); start++) { > + struct seg_entry *se = &sit_i->sentries[start]; > + > + if (se->cur_valid_map && > + se->cur_valid_map != sit_i->bitmap_zero && > + se->cur_valid_map != sit_i->bitmap_full) { > + struct f2fs_sit_bitmap *b; > + > + b = f2fs_sit_bitmap_from_map(se->cur_valid_map); > + kmem_cache_free(sit_bitmap_slab, b); > + } > + } > + kfree(sit_i->bitmap_zero); > + kfree(sit_i->bitmap_full); > + if (sit_i->bitmap) > + kvfree(sit_i->bitmap); > + } else { > + kvfree(sit_i->bitmap); > + } > + } > kfree(sit_i->tmp_map); > > kvfree(sit_i->sentries); > @@ -5898,8 +6171,16 @@ int __init f2fs_create_segment_manager_caches(void) > sizeof(struct revoke_entry)); > if (!revoke_entry_slab) > goto destroy_sit_entry_set; > + > + sit_bitmap_slab = f2fs_kmem_cache_create("f2fs_sit_bitmap", > + sizeof(struct f2fs_sit_bitmap)); > + if (!sit_bitmap_slab) > + goto destroy_revoke_entry; > + > return 0; > > +destroy_revoke_entry: > + kmem_cache_destroy(revoke_entry_slab); > destroy_sit_entry_set: > kmem_cache_destroy(sit_entry_set_slab); > destroy_discard_cmd: > @@ -5916,4 +6197,5 @@ void f2fs_destroy_segment_manager_caches(void) > kmem_cache_destroy(discard_cmd_slab); > kmem_cache_destroy(discard_entry_slab); > kmem_cache_destroy(revoke_entry_slab); > + kmem_cache_destroy(sit_bitmap_slab); > } > diff --git a/fs/f2fs/segment.h b/fs/f2fs/segment.h > index 068845660b0f..cb45cfa7a658 100644 > --- a/fs/f2fs/segment.h > +++ b/fs/f2fs/segment.h > @@ -233,6 +233,9 @@ struct sit_info { > unsigned long long dirty_max_mtime; /* rerange candidates in GC_AT */ > > unsigned int last_victim[MAX_GC_POLICY]; /* last victim segment # */ > + > + unsigned char *bitmap_zero; /* shared zero bitmap */ > + unsigned char *bitmap_full; /* shared full bitmap */ > }; > > struct free_segmap_info { > diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c > index 8774c60b4be4..83ce88ce12cb 100644 > --- a/fs/f2fs/super.c > +++ b/fs/f2fs/super.c > @@ -2617,6 +2617,10 @@ static int f2fs_disable_checkpoint(struct f2fs_sb_info *sbi) > f2fs_err(sbi, "checkpoint=disable on readonly fs"); > return -EINVAL; > } > + if (f2fs_use_shared_sit_map(sbi)) { > + f2fs_err(sbi, "checkpoint=disable is not supported in zoned shared SIT mode"); > + return -EOPNOTSUPP; > + } > sbi->sb->s_flags |= SB_ACTIVE; > > /* check if we need more GC first */ > diff --git a/fs/f2fs/sysfs.c b/fs/f2fs/sysfs.c > index 9c79f7b63583..2baf349721c9 100644 > --- a/fs/f2fs/sysfs.c > +++ b/fs/f2fs/sysfs.c > @@ -1788,8 +1788,7 @@ static int __maybe_unused segment_bits_seq_show(struct seq_file *seq, > seq_printf(seq, "%d|%-3u|", se->type, se->valid_blocks); > > rcu_read_lock(); > - memcpy(map, rcu_dereference(se->cur_valid_map), > - SIT_VBLOCK_MAP_SIZE); > + memcpy(map, rcu_dereference(se->cur_valid_map), SIT_VBLOCK_MAP_SIZE); > rcu_read_unlock(); > > for (j = 0; j < SIT_VBLOCK_MAP_SIZE; j++) ^ permalink raw reply [flat|nested] 6+ messages in thread
* [RFC PATCH 3/3] f2fs: add mount option to disable shared SIT mirror checks 2026-03-09 10:36 [RFC PATCH 0/3] f2fs: reduce zoned LFS memory by sharing SIT valid maps 'wallentx 2026-03-09 10:36 ` [RFC PATCH 1/3] f2fs: prepare cur_valid_map for safe lockless access 'wallentx 2026-03-09 10:36 ` [RFC PATCH 2/3] f2fs: reduce zoned LFS memory by sharing SIT valid maps 'wallentx @ 2026-03-09 10:37 ` 'wallentx 2026-03-14 5:14 ` Chao Yu 2 siblings, 1 reply; 6+ messages in thread From: 'wallentx @ 2026-03-09 10:37 UTC (permalink / raw) To: linux-f2fs-devel; +Cc: jaegeuk, chao, linux-kernel, wallentx From: wallentx <william.allentx@gmail.com> When CONFIG_F2FS_CHECK_FS=y, zoned shared-SIT mode still keeps per-segment cur_valid_map_mir together with auxiliary SIT mirror state for runtime consistency checks. After shared SIT maps are introduced, that mirror path becomes the next major static memory cost on large zoned systems. Add shared_sit_check/noshared_sit_check to control that extra mirror tracking explicitly. Keep shared_sit_check as the default, limit the option to zoned shared-SIT mounts, and reject changing it by remount because the mirror state is built at mount time. With noshared_sit_check, shared-SIT mode skips cur_valid_map_mir, sit_bitmap_mir, and invalid_segmap allocation and their associated check paths. This does not disable all CONFIG_F2FS_CHECK_FS behavior; it only disables the extra shared-SIT mirror tracking. On the same 43-volume HM-SMR test system (~550 TB total) with CONFIG_F2FS_CHECK_FS=y, shared SIT with the default mirror path enabled reported 27.70 GiB of static F2FS memory. Mounting with noshared_sit_check reduced that to 12.10 GiB. Signed-off-by: wallentx <william.allentx@gmail.com> --- Documentation/filesystems/f2fs.rst | 11 ++ fs/f2fs/debug.c | 7 +- fs/f2fs/f2fs.h | 16 +++ fs/f2fs/gc.c | 29 ++-- fs/f2fs/segment.c | 213 ++++++++++++++++------------- fs/f2fs/segment.h | 28 ++-- fs/f2fs/super.c | 21 +++ 7 files changed, 204 insertions(+), 121 deletions(-) diff --git a/Documentation/filesystems/f2fs.rst b/Documentation/filesystems/f2fs.rst index 7e4031631286..3ca191f92309 100644 --- a/Documentation/filesystems/f2fs.rst +++ b/Documentation/filesystems/f2fs.rst @@ -363,6 +363,17 @@ memory=%s Control memory mode. This supports "normal" and "low" modes. Because of the nature of low memory devices, in this mode, f2fs will try to save memory sometimes by sacrificing performance. "normal" mode is the default mode and same as before. +shared_sit_check/ +noshared_sit_check Enable or disable extra SIT mirror consistency checks in + zoned shared-SIT mode. This only affects zoned devices + mounted with ``mode=lfs``, where shared SIT valid maps are + used. ``shared_sit_check`` is enabled by default. Setting + ``noshared_sit_check`` reduces memory usage when the kernel + is built with ``CONFIG_F2FS_CHECK_FS=y`` by skipping the + per-segment SIT mirror tracking. This option has no effect + when shared SIT maps are not in use and cannot be changed + by remount. To use a different setting, unmount and + mount again with the new option. age_extent_cache Enable an age extent cache based on rb-tree. It records data block update frequency of the extent per inode, in order to provide better temperature hints for data block diff --git a/fs/f2fs/debug.c b/fs/f2fs/debug.c index d8bfdac5c1e4..ed6e03b42836 100644 --- a/fs/f2fs/debug.c +++ b/fs/f2fs/debug.c @@ -325,9 +325,10 @@ static void update_mem_info(struct f2fs_sb_info *sbi) si->base_mem += SIT_VBLOCK_MAP_SIZE * 2; /* Approximate private bitmaps for active logs */ si->base_mem += SIT_VBLOCK_MAP_SIZE * NR_CURSEG_TYPE; -#ifdef CONFIG_F2FS_CHECK_FS - si->base_mem += SIT_VBLOCK_MAP_SIZE * MAIN_SEGS(sbi); -#endif + #ifdef CONFIG_F2FS_CHECK_FS + if (f2fs_sit_check_enabled(sbi)) + si->base_mem += SIT_VBLOCK_MAP_SIZE * MAIN_SEGS(sbi); + #endif if (f2fs_block_unit_discard(sbi)) si->base_mem += SIT_VBLOCK_MAP_SIZE * MAIN_SEGS(sbi); } else { diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index 11f3601ffd34..d8526103d86a 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -130,6 +130,7 @@ enum f2fs_mount_opt { F2FS_MOUNT_COMPRESS_CACHE, F2FS_MOUNT_AGE_EXTENT_CACHE, F2FS_MOUNT_NAT_BITS, + F2FS_MOUNT_SHARED_SIT_CHECK, F2FS_MOUNT_INLINECRYPT, /* * Some f2fs environments expect to be able to pass the "lazytime" option @@ -4920,6 +4921,21 @@ static inline bool f2fs_use_shared_sit_map(struct f2fs_sb_info *sbi) return f2fs_sb_has_blkzoned(sbi) && f2fs_lfs_mode(sbi); } +/* + * shared_sit_check is a mount-time escape hatch for the extra SIT mirror + * tracking kept by CONFIG_F2FS_CHECK_FS in zoned shared-SIT mode. + */ +static inline bool f2fs_sit_check_enabled(struct f2fs_sb_info *sbi) +{ + if (!IS_ENABLED(CONFIG_F2FS_CHECK_FS)) + return false; + + if (!f2fs_use_shared_sit_map(sbi)) + return true; + + return test_opt(sbi, SHARED_SIT_CHECK); +} + static inline bool f2fs_is_sequential_zone_area(struct f2fs_sb_info *sbi, block_t blkaddr) { diff --git a/fs/f2fs/gc.c b/fs/f2fs/gc.c index 80b8500fa987..1decb0caea1b 100644 --- a/fs/f2fs/gc.c +++ b/fs/f2fs/gc.c @@ -877,15 +877,16 @@ int f2fs_get_victim(struct f2fs_sb_info *sbi, unsigned int *result, p.offset = segno + p.ofs_unit; nsearched++; -#ifdef CONFIG_F2FS_CHECK_FS - /* - * skip selecting the invalid segno (that is failed due to block - * validity check failure during GC) to avoid endless GC loop in - * such cases. - */ - if (test_bit(segno, sm->invalid_segmap)) - goto next; -#endif + #ifdef CONFIG_F2FS_CHECK_FS + /* + * skip selecting the invalid segno (that is failed due to block + * validity check failure during GC) to avoid endless GC loop in + * such cases. + */ + if (f2fs_sit_check_enabled(sbi) && + test_bit(segno, sm->invalid_segmap)) + goto next; + #endif secno = GET_SEC_FROM_SEG(sbi, segno); @@ -1197,18 +1198,20 @@ static bool is_alive(struct f2fs_sb_info *sbi, struct f2fs_summary *sum, f2fs_folio_put(node_folio, true); if (source_blkaddr != blkaddr) { -#ifdef CONFIG_F2FS_CHECK_FS + #ifdef CONFIG_F2FS_CHECK_FS unsigned int segno = GET_SEGNO(sbi, blkaddr); unsigned long offset = GET_BLKOFF_FROM_SEG0(sbi, blkaddr); - if (unlikely(check_valid_map(sbi, segno, offset))) { + if (f2fs_sit_check_enabled(sbi) && + unlikely(check_valid_map(sbi, segno, offset))) { if (!test_and_set_bit(segno, SIT_I(sbi)->invalid_segmap)) { - f2fs_err(sbi, "mismatched blkaddr %u (source_blkaddr %u) in seg %u", + f2fs_err(sbi, + "mismatched blkaddr %u (source_blkaddr %u) in seg %u", blkaddr, source_blkaddr, segno); set_sbi_flag(sbi, SBI_NEED_FSCK); } } -#endif + #endif return false; } return true; diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index 0dab6b16ba56..dce66eec2a5f 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -38,6 +38,11 @@ struct f2fs_sit_bitmap { unsigned char map[SIT_VBLOCK_MAP_SIZE]; }; +static inline struct f2fs_sit_bitmap *f2fs_sit_bitmap_from_map(void *map) +{ + return container_of(map, struct f2fs_sit_bitmap, map); +} + static void f2fs_free_sit_bitmap_rcu(struct rcu_head *rcu) { struct f2fs_sit_bitmap *b = container_of(rcu, struct f2fs_sit_bitmap, rcu); @@ -45,11 +50,6 @@ static void f2fs_free_sit_bitmap_rcu(struct rcu_head *rcu) kmem_cache_free(sit_bitmap_slab, b); } -static struct f2fs_sit_bitmap *f2fs_sit_bitmap_from_map(void *map) -{ - return container_of(map, struct f2fs_sit_bitmap, map); -} - static unsigned long __reverse_ulong(unsigned char *str) { unsigned long tmp = 0; @@ -845,9 +845,10 @@ static void __remove_dirty_segment(struct f2fs_sb_info *sbi, unsigned int segno, if (valid_blocks == 0) { clear_bit(GET_SEC_FROM_SEG(sbi, segno), dirty_i->victim_secmap); -#ifdef CONFIG_F2FS_CHECK_FS - clear_bit(segno, SIT_I(sbi)->invalid_segmap); -#endif + #ifdef CONFIG_F2FS_CHECK_FS + if (f2fs_sit_check_enabled(sbi)) + clear_bit(segno, SIT_I(sbi)->invalid_segmap); + #endif } if (__is_large_section(sbi)) { unsigned int secno = GET_SEC_FROM_SEG(sbi, segno); @@ -2471,9 +2472,10 @@ static int update_sit_entry_for_release(struct f2fs_sb_info *sbi, struct seg_ent memset(b->map, 0xff, SIT_VBLOCK_MAP_SIZE); rcu_assign_pointer(se->cur_valid_map, b->map); se->ckpt_valid_map = b->map; -#ifdef CONFIG_F2FS_CHECK_FS - memcpy(se->cur_valid_map_mir, b->map, SIT_VBLOCK_MAP_SIZE); -#endif + #ifdef CONFIG_F2FS_CHECK_FS + if (f2fs_sit_check_enabled(sbi)) + memcpy(se->cur_valid_map_mir, b->map, SIT_VBLOCK_MAP_SIZE); + #endif } else if (se->cur_valid_map == sit_i->bitmap_zero) { /* Should not happen, freeing empty segment */ struct f2fs_sit_bitmap *b = f2fs_kmem_cache_alloc(sit_bitmap_slab, @@ -2481,23 +2483,27 @@ static int update_sit_entry_for_release(struct f2fs_sb_info *sbi, struct seg_ent memset(b->map, 0, SIT_VBLOCK_MAP_SIZE); rcu_assign_pointer(se->cur_valid_map, b->map); se->ckpt_valid_map = b->map; -#ifdef CONFIG_F2FS_CHECK_FS - memcpy(se->cur_valid_map_mir, b->map, SIT_VBLOCK_MAP_SIZE); -#endif + #ifdef CONFIG_F2FS_CHECK_FS + if (f2fs_sit_check_enabled(sbi)) + memcpy(se->cur_valid_map_mir, b->map, SIT_VBLOCK_MAP_SIZE); + #endif } } for (i = 0; i < del_count; i++) { exist = f2fs_test_and_clear_bit(offset + i, se->cur_valid_map); -#ifdef CONFIG_F2FS_CHECK_FS - mir_exist = f2fs_test_and_clear_bit(offset + i, - se->cur_valid_map_mir); - if (unlikely(exist != mir_exist)) { - f2fs_err(sbi, "Inconsistent error when clearing bitmap, blk:%u, old bit:%d", - blkaddr + i, exist); - f2fs_bug_on(sbi, 1); - } -#endif + #ifdef CONFIG_F2FS_CHECK_FS + if (f2fs_sit_check_enabled(sbi)) { + mir_exist = f2fs_test_and_clear_bit(offset + i, + se->cur_valid_map_mir); + if (unlikely(exist != mir_exist)) { + f2fs_err(sbi, + "Inconsistent error when clearing bitmap, blk:%u, old bit:%d", + blkaddr + i, exist); + f2fs_bug_on(sbi, 1); + } + } + #endif if (unlikely(!exist)) { f2fs_err(sbi, "Bitmap was wrongly cleared, blk:%u", blkaddr + i); f2fs_bug_on(sbi, 1); @@ -2559,9 +2565,10 @@ static int update_sit_entry_for_alloc(struct f2fs_sb_info *sbi, struct seg_entry memset(b->map, 0, SIT_VBLOCK_MAP_SIZE); rcu_assign_pointer(se->cur_valid_map, b->map); se->ckpt_valid_map = b->map; -#ifdef CONFIG_F2FS_CHECK_FS - memcpy(se->cur_valid_map_mir, b->map, SIT_VBLOCK_MAP_SIZE); -#endif + #ifdef CONFIG_F2FS_CHECK_FS + if (f2fs_sit_check_enabled(sbi)) + memcpy(se->cur_valid_map_mir, b->map, SIT_VBLOCK_MAP_SIZE); + #endif } else if (se->cur_valid_map == sit_i->bitmap_full) { /* Should not happen in LFS alloc, but for safety */ struct f2fs_sit_bitmap *b = f2fs_kmem_cache_alloc(sit_bitmap_slab, @@ -2569,22 +2576,26 @@ static int update_sit_entry_for_alloc(struct f2fs_sb_info *sbi, struct seg_entry memset(b->map, 0xff, SIT_VBLOCK_MAP_SIZE); rcu_assign_pointer(se->cur_valid_map, b->map); se->ckpt_valid_map = b->map; -#ifdef CONFIG_F2FS_CHECK_FS - memcpy(se->cur_valid_map_mir, b->map, SIT_VBLOCK_MAP_SIZE); -#endif + #ifdef CONFIG_F2FS_CHECK_FS + if (f2fs_sit_check_enabled(sbi)) + memcpy(se->cur_valid_map_mir, b->map, SIT_VBLOCK_MAP_SIZE); + #endif } } exist = f2fs_test_and_set_bit(offset, se->cur_valid_map); -#ifdef CONFIG_F2FS_CHECK_FS - mir_exist = f2fs_test_and_set_bit(offset, - se->cur_valid_map_mir); - if (unlikely(exist != mir_exist)) { - f2fs_err(sbi, "Inconsistent error when setting bitmap, blk:%u, old bit:%d", - blkaddr, exist); - f2fs_bug_on(sbi, 1); + #ifdef CONFIG_F2FS_CHECK_FS + if (f2fs_sit_check_enabled(sbi)) { + mir_exist = f2fs_test_and_set_bit(offset, + se->cur_valid_map_mir); + if (unlikely(exist != mir_exist)) { + f2fs_err(sbi, + "Inconsistent error when setting bitmap, blk:%u, old bit:%d", + blkaddr, exist); + f2fs_bug_on(sbi, 1); + } } -#endif + #endif if (unlikely(exist)) { f2fs_err(sbi, "Bitmap was wrongly set, blk:%u", blkaddr); f2fs_bug_on(sbi, 1); @@ -2677,13 +2688,15 @@ static void update_sit_entry(struct f2fs_sb_info *sbi, block_t blkaddr, int del) rcu_assign_pointer(se->cur_valid_map, sit_i->bitmap_zero); se->ckpt_valid_map = sit_i->bitmap_zero; -#ifdef CONFIG_F2FS_CHECK_FS - memset(se->cur_valid_map_mir, 0, SIT_VBLOCK_MAP_SIZE); -#endif + #ifdef CONFIG_F2FS_CHECK_FS + if (f2fs_sit_check_enabled(sbi)) + memset(se->cur_valid_map_mir, 0, SIT_VBLOCK_MAP_SIZE); + #endif if (old_map != sit_i->bitmap_zero && old_map != sit_i->bitmap_full) { - call_rcu(&f2fs_sit_bitmap_from_map(old_map)->rcu, - f2fs_free_sit_bitmap_rcu); + struct f2fs_sit_bitmap *b = + f2fs_sit_bitmap_from_map(old_map); + call_rcu(&b->rcu, f2fs_free_sit_bitmap_rcu); } } else if (new_vblocks == BLKS_PER_SEG(sbi) && se->cur_valid_map != sit_i->bitmap_full) { @@ -2691,13 +2704,15 @@ static void update_sit_entry(struct f2fs_sb_info *sbi, block_t blkaddr, int del) rcu_assign_pointer(se->cur_valid_map, sit_i->bitmap_full); se->ckpt_valid_map = sit_i->bitmap_full; -#ifdef CONFIG_F2FS_CHECK_FS - memset(se->cur_valid_map_mir, 0xff, SIT_VBLOCK_MAP_SIZE); -#endif + #ifdef CONFIG_F2FS_CHECK_FS + if (f2fs_sit_check_enabled(sbi)) + memset(se->cur_valid_map_mir, 0xff, SIT_VBLOCK_MAP_SIZE); + #endif if (old_map != sit_i->bitmap_zero && old_map != sit_i->bitmap_full) { - call_rcu(&f2fs_sit_bitmap_from_map(old_map)->rcu, - f2fs_free_sit_bitmap_rcu); + struct f2fs_sit_bitmap *b = + f2fs_sit_bitmap_from_map(old_map); + call_rcu(&b->rcu, f2fs_free_sit_bitmap_rcu); } } } @@ -4701,7 +4716,7 @@ static struct folio *get_next_sit_folio(struct f2fs_sb_info *sbi, seg_info_to_sit_folio(sbi, folio, start); folio_mark_dirty(folio); - set_to_next_sit(sit_i, start); + set_to_next_sit(sbi, sit_i, start); return folio; } @@ -4857,11 +4872,12 @@ void f2fs_flush_sit_entries(struct f2fs_sb_info *sbi, struct cp_control *cpc) int offset, sit_offset; se = get_seg_entry(sbi, segno); -#ifdef CONFIG_F2FS_CHECK_FS - if (memcmp(se->cur_valid_map, se->cur_valid_map_mir, - SIT_VBLOCK_MAP_SIZE)) + #ifdef CONFIG_F2FS_CHECK_FS + if (f2fs_sit_check_enabled(sbi) && + memcmp(se->cur_valid_map, se->cur_valid_map_mir, + SIT_VBLOCK_MAP_SIZE)) f2fs_bug_on(sbi, 1); -#endif + #endif /* add discard candidates */ if (!(cpc->reason & CP_DISCARD)) { @@ -4953,11 +4969,8 @@ static int build_sit_info(struct f2fs_sb_info *sbi) if (!sit_i->dirty_sentries_bitmap) return -ENOMEM; -#ifdef CONFIG_F2FS_CHECK_FS - bitmap_size = MAIN_SEGS(sbi) * SIT_VBLOCK_MAP_SIZE * (3 + discard_map); -#else - bitmap_size = MAIN_SEGS(sbi) * SIT_VBLOCK_MAP_SIZE * (2 + discard_map); -#endif + bitmap_size = MAIN_SEGS(sbi) * SIT_VBLOCK_MAP_SIZE * + (2 + discard_map + f2fs_sit_check_enabled(sbi)); if (share_map) { sit_i->bitmap_zero = f2fs_kzalloc(sbi, SIT_VBLOCK_MAP_SIZE, GFP_KERNEL); @@ -4971,10 +4984,9 @@ static int build_sit_info(struct f2fs_sb_info *sbi) } memset(sit_i->bitmap_full, 0xff, SIT_VBLOCK_MAP_SIZE); - bitmap_size = MAIN_SEGS(sbi) * SIT_VBLOCK_MAP_SIZE * discard_map; -#ifdef CONFIG_F2FS_CHECK_FS - bitmap_size += MAIN_SEGS(sbi) * SIT_VBLOCK_MAP_SIZE; -#endif + bitmap_size = MAIN_SEGS(sbi) * SIT_VBLOCK_MAP_SIZE * discard_map; + if (f2fs_sit_check_enabled(sbi)) + bitmap_size += MAIN_SEGS(sbi) * SIT_VBLOCK_MAP_SIZE; if (bitmap_size) { sit_i->bitmap = f2fs_kvzalloc(sbi, bitmap_size, GFP_KERNEL); if (!sit_i->bitmap) { @@ -4993,9 +5005,10 @@ static int build_sit_info(struct f2fs_sb_info *sbi) sit_i->sentries[start].ckpt_valid_map = sit_i->bitmap_zero; #ifdef CONFIG_F2FS_CHECK_FS - sit_i->sentries[start].cur_valid_map_mir = - bitmap; - bitmap += SIT_VBLOCK_MAP_SIZE; + if (f2fs_sit_check_enabled(sbi)) { + sit_i->sentries[start].cur_valid_map_mir = bitmap; + bitmap += SIT_VBLOCK_MAP_SIZE; + } #endif if (discard_map) { sit_i->sentries[start].discard_map = bitmap; @@ -5017,8 +5030,10 @@ static int build_sit_info(struct f2fs_sb_info *sbi) bitmap += SIT_VBLOCK_MAP_SIZE; #ifdef CONFIG_F2FS_CHECK_FS - sit_i->sentries[start].cur_valid_map_mir = bitmap; - bitmap += SIT_VBLOCK_MAP_SIZE; + if (f2fs_sit_check_enabled(sbi)) { + sit_i->sentries[start].cur_valid_map_mir = bitmap; + bitmap += SIT_VBLOCK_MAP_SIZE; + } #endif if (discard_map) { @@ -5052,16 +5067,20 @@ static int build_sit_info(struct f2fs_sb_info *sbi) if (!sit_i->sit_bitmap) return -ENOMEM; -#ifdef CONFIG_F2FS_CHECK_FS - sit_i->sit_bitmap_mir = kmemdup(src_bitmap, - sit_bitmap_size, GFP_KERNEL); - if (!sit_i->sit_bitmap_mir) - return -ENOMEM; + #ifdef CONFIG_F2FS_CHECK_FS + if (f2fs_sit_check_enabled(sbi)) { + sit_i->sit_bitmap_mir = kmemdup(src_bitmap, + sit_bitmap_size, + GFP_KERNEL); + if (!sit_i->sit_bitmap_mir) + return -ENOMEM; - sit_i->invalid_segmap = f2fs_kvzalloc(sbi, - main_bitmap_size, GFP_KERNEL); - if (!sit_i->invalid_segmap) - return -ENOMEM; + sit_i->invalid_segmap = f2fs_kvzalloc(sbi, + main_bitmap_size, + GFP_KERNEL); + if (!sit_i->invalid_segmap) + return -ENOMEM; + } #endif sit_i->sit_base_addr = le32_to_cpu(raw_super->sit_blkaddr); @@ -5200,12 +5219,13 @@ static int build_sit_entries(struct f2fs_sb_info *sbi) rcu_assign_pointer(se->cur_valid_map, map); se->ckpt_valid_map = map; #ifdef CONFIG_F2FS_CHECK_FS - memcpy(se->cur_valid_map_mir, map, SIT_VBLOCK_MAP_SIZE); + if (f2fs_sit_check_enabled(sbi)) + memcpy(se->cur_valid_map_mir, map, SIT_VBLOCK_MAP_SIZE); #endif se->type = GET_SIT_TYPE(&sit); se->mtime = le64_to_cpu(sit.mtime); } else { - seg_info_from_raw_sit(se, &sit); + seg_info_from_raw_sit(sbi, se, &sit); } if (se->type >= NR_PERSISTENT_LOG) { @@ -5279,15 +5299,19 @@ static int build_sit_entries(struct f2fs_sb_info *sbi) if (vblocks == 0 && !is_active) { if (map != sit_i->bitmap_zero && - map != sit_i->bitmap_full) - kmem_cache_free(sit_bitmap_slab, - f2fs_sit_bitmap_from_map(map)); + map != sit_i->bitmap_full) { + struct f2fs_sit_bitmap *b = + f2fs_sit_bitmap_from_map(map); + kmem_cache_free(sit_bitmap_slab, b); + } map = sit_i->bitmap_zero; } else if (vblocks == BLKS_PER_SEG(sbi) && !is_active) { if (map != sit_i->bitmap_zero && - map != sit_i->bitmap_full) - kmem_cache_free(sit_bitmap_slab, - f2fs_sit_bitmap_from_map(map)); + map != sit_i->bitmap_full) { + struct f2fs_sit_bitmap *b = + f2fs_sit_bitmap_from_map(map); + kmem_cache_free(sit_bitmap_slab, b); + } map = sit_i->bitmap_full; } else { if (map == sit_i->bitmap_zero || @@ -5310,12 +5334,13 @@ static int build_sit_entries(struct f2fs_sb_info *sbi) rcu_assign_pointer(se->cur_valid_map, map); se->ckpt_valid_map = map; #ifdef CONFIG_F2FS_CHECK_FS - memcpy(se->cur_valid_map_mir, map, SIT_VBLOCK_MAP_SIZE); + if (f2fs_sit_check_enabled(sbi)) + memcpy(se->cur_valid_map_mir, map, SIT_VBLOCK_MAP_SIZE); #endif se->type = GET_SIT_TYPE(&sit); se->mtime = le64_to_cpu(sit.mtime); } else { - seg_info_from_raw_sit(se, &sit); + seg_info_from_raw_sit(sbi, se, &sit); } if (se->type >= NR_PERSISTENT_LOG) { @@ -6099,18 +6124,18 @@ static void destroy_sit_info(struct f2fs_sb_info *sbi) if (f2fs_use_shared_sit_map(sbi)) { unsigned int start; - for (start = 0; start < MAIN_SEGS(sbi); start++) { - struct seg_entry *se = &sit_i->sentries[start]; + for (start = 0; start < MAIN_SEGS(sbi); start++) { + struct seg_entry *se = &sit_i->sentries[start]; - if (se->cur_valid_map && - se->cur_valid_map != sit_i->bitmap_zero && - se->cur_valid_map != sit_i->bitmap_full) { - struct f2fs_sit_bitmap *b; + if (se->cur_valid_map && + se->cur_valid_map != sit_i->bitmap_zero && + se->cur_valid_map != sit_i->bitmap_full) { + struct f2fs_sit_bitmap *b; - b = f2fs_sit_bitmap_from_map(se->cur_valid_map); - kmem_cache_free(sit_bitmap_slab, b); + b = f2fs_sit_bitmap_from_map(se->cur_valid_map); + kmem_cache_free(sit_bitmap_slab, b); + } } - } kfree(sit_i->bitmap_zero); kfree(sit_i->bitmap_full); if (sit_i->bitmap) diff --git a/fs/f2fs/segment.h b/fs/f2fs/segment.h index cb45cfa7a658..0f4445375ed1 100644 --- a/fs/f2fs/segment.h +++ b/fs/f2fs/segment.h @@ -404,15 +404,17 @@ static inline void sanity_check_valid_blocks(struct f2fs_sb_info *sbi, { } #endif -static inline void seg_info_from_raw_sit(struct seg_entry *se, - struct f2fs_sit_entry *rs) +static inline void seg_info_from_raw_sit(struct f2fs_sb_info *sbi, + struct seg_entry *se, + struct f2fs_sit_entry *rs) { se->valid_blocks = GET_SIT_VBLOCKS(rs); se->ckpt_valid_blocks = GET_SIT_VBLOCKS(rs); memcpy(se->cur_valid_map, rs->valid_map, SIT_VBLOCK_MAP_SIZE); memcpy(se->ckpt_valid_map, rs->valid_map, SIT_VBLOCK_MAP_SIZE); #ifdef CONFIG_F2FS_CHECK_FS - memcpy(se->cur_valid_map_mir, rs->valid_map, SIT_VBLOCK_MAP_SIZE); + if (f2fs_sit_check_enabled(sbi)) + memcpy(se->cur_valid_map_mir, rs->valid_map, SIT_VBLOCK_MAP_SIZE); #endif se->type = GET_SIT_TYPE(rs); se->mtime = le64_to_cpu(rs->mtime); @@ -558,11 +560,12 @@ static inline void get_sit_bitmap(struct f2fs_sb_info *sbi, { struct sit_info *sit_i = SIT_I(sbi); -#ifdef CONFIG_F2FS_CHECK_FS - if (memcmp(sit_i->sit_bitmap, sit_i->sit_bitmap_mir, - sit_i->bitmap_size)) + #ifdef CONFIG_F2FS_CHECK_FS + if (f2fs_sit_check_enabled(sbi) && + memcmp(sit_i->sit_bitmap, sit_i->sit_bitmap_mir, + sit_i->bitmap_size)) f2fs_bug_on(sbi, 1); -#endif + #endif memcpy(dst_addr, sit_i->sit_bitmap, sit_i->bitmap_size); } @@ -904,8 +907,9 @@ static inline pgoff_t current_sit_addr(struct f2fs_sb_info *sbi, f2fs_bug_on(sbi, !valid_main_segno(sbi, start)); #ifdef CONFIG_F2FS_CHECK_FS - if (f2fs_test_bit(offset, sit_i->sit_bitmap) != - f2fs_test_bit(offset, sit_i->sit_bitmap_mir)) + if (f2fs_sit_check_enabled(sbi) && + f2fs_test_bit(offset, sit_i->sit_bitmap) != + f2fs_test_bit(offset, sit_i->sit_bitmap_mir)) f2fs_bug_on(sbi, 1); #endif @@ -929,13 +933,15 @@ static inline pgoff_t next_sit_addr(struct f2fs_sb_info *sbi, return block_addr + sit_i->sit_base_addr; } -static inline void set_to_next_sit(struct sit_info *sit_i, unsigned int start) +static inline void set_to_next_sit(struct f2fs_sb_info *sbi, + struct sit_info *sit_i, unsigned int start) { unsigned int block_off = SIT_BLOCK_OFFSET(start); f2fs_change_bit(block_off, sit_i->sit_bitmap); #ifdef CONFIG_F2FS_CHECK_FS - f2fs_change_bit(block_off, sit_i->sit_bitmap_mir); + if (f2fs_sit_check_enabled(sbi)) + f2fs_change_bit(block_off, sit_i->sit_bitmap_mir); #endif } diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c index 83ce88ce12cb..e2bb5d0ab3e1 100644 --- a/fs/f2fs/super.c +++ b/fs/f2fs/super.c @@ -228,6 +228,7 @@ enum { Opt_gc_merge, Opt_discard_unit, Opt_memory_mode, + Opt_shared_sit_check, Opt_age_extent_cache, Opt_errors, Opt_nat_bits, @@ -359,6 +360,7 @@ static const struct fs_parameter_spec f2fs_param_specs[] = { fsparam_flag_no("gc_merge", Opt_gc_merge), fsparam_enum("discard_unit", Opt_discard_unit, f2fs_param_discard_unit), fsparam_enum("memory", Opt_memory_mode, f2fs_param_memory_mode), + fsparam_flag_no("shared_sit_check", Opt_shared_sit_check), fsparam_flag("age_extent_cache", Opt_age_extent_cache), fsparam_enum("errors", Opt_errors, f2fs_param_errors), fsparam_enum("lookup_mode", Opt_lookup_mode, f2fs_param_lookup_mode), @@ -1217,6 +1219,12 @@ static int f2fs_parse_param(struct fs_context *fc, struct fs_parameter *param) F2FS_CTX_INFO(ctx).memory_mode = result.uint_32; ctx->spec_mask |= F2FS_SPEC_memory_mode; break; + case Opt_shared_sit_check: + if (result.negated) + ctx_clear_opt(ctx, F2FS_MOUNT_SHARED_SIT_CHECK); + else + ctx_set_opt(ctx, F2FS_MOUNT_SHARED_SIT_CHECK); + break; case Opt_age_extent_cache: ctx_set_opt(ctx, F2FS_MOUNT_AGE_EXTENT_CACHE); break; @@ -2514,6 +2522,11 @@ static int f2fs_show_options(struct seq_file *seq, struct dentry *root) else if (F2FS_OPTION(sbi).memory_mode == MEMORY_MODE_LOW) seq_printf(seq, ",memory=%s", "low"); + if (test_opt(sbi, SHARED_SIT_CHECK)) + seq_puts(seq, ",shared_sit_check"); + else + seq_puts(seq, ",noshared_sit_check"); + if (F2FS_OPTION(sbi).errors == MOUNT_ERRORS_READONLY) seq_printf(seq, ",errors=%s", "remount-ro"); else if (F2FS_OPTION(sbi).errors == MOUNT_ERRORS_CONTINUE) @@ -2573,6 +2586,7 @@ static void default_options(struct f2fs_sb_info *sbi, bool remount) F2FS_OPTION(sbi).bggc_mode = BGGC_MODE_ON; F2FS_OPTION(sbi).memory_mode = MEMORY_MODE_NORMAL; F2FS_OPTION(sbi).errors = MOUNT_ERRORS_CONTINUE; + set_opt(sbi, SHARED_SIT_CHECK); set_opt(sbi, INLINE_XATTR); set_opt(sbi, INLINE_DATA); @@ -2782,6 +2796,7 @@ static int __f2fs_remount(struct fs_context *fc, struct super_block *sb) bool no_compress_cache = !test_opt(sbi, COMPRESS_CACHE); bool block_unit_discard = f2fs_block_unit_discard(sbi); bool no_nat_bits = !test_opt(sbi, NAT_BITS); + bool shared_sit_check = test_opt(sbi, SHARED_SIT_CHECK); #ifdef CONFIG_QUOTA int i, j; #endif @@ -2904,6 +2919,12 @@ static int __f2fs_remount(struct fs_context *fc, struct super_block *sb) goto restore_opts; } + if (shared_sit_check != !!test_opt(sbi, SHARED_SIT_CHECK)) { + err = -EINVAL; + f2fs_warn(sbi, "switch shared_sit_check option is not allowed"); + goto restore_opts; + } + if ((flags & SB_RDONLY) && test_opt(sbi, DISABLE_CHECKPOINT)) { err = -EINVAL; f2fs_warn(sbi, "disabling checkpoint not compatible with read-only"); -- 2.53.0 ^ permalink raw reply related [flat|nested] 6+ messages in thread
* Re: [RFC PATCH 3/3] f2fs: add mount option to disable shared SIT mirror checks 2026-03-09 10:37 ` [RFC PATCH 3/3] f2fs: add mount option to disable shared SIT mirror checks 'wallentx @ 2026-03-14 5:14 ` Chao Yu 0 siblings, 0 replies; 6+ messages in thread From: Chao Yu @ 2026-03-14 5:14 UTC (permalink / raw) To: 'wallentx, linux-f2fs-devel; +Cc: chao, jaegeuk, linux-kernel On 2026/3/9 18:37, 'wallentx wrote: > From: wallentx <william.allentx@gmail.com> > > When CONFIG_F2FS_CHECK_FS=y, zoned shared-SIT mode still keeps > per-segment cur_valid_map_mir together with auxiliary SIT mirror state > for runtime consistency checks. After shared SIT maps are introduced, > that mirror path becomes the next major static memory cost on large > zoned systems. > > Add shared_sit_check/noshared_sit_check to control that extra mirror > tracking explicitly. Keep shared_sit_check as the default, limit the > option to zoned shared-SIT mounts, and reject changing it by remount > because the mirror state is built at mount time. > > With noshared_sit_check, shared-SIT mode skips cur_valid_map_mir, > sit_bitmap_mir, and invalid_segmap allocation and their associated > check paths. This does not disable all CONFIG_F2FS_CHECK_FS behavior; it > only disables the extra shared-SIT mirror tracking. > > On the same 43-volume HM-SMR test system (~550 TB total) with > CONFIG_F2FS_CHECK_FS=y, shared SIT with the default mirror path enabled > reported 27.70 GiB of static F2FS memory. Mounting with > noshared_sit_check reduced that to 12.10 GiB. Jaegeuk and me think that we can drop sit bitmap and version bitmap sanity check, since we haven't got any report for almost a decade. So how about dropping the bitmaps w/ two separated patch? Thanks, > > Signed-off-by: wallentx <william.allentx@gmail.com> > --- > Documentation/filesystems/f2fs.rst | 11 ++ > fs/f2fs/debug.c | 7 +- > fs/f2fs/f2fs.h | 16 +++ > fs/f2fs/gc.c | 29 ++-- > fs/f2fs/segment.c | 213 ++++++++++++++++------------- > fs/f2fs/segment.h | 28 ++-- > fs/f2fs/super.c | 21 +++ > 7 files changed, 204 insertions(+), 121 deletions(-) > > diff --git a/Documentation/filesystems/f2fs.rst b/Documentation/filesystems/f2fs.rst > index 7e4031631286..3ca191f92309 100644 > --- a/Documentation/filesystems/f2fs.rst > +++ b/Documentation/filesystems/f2fs.rst > @@ -363,6 +363,17 @@ memory=%s Control memory mode. This supports "normal" and "low" modes. > Because of the nature of low memory devices, in this mode, f2fs > will try to save memory sometimes by sacrificing performance. > "normal" mode is the default mode and same as before. > +shared_sit_check/ > +noshared_sit_check Enable or disable extra SIT mirror consistency checks in > + zoned shared-SIT mode. This only affects zoned devices > + mounted with ``mode=lfs``, where shared SIT valid maps are > + used. ``shared_sit_check`` is enabled by default. Setting > + ``noshared_sit_check`` reduces memory usage when the kernel > + is built with ``CONFIG_F2FS_CHECK_FS=y`` by skipping the > + per-segment SIT mirror tracking. This option has no effect > + when shared SIT maps are not in use and cannot be changed > + by remount. To use a different setting, unmount and > + mount again with the new option. > age_extent_cache Enable an age extent cache based on rb-tree. It records > data block update frequency of the extent per inode, in > order to provide better temperature hints for data block > diff --git a/fs/f2fs/debug.c b/fs/f2fs/debug.c > index d8bfdac5c1e4..ed6e03b42836 100644 > --- a/fs/f2fs/debug.c > +++ b/fs/f2fs/debug.c > @@ -325,9 +325,10 @@ static void update_mem_info(struct f2fs_sb_info *sbi) > si->base_mem += SIT_VBLOCK_MAP_SIZE * 2; > /* Approximate private bitmaps for active logs */ > si->base_mem += SIT_VBLOCK_MAP_SIZE * NR_CURSEG_TYPE; > -#ifdef CONFIG_F2FS_CHECK_FS > - si->base_mem += SIT_VBLOCK_MAP_SIZE * MAIN_SEGS(sbi); > -#endif > + #ifdef CONFIG_F2FS_CHECK_FS > + if (f2fs_sit_check_enabled(sbi)) > + si->base_mem += SIT_VBLOCK_MAP_SIZE * MAIN_SEGS(sbi); > + #endif > if (f2fs_block_unit_discard(sbi)) > si->base_mem += SIT_VBLOCK_MAP_SIZE * MAIN_SEGS(sbi); > } else { > diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h > index 11f3601ffd34..d8526103d86a 100644 > --- a/fs/f2fs/f2fs.h > +++ b/fs/f2fs/f2fs.h > @@ -130,6 +130,7 @@ enum f2fs_mount_opt { > F2FS_MOUNT_COMPRESS_CACHE, > F2FS_MOUNT_AGE_EXTENT_CACHE, > F2FS_MOUNT_NAT_BITS, > + F2FS_MOUNT_SHARED_SIT_CHECK, > F2FS_MOUNT_INLINECRYPT, > /* > * Some f2fs environments expect to be able to pass the "lazytime" option > @@ -4920,6 +4921,21 @@ static inline bool f2fs_use_shared_sit_map(struct f2fs_sb_info *sbi) > return f2fs_sb_has_blkzoned(sbi) && f2fs_lfs_mode(sbi); > } > > +/* > + * shared_sit_check is a mount-time escape hatch for the extra SIT mirror > + * tracking kept by CONFIG_F2FS_CHECK_FS in zoned shared-SIT mode. > + */ > +static inline bool f2fs_sit_check_enabled(struct f2fs_sb_info *sbi) > +{ > + if (!IS_ENABLED(CONFIG_F2FS_CHECK_FS)) > + return false; > + > + if (!f2fs_use_shared_sit_map(sbi)) > + return true; > + > + return test_opt(sbi, SHARED_SIT_CHECK); > +} > + > static inline bool f2fs_is_sequential_zone_area(struct f2fs_sb_info *sbi, > block_t blkaddr) > { > diff --git a/fs/f2fs/gc.c b/fs/f2fs/gc.c > index 80b8500fa987..1decb0caea1b 100644 > --- a/fs/f2fs/gc.c > +++ b/fs/f2fs/gc.c > @@ -877,15 +877,16 @@ int f2fs_get_victim(struct f2fs_sb_info *sbi, unsigned int *result, > p.offset = segno + p.ofs_unit; > nsearched++; > > -#ifdef CONFIG_F2FS_CHECK_FS > - /* > - * skip selecting the invalid segno (that is failed due to block > - * validity check failure during GC) to avoid endless GC loop in > - * such cases. > - */ > - if (test_bit(segno, sm->invalid_segmap)) > - goto next; > -#endif > + #ifdef CONFIG_F2FS_CHECK_FS > + /* > + * skip selecting the invalid segno (that is failed due to block > + * validity check failure during GC) to avoid endless GC loop in > + * such cases. > + */ > + if (f2fs_sit_check_enabled(sbi) && > + test_bit(segno, sm->invalid_segmap)) > + goto next; > + #endif > > secno = GET_SEC_FROM_SEG(sbi, segno); > > @@ -1197,18 +1198,20 @@ static bool is_alive(struct f2fs_sb_info *sbi, struct f2fs_summary *sum, > f2fs_folio_put(node_folio, true); > > if (source_blkaddr != blkaddr) { > -#ifdef CONFIG_F2FS_CHECK_FS > + #ifdef CONFIG_F2FS_CHECK_FS > unsigned int segno = GET_SEGNO(sbi, blkaddr); > unsigned long offset = GET_BLKOFF_FROM_SEG0(sbi, blkaddr); > > - if (unlikely(check_valid_map(sbi, segno, offset))) { > + if (f2fs_sit_check_enabled(sbi) && > + unlikely(check_valid_map(sbi, segno, offset))) { > if (!test_and_set_bit(segno, SIT_I(sbi)->invalid_segmap)) { > - f2fs_err(sbi, "mismatched blkaddr %u (source_blkaddr %u) in seg %u", > + f2fs_err(sbi, > + "mismatched blkaddr %u (source_blkaddr %u) in seg %u", > blkaddr, source_blkaddr, segno); > set_sbi_flag(sbi, SBI_NEED_FSCK); > } > } > -#endif > + #endif > return false; > } > return true; > diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c > index 0dab6b16ba56..dce66eec2a5f 100644 > --- a/fs/f2fs/segment.c > +++ b/fs/f2fs/segment.c > @@ -38,6 +38,11 @@ struct f2fs_sit_bitmap { > unsigned char map[SIT_VBLOCK_MAP_SIZE]; > }; > > +static inline struct f2fs_sit_bitmap *f2fs_sit_bitmap_from_map(void *map) > +{ > + return container_of(map, struct f2fs_sit_bitmap, map); > +} > + > static void f2fs_free_sit_bitmap_rcu(struct rcu_head *rcu) > { > struct f2fs_sit_bitmap *b = container_of(rcu, struct f2fs_sit_bitmap, rcu); > @@ -45,11 +50,6 @@ static void f2fs_free_sit_bitmap_rcu(struct rcu_head *rcu) > kmem_cache_free(sit_bitmap_slab, b); > } > > -static struct f2fs_sit_bitmap *f2fs_sit_bitmap_from_map(void *map) > -{ > - return container_of(map, struct f2fs_sit_bitmap, map); > -} > - > static unsigned long __reverse_ulong(unsigned char *str) > { > unsigned long tmp = 0; > @@ -845,9 +845,10 @@ static void __remove_dirty_segment(struct f2fs_sb_info *sbi, unsigned int segno, > if (valid_blocks == 0) { > clear_bit(GET_SEC_FROM_SEG(sbi, segno), > dirty_i->victim_secmap); > -#ifdef CONFIG_F2FS_CHECK_FS > - clear_bit(segno, SIT_I(sbi)->invalid_segmap); > -#endif > + #ifdef CONFIG_F2FS_CHECK_FS > + if (f2fs_sit_check_enabled(sbi)) > + clear_bit(segno, SIT_I(sbi)->invalid_segmap); > + #endif > } > if (__is_large_section(sbi)) { > unsigned int secno = GET_SEC_FROM_SEG(sbi, segno); > @@ -2471,9 +2472,10 @@ static int update_sit_entry_for_release(struct f2fs_sb_info *sbi, struct seg_ent > memset(b->map, 0xff, SIT_VBLOCK_MAP_SIZE); > rcu_assign_pointer(se->cur_valid_map, b->map); > se->ckpt_valid_map = b->map; > -#ifdef CONFIG_F2FS_CHECK_FS > - memcpy(se->cur_valid_map_mir, b->map, SIT_VBLOCK_MAP_SIZE); > -#endif > + #ifdef CONFIG_F2FS_CHECK_FS > + if (f2fs_sit_check_enabled(sbi)) > + memcpy(se->cur_valid_map_mir, b->map, SIT_VBLOCK_MAP_SIZE); > + #endif > } else if (se->cur_valid_map == sit_i->bitmap_zero) { > /* Should not happen, freeing empty segment */ > struct f2fs_sit_bitmap *b = f2fs_kmem_cache_alloc(sit_bitmap_slab, > @@ -2481,23 +2483,27 @@ static int update_sit_entry_for_release(struct f2fs_sb_info *sbi, struct seg_ent > memset(b->map, 0, SIT_VBLOCK_MAP_SIZE); > rcu_assign_pointer(se->cur_valid_map, b->map); > se->ckpt_valid_map = b->map; > -#ifdef CONFIG_F2FS_CHECK_FS > - memcpy(se->cur_valid_map_mir, b->map, SIT_VBLOCK_MAP_SIZE); > -#endif > + #ifdef CONFIG_F2FS_CHECK_FS > + if (f2fs_sit_check_enabled(sbi)) > + memcpy(se->cur_valid_map_mir, b->map, SIT_VBLOCK_MAP_SIZE); > + #endif > } > } > > for (i = 0; i < del_count; i++) { > exist = f2fs_test_and_clear_bit(offset + i, se->cur_valid_map); > -#ifdef CONFIG_F2FS_CHECK_FS > - mir_exist = f2fs_test_and_clear_bit(offset + i, > - se->cur_valid_map_mir); > - if (unlikely(exist != mir_exist)) { > - f2fs_err(sbi, "Inconsistent error when clearing bitmap, blk:%u, old bit:%d", > - blkaddr + i, exist); > - f2fs_bug_on(sbi, 1); > - } > -#endif > + #ifdef CONFIG_F2FS_CHECK_FS > + if (f2fs_sit_check_enabled(sbi)) { > + mir_exist = f2fs_test_and_clear_bit(offset + i, > + se->cur_valid_map_mir); > + if (unlikely(exist != mir_exist)) { > + f2fs_err(sbi, > + "Inconsistent error when clearing bitmap, blk:%u, old bit:%d", > + blkaddr + i, exist); > + f2fs_bug_on(sbi, 1); > + } > + } > + #endif > if (unlikely(!exist)) { > f2fs_err(sbi, "Bitmap was wrongly cleared, blk:%u", blkaddr + i); > f2fs_bug_on(sbi, 1); > @@ -2559,9 +2565,10 @@ static int update_sit_entry_for_alloc(struct f2fs_sb_info *sbi, struct seg_entry > memset(b->map, 0, SIT_VBLOCK_MAP_SIZE); > rcu_assign_pointer(se->cur_valid_map, b->map); > se->ckpt_valid_map = b->map; > -#ifdef CONFIG_F2FS_CHECK_FS > - memcpy(se->cur_valid_map_mir, b->map, SIT_VBLOCK_MAP_SIZE); > -#endif > + #ifdef CONFIG_F2FS_CHECK_FS > + if (f2fs_sit_check_enabled(sbi)) > + memcpy(se->cur_valid_map_mir, b->map, SIT_VBLOCK_MAP_SIZE); > + #endif > } else if (se->cur_valid_map == sit_i->bitmap_full) { > /* Should not happen in LFS alloc, but for safety */ > struct f2fs_sit_bitmap *b = f2fs_kmem_cache_alloc(sit_bitmap_slab, > @@ -2569,22 +2576,26 @@ static int update_sit_entry_for_alloc(struct f2fs_sb_info *sbi, struct seg_entry > memset(b->map, 0xff, SIT_VBLOCK_MAP_SIZE); > rcu_assign_pointer(se->cur_valid_map, b->map); > se->ckpt_valid_map = b->map; > -#ifdef CONFIG_F2FS_CHECK_FS > - memcpy(se->cur_valid_map_mir, b->map, SIT_VBLOCK_MAP_SIZE); > -#endif > + #ifdef CONFIG_F2FS_CHECK_FS > + if (f2fs_sit_check_enabled(sbi)) > + memcpy(se->cur_valid_map_mir, b->map, SIT_VBLOCK_MAP_SIZE); > + #endif > } > } > > exist = f2fs_test_and_set_bit(offset, se->cur_valid_map); > -#ifdef CONFIG_F2FS_CHECK_FS > - mir_exist = f2fs_test_and_set_bit(offset, > - se->cur_valid_map_mir); > - if (unlikely(exist != mir_exist)) { > - f2fs_err(sbi, "Inconsistent error when setting bitmap, blk:%u, old bit:%d", > - blkaddr, exist); > - f2fs_bug_on(sbi, 1); > + #ifdef CONFIG_F2FS_CHECK_FS > + if (f2fs_sit_check_enabled(sbi)) { > + mir_exist = f2fs_test_and_set_bit(offset, > + se->cur_valid_map_mir); > + if (unlikely(exist != mir_exist)) { > + f2fs_err(sbi, > + "Inconsistent error when setting bitmap, blk:%u, old bit:%d", > + blkaddr, exist); > + f2fs_bug_on(sbi, 1); > + } > } > -#endif > + #endif > if (unlikely(exist)) { > f2fs_err(sbi, "Bitmap was wrongly set, blk:%u", blkaddr); > f2fs_bug_on(sbi, 1); > @@ -2677,13 +2688,15 @@ static void update_sit_entry(struct f2fs_sb_info *sbi, block_t blkaddr, int del) > > rcu_assign_pointer(se->cur_valid_map, sit_i->bitmap_zero); > se->ckpt_valid_map = sit_i->bitmap_zero; > -#ifdef CONFIG_F2FS_CHECK_FS > - memset(se->cur_valid_map_mir, 0, SIT_VBLOCK_MAP_SIZE); > -#endif > + #ifdef CONFIG_F2FS_CHECK_FS > + if (f2fs_sit_check_enabled(sbi)) > + memset(se->cur_valid_map_mir, 0, SIT_VBLOCK_MAP_SIZE); > + #endif > if (old_map != sit_i->bitmap_zero && > old_map != sit_i->bitmap_full) { > - call_rcu(&f2fs_sit_bitmap_from_map(old_map)->rcu, > - f2fs_free_sit_bitmap_rcu); > + struct f2fs_sit_bitmap *b = > + f2fs_sit_bitmap_from_map(old_map); > + call_rcu(&b->rcu, f2fs_free_sit_bitmap_rcu); > } > } else if (new_vblocks == BLKS_PER_SEG(sbi) && > se->cur_valid_map != sit_i->bitmap_full) { > @@ -2691,13 +2704,15 @@ static void update_sit_entry(struct f2fs_sb_info *sbi, block_t blkaddr, int del) > > rcu_assign_pointer(se->cur_valid_map, sit_i->bitmap_full); > se->ckpt_valid_map = sit_i->bitmap_full; > -#ifdef CONFIG_F2FS_CHECK_FS > - memset(se->cur_valid_map_mir, 0xff, SIT_VBLOCK_MAP_SIZE); > -#endif > + #ifdef CONFIG_F2FS_CHECK_FS > + if (f2fs_sit_check_enabled(sbi)) > + memset(se->cur_valid_map_mir, 0xff, SIT_VBLOCK_MAP_SIZE); > + #endif > if (old_map != sit_i->bitmap_zero && > old_map != sit_i->bitmap_full) { > - call_rcu(&f2fs_sit_bitmap_from_map(old_map)->rcu, > - f2fs_free_sit_bitmap_rcu); > + struct f2fs_sit_bitmap *b = > + f2fs_sit_bitmap_from_map(old_map); > + call_rcu(&b->rcu, f2fs_free_sit_bitmap_rcu); > } > } > } > @@ -4701,7 +4716,7 @@ static struct folio *get_next_sit_folio(struct f2fs_sb_info *sbi, > seg_info_to_sit_folio(sbi, folio, start); > > folio_mark_dirty(folio); > - set_to_next_sit(sit_i, start); > + set_to_next_sit(sbi, sit_i, start); > > return folio; > } > @@ -4857,11 +4872,12 @@ void f2fs_flush_sit_entries(struct f2fs_sb_info *sbi, struct cp_control *cpc) > int offset, sit_offset; > > se = get_seg_entry(sbi, segno); > -#ifdef CONFIG_F2FS_CHECK_FS > - if (memcmp(se->cur_valid_map, se->cur_valid_map_mir, > - SIT_VBLOCK_MAP_SIZE)) > + #ifdef CONFIG_F2FS_CHECK_FS > + if (f2fs_sit_check_enabled(sbi) && > + memcmp(se->cur_valid_map, se->cur_valid_map_mir, > + SIT_VBLOCK_MAP_SIZE)) > f2fs_bug_on(sbi, 1); > -#endif > + #endif > > /* add discard candidates */ > if (!(cpc->reason & CP_DISCARD)) { > @@ -4953,11 +4969,8 @@ static int build_sit_info(struct f2fs_sb_info *sbi) > if (!sit_i->dirty_sentries_bitmap) > return -ENOMEM; > > -#ifdef CONFIG_F2FS_CHECK_FS > - bitmap_size = MAIN_SEGS(sbi) * SIT_VBLOCK_MAP_SIZE * (3 + discard_map); > -#else > - bitmap_size = MAIN_SEGS(sbi) * SIT_VBLOCK_MAP_SIZE * (2 + discard_map); > -#endif > + bitmap_size = MAIN_SEGS(sbi) * SIT_VBLOCK_MAP_SIZE * > + (2 + discard_map + f2fs_sit_check_enabled(sbi)); > > if (share_map) { > sit_i->bitmap_zero = f2fs_kzalloc(sbi, SIT_VBLOCK_MAP_SIZE, GFP_KERNEL); > @@ -4971,10 +4984,9 @@ static int build_sit_info(struct f2fs_sb_info *sbi) > } > memset(sit_i->bitmap_full, 0xff, SIT_VBLOCK_MAP_SIZE); > > - bitmap_size = MAIN_SEGS(sbi) * SIT_VBLOCK_MAP_SIZE * discard_map; > -#ifdef CONFIG_F2FS_CHECK_FS > - bitmap_size += MAIN_SEGS(sbi) * SIT_VBLOCK_MAP_SIZE; > -#endif > + bitmap_size = MAIN_SEGS(sbi) * SIT_VBLOCK_MAP_SIZE * discard_map; > + if (f2fs_sit_check_enabled(sbi)) > + bitmap_size += MAIN_SEGS(sbi) * SIT_VBLOCK_MAP_SIZE; > if (bitmap_size) { > sit_i->bitmap = f2fs_kvzalloc(sbi, bitmap_size, GFP_KERNEL); > if (!sit_i->bitmap) { > @@ -4993,9 +5005,10 @@ static int build_sit_info(struct f2fs_sb_info *sbi) > sit_i->sentries[start].ckpt_valid_map = > sit_i->bitmap_zero; > #ifdef CONFIG_F2FS_CHECK_FS > - sit_i->sentries[start].cur_valid_map_mir = > - bitmap; > - bitmap += SIT_VBLOCK_MAP_SIZE; > + if (f2fs_sit_check_enabled(sbi)) { > + sit_i->sentries[start].cur_valid_map_mir = bitmap; > + bitmap += SIT_VBLOCK_MAP_SIZE; > + } > #endif > if (discard_map) { > sit_i->sentries[start].discard_map = bitmap; > @@ -5017,8 +5030,10 @@ static int build_sit_info(struct f2fs_sb_info *sbi) > bitmap += SIT_VBLOCK_MAP_SIZE; > > #ifdef CONFIG_F2FS_CHECK_FS > - sit_i->sentries[start].cur_valid_map_mir = bitmap; > - bitmap += SIT_VBLOCK_MAP_SIZE; > + if (f2fs_sit_check_enabled(sbi)) { > + sit_i->sentries[start].cur_valid_map_mir = bitmap; > + bitmap += SIT_VBLOCK_MAP_SIZE; > + } > #endif > > if (discard_map) { > @@ -5052,16 +5067,20 @@ static int build_sit_info(struct f2fs_sb_info *sbi) > if (!sit_i->sit_bitmap) > return -ENOMEM; > > -#ifdef CONFIG_F2FS_CHECK_FS > - sit_i->sit_bitmap_mir = kmemdup(src_bitmap, > - sit_bitmap_size, GFP_KERNEL); > - if (!sit_i->sit_bitmap_mir) > - return -ENOMEM; > + #ifdef CONFIG_F2FS_CHECK_FS > + if (f2fs_sit_check_enabled(sbi)) { > + sit_i->sit_bitmap_mir = kmemdup(src_bitmap, > + sit_bitmap_size, > + GFP_KERNEL); > + if (!sit_i->sit_bitmap_mir) > + return -ENOMEM; > > - sit_i->invalid_segmap = f2fs_kvzalloc(sbi, > - main_bitmap_size, GFP_KERNEL); > - if (!sit_i->invalid_segmap) > - return -ENOMEM; > + sit_i->invalid_segmap = f2fs_kvzalloc(sbi, > + main_bitmap_size, > + GFP_KERNEL); > + if (!sit_i->invalid_segmap) > + return -ENOMEM; > + } > #endif > > sit_i->sit_base_addr = le32_to_cpu(raw_super->sit_blkaddr); > @@ -5200,12 +5219,13 @@ static int build_sit_entries(struct f2fs_sb_info *sbi) > rcu_assign_pointer(se->cur_valid_map, map); > se->ckpt_valid_map = map; > #ifdef CONFIG_F2FS_CHECK_FS > - memcpy(se->cur_valid_map_mir, map, SIT_VBLOCK_MAP_SIZE); > + if (f2fs_sit_check_enabled(sbi)) > + memcpy(se->cur_valid_map_mir, map, SIT_VBLOCK_MAP_SIZE); > #endif > se->type = GET_SIT_TYPE(&sit); > se->mtime = le64_to_cpu(sit.mtime); > } else { > - seg_info_from_raw_sit(se, &sit); > + seg_info_from_raw_sit(sbi, se, &sit); > } > > if (se->type >= NR_PERSISTENT_LOG) { > @@ -5279,15 +5299,19 @@ static int build_sit_entries(struct f2fs_sb_info *sbi) > > if (vblocks == 0 && !is_active) { > if (map != sit_i->bitmap_zero && > - map != sit_i->bitmap_full) > - kmem_cache_free(sit_bitmap_slab, > - f2fs_sit_bitmap_from_map(map)); > + map != sit_i->bitmap_full) { > + struct f2fs_sit_bitmap *b = > + f2fs_sit_bitmap_from_map(map); > + kmem_cache_free(sit_bitmap_slab, b); > + } > map = sit_i->bitmap_zero; > } else if (vblocks == BLKS_PER_SEG(sbi) && !is_active) { > if (map != sit_i->bitmap_zero && > - map != sit_i->bitmap_full) > - kmem_cache_free(sit_bitmap_slab, > - f2fs_sit_bitmap_from_map(map)); > + map != sit_i->bitmap_full) { > + struct f2fs_sit_bitmap *b = > + f2fs_sit_bitmap_from_map(map); > + kmem_cache_free(sit_bitmap_slab, b); > + } > map = sit_i->bitmap_full; > } else { > if (map == sit_i->bitmap_zero || > @@ -5310,12 +5334,13 @@ static int build_sit_entries(struct f2fs_sb_info *sbi) > rcu_assign_pointer(se->cur_valid_map, map); > se->ckpt_valid_map = map; > #ifdef CONFIG_F2FS_CHECK_FS > - memcpy(se->cur_valid_map_mir, map, SIT_VBLOCK_MAP_SIZE); > + if (f2fs_sit_check_enabled(sbi)) > + memcpy(se->cur_valid_map_mir, map, SIT_VBLOCK_MAP_SIZE); > #endif > se->type = GET_SIT_TYPE(&sit); > se->mtime = le64_to_cpu(sit.mtime); > } else { > - seg_info_from_raw_sit(se, &sit); > + seg_info_from_raw_sit(sbi, se, &sit); > } > > if (se->type >= NR_PERSISTENT_LOG) { > @@ -6099,18 +6124,18 @@ static void destroy_sit_info(struct f2fs_sb_info *sbi) > if (f2fs_use_shared_sit_map(sbi)) { > unsigned int start; > > - for (start = 0; start < MAIN_SEGS(sbi); start++) { > - struct seg_entry *se = &sit_i->sentries[start]; > + for (start = 0; start < MAIN_SEGS(sbi); start++) { > + struct seg_entry *se = &sit_i->sentries[start]; > > - if (se->cur_valid_map && > - se->cur_valid_map != sit_i->bitmap_zero && > - se->cur_valid_map != sit_i->bitmap_full) { > - struct f2fs_sit_bitmap *b; > + if (se->cur_valid_map && > + se->cur_valid_map != sit_i->bitmap_zero && > + se->cur_valid_map != sit_i->bitmap_full) { > + struct f2fs_sit_bitmap *b; > > - b = f2fs_sit_bitmap_from_map(se->cur_valid_map); > - kmem_cache_free(sit_bitmap_slab, b); > + b = f2fs_sit_bitmap_from_map(se->cur_valid_map); > + kmem_cache_free(sit_bitmap_slab, b); > + } > } > - } > kfree(sit_i->bitmap_zero); > kfree(sit_i->bitmap_full); > if (sit_i->bitmap) > diff --git a/fs/f2fs/segment.h b/fs/f2fs/segment.h > index cb45cfa7a658..0f4445375ed1 100644 > --- a/fs/f2fs/segment.h > +++ b/fs/f2fs/segment.h > @@ -404,15 +404,17 @@ static inline void sanity_check_valid_blocks(struct f2fs_sb_info *sbi, > { > } > #endif > -static inline void seg_info_from_raw_sit(struct seg_entry *se, > - struct f2fs_sit_entry *rs) > +static inline void seg_info_from_raw_sit(struct f2fs_sb_info *sbi, > + struct seg_entry *se, > + struct f2fs_sit_entry *rs) > { > se->valid_blocks = GET_SIT_VBLOCKS(rs); > se->ckpt_valid_blocks = GET_SIT_VBLOCKS(rs); > memcpy(se->cur_valid_map, rs->valid_map, SIT_VBLOCK_MAP_SIZE); > memcpy(se->ckpt_valid_map, rs->valid_map, SIT_VBLOCK_MAP_SIZE); > #ifdef CONFIG_F2FS_CHECK_FS > - memcpy(se->cur_valid_map_mir, rs->valid_map, SIT_VBLOCK_MAP_SIZE); > + if (f2fs_sit_check_enabled(sbi)) > + memcpy(se->cur_valid_map_mir, rs->valid_map, SIT_VBLOCK_MAP_SIZE); > #endif > se->type = GET_SIT_TYPE(rs); > se->mtime = le64_to_cpu(rs->mtime); > @@ -558,11 +560,12 @@ static inline void get_sit_bitmap(struct f2fs_sb_info *sbi, > { > struct sit_info *sit_i = SIT_I(sbi); > > -#ifdef CONFIG_F2FS_CHECK_FS > - if (memcmp(sit_i->sit_bitmap, sit_i->sit_bitmap_mir, > - sit_i->bitmap_size)) > + #ifdef CONFIG_F2FS_CHECK_FS > + if (f2fs_sit_check_enabled(sbi) && > + memcmp(sit_i->sit_bitmap, sit_i->sit_bitmap_mir, > + sit_i->bitmap_size)) > f2fs_bug_on(sbi, 1); > -#endif > + #endif > memcpy(dst_addr, sit_i->sit_bitmap, sit_i->bitmap_size); > } > > @@ -904,8 +907,9 @@ static inline pgoff_t current_sit_addr(struct f2fs_sb_info *sbi, > f2fs_bug_on(sbi, !valid_main_segno(sbi, start)); > > #ifdef CONFIG_F2FS_CHECK_FS > - if (f2fs_test_bit(offset, sit_i->sit_bitmap) != > - f2fs_test_bit(offset, sit_i->sit_bitmap_mir)) > + if (f2fs_sit_check_enabled(sbi) && > + f2fs_test_bit(offset, sit_i->sit_bitmap) != > + f2fs_test_bit(offset, sit_i->sit_bitmap_mir)) > f2fs_bug_on(sbi, 1); > #endif > > @@ -929,13 +933,15 @@ static inline pgoff_t next_sit_addr(struct f2fs_sb_info *sbi, > return block_addr + sit_i->sit_base_addr; > } > > -static inline void set_to_next_sit(struct sit_info *sit_i, unsigned int start) > +static inline void set_to_next_sit(struct f2fs_sb_info *sbi, > + struct sit_info *sit_i, unsigned int start) > { > unsigned int block_off = SIT_BLOCK_OFFSET(start); > > f2fs_change_bit(block_off, sit_i->sit_bitmap); > #ifdef CONFIG_F2FS_CHECK_FS > - f2fs_change_bit(block_off, sit_i->sit_bitmap_mir); > + if (f2fs_sit_check_enabled(sbi)) > + f2fs_change_bit(block_off, sit_i->sit_bitmap_mir); > #endif > } > > diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c > index 83ce88ce12cb..e2bb5d0ab3e1 100644 > --- a/fs/f2fs/super.c > +++ b/fs/f2fs/super.c > @@ -228,6 +228,7 @@ enum { > Opt_gc_merge, > Opt_discard_unit, > Opt_memory_mode, > + Opt_shared_sit_check, > Opt_age_extent_cache, > Opt_errors, > Opt_nat_bits, > @@ -359,6 +360,7 @@ static const struct fs_parameter_spec f2fs_param_specs[] = { > fsparam_flag_no("gc_merge", Opt_gc_merge), > fsparam_enum("discard_unit", Opt_discard_unit, f2fs_param_discard_unit), > fsparam_enum("memory", Opt_memory_mode, f2fs_param_memory_mode), > + fsparam_flag_no("shared_sit_check", Opt_shared_sit_check), > fsparam_flag("age_extent_cache", Opt_age_extent_cache), > fsparam_enum("errors", Opt_errors, f2fs_param_errors), > fsparam_enum("lookup_mode", Opt_lookup_mode, f2fs_param_lookup_mode), > @@ -1217,6 +1219,12 @@ static int f2fs_parse_param(struct fs_context *fc, struct fs_parameter *param) > F2FS_CTX_INFO(ctx).memory_mode = result.uint_32; > ctx->spec_mask |= F2FS_SPEC_memory_mode; > break; > + case Opt_shared_sit_check: > + if (result.negated) > + ctx_clear_opt(ctx, F2FS_MOUNT_SHARED_SIT_CHECK); > + else > + ctx_set_opt(ctx, F2FS_MOUNT_SHARED_SIT_CHECK); > + break; > case Opt_age_extent_cache: > ctx_set_opt(ctx, F2FS_MOUNT_AGE_EXTENT_CACHE); > break; > @@ -2514,6 +2522,11 @@ static int f2fs_show_options(struct seq_file *seq, struct dentry *root) > else if (F2FS_OPTION(sbi).memory_mode == MEMORY_MODE_LOW) > seq_printf(seq, ",memory=%s", "low"); > > + if (test_opt(sbi, SHARED_SIT_CHECK)) > + seq_puts(seq, ",shared_sit_check"); > + else > + seq_puts(seq, ",noshared_sit_check"); > + > if (F2FS_OPTION(sbi).errors == MOUNT_ERRORS_READONLY) > seq_printf(seq, ",errors=%s", "remount-ro"); > else if (F2FS_OPTION(sbi).errors == MOUNT_ERRORS_CONTINUE) > @@ -2573,6 +2586,7 @@ static void default_options(struct f2fs_sb_info *sbi, bool remount) > F2FS_OPTION(sbi).bggc_mode = BGGC_MODE_ON; > F2FS_OPTION(sbi).memory_mode = MEMORY_MODE_NORMAL; > F2FS_OPTION(sbi).errors = MOUNT_ERRORS_CONTINUE; > + set_opt(sbi, SHARED_SIT_CHECK); > > set_opt(sbi, INLINE_XATTR); > set_opt(sbi, INLINE_DATA); > @@ -2782,6 +2796,7 @@ static int __f2fs_remount(struct fs_context *fc, struct super_block *sb) > bool no_compress_cache = !test_opt(sbi, COMPRESS_CACHE); > bool block_unit_discard = f2fs_block_unit_discard(sbi); > bool no_nat_bits = !test_opt(sbi, NAT_BITS); > + bool shared_sit_check = test_opt(sbi, SHARED_SIT_CHECK); > #ifdef CONFIG_QUOTA > int i, j; > #endif > @@ -2904,6 +2919,12 @@ static int __f2fs_remount(struct fs_context *fc, struct super_block *sb) > goto restore_opts; > } > > + if (shared_sit_check != !!test_opt(sbi, SHARED_SIT_CHECK)) { > + err = -EINVAL; > + f2fs_warn(sbi, "switch shared_sit_check option is not allowed"); > + goto restore_opts; > + } > + > if ((flags & SB_RDONLY) && test_opt(sbi, DISABLE_CHECKPOINT)) { > err = -EINVAL; > f2fs_warn(sbi, "disabling checkpoint not compatible with read-only"); ^ permalink raw reply [flat|nested] 6+ messages in thread
end of thread, other threads:[~2026-03-14 5:14 UTC | newest] Thread overview: 6+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2026-03-09 10:36 [RFC PATCH 0/3] f2fs: reduce zoned LFS memory by sharing SIT valid maps 'wallentx 2026-03-09 10:36 ` [RFC PATCH 1/3] f2fs: prepare cur_valid_map for safe lockless access 'wallentx 2026-03-09 10:36 ` [RFC PATCH 2/3] f2fs: reduce zoned LFS memory by sharing SIT valid maps 'wallentx 2026-03-14 5:08 ` Chao Yu 2026-03-09 10:37 ` [RFC PATCH 3/3] f2fs: add mount option to disable shared SIT mirror checks 'wallentx 2026-03-14 5:14 ` Chao Yu
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox