From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from mail-pl1-f178.google.com (mail-pl1-f178.google.com [209.85.214.178]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 750083B14C0 for ; Mon, 31 Aug 2026 22:08:18 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.214.178 ARC-Seal:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788214100; cv=none; b=Zs/BNolrhoFRvtwM82BKpLDxdWGO1tAqFjAwZFkhbj34ufOHLRiKZhKeqZMHcD6A8NwKSEeobTEnTpqfDH6QeC4TK/tPikLgSdAfytCiCu/kjw6/5Df874kgT/slxIZ3affaJg0nAsObGCzDKbAdJEFxqSTHKAyQMfJui4e97Dk= ARC-Message-Signature:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788214100; c=relaxed/simple; bh=mgoMyXlC/lUnw14AUcWcJs+axOEXVsmV0cqIdXOP69Q=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=kMJJpca8SNgc+nh/37THzn1iV+yiMEs/Mqu5CE4h3Z9EjLql+cYP6Rh4FDGnbzEWjocciJChtDbrGIni2ycuyzotBiqSL2Qdp71BHVJu4kA6H1/oGAXun0JWpWlqUehFZHhpa/MZHbue2R9s1VzHTW8EcLXdBut4BiUdDtUsP/w= ARC-Authentication-Results:i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=Qlrv131w; arc=none smtp.client-ip=209.85.214.178 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="Qlrv131w" Received: by mail-pl1-f178.google.com with SMTP id d9443c01a7336-2cf452def93so3519445ad.1 for ; Mon, 31 Aug 2026 15:08:18 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1788214098; x=1788818898; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to:content-type; bh=D0PaHFXOwSud6UZFH2TxjY6/bCXYw/h8j6SRlXWiD8U=; b=Qlrv131wImiGGmxFk9135PkkOJezZ1yJSXMwRhSgXud8rVYT6hpcSnThLiw+NtTeEe NLGzrQ48afFDZrZXFkHsAxjZxl2Lq4MJaIfLUPT6CGRKgXjjZDai+BPch52huOe9GG+4 Vj65DbPfo2kryw9HK2Fq42U8UzHX34zLPz3Y7iei6BpuBLFVYSWlXdFMysAfCySykiv1 XUiPi0EwaQqgUNwH8fs9fl+ETw4WGG/uoSDDvKrjWfTKdPReLoS7ekmTY1Xg0ownlhtF wl0xognsOggdzx24pXUgvsmjBeKIOgDmPx1FzV+c1jxvlHrK3F3xvn8uZZexdvwkO6Ua qLtA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1788214098; x=1788818898; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to:content-type; bh=D0PaHFXOwSud6UZFH2TxjY6/bCXYw/h8j6SRlXWiD8U=; b=OdTv9WQR3P5HbE2JIMz0eDPmgvvpve/PSNKc1HxYIyqrrLptD0fhU3HNhxQEFlTqPZ uZ507EgLccTmVFZ3OMa/EiGaKV3oRkzKPluZXwjAmKQdMbmeH9CA2v4EfTcejSZlvanO l2Zx/psAxgYY79cry3UYgLJIUbhuoqt14B2gaiep+SFlSQSATraMsUyfk7lqS2a8K1fI by1GpoP6MmCbr+DYRNFZK2faEReEU5IVZypqR2CUTkO4yDH5xYBnWvNL9mJjrlysecKR QxUu5Iws0qZDzmjlABr6hEb2Vv1jd+BqcbZ3TZKifJNe11+WGruJA0IBjlCON3xPZnvu 2lng== X-Forwarded-Encrypted: i=1; AKwUvBxqbG8mqXHR9lRo2hnbvyRXU7sAGmYfYF+iPxsrUOvbcf5QCNza3WF2+q/z+38QMKOe+BzQCYtoIaz5Qjg=@vger.kernel.org X-Gm-Message-State: AFuF++l+4AHRdExjiVnxGohAO+zRxL/2ZRrSDRmLc4qwrlD39l0QdTo4 ONkTRzQNnwg8V1O/6IO9ZtjTLChLhVWobhe5ZD2o80XT4wRi9rgpPcOAPFk4tZwLwFU= X-Gm-Gg: AYBFou0snMCiNg4AFu9fObMxsx6mChrCNyROL33DaqKyS4pLdsMVgC9nqtv6+Z7b5X5 6m+2jfU0TIIU0uhx3D7fkLt9fFYrIXJrdowdvqQFt0TrUInbup09/wqK8Hd6UBS9oTcJvj4JjZR rzC6oWtNJQHcpx2qL/dLct9DUA4V4MMdKjupjfP3U6GiZDiq4YAu4QpbXFDh+HsmX91bYG6V/LQ 9EzhPNC/wrOJKFVtRMFcBnUiNPcMV+fbFxFcgcz5lCJQazTwvaMDn3rcnXxSVBsmulDxh2NOIEt GOwSJOpOEjotIXM4riudw8Ij7uu3XE5f3GHEpQMmQ9Avc9UwDdj9Hj9as+pXn+iaS2zRwgdPwHL 1i2U98/fua316Nmn4Ox2RNWfKW1JzRLWxKhUBl27sLt7SM7uf7m3sZ+A231xNDhrwhXEVbN1Hjf 8BE76voXccSkgWFdHBXzPmcmu/rWzjEhvd3pBu7EaLXBy+SnAcyd3U9h0C3Gy80XPe5w== X-Received: by 2002:a17:90b:3808:b0:398:ba46:1d9f with SMTP id 98e67ed59e1d1-3990f88aabdmr759903a91.13.1788214097479; Mon, 31 Aug 2026 15:08:17 -0700 (PDT) Received: from [127.0.1.1] ([2601:647:4184:b3f0:91a4:c2d8:1d54:21ad]) by smtp.gmail.com with ESMTPSA id 98e67ed59e1d1-3990baaeec3sm1827712a91.0.2026.08.31.15.08.16 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 31 Aug 2026 15:08:17 -0700 (PDT) From: Kelvin Zhang To: linux-f2fs-devel@lists.sourceforge.net Cc: jaegeuk@kernel.org, chao@kernel.org, linux-kernel@vger.kernel.org Subject: [PATCH v7 05/11] f2fs: describe dentry block layout dynamically Date: Mon, 31 Aug 2026 15:08:06 -0700 Message-ID: X-Mailer: git-send-email 2.53.0 In-Reply-To: References: Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: 8bit An on-disk directory block contains a bitmap, reserved padding, an array of directory entries (struct f2fs_dir_entry), and matching filename slots. A fixed compile-time structure couples their offsets to 4KB blocks and relies on static reserved-space definitions. Remove struct f2fs_dentry_block and compute region offsets (bitmap bytes, directory-entry count, and filename slots) dynamically from the filesystem block size. Access directory blocks through struct f2fs_dentry_ptr views initialized with the runtime geometry. Update directory operations, inline dentry handling, and recovery paths to use the dynamic block layout. No functional change is introduced for 4KB blocks. Signed-off-by: Kelvin Zhang --- 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 2a4ba7d4ec0e..d2e54573ed34 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -669,17 +669,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) { @@ -1866,6 +1855,9 @@ struct f2fs_sb_info { unsigned int nat_entries_per_block; /* NAT entries in a 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 */ + 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*/ @@ -2268,6 +2260,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 f9811d571ae5..8d2224e585d3 100644 --- a/fs/f2fs/super.c +++ b/fs/f2fs/super.c @@ -4388,6 +4388,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 7d2cbceb42d5..d1059a27e1b9 100644 --- a/include/linux/f2fs_fs.h +++ b/include/linux/f2fs_fs.h @@ -601,15 +601,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 */ @@ -620,14 +612,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