* [PATCH 1/5] f2fs: avoid underflow when counting free NIDs
2026-08-26 21:39 [PATCH 0/5] f2fs: prepare metadata layouts for runtime block sizes Xinping Zhang
@ 2026-08-26 21:39 ` Xinping Zhang
2026-08-27 3:39 ` Chao Yu
2026-08-31 23:22 ` Chao Yu
2026-08-26 21:39 ` [PATCH 2/5] f2fs: describe SIT block layout dynamically Xinping Zhang
` (3 subsequent siblings)
4 siblings, 2 replies; 9+ messages in thread
From: Xinping Zhang @ 2026-08-26 21:39 UTC (permalink / raw)
To: linux-f2fs-devel; +Cc: jaegeuk, chao, linux-kernel
__count_free_nids() subtracts the retention threshold before checking
whether the cached count exceeds it. The operands are unsigned, so a
smaller cache wraps before the result is assigned to long.
The PAGE_SIZE-derived threshold happens to make this an unsigned long
subtraction, whose wrapped result becomes negative when converted to
long by supported toolchains. Do not rely on operand width or
unsigned-to-signed conversion. Compare values before subtracting.
Signed-off-by: Kelvin Zhang <zhangxp1998@gmail.com>
---
fs/f2fs/shrinker.c | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/fs/f2fs/shrinker.c b/fs/f2fs/shrinker.c
index 4f6bf5926de4..1fd0ee4f89a9 100644
--- a/fs/f2fs/shrinker.c
+++ b/fs/f2fs/shrinker.c
@@ -23,9 +23,10 @@ static unsigned long __count_nat_entries(struct
f2fs_sb_info *sbi)
static unsigned long __count_free_nids(struct f2fs_sb_info *sbi)
{
- long count = NM_I(sbi)->nid_cnt[FREE_NID] - MAX_FREE_NIDS;
+ unsigned long count = NM_I(sbi)->nid_cnt[FREE_NID];
+ unsigned long max = MAX_FREE_NIDS;
- return count > 0 ? count : 0;
+ return count > max ? count - max : 0;
}
static unsigned long __count_extent_cache(struct f2fs_sb_info *sbi,
--
2.53.0
^ permalink raw reply [flat|nested] 9+ messages in thread* Re: [PATCH 1/5] f2fs: avoid underflow when counting free NIDs
2026-08-26 21:39 ` [PATCH 1/5] f2fs: avoid underflow when counting free NIDs Xinping Zhang
@ 2026-08-27 3:39 ` Chao Yu
2026-08-31 23:22 ` Chao Yu
1 sibling, 0 replies; 9+ messages in thread
From: Chao Yu @ 2026-08-27 3:39 UTC (permalink / raw)
To: Xinping Zhang, linux-f2fs-devel; +Cc: chao, jaegeuk, linux-kernel
On 8/27/26 05:39, Xinping Zhang wrote:
> __count_free_nids() subtracts the retention threshold before checking
> whether the cached count exceeds it. The operands are unsigned, so a
> smaller cache wraps before the result is assigned to long.
>
> The PAGE_SIZE-derived threshold happens to make this an unsigned long
> subtraction, whose wrapped result becomes negative when converted to
> long by supported toolchains. Do not rely on operand width or
> unsigned-to-signed conversion. Compare values before subtracting.
>
Cc: stable@kernel.org
Fixes: 1b38dc8e74a3 ("f2fs: shrink nat_cache entries")
Fixes: 02110a4fd531 ("f2fs: avoid casted negative value as shrink count")
> Signed-off-by: Kelvin Zhang <zhangxp1998@gmail.com>
Reviewed-by: Chao Yu <chao@kernel.org>
Thanks,
^ permalink raw reply [flat|nested] 9+ messages in thread* Re: [PATCH 1/5] f2fs: avoid underflow when counting free NIDs
2026-08-26 21:39 ` [PATCH 1/5] f2fs: avoid underflow when counting free NIDs Xinping Zhang
2026-08-27 3:39 ` Chao Yu
@ 2026-08-31 23:22 ` Chao Yu
2026-09-01 1:21 ` [PATCH v2] " Kelvin Zhang
1 sibling, 1 reply; 9+ messages in thread
From: Chao Yu @ 2026-08-31 23:22 UTC (permalink / raw)
To: Xinping Zhang, linux-f2fs-devel; +Cc: chao, jaegeuk, linux-kernel
Kelvin,
This patch was wrapped, please resend it as well.
On 8/27/26 05:39, Xinping Zhang wrote:
> __count_free_nids() subtracts the retention threshold before checking
> whether the cached count exceeds it. The operands are unsigned, so a
> smaller cache wraps before the result is assigned to long.
>
> The PAGE_SIZE-derived threshold happens to make this an unsigned long
> subtraction, whose wrapped result becomes negative when converted to
> long by supported toolchains. Do not rely on operand width or
> unsigned-to-signed conversion. Compare values before subtracting.
>
> Signed-off-by: Kelvin Zhang <zhangxp1998@gmail.com>
> ---
> fs/f2fs/shrinker.c | 5 +++--
> 1 file changed, 3 insertions(+), 2 deletions(-)
>
> diff --git a/fs/f2fs/shrinker.c b/fs/f2fs/shrinker.c
> index 4f6bf5926de4..1fd0ee4f89a9 100644
> --- a/fs/f2fs/shrinker.c
> +++ b/fs/f2fs/shrinker.c
> @@ -23,9 +23,10 @@ static unsigned long __count_nat_entries(struct
> f2fs_sb_info *sbi)
>
> static unsigned long __count_free_nids(struct f2fs_sb_info *sbi)
> {
> - long count = NM_I(sbi)->nid_cnt[FREE_NID] - MAX_FREE_NIDS;
> + unsigned long count = NM_I(sbi)->nid_cnt[FREE_NID];
> + unsigned long max = MAX_FREE_NIDS;
>
> - return count > 0 ? count : 0;
> + return count > max ? count - max : 0;
> }
>
> static unsigned long __count_extent_cache(struct f2fs_sb_info *sbi,
^ permalink raw reply [flat|nested] 9+ messages in thread* [PATCH v2] f2fs: avoid underflow when counting free NIDs
2026-08-31 23:22 ` Chao Yu
@ 2026-09-01 1:21 ` Kelvin Zhang
0 siblings, 0 replies; 9+ messages in thread
From: Kelvin Zhang @ 2026-09-01 1:21 UTC (permalink / raw)
To: linux-f2fs-devel; +Cc: jaegeuk, chao, linux-kernel, stable
__count_free_nids() subtracts the retention threshold before checking
whether the cached count exceeds it. The operands are unsigned, so a
smaller cache wraps before the result is assigned to long.
The PAGE_SIZE-derived threshold happens to make this an unsigned long
subtraction, whose wrapped result becomes negative when converted to
long by supported toolchains. Do not rely on operand width or
unsigned-to-signed conversion. Compare values before subtracting.
Fixes: 1b38dc8e74a3 ("f2fs: shrink nat_cache entries")
Fixes: 02110a4fd531 ("f2fs: avoid casted negative value as shrink count")
Cc: stable@kernel.org
Reviewed-by: Chao Yu <chao@kernel.org>
Signed-off-by: Kelvin Zhang <zhangxp1998@gmail.com>
---
Changes since v1:
- add the Fixes and stable Cc trailers suggested by Chao
- carry Chao's Reviewed-by
- resend as a standalone patch through direct SMTP because the v1 list copy
was corrupted by mail transport line wrapping
v1: https://lore.kernel.org/linux-f2fs-devel/CAH=xXfG2Hzc-kNAPqEUsTJ4hwrQ-gYEnFrXHGxL7qpYpggkLUQ@mail.gmail.com/
---
fs/f2fs/shrinker.c | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/fs/f2fs/shrinker.c b/fs/f2fs/shrinker.c
index 4f6bf5926de4..1fd0ee4f89a9 100644
--- a/fs/f2fs/shrinker.c
+++ b/fs/f2fs/shrinker.c
@@ -23,9 +23,10 @@ static unsigned long __count_nat_entries(struct f2fs_sb_info *sbi)
static unsigned long __count_free_nids(struct f2fs_sb_info *sbi)
{
- long count = NM_I(sbi)->nid_cnt[FREE_NID] - MAX_FREE_NIDS;
+ unsigned long count = NM_I(sbi)->nid_cnt[FREE_NID];
+ unsigned long max = MAX_FREE_NIDS;
- return count > 0 ? count : 0;
+ return count > max ? count - max : 0;
}
static unsigned long __count_extent_cache(struct f2fs_sb_info *sbi,
base-commit: c966d29e01bbf829f8bb4a39a49811c56cdb49c3
--
2.53.0
^ permalink raw reply [flat|nested] 9+ messages in thread
* [PATCH 2/5] f2fs: describe SIT block layout dynamically
2026-08-26 21:39 [PATCH 0/5] f2fs: prepare metadata layouts for runtime block sizes Xinping Zhang
2026-08-26 21:39 ` [PATCH 1/5] f2fs: avoid underflow when counting free NIDs Xinping Zhang
@ 2026-08-26 21:39 ` Xinping Zhang
2026-08-26 21:39 ` [PATCH 3/5] f2fs: describe orphan " Xinping Zhang
` (2 subsequent siblings)
4 siblings, 0 replies; 9+ messages in thread
From: Xinping Zhang @ 2026-08-26 21:39 UTC (permalink / raw)
To: linux-f2fs-devel; +Cc: jaegeuk, chao, linux-kernel
An SIT block is a filesystem-block-sized array of SIT entries. A
fixed-length array makes the C structure describe a particular block size
rather than the on-disk layout.
Use a C flexible array member to model that dynamic layout, retaining the
entry count as an explicit filesystem geometry calculation. Unlike a
zero-length GNU array, this lets bounds sanitizers retain the runtime
extent. This is a layout-only change.
Signed-off-by: Kelvin Zhang <zhangxp1998@gmail.com>
---
fs/f2fs/checkpoint.c | 2 +-
fs/f2fs/f2fs.h | 3 +++
fs/f2fs/segment.c | 15 ++++++++-------
fs/f2fs/segment.h | 19 ++++++++++---------
fs/f2fs/super.c | 4 +++-
include/linux/f2fs_fs.h | 9 ++++++---
6 files changed, 31 insertions(+), 21 deletions(-)
diff --git a/fs/f2fs/checkpoint.c b/fs/f2fs/checkpoint.c
index 4b59f30ef45d..47c4a5c83a70 100644
--- a/fs/f2fs/checkpoint.c
+++ b/fs/f2fs/checkpoint.c
@@ -487,7 +487,7 @@ int f2fs_ra_meta_pages(struct f2fs_sb_info *sbi,
block_t start, int nrpages,
goto out;
/* get sit block addr */
fio.new_blkaddr = current_sit_addr(sbi,
- blkno * SIT_ENTRY_PER_BLOCK);
+ blkno * SIT_ENTRY_PER_BLOCK(sbi));
break;
case META_SSA:
case META_CP:
diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h
index a1f5f375045a..4423f899b2b5 100644
--- a/fs/f2fs/f2fs.h
+++ b/fs/f2fs/f2fs.h
@@ -1862,6 +1862,7 @@ struct f2fs_sb_info {
unsigned int log_sectors_per_block; /* log2 sectors per block */
unsigned int log_blocksize; /* log2 block size */
unsigned int blocksize; /* block size */
+ unsigned int sit_entries_per_block; /* SIT entries in a block */
unsigned int root_ino_num; /* root inode number*/
unsigned int node_ino_num; /* node inode number*/
unsigned int meta_ino_num; /* meta inode number*/
@@ -2249,6 +2250,8 @@ static inline struct f2fs_sb_info
*F2FS_F_SB(const struct folio *folio)
return F2FS_M_SB(folio->mapping);
}
+#define SIT_ENTRY_PER_BLOCK(sbi) ((sbi)->sit_entries_per_block)
+
static inline struct f2fs_super_block *F2FS_RAW_SUPER(struct f2fs_sb_info *sbi)
{
return (struct f2fs_super_block *)(sbi->raw_super);
diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c
index 56decf9c691c..8b8fbd5072b6 100644
--- a/fs/f2fs/segment.c
+++ b/fs/f2fs/segment.c
@@ -4707,7 +4707,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;
}
@@ -4746,10 +4746,11 @@ static void adjust_sit_entry_set(struct
sit_entry_set *ses,
list_move_tail(&ses->set_list, head);
}
-static void add_sit_entry(unsigned int segno, struct list_head *head)
+static void add_sit_entry(struct f2fs_sb_info *sbi, unsigned int segno,
+ struct list_head *head)
{
struct sit_entry_set *ses;
- unsigned int start_segno = START_SEGNO(segno);
+ unsigned int start_segno = START_SEGNO(sbi, segno);
list_for_each_entry(ses, head, set_list) {
if (ses->start_segno == start_segno) {
@@ -4774,7 +4775,7 @@ static void add_sits_in_set(struct f2fs_sb_info *sbi)
unsigned int segno;
for_each_set_bit(segno, bitmap, MAIN_SEGS(sbi))
- add_sit_entry(segno, set_list);
+ add_sit_entry(sbi, segno, set_list);
}
static void remove_sits_in_journal(struct f2fs_sb_info *sbi)
@@ -4792,7 +4793,7 @@ static void remove_sits_in_journal(struct
f2fs_sb_info *sbi)
dirtied = __mark_sit_entry_dirty(sbi, segno);
if (!dirtied)
- add_sit_entry(segno, &SM_I(sbi)->sit_entry_set);
+ add_sit_entry(sbi, segno, &SM_I(sbi)->sit_entry_set);
}
update_sits_in_cursum(journal, -i);
up_write(&curseg->journal_rwsem);
@@ -4842,7 +4843,7 @@ void f2fs_flush_sit_entries(struct f2fs_sb_info
*sbi, struct cp_control *cpc)
struct folio *folio = NULL;
struct f2fs_sit_block *raw_sit = NULL;
unsigned int start_segno = ses->start_segno;
- unsigned int end = min(start_segno + SIT_ENTRY_PER_BLOCK,
+ unsigned int end = min(start_segno + SIT_ENTRY_PER_BLOCK(sbi),
(unsigned long)MAIN_SEGS(sbi));
unsigned int segno = start_segno;
@@ -5007,7 +5008,7 @@ static int build_sit_info(struct f2fs_sb_info *sbi)
sit_i->written_valid_blocks = 0;
sit_i->bitmap_size = sit_bitmap_size;
sit_i->dirty_sentries = 0;
- sit_i->sents_per_block = SIT_ENTRY_PER_BLOCK;
+ sit_i->sents_per_block = SIT_ENTRY_PER_BLOCK(sbi);
sit_i->elapsed_time = le64_to_cpu(sbi->ckpt->elapsed_time);
sit_i->mounted_time = ktime_get_boottime_seconds();
init_rwsem(&sit_i->sentry_lock);
diff --git a/fs/f2fs/segment.h b/fs/f2fs/segment.h
index db1079169a23..4f90f3bc2a7f 100644
--- a/fs/f2fs/segment.h
+++ b/fs/f2fs/segment.h
@@ -101,12 +101,12 @@ static inline void sanity_check_seg_type(struct
f2fs_sb_info *sbi,
#define SIT_ENTRY_OFFSET(sit_i, segno) \
((segno) % (sit_i)->sents_per_block)
-#define SIT_BLOCK_OFFSET(segno) \
- ((segno) / SIT_ENTRY_PER_BLOCK)
-#define START_SEGNO(segno) \
- (SIT_BLOCK_OFFSET(segno) * SIT_ENTRY_PER_BLOCK)
+#define SIT_BLOCK_OFFSET(sbi, segno) \
+ ((segno) / SIT_ENTRY_PER_BLOCK(sbi))
+#define START_SEGNO(sbi, segno) \
+ (SIT_BLOCK_OFFSET(sbi, segno) * SIT_ENTRY_PER_BLOCK(sbi))
#define SIT_BLK_CNT(sbi) \
- DIV_ROUND_UP(MAIN_SEGS(sbi), SIT_ENTRY_PER_BLOCK)
+ DIV_ROUND_UP(MAIN_SEGS(sbi), SIT_ENTRY_PER_BLOCK(sbi))
#define f2fs_bitmap_size(nr) \
(BITS_TO_LONGS(nr) * sizeof(unsigned long))
@@ -423,7 +423,7 @@ static inline void seg_info_to_sit_folio(struct
f2fs_sb_info *sbi,
struct f2fs_sit_block *raw_sit;
struct seg_entry *se;
struct f2fs_sit_entry *rs;
- unsigned int end = min(start + SIT_ENTRY_PER_BLOCK,
+ unsigned int end = min(start + SIT_ENTRY_PER_BLOCK(sbi),
(unsigned long)MAIN_SEGS(sbi));
int i;
@@ -868,7 +868,7 @@ static inline pgoff_t current_sit_addr(struct
f2fs_sb_info *sbi,
unsigned int start)
{
struct sit_info *sit_i = SIT_I(sbi);
- unsigned int offset = SIT_BLOCK_OFFSET(start);
+ unsigned int offset = SIT_BLOCK_OFFSET(sbi, start);
block_t blk_addr = sit_i->sit_base_addr + offset;
f2fs_bug_on(sbi, !valid_main_segno(sbi, start));
@@ -893,9 +893,10 @@ 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);
+ unsigned int block_off = SIT_BLOCK_OFFSET(sbi, start);
f2fs_change_bit(block_off, sit_i->sit_bitmap);
}
diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c
index 3bdb0f891c35..f71a2b63f8ab 100644
--- a/fs/f2fs/super.c
+++ b/fs/f2fs/super.c
@@ -4295,7 +4295,7 @@ int f2fs_sanity_check_ckpt(struct f2fs_sb_info *sbi)
return 1;
}
- sit_blk_cnt = DIV_ROUND_UP(main_segs, SIT_ENTRY_PER_BLOCK);
+ sit_blk_cnt = DIV_ROUND_UP(main_segs, SIT_ENTRY_PER_BLOCK(sbi));
if (sit_bitmap_size * 8 < sit_blk_cnt) {
f2fs_err(sbi, "Wrong bitmap size: sit: %u, sit_blk_cnt:%u",
sit_bitmap_size, sit_blk_cnt);
@@ -4348,6 +4348,8 @@ static void init_sb_info(struct f2fs_sb_info *sbi)
le32_to_cpu(raw_super->log_sectors_per_block);
sbi->log_blocksize = le32_to_cpu(raw_super->log_blocksize);
sbi->blocksize = BIT(sbi->log_blocksize);
+ sbi->sit_entries_per_block = sbi->blocksize /
+ sizeof(struct f2fs_sit_entry);
sbi->log_blocks_per_seg = le32_to_cpu(raw_super->log_blocks_per_seg);
sbi->blocks_per_seg = BIT(sbi->log_blocks_per_seg);
sbi->segs_per_sec = le32_to_cpu(raw_super->segs_per_sec);
diff --git a/include/linux/f2fs_fs.h b/include/linux/f2fs_fs.h
index bb2b6cd5d507..324427cc29dc 100644
--- a/include/linux/f2fs_fs.h
+++ b/include/linux/f2fs_fs.h
@@ -396,8 +396,6 @@ struct f2fs_nat_block {
* Not allow to change this.
*/
#define SIT_VBLOCK_MAP_SIZE 64
-#define SIT_ENTRY_PER_BLOCK (F2FS_BLKSIZE / sizeof(struct f2fs_sit_entry))
-
/*
* F2FS uses 4 bytes to represent block address. As a result, supported size of
* disk is 16 TB for a 4K page size and 64 TB for a 16K page size and it equals
@@ -424,8 +422,13 @@ struct f2fs_sit_entry {
__le64 mtime; /* segment age for cleaning */
} __packed;
+/*
+ * The on-disk SIT block is a filesystem-block-sized array of SIT entries.
+ * Its entry count depends on the filesystem block size, so it must be
+ * calculated by the caller rather than implied by this C structure.
+ */
struct f2fs_sit_block {
- struct f2fs_sit_entry entries[SIT_ENTRY_PER_BLOCK];
+ struct f2fs_sit_entry entries[];
} __packed;
/*
--
2.53.0
^ permalink raw reply [flat|nested] 9+ messages in thread* [PATCH 3/5] f2fs: describe orphan block layout dynamically
2026-08-26 21:39 [PATCH 0/5] f2fs: prepare metadata layouts for runtime block sizes Xinping Zhang
2026-08-26 21:39 ` [PATCH 1/5] f2fs: avoid underflow when counting free NIDs Xinping Zhang
2026-08-26 21:39 ` [PATCH 2/5] f2fs: describe SIT block layout dynamically Xinping Zhang
@ 2026-08-26 21:39 ` Xinping Zhang
2026-08-26 21:39 ` [PATCH 4/5] f2fs: describe dentry " Xinping Zhang
2026-08-26 21:39 ` [PATCH 5/5] f2fs: describe inode node " Xinping Zhang
4 siblings, 0 replies; 9+ messages in thread
From: Xinping Zhang @ 2026-08-26 21:39 UTC (permalink / raw)
To: linux-f2fs-devel; +Cc: jaegeuk, chao, linux-kernel
An on-disk orphan block consists of a variable-length array of inode
numbers followed by a fixed footer. The compile time definition of
orphan block struct cannot describe a runtime block length layout.
Remove the whole-block structure, document the exact layout, and add
helpers that derive the inode capacity and footer address from a supplied
block size. Access the inode array and footer separately.
The caller still supplies the existing fixed F2FS block size, so valid
filesystems retain the same on-disk format and behavior. This is a
layout-only refactoring to accommodate a runtime block length.
Signed-off-by: Kelvin Zhang <zhangxp1998@gmail.com>
---
fs/f2fs/checkpoint.c | 44 ++++++++++++++++++++++-------------------
fs/f2fs/f2fs.h | 12 +++++++++++
fs/f2fs/super.c | 2 ++
include/linux/f2fs_fs.h | 18 ++++++++++-------
4 files changed, 49 insertions(+), 27 deletions(-)
diff --git a/fs/f2fs/checkpoint.c b/fs/f2fs/checkpoint.c
index 47c4a5c83a70..51ef4f421937 100644
--- a/fs/f2fs/checkpoint.c
+++ b/fs/f2fs/checkpoint.c
@@ -1041,7 +1041,8 @@ int f2fs_recover_orphan_inodes(struct f2fs_sb_info *sbi)
for (i = 0; i < orphan_blocks; i++) {
struct folio *folio;
- struct f2fs_orphan_block *orphan_blk;
+ __le32 *orphan_inos;
+ struct f2fs_orphan_footer *footer;
unsigned int entry_count;
folio = f2fs_get_meta_folio(sbi, start_blk + i);
@@ -1050,9 +1051,10 @@ int f2fs_recover_orphan_inodes(struct f2fs_sb_info *sbi)
goto out;
}
- orphan_blk = folio_address(folio);
- entry_count = le32_to_cpu(orphan_blk->entry_count);
- if (entry_count > F2FS_ORPHANS_PER_BLOCK) {
+ orphan_inos = folio_address(folio);
+ footer = f2fs_orphan_footer(orphan_inos, sbi);
+ entry_count = le32_to_cpu(footer->entry_count);
+ if (entry_count > F2FS_ORPHANS_PER_BLOCK(sbi)) {
f2fs_err(sbi, "invalid orphan inode entry count %u",
entry_count);
set_sbi_flag(sbi, SBI_NEED_FSCK);
@@ -1063,7 +1065,7 @@ int f2fs_recover_orphan_inodes(struct f2fs_sb_info *sbi)
}
for (j = 0; j < entry_count; j++) {
- nid_t ino = le32_to_cpu(orphan_blk->ino[j]);
+ nid_t ino = le32_to_cpu(orphan_inos[j]);
err = recover_orphan_inode(sbi, ino);
if (err) {
@@ -1084,7 +1086,8 @@ int f2fs_recover_orphan_inodes(struct f2fs_sb_info *sbi)
static void write_orphan_inodes(struct f2fs_sb_info *sbi, block_t start_blk)
{
struct list_head *head;
- struct f2fs_orphan_block *orphan_blk = NULL;
+ __le32 *orphan_inos = NULL;
+ struct f2fs_orphan_footer *footer = NULL;
unsigned int nentries = 0;
unsigned short index = 1;
unsigned short orphan_blocks;
@@ -1092,7 +1095,7 @@ static void write_orphan_inodes(struct
f2fs_sb_info *sbi, block_t start_blk)
struct ino_entry *orphan = NULL;
struct inode_management *im = &sbi->im[ORPHAN_INO];
- orphan_blocks = GET_ORPHAN_BLOCKS(im->ino_num);
+ orphan_blocks = GET_ORPHAN_BLOCKS(sbi, im->ino_num);
/*
* we don't need to do spin_lock(&im->ino_lock) here, since all the
@@ -1105,21 +1108,22 @@ static void write_orphan_inodes(struct
f2fs_sb_info *sbi, block_t start_blk)
list_for_each_entry(orphan, head, list) {
if (!folio) {
folio = f2fs_grab_meta_folio(sbi, start_blk++);
- orphan_blk = folio_address(folio);
- memset(orphan_blk, 0, sizeof(*orphan_blk));
+ orphan_inos = folio_address(folio);
+ footer = f2fs_orphan_footer(orphan_inos, sbi);
+ memset(orphan_inos, 0, sbi->blocksize);
}
- orphan_blk->ino[nentries++] = cpu_to_le32(orphan->ino);
+ orphan_inos[nentries++] = cpu_to_le32(orphan->ino);
- if (nentries == F2FS_ORPHANS_PER_BLOCK) {
+ if (nentries == F2FS_ORPHANS_PER_BLOCK(sbi)) {
/*
- * an orphan block is full of 1020 entries,
+ * an orphan block is full,
* then we need to flush current orphan blocks
* and bring another one in memory
*/
- orphan_blk->blk_addr = cpu_to_le16(index);
- orphan_blk->blk_count = cpu_to_le16(orphan_blocks);
- orphan_blk->entry_count = cpu_to_le32(nentries);
+ footer->blk_addr = cpu_to_le16(index);
+ footer->blk_count = cpu_to_le16(orphan_blocks);
+ footer->entry_count = cpu_to_le32(nentries);
folio_mark_dirty(folio);
f2fs_folio_put(folio, true);
index++;
@@ -1129,9 +1133,9 @@ static void write_orphan_inodes(struct
f2fs_sb_info *sbi, block_t start_blk)
}
if (folio) {
- orphan_blk->blk_addr = cpu_to_le16(index);
- orphan_blk->blk_count = cpu_to_le16(orphan_blocks);
- orphan_blk->entry_count = cpu_to_le32(nentries);
+ footer->blk_addr = cpu_to_le16(index);
+ footer->blk_count = cpu_to_le16(orphan_blocks);
+ footer->entry_count = cpu_to_le32(nentries);
folio_mark_dirty(folio);
f2fs_folio_put(folio, true);
}
@@ -1824,7 +1828,7 @@ static int do_checkpoint(struct f2fs_sb_info
*sbi, struct cp_control *cpc)
__clear_ckpt_flags(ckpt, CP_COMPACT_SUM_FLAG);
spin_unlock_irqrestore(&sbi->cp_lock, flags);
- orphan_blocks = GET_ORPHAN_BLOCKS(orphan_num);
+ orphan_blocks = GET_ORPHAN_BLOCKS(sbi, orphan_num);
ckpt->cp_pack_start_sum = cpu_to_le32(1 + cp_payload_blks +
orphan_blocks);
@@ -2080,7 +2084,7 @@ void f2fs_init_ino_entry_info(struct f2fs_sb_info *sbi)
sbi->max_orphans = (BLKS_PER_SEG(sbi) - F2FS_CP_PACKS -
NR_CURSEG_PERSIST_TYPE - __cp_payload(sbi)) *
- F2FS_ORPHANS_PER_BLOCK;
+ F2FS_ORPHANS_PER_BLOCK(sbi);
}
int __init f2fs_create_checkpoint_caches(void)
diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h
index 4423f899b2b5..d1235ba80331 100644
--- a/fs/f2fs/f2fs.h
+++ b/fs/f2fs/f2fs.h
@@ -1863,6 +1863,7 @@ struct f2fs_sb_info {
unsigned int log_blocksize; /* log2 block size */
unsigned int blocksize; /* block size */
unsigned int sit_entries_per_block; /* SIT entries in a block */
+ unsigned int orphans_per_block; /* orphan inodes in a block */
unsigned int root_ino_num; /* root inode number*/
unsigned int node_ino_num; /* node inode number*/
unsigned int meta_ino_num; /* meta inode number*/
@@ -2251,6 +2252,17 @@ static inline struct f2fs_sb_info
*F2FS_F_SB(const struct folio *folio)
}
#define SIT_ENTRY_PER_BLOCK(sbi) ((sbi)->sit_entries_per_block)
+#define F2FS_ORPHANS_PER_BLOCK(sbi) ((sbi)->orphans_per_block)
+#define GET_ORPHAN_BLOCKS(sbi, n) DIV_ROUND_UP((n), \
+ F2FS_ORPHANS_PER_BLOCK(sbi))
+
+static inline struct f2fs_orphan_footer *
+f2fs_orphan_footer(void *orphan_block, struct f2fs_sb_info *sbi)
+{
+ return (struct f2fs_orphan_footer *)
+ ((char *)orphan_block + sbi->blocksize -
+ sizeof(struct f2fs_orphan_footer));
+}
static inline struct f2fs_super_block *F2FS_RAW_SUPER(struct f2fs_sb_info *sbi)
{
diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c
index f71a2b63f8ab..f324e9cf4264 100644
--- a/fs/f2fs/super.c
+++ b/fs/f2fs/super.c
@@ -4350,6 +4350,8 @@ static void init_sb_info(struct f2fs_sb_info *sbi)
sbi->blocksize = BIT(sbi->log_blocksize);
sbi->sit_entries_per_block = sbi->blocksize /
sizeof(struct f2fs_sit_entry);
+ sbi->orphans_per_block = (sbi->blocksize -
+ sizeof(struct f2fs_orphan_footer)) / sizeof(__le32);
sbi->log_blocks_per_seg = le32_to_cpu(raw_super->log_blocks_per_seg);
sbi->blocks_per_seg = BIT(sbi->log_blocks_per_seg);
sbi->segs_per_sec = le32_to_cpu(raw_super->segs_per_sec);
diff --git a/include/linux/f2fs_fs.h b/include/linux/f2fs_fs.h
index 324427cc29dc..a5111556d923 100644
--- a/include/linux/f2fs_fs.h
+++ b/include/linux/f2fs_fs.h
@@ -220,14 +220,18 @@ struct f2fs_checkpoint {
/*
* For orphan inode management
+ *
+ * An orphan block has no fixed-size C structure because the number of inode
+ * entries depends on the filesystem block size. Its exact on-disk layout is:
+ *
+ * 0 blocksize - 16 blocksize
+ * +--------------------------+--------------------------+
+ * | ino[0] ... ino[n - 1] | struct f2fs_orphan_footer |
+ * +--------------------------+--------------------------+
+ *
+ * n = (blocksize - sizeof(struct f2fs_orphan_footer)) / sizeof(__le32)
*/
-#define F2FS_ORPHANS_PER_BLOCK ((F2FS_BLKSIZE - 4 * sizeof(__le32)) /
sizeof(__le32))
-
-#define GET_ORPHAN_BLOCKS(n) (((n) + F2FS_ORPHANS_PER_BLOCK - 1) / \
- F2FS_ORPHANS_PER_BLOCK)
-
-struct f2fs_orphan_block {
- __le32 ino[F2FS_ORPHANS_PER_BLOCK]; /* inode numbers */
+struct f2fs_orphan_footer {
__le32 reserved; /* reserved */
__le16 blk_addr; /* block index in current CP */
__le16 blk_count; /* Number of orphan inode blocks in CP */
--
2.53.0
^ permalink raw reply [flat|nested] 9+ messages in thread* [PATCH 4/5] f2fs: describe dentry block layout dynamically
2026-08-26 21:39 [PATCH 0/5] f2fs: prepare metadata layouts for runtime block sizes Xinping Zhang
` (2 preceding siblings ...)
2026-08-26 21:39 ` [PATCH 3/5] f2fs: describe orphan " Xinping Zhang
@ 2026-08-26 21:39 ` Xinping Zhang
2026-08-26 21:39 ` [PATCH 5/5] f2fs: describe inode node " Xinping Zhang
4 siblings, 0 replies; 9+ messages in thread
From: Xinping Zhang @ 2026-08-26 21:39 UTC (permalink / raw)
To: linux-f2fs-devel; +Cc: jaegeuk, chao, linux-kernel
An on-disk dentry block contains a bitmap, reserved padding, a
variable-length dir-entry array, and matching filename slots. A fixed C
structure ties their offsets to the compile-time block size and requires
error-prone reserved-size definitions.
Remove the whole-block structure and its compile-time layout macros.
Compute the offsets from the block size and expose the regions through the
existing zero-copy dentry pointer. Pass the owning directory inode at every
block-view initialization to keep the helper contract consistent.
No functional change is intended; this commit continues to use the fixed
F2FS block size.
Signed-off-by: Kelvin Zhang <zhangxp1998@gmail.com>
---
fs/f2fs/dir.c | 51 +++++++++++++++++++++--------------------
fs/f2fs/f2fs.h | 31 ++++++++++++++++---------
fs/f2fs/inline.c | 2 +-
fs/f2fs/super.c | 6 +++++
include/linux/f2fs_fs.h | 31 ++++++++++++-------------
5 files changed, 68 insertions(+), 53 deletions(-)
diff --git a/fs/f2fs/dir.c b/fs/f2fs/dir.c
index fd0e2cd31a81..75ff36a13f60 100644
--- a/fs/f2fs/dir.c
+++ b/fs/f2fs/dir.c
@@ -195,7 +195,7 @@ static struct f2fs_dir_entry *find_in_block(struct
inode *dir,
int *max_slots,
bool use_hash)
{
- struct f2fs_dentry_block *dentry_blk;
+ void *dentry_blk;
struct f2fs_dentry_ptr d;
dentry_blk = folio_address(dentry_folio);
@@ -518,7 +518,7 @@ static int make_empty_dir(struct inode *inode,
struct inode *parent, struct folio *folio)
{
struct folio *dentry_folio;
- struct f2fs_dentry_block *dentry_blk;
+ void *dentry_blk;
struct f2fs_dentry_ptr d;
if (f2fs_has_inline_dentry(inode))
@@ -530,7 +530,7 @@ static int make_empty_dir(struct inode *inode,
dentry_blk = folio_address(dentry_folio);
- make_dentry_ptr_block(NULL, &d, dentry_blk);
+ make_dentry_ptr_block(inode, &d, dentry_blk);
f2fs_do_make_empty_dir(inode, parent, &d);
folio_mark_dirty(dentry_folio);
@@ -690,7 +690,7 @@ int f2fs_add_regular_entry(struct inode *dir,
const struct f2fs_filename *fname,
unsigned long bidx, block;
unsigned int nbucket, nblock;
struct folio *dentry_folio = NULL;
- struct f2fs_dentry_block *dentry_blk = NULL;
+ void *dentry_blk = NULL;
struct f2fs_dentry_ptr d;
struct folio *folio = NULL;
int slots, err = 0;
@@ -727,9 +727,9 @@ int f2fs_add_regular_entry(struct inode *dir,
const struct f2fs_filename *fname,
return PTR_ERR(dentry_folio);
dentry_blk = folio_address(dentry_folio);
- bit_pos = f2fs_room_for_filename(&dentry_blk->dentry_bitmap,
- slots, NR_DENTRY_IN_BLOCK);
- if (bit_pos < NR_DENTRY_IN_BLOCK)
+ make_dentry_ptr_block(dir, &d, dentry_blk);
+ bit_pos = f2fs_room_for_filename(d.bitmap, slots, d.max);
+ if (bit_pos < d.max)
goto add_dentry;
f2fs_folio_put(dentry_folio, true);
@@ -750,7 +750,6 @@ int f2fs_add_regular_entry(struct inode *dir,
const struct f2fs_filename *fname,
}
}
- make_dentry_ptr_block(NULL, &d, dentry_blk);
f2fs_update_dentry(ino, mode, &d, &fname->disk_name, fname->hash,
bit_pos);
@@ -887,7 +886,8 @@ void f2fs_drop_nlink(struct inode *dir, struct inode *inode)
void f2fs_delete_entry(struct f2fs_dir_entry *dentry, struct folio *folio,
struct inode *dir, struct inode *inode)
{
- struct f2fs_dentry_block *dentry_blk;
+ void *dentry_blk;
+ struct f2fs_dentry_ptr d;
unsigned int bit_pos;
int slots = GET_DENTRY_SLOTS(le16_to_cpu(dentry->name_len));
pgoff_t index = folio->index;
@@ -905,18 +905,17 @@ void f2fs_delete_entry(struct f2fs_dir_entry
*dentry, struct folio *folio,
f2fs_folio_wait_writeback(folio, DATA, true, true);
dentry_blk = folio_address(folio);
- bit_pos = dentry - dentry_blk->dentry;
+ make_dentry_ptr_block(dir, &d, dentry_blk);
+ bit_pos = dentry - d.dentry;
for (i = 0; i < slots; i++)
- __clear_bit_le(bit_pos + i, &dentry_blk->dentry_bitmap);
+ __clear_bit_le(bit_pos + i, d.bitmap);
/* Let's check and deallocate this dentry page */
- bit_pos = find_next_bit_le(&dentry_blk->dentry_bitmap,
- NR_DENTRY_IN_BLOCK,
- 0);
+ bit_pos = find_next_bit_le(d.bitmap, d.max, 0);
folio_mark_dirty(folio);
- if (bit_pos == NR_DENTRY_IN_BLOCK &&
- !f2fs_truncate_hole(dir, index, index + 1)) {
+ if (bit_pos == d.max &&
+ !f2fs_truncate_hole(dir, index, index + 1)) {
f2fs_clear_page_cache_dirty_tag(folio);
folio_clear_dirty_for_io(folio);
folio_clear_uptodate(folio);
@@ -938,7 +937,8 @@ bool f2fs_empty_dir(struct inode *dir)
{
unsigned long bidx = 0;
unsigned int bit_pos;
- struct f2fs_dentry_block *dentry_blk;
+ void *dentry_blk;
+ struct f2fs_dentry_ptr d;
unsigned long nblock = dir_blocks(dir);
if (f2fs_has_inline_dentry(dir))
@@ -959,17 +959,16 @@ bool f2fs_empty_dir(struct inode *dir)
}
dentry_blk = folio_address(dentry_folio);
+ make_dentry_ptr_block(dir, &d, dentry_blk);
if (bidx == 0)
bit_pos = 2;
else
bit_pos = 0;
- bit_pos = find_next_bit_le(&dentry_blk->dentry_bitmap,
- NR_DENTRY_IN_BLOCK,
- bit_pos);
+ bit_pos = find_next_bit_le(d.bitmap, d.max, bit_pos);
f2fs_folio_put(dentry_folio, false);
- if (bit_pos < NR_DENTRY_IN_BLOCK)
+ if (bit_pos < d.max)
return false;
bidx++;
@@ -1066,10 +1065,12 @@ static int f2fs_readdir(struct file *file,
struct dir_context *ctx)
{
struct inode *inode = file_inode(file);
unsigned long npages = dir_blocks(inode);
- struct f2fs_dentry_block *dentry_blk = NULL;
+ void *dentry_blk = NULL;
struct file_ra_state *ra = &file->f_ra;
loff_t start_pos = ctx->pos;
- unsigned int n = ((unsigned long)ctx->pos / NR_DENTRY_IN_BLOCK);
+ struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
+ unsigned int entries = sbi->dentries_per_block;
+ unsigned int n = (unsigned long)ctx->pos / entries;
struct f2fs_dentry_ptr d;
struct fscrypt_str fstr = FSTR_INIT(NULL, 0);
int err = 0;
@@ -1089,7 +1090,7 @@ static int f2fs_readdir(struct file *file,
struct dir_context *ctx)
goto out_free;
}
- for (; n < npages; ctx->pos = n * NR_DENTRY_IN_BLOCK) {
+ for (; n < npages; ctx->pos = n * entries) {
struct folio *dentry_folio;
pgoff_t next_pgofs;
@@ -1122,7 +1123,7 @@ static int f2fs_readdir(struct file *file,
struct dir_context *ctx)
make_dentry_ptr_block(inode, &d, dentry_blk);
err = f2fs_fill_dentries(ctx, &d,
- n * NR_DENTRY_IN_BLOCK, &fstr);
+ n * entries, &fstr);
f2fs_folio_put(dentry_folio, false);
if (err)
break;
diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h
index d1235ba80331..026419aed2e9 100644
--- a/fs/f2fs/f2fs.h
+++ b/fs/f2fs/f2fs.h
@@ -668,17 +668,6 @@ struct f2fs_dentry_ptr {
int nr_bitmap;
};
-static inline void make_dentry_ptr_block(struct inode *inode,
- struct f2fs_dentry_ptr *d, struct f2fs_dentry_block *t)
-{
- d->inode = inode;
- d->max = NR_DENTRY_IN_BLOCK;
- d->nr_bitmap = SIZE_OF_DENTRY_BITMAP;
- d->bitmap = t->dentry_bitmap;
- d->dentry = t->dentry;
- d->filename = t->filename;
-}
-
static inline void make_dentry_ptr_inline(struct inode *inode,
struct f2fs_dentry_ptr *d, void *t)
{
@@ -1864,6 +1853,9 @@ struct f2fs_sb_info {
unsigned int blocksize; /* block size */
unsigned int sit_entries_per_block; /* SIT entries in a block */
unsigned int orphans_per_block; /* orphan inodes in a block */
+ unsigned int dentries_per_block; /* dentries in a block */
+ unsigned int dentry_bitmap_size; /* dentry bitmap size in bytes */
+ unsigned int dentry_reserved_size; /* dentry reserved bytes */
unsigned int root_ino_num; /* root inode number*/
unsigned int node_ino_num; /* node inode number*/
unsigned int meta_ino_num; /* meta inode number*/
@@ -2264,6 +2256,23 @@ f2fs_orphan_footer(void *orphan_block, struct
f2fs_sb_info *sbi)
sizeof(struct f2fs_orphan_footer));
}
+static inline void make_dentry_ptr_block(struct inode *inode,
+ struct f2fs_dentry_ptr *d, void *t)
+{
+ struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
+ unsigned int entries = sbi->dentries_per_block;
+ unsigned int bitmap_size = sbi->dentry_bitmap_size;
+ unsigned int reserved_size = sbi->dentry_reserved_size;
+
+ d->inode = inode;
+ d->max = entries;
+ d->nr_bitmap = bitmap_size;
+ d->bitmap = t;
+ d->dentry = t + bitmap_size + reserved_size;
+ d->filename = t + bitmap_size + reserved_size +
+ SIZE_OF_DIR_ENTRY * entries;
+}
+
static inline struct f2fs_super_block *F2FS_RAW_SUPER(struct f2fs_sb_info *sbi)
{
return (struct f2fs_super_block *)(sbi->raw_super);
diff --git a/fs/f2fs/inline.c b/fs/f2fs/inline.c
index aec06fb4fd76..718dd785865a 100644
--- a/fs/f2fs/inline.c
+++ b/fs/f2fs/inline.c
@@ -415,7 +415,7 @@ static int f2fs_move_inline_dirents(struct inode
*dir, struct folio *ifolio,
{
struct folio *folio;
struct dnode_of_data dn;
- struct f2fs_dentry_block *dentry_blk;
+ void *dentry_blk;
struct f2fs_dentry_ptr src, dst;
int err;
diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c
index f324e9cf4264..133c279b9931 100644
--- a/fs/f2fs/super.c
+++ b/fs/f2fs/super.c
@@ -4352,6 +4352,12 @@ static void init_sb_info(struct f2fs_sb_info *sbi)
sizeof(struct f2fs_sit_entry);
sbi->orphans_per_block = (sbi->blocksize -
sizeof(struct f2fs_orphan_footer)) / sizeof(__le32);
+ sbi->dentries_per_block = (BITS_PER_BYTE * sbi->blocksize) /
+ ((SIZE_OF_DIR_ENTRY + F2FS_SLOT_LEN) * BITS_PER_BYTE + 1);
+ sbi->dentry_bitmap_size = DIV_ROUND_UP(sbi->dentries_per_block,
+ BITS_PER_BYTE);
+ sbi->dentry_reserved_size = sbi->blocksize - sbi->dentry_bitmap_size -
+ (SIZE_OF_DIR_ENTRY + F2FS_SLOT_LEN) * sbi->dentries_per_block;
sbi->log_blocks_per_seg = le32_to_cpu(raw_super->log_blocks_per_seg);
sbi->blocks_per_seg = BIT(sbi->log_blocks_per_seg);
sbi->segs_per_sec = le32_to_cpu(raw_super->segs_per_sec);
diff --git a/include/linux/f2fs_fs.h b/include/linux/f2fs_fs.h
index a5111556d923..b4bb4e2f99b3 100644
--- a/include/linux/f2fs_fs.h
+++ b/include/linux/f2fs_fs.h
@@ -602,15 +602,7 @@ typedef __le32 f2fs_hash_t;
* dentry, when converting inline dentry we should handle this carefully.
*/
-/* the number of dentry in a block */
-#define NR_DENTRY_IN_BLOCK ((BITS_PER_BYTE * F2FS_BLKSIZE) / \
- ((SIZE_OF_DIR_ENTRY + F2FS_SLOT_LEN) * BITS_PER_BYTE + 1))
#define SIZE_OF_DIR_ENTRY 11 /* by byte */
-#define SIZE_OF_DENTRY_BITMAP ((NR_DENTRY_IN_BLOCK + BITS_PER_BYTE - 1) / \
- BITS_PER_BYTE)
-#define SIZE_OF_RESERVED (F2FS_BLKSIZE - ((SIZE_OF_DIR_ENTRY + \
- F2FS_SLOT_LEN) * \
- NR_DENTRY_IN_BLOCK + SIZE_OF_DENTRY_BITMAP))
#define MIN_INLINE_DENTRY_SIZE 40 /* just include '.' and '..' entries */
/* One directory entry slot representing F2FS_SLOT_LEN-sized file name */
@@ -621,14 +613,21 @@ struct f2fs_dir_entry {
__u8 file_type; /* file type */
} __packed;
-/* Block-sized directory entry block */
-struct f2fs_dentry_block {
- /* validity bitmap for directory entries in each block */
- __u8 dentry_bitmap[SIZE_OF_DENTRY_BITMAP];
- __u8 reserved[SIZE_OF_RESERVED];
- struct f2fs_dir_entry dentry[NR_DENTRY_IN_BLOCK];
- __u8 filename[NR_DENTRY_IN_BLOCK][F2FS_SLOT_LEN];
-} __packed;
+/*
+ * A dentry block is laid out as follows, where the number of entries and all
+ * offsets are determined by the filesystem block size at runtime:
+ *
+ * 0 blocksize
+ * +--------+----------+-------------------+-----------------------+
+ * | bitmap | reserved | dir_entry[entries]| filename[entries][8] |
+ * +--------+----------+-------------------+-----------------------+
+ *
+ * entries = (BITS_PER_BYTE * blocksize) /
+ * ((SIZE_OF_DIR_ENTRY + F2FS_SLOT_LEN) * BITS_PER_BYTE + 1)
+ * bitmap_size = DIV_ROUND_UP(entries, BITS_PER_BYTE)
+ * reserved_size = blocksize - bitmap_size -
+ * (SIZE_OF_DIR_ENTRY + F2FS_SLOT_LEN) * entries
+ */
#define F2FS_DEF_PROJID 0 /* default project ID */
--
2.53.0
^ permalink raw reply [flat|nested] 9+ messages in thread* [PATCH 5/5] f2fs: describe inode node layout dynamically
2026-08-26 21:39 [PATCH 0/5] f2fs: prepare metadata layouts for runtime block sizes Xinping Zhang
` (3 preceding siblings ...)
2026-08-26 21:39 ` [PATCH 4/5] f2fs: describe dentry " Xinping Zhang
@ 2026-08-26 21:39 ` Xinping Zhang
4 siblings, 0 replies; 9+ messages in thread
From: Xinping Zhang @ 2026-08-26 21:39 UTC (permalink / raw)
To: linux-f2fs-devel; +Cc: jaegeuk, chao, linux-kernel
An inode node block ends with its five i_nid entries followed by the node
footer. Because the i_addr array spans all preceding space, the offset of
i_nid depends on the filesystem block size.
Describe f2fs_inode and the direct and indirect node types with
maximum-size arrays, then calculate the locations of i_nid and the node
footer from the actual filesystem block size. Replace all direct accesses
to those tail fields with F2FS_INODE_NIDS() and F2FS_NODE_FOOTER().
Keep the former i_nid and footer declarations as comments documenting the
exact on-disk layout. This is a layout-only cleanup while the filesystem
block size is still fixed; the following change makes the helpers use the
runtime geometry.
Signed-off-by: Kelvin Zhang <zhangxp1998@gmail.com>
---
fs/f2fs/data.c | 2 +-
fs/f2fs/f2fs.h | 30 ++++++++++++++++-------
fs/f2fs/gc.c | 2 +-
fs/f2fs/inline.c | 3 +--
fs/f2fs/inode.c | 5 ++--
fs/f2fs/node.c | 4 +---
fs/f2fs/node.h | 53 +++++++++++++++++++----------------------
fs/f2fs/super.c | 2 ++
include/linux/f2fs_fs.h | 26 +++++++++++++++-----
9 files changed, 74 insertions(+), 53 deletions(-)
diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c
index 6ae0eb37d20f..751b7a457d9a 100644
--- a/fs/f2fs/data.c
+++ b/fs/f2fs/data.c
@@ -2027,7 +2027,7 @@ static int f2fs_xattr_fiemap(struct inode *inode,
phys = F2FS_BLK_TO_BYTES(ni.blk_addr);
offset = offsetof(struct f2fs_inode, i_addr) +
- sizeof(__le32) * (DEF_ADDRS_PER_INODE -
+ sizeof(__le32) * (DEF_ADDRS_PER_INODE_SBI(sbi) -
get_inline_xattr_addrs(inode));
phys += offset;
diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h
index 026419aed2e9..1413aa7acc09 100644
--- a/fs/f2fs/f2fs.h
+++ b/fs/f2fs/f2fs.h
@@ -1851,6 +1851,7 @@ struct f2fs_sb_info {
unsigned int log_sectors_per_block; /* log2 sectors per block */
unsigned int log_blocksize; /* log2 block size */
unsigned int blocksize; /* block size */
+ unsigned int addrs_per_inode; /* addresses in an inode block */
unsigned int sit_entries_per_block; /* SIT entries in a block */
unsigned int orphans_per_block; /* orphan inodes in a block */
unsigned int dentries_per_block; /* dentries in a block */
@@ -2244,6 +2245,7 @@ static inline struct f2fs_sb_info
*F2FS_F_SB(const struct folio *folio)
}
#define SIT_ENTRY_PER_BLOCK(sbi) ((sbi)->sit_entries_per_block)
+#define DEF_ADDRS_PER_INODE_SBI(sbi) ((sbi)->addrs_per_inode)
#define F2FS_ORPHANS_PER_BLOCK(sbi) ((sbi)->orphans_per_block)
#define GET_ORPHAN_BLOCKS(sbi, n) DIV_ROUND_UP((n), \
F2FS_ORPHANS_PER_BLOCK(sbi))
@@ -2293,6 +2295,12 @@ static inline struct f2fs_checkpoint
*F2FS_CKPT(struct f2fs_sb_info *sbi)
return (struct f2fs_checkpoint *)(sbi->ckpt);
}
+static inline struct node_footer *F2FS_NODE_FOOTER(const struct folio *folio)
+{
+ return folio_address(folio) + F2FS_BLKSIZE -
+ sizeof(struct node_footer);
+}
+
static inline struct f2fs_node *F2FS_NODE(const struct folio *folio)
{
return (struct f2fs_node *)folio_address(folio);
@@ -2303,6 +2311,12 @@ static inline struct f2fs_inode
*F2FS_INODE(const struct folio *folio)
return &((struct f2fs_node *)folio_address(folio))->i;
}
+static inline __le32 *F2FS_INODE_NIDS(const struct folio *folio)
+{
+ return folio_address(folio) + F2FS_BLKSIZE - sizeof(struct node_footer) -
+ SIZE_OF_I_NID;
+}
+
static inline struct f2fs_nm_info *NM_I(struct f2fs_sb_info *sbi)
{
return (struct f2fs_nm_info *)(sbi->nm_info);
@@ -3273,13 +3287,11 @@ static inline void
f2fs_radix_tree_insert(struct radix_tree_root *root,
cond_resched();
}
-#define RAW_IS_INODE(p) ((p)->footer.nid == (p)->footer.ino)
-
static inline bool IS_INODE(const struct folio *folio)
{
- struct f2fs_node *p = F2FS_NODE(folio);
+ struct node_footer *footer = F2FS_NODE_FOOTER(folio);
- return RAW_IS_INODE(p);
+ return footer->nid == footer->ino;
}
static inline int offset_in_addr(struct f2fs_inode *i)
@@ -3288,9 +3300,11 @@ static inline int offset_in_addr(struct f2fs_inode *i)
(le16_to_cpu(i->i_extra_isize) / sizeof(__le32)) : 0;
}
-static inline __le32 *blkaddr_in_node(struct f2fs_node *node)
+static inline __le32 *blkaddr_in_node(const struct folio *folio)
{
- return RAW_IS_INODE(node) ? node->i.i_addr : node->dn.addr;
+ struct f2fs_node *node = F2FS_NODE(folio);
+
+ return IS_INODE(folio) ? node->i.i_addr : node->dn.addr;
}
static inline int f2fs_has_extra_attr(struct inode *inode);
@@ -3307,7 +3321,7 @@ static inline unsigned int get_dnode_base(struct
inode *inode,
static inline __le32 *get_dnode_addr(struct inode *inode,
struct folio *node_folio)
{
- return blkaddr_in_node(F2FS_NODE(node_folio)) +
+ return blkaddr_in_node(node_folio) +
get_dnode_base(inode, node_folio);
}
@@ -3635,7 +3649,7 @@ void *inline_xattr_addr(struct inode *inode,
const struct folio *folio)
{
struct f2fs_inode *ri = F2FS_INODE(folio);
- return (void *)&(ri->i_addr[DEF_ADDRS_PER_INODE -
+ return (void *)&(ri->i_addr[DEF_ADDRS_PER_INODE_SBI(F2FS_I_SB(inode)) -
get_inline_xattr_addrs(inode)]);
}
diff --git a/fs/f2fs/gc.c b/fs/f2fs/gc.c
index 0c17038fcfd7..666acf9d528a 100644
--- a/fs/f2fs/gc.c
+++ b/fs/f2fs/gc.c
@@ -1176,7 +1176,7 @@ static bool is_alive(struct f2fs_sb_info *sbi,
struct f2fs_summary *sum,
if (IS_INODE(node_folio)) {
base = offset_in_addr(F2FS_INODE(node_folio));
- max_addrs = DEF_ADDRS_PER_INODE;
+ max_addrs = DEF_ADDRS_PER_INODE_SBI(sbi);
} else {
base = 0;
max_addrs = DEF_ADDRS_PER_BLOCK;
diff --git a/fs/f2fs/inline.c b/fs/f2fs/inline.c
index 718dd785865a..73cd9b6ddcc5 100644
--- a/fs/f2fs/inline.c
+++ b/fs/f2fs/inline.c
@@ -36,14 +36,13 @@ bool f2fs_may_inline_data(struct inode *inode)
static bool inode_has_blocks(struct inode *inode, struct folio *ifolio)
{
- struct f2fs_inode *ri = F2FS_INODE(ifolio);
int i;
if (F2FS_HAS_BLOCKS(inode))
return true;
for (i = 0; i < DEF_NIDS_PER_INODE; i++) {
- if (ri->i_nid[i])
+ if (F2FS_INODE_NIDS(ifolio)[i])
return true;
}
return false;
diff --git a/fs/f2fs/inode.c b/fs/f2fs/inode.c
index bac1e360d966..046cc21547eb 100644
--- a/fs/f2fs/inode.c
+++ b/fs/f2fs/inode.c
@@ -150,9 +150,8 @@ bool f2fs_enable_inode_chksum(struct f2fs_sb_info
*sbi, struct folio *folio)
static __u32 f2fs_inode_chksum(struct f2fs_sb_info *sbi, struct folio *folio)
{
- struct f2fs_node *node = F2FS_NODE(folio);
- struct f2fs_inode *ri = &node->i;
- __le32 ino = node->footer.ino;
+ struct f2fs_inode *ri = F2FS_INODE(folio);
+ __le32 ino = F2FS_NODE_FOOTER(folio)->ino;
__le32 gen = ri->i_generation;
__u32 chksum, chksum_seed;
__u32 dummy_cs = 0;
diff --git a/fs/f2fs/node.c b/fs/f2fs/node.c
index 968e5ed38816..3c8bb24e68b6 100644
--- a/fs/f2fs/node.c
+++ b/fs/f2fs/node.c
@@ -2993,7 +2993,6 @@ int f2fs_recover_inode_page(struct f2fs_sb_info
*sbi, struct folio *folio)
int f2fs_restore_node_summary(struct f2fs_sb_info *sbi,
unsigned int segno, struct f2fs_summary_block *sum)
{
- struct f2fs_node *rn;
struct f2fs_summary *sum_entry;
block_t addr;
int i, idx, last_offset, nrpages;
@@ -3015,8 +3014,7 @@ int f2fs_restore_node_summary(struct f2fs_sb_info *sbi,
if (IS_ERR(folio))
return PTR_ERR(folio);
- rn = F2FS_NODE(folio);
- sum_entry->nid = rn->footer.nid;
+ sum_entry->nid = F2FS_NODE_FOOTER(folio)->nid;
sum_entry->version = 0;
sum_entry->ofs_in_node = 0;
sum_entry++;
diff --git a/fs/f2fs/node.h b/fs/f2fs/node.h
index 5e114f352099..6e0097486e9b 100644
--- a/fs/f2fs/node.h
+++ b/fs/f2fs/node.h
@@ -242,73 +242,68 @@ static inline void set_to_next_nat(struct
f2fs_nm_info *nm_i, nid_t start_nid)
static inline nid_t ino_of_node(const struct folio *node_folio)
{
- struct f2fs_node *rn = F2FS_NODE(node_folio);
- return le32_to_cpu(rn->footer.ino);
+ return le32_to_cpu(F2FS_NODE_FOOTER(node_folio)->ino);
}
static inline nid_t nid_of_node(const struct folio *node_folio)
{
- struct f2fs_node *rn = F2FS_NODE(node_folio);
- return le32_to_cpu(rn->footer.nid);
+ return le32_to_cpu(F2FS_NODE_FOOTER(node_folio)->nid);
}
static inline unsigned int ofs_of_node(const struct folio *node_folio)
{
- struct f2fs_node *rn = F2FS_NODE(node_folio);
- unsigned flag = le32_to_cpu(rn->footer.flag);
+ unsigned int flag = le32_to_cpu(F2FS_NODE_FOOTER(node_folio)->flag);
return flag >> OFFSET_BIT_SHIFT;
}
static inline __u64 cpver_of_node(const struct folio *node_folio)
{
- struct f2fs_node *rn = F2FS_NODE(node_folio);
- return le64_to_cpu(rn->footer.cp_ver);
+ return le64_to_cpu(F2FS_NODE_FOOTER(node_folio)->cp_ver);
}
static inline block_t next_blkaddr_of_node(const struct folio *node_folio)
{
- struct f2fs_node *rn = F2FS_NODE(node_folio);
- return le32_to_cpu(rn->footer.next_blkaddr);
+ return le32_to_cpu(F2FS_NODE_FOOTER(node_folio)->next_blkaddr);
}
static inline void fill_node_footer(const struct folio *folio, nid_t nid,
nid_t ino, unsigned int ofs, bool reset)
{
struct f2fs_node *rn = F2FS_NODE(folio);
+ struct node_footer *footer = F2FS_NODE_FOOTER(folio);
unsigned int old_flag = 0;
if (reset)
- memset(rn, 0, sizeof(*rn));
+ memset(rn, 0, F2FS_BLKSIZE);
else
- old_flag = le32_to_cpu(rn->footer.flag);
+ old_flag = le32_to_cpu(footer->flag);
- rn->footer.nid = cpu_to_le32(nid);
- rn->footer.ino = cpu_to_le32(ino);
+ footer->nid = cpu_to_le32(nid);
+ footer->ino = cpu_to_le32(ino);
/* should remain old flag bits such as COLD_BIT_SHIFT */
- rn->footer.flag = cpu_to_le32((ofs << OFFSET_BIT_SHIFT) |
+ footer->flag = cpu_to_le32((ofs << OFFSET_BIT_SHIFT) |
(old_flag & OFFSET_BIT_MASK));
}
static inline void copy_node_footer(const struct folio *dst,
const struct folio *src)
{
- struct f2fs_node *src_rn = F2FS_NODE(src);
- struct f2fs_node *dst_rn = F2FS_NODE(dst);
- memcpy(&dst_rn->footer, &src_rn->footer, sizeof(struct node_footer));
+ memcpy(F2FS_NODE_FOOTER(dst), F2FS_NODE_FOOTER(src),
+ sizeof(struct node_footer));
}
static inline void fill_node_footer_blkaddr(struct folio *folio,
block_t blkaddr)
{
struct f2fs_checkpoint *ckpt = F2FS_CKPT(F2FS_F_SB(folio));
- struct f2fs_node *rn = F2FS_NODE(folio);
+ struct node_footer *footer = F2FS_NODE_FOOTER(folio);
__u64 cp_ver = cur_cp_version(ckpt);
if (__is_set_ckpt_flags(ckpt, CP_CRC_RECOVERY_FLAG))
cp_ver |= (cur_cp_crc(ckpt) << 32);
- rn->footer.cp_ver = cpu_to_le64(cp_ver);
- rn->footer.next_blkaddr = cpu_to_le32(blkaddr);
+ footer->cp_ver = cpu_to_le64(cp_ver);
+ footer->next_blkaddr = cpu_to_le32(blkaddr);
}
static inline bool is_recoverable_dnode(const struct folio *folio)
@@ -368,11 +363,12 @@ static inline bool IS_DNODE(const struct folio
*node_folio)
static inline int set_nid(struct folio *folio, int off, nid_t nid, bool i)
{
struct f2fs_node *rn = F2FS_NODE(folio);
+ __le32 *inode_nids = F2FS_INODE_NIDS(folio);
f2fs_folio_wait_writeback(folio, NODE, true, true);
if (i)
- rn->i.i_nid[off - NODE_DIR1_BLOCK] = cpu_to_le32(nid);
+ inode_nids[off - NODE_DIR1_BLOCK] = cpu_to_le32(nid);
else
rn->in.nid[off] = cpu_to_le32(nid);
return folio_mark_dirty(folio);
@@ -381,9 +377,10 @@ static inline int set_nid(struct folio *folio,
int off, nid_t nid, bool i)
static inline nid_t get_nid(const struct folio *folio, int off, bool i)
{
struct f2fs_node *rn = F2FS_NODE(folio);
+ const __le32 *inode_nids = F2FS_INODE_NIDS(folio);
if (i)
- return le32_to_cpu(rn->i.i_nid[off - NODE_DIR1_BLOCK]);
+ return le32_to_cpu(inode_nids[off - NODE_DIR1_BLOCK]);
return le32_to_cpu(rn->in.nid[off]);
}
@@ -396,8 +393,7 @@ static inline nid_t get_nid(const struct folio
*folio, int off, bool i)
static inline int is_node(const struct folio *folio, int type)
{
- struct f2fs_node *rn = F2FS_NODE(folio);
- return le32_to_cpu(rn->footer.flag) & BIT(type);
+ return le32_to_cpu(F2FS_NODE_FOOTER(folio)->flag) & BIT(type);
}
#define is_cold_node(folio) is_node(folio, COLD_BIT_SHIFT)
@@ -406,14 +402,13 @@ static inline int is_node(const struct folio
*folio, int type)
static inline void __set_mark(const struct folio *folio, bool mark, int type)
{
- struct f2fs_node *rn = F2FS_NODE(folio);
- unsigned int flag = le32_to_cpu(rn->footer.flag);
-
+ struct node_footer *footer = F2FS_NODE_FOOTER(folio);
+ unsigned int flag = le32_to_cpu(footer->flag);
if (mark)
flag |= BIT(type);
else
flag &= ~BIT(type);
- rn->footer.flag = cpu_to_le32(flag);
+ footer->flag = cpu_to_le32(flag);
}
static inline void set_cold_node(const struct folio *folio, bool is_dir)
diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c
index 133c279b9931..1f6dcb890eab 100644
--- a/fs/f2fs/super.c
+++ b/fs/f2fs/super.c
@@ -4348,6 +4348,8 @@ static void init_sb_info(struct f2fs_sb_info *sbi)
le32_to_cpu(raw_super->log_sectors_per_block);
sbi->log_blocksize = le32_to_cpu(raw_super->log_blocksize);
sbi->blocksize = BIT(sbi->log_blocksize);
+ sbi->addrs_per_inode = (sbi->blocksize - OFFSET_OF_END_OF_I_EXT -
+ SIZE_OF_I_NID - sizeof(struct node_footer)) / sizeof(__le32);
sbi->sit_entries_per_block = sbi->blocksize /
sizeof(struct f2fs_sit_entry);
sbi->orphans_per_block = (sbi->blocksize -
diff --git a/include/linux/f2fs_fs.h b/include/linux/f2fs_fs.h
index b4bb4e2f99b3..34c19bcd50e3 100644
--- a/include/linux/f2fs_fs.h
+++ b/include/linux/f2fs_fs.h
@@ -16,6 +16,7 @@
#define F2FS_MAX_LOG_SECTOR_SIZE PAGE_SHIFT /* Max is Block Size */
#define F2FS_LOG_SECTORS_PER_BLOCK (PAGE_SHIFT - 9) /* log number for
sector/blk */
#define F2FS_BLKSIZE PAGE_SIZE /* support only block == page */
+#define F2FS_MAX_BLKSIZE PAGE_SIZE
#define F2FS_BLKSIZE_BITS PAGE_SHIFT /* bits for F2FS_BLKSIZE */
#define F2FS_MAX_EXTENSION 64 /* # of extension entries */
#define F2FS_EXTENSION_LEN 8 /* max size of extension */
@@ -343,18 +344,25 @@ struct f2fs_inode {
*/
__le32 i_extra_end[0]; /* for attribute size calculation */
} __packed;
- __le32 i_addr[DEF_ADDRS_PER_INODE]; /* Pointers to data blocks */
+ __le32 i_addr[]; /* data block pointers */
};
- __le32 i_nid[DEF_NIDS_PER_INODE]; /* direct(2), indirect(2),
- double_indirect(1) node id */
+ /*
+ * __le32 i_nid[DEF_NIDS_PER_INODE];
+ *
+ * It is stored immediately before the node footer at the end of the
+ * filesystem block. Its offset depends on the filesystem block size, so
+ * locate it dynamically with F2FS_INODE_NIDS().
+ */
} __packed;
struct direct_node {
- __le32 addr[DEF_ADDRS_PER_BLOCK]; /* array of data block address */
+ __le32 addr[(F2FS_MAX_BLKSIZE - sizeof(struct node_footer)) /
+ sizeof(__le32)]; /* array of data block address */
} __packed;
struct indirect_node {
- __le32 nid[NIDS_PER_BLOCK]; /* array of data block address */
+ __le32 nid[(F2FS_MAX_BLKSIZE - sizeof(struct node_footer)) /
+ sizeof(__le32)]; /* array of data block address */
} __packed;
enum {
@@ -373,7 +381,13 @@ struct f2fs_node {
struct direct_node dn;
struct indirect_node in;
};
- struct node_footer footer;
+ /*
+ * struct node_footer footer;
+ *
+ * It is stored at the end of the filesystem block, after the inode or
+ * direct/indirect node data. Its offset depends on the filesystem block
+ * size, so locate it dynamically with F2FS_NODE_FOOTER().
+ */
} __packed;
/*
--
2.53.0
^ permalink raw reply [flat|nested] 9+ messages in thread