[PATCH 3/5] f2fs: describe orphan block layout dynamically
From: Xinping Zhang
Date: Wed Aug 26 2026 - 17:43:53 EST
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@xxxxxxxxx>
---
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