[PATCH v2 07/11] f2fs: describe xattr block layout dynamically
From: Xinping Zhang
Date: Sat Aug 29 2026 - 13:51:27 EST
The usable capacity of dedicated on-disk extended attribute blocks and
inline xattr regions scales with the filesystem block size.
Parameterize VALID_XATTR_BLOCK_SIZE and MAX_INLINE_XATTR_SIZE to
calculate usable xattr limits dynamically from sbi->blocksize rather than
hardcoding PAGE_SIZE or DEF_ADDRS_PER_INODE.
Update mount option consistency validation for inline_xattr_size to
evaluate allowed boundaries dynamically against the runtime block size.
Signed-off-by: Kelvin Zhang <zhangxp1998@xxxxxxxxx>
---
fs/f2fs/inode.c | 4 ++--
fs/f2fs/node.c | 2 +-
fs/f2fs/super.c | 17 +++++++++++++++--
fs/f2fs/xattr.c | 8 +++++---
fs/f2fs/xattr.h | 15 ++++++++++-----
include/linux/f2fs_fs.h | 2 ++
6 files changed, 35 insertions(+), 13 deletions(-)
diff --git a/fs/f2fs/inode.c b/fs/f2fs/inode.c
index fda9ee3bfc61..20b0561fe35d 100644
--- a/fs/f2fs/inode.c
+++ b/fs/f2fs/inode.c
@@ -337,12 +337,12 @@ static bool sanity_check_inode(struct inode
*inode, struct folio *node_folio)
}
if (f2fs_sb_has_flexible_inline_xattr(sbi) &&
- (fi->i_inline_xattr_size > MAX_INLINE_XATTR_SIZE ||
+ (fi->i_inline_xattr_size > MAX_INLINE_XATTR_SIZE(inode) ||
(f2fs_has_inline_xattr(inode) &&
fi->i_inline_xattr_size < MIN_INLINE_XATTR_SIZE))) {
f2fs_warn(sbi, "%s: inode (ino=%llx) has corrupted
i_inline_xattr_size: %d, min: %zu, max: %lu",
__func__, inode->i_ino, fi->i_inline_xattr_size,
- MIN_INLINE_XATTR_SIZE, MAX_INLINE_XATTR_SIZE);
+ MIN_INLINE_XATTR_SIZE, MAX_INLINE_XATTR_SIZE(inode));
return false;
}
diff --git a/fs/f2fs/node.c b/fs/f2fs/node.c
index e3f594f7bcce..7dabfada1b54 100644
--- a/fs/f2fs/node.c
+++ b/fs/f2fs/node.c
@@ -2919,7 +2919,7 @@ int f2fs_recover_xattr_data(struct inode *inode,
struct folio *folio)
/* 3: update and set xattr node page dirty */
if (folio) {
memcpy(F2FS_NODE(xfolio), F2FS_NODE(folio),
- VALID_XATTR_BLOCK_SIZE);
+ VALID_XATTR_BLOCK_SIZE(inode));
folio_mark_dirty(xfolio);
}
f2fs_folio_put(xfolio, true);
diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c
index fcaeaacaab33..3ba4df757213 100644
--- a/fs/f2fs/super.c
+++ b/fs/f2fs/super.c
@@ -855,9 +855,11 @@ static int f2fs_parse_param(struct fs_context
*fc, struct fs_parameter *param)
break;
case Opt_inline_xattr_size:
if (result.int_32 < MIN_INLINE_XATTR_SIZE ||
- result.int_32 > MAX_INLINE_XATTR_SIZE) {
+ result.int_32 >
+ MAX_INLINE_XATTR_SIZE_FOR_BLOCKSIZE(F2FS_MAX_BLKSIZE)) {
f2fs_err(NULL, "inline xattr size is out of range: %u ~ %u",
- (u32)MIN_INLINE_XATTR_SIZE, (u32)MAX_INLINE_XATTR_SIZE);
+ (u32)MIN_INLINE_XATTR_SIZE,
+ (u32)MAX_INLINE_XATTR_SIZE_FOR_BLOCKSIZE(F2FS_MAX_BLKSIZE));
return -EINVAL;
}
ctx_set_opt(ctx, F2FS_MOUNT_INLINE_XATTR_SIZE);
@@ -1596,6 +1598,8 @@ static int f2fs_check_opt_consistency(struct
fs_context *fc,
}
if (ctx_test_opt(ctx, F2FS_MOUNT_INLINE_XATTR_SIZE)) {
+ int min_size, max_size;
+
if (!f2fs_sb_has_extra_attr(sbi) ||
!f2fs_sb_has_flexible_inline_xattr(sbi)) {
f2fs_err(sbi, "extra_attr or flexible_inline_xattr feature is off");
@@ -1605,6 +1609,15 @@ static int f2fs_check_opt_consistency(struct
fs_context *fc,
f2fs_err(sbi, "inline_xattr_size option should be set with
inline_xattr option");
return -EINVAL;
}
+ min_size = MIN_INLINE_XATTR_SIZE;
+ max_size = MAX_INLINE_XATTR_SIZE_FOR_BLOCKSIZE(F2FS_BLKSIZE);
+
+ if (F2FS_OPTION(sbi).inline_xattr_size < min_size ||
+ F2FS_OPTION(sbi).inline_xattr_size > max_size) {
+ f2fs_err(sbi, "inline xattr size is out of range: %d ~ %d",
+ min_size, max_size);
+ return -EINVAL;
+ }
}
if (ctx_test_opt(ctx, F2FS_MOUNT_ATGC) &&
diff --git a/fs/f2fs/xattr.c b/fs/f2fs/xattr.c
index 6728d1488cad..99c863513725 100644
--- a/fs/f2fs/xattr.c
+++ b/fs/f2fs/xattr.c
@@ -310,7 +310,8 @@ static int read_xattr_block(struct inode *inode,
void *txattr_addr)
return PTR_ERR(xfolio);
xattr_addr = folio_address(xfolio);
- memcpy(txattr_addr + inline_size, xattr_addr, VALID_XATTR_BLOCK_SIZE);
+ memcpy(txattr_addr + inline_size, xattr_addr,
+ VALID_XATTR_BLOCK_SIZE(inode));
f2fs_folio_put(xfolio, true);
return 0;
@@ -393,7 +394,7 @@ static int read_all_xattrs(struct inode *inode,
struct folio *ifolio,
{
struct f2fs_xattr_header *header;
nid_t xnid = F2FS_I(inode)->i_xattr_nid;
- unsigned int size = VALID_XATTR_BLOCK_SIZE;
+ unsigned int size = VALID_XATTR_BLOCK_SIZE(inode);
unsigned int inline_size = inline_xattr_size(inode);
void *txattr_addr;
int err;
@@ -502,7 +503,8 @@ static inline int write_all_xattrs(struct inode
*inode, __u32 hsize,
if (inline_size)
memcpy(inline_addr, txattr_addr, inline_size);
- memcpy(xattr_addr, txattr_addr + inline_size, VALID_XATTR_BLOCK_SIZE);
+ memcpy(xattr_addr, txattr_addr + inline_size,
+ VALID_XATTR_BLOCK_SIZE(inode));
if (inline_size)
folio_mark_dirty(ifolio ? ifolio : in_folio);
diff --git a/fs/f2fs/xattr.h b/fs/f2fs/xattr.h
index bce3d93e4755..d9e2c65a1fc7 100644
--- a/fs/f2fs/xattr.h
+++ b/fs/f2fs/xattr.h
@@ -71,24 +71,29 @@ struct f2fs_xattr_entry {
for (entry = XATTR_FIRST_ENTRY(addr);\
!IS_XATTR_LAST_ENTRY(entry);\
entry = XATTR_NEXT_ENTRY(entry))
-#define VALID_XATTR_BLOCK_SIZE (PAGE_SIZE - sizeof(struct node_footer))
+#define VALID_XATTR_BLOCK_SIZE(i) (i_blocksize(i) - \
+ sizeof(struct node_footer))
#define XATTR_PADDING_SIZE (sizeof(__u32))
#define XATTR_SIZE(i) ((F2FS_I(i)->i_xattr_nid ? \
- VALID_XATTR_BLOCK_SIZE : 0) + \
+ VALID_XATTR_BLOCK_SIZE(i) : 0) + \
(inline_xattr_size(i)))
#define MIN_OFFSET(i) XATTR_ALIGN(inline_xattr_size(i) + \
- VALID_XATTR_BLOCK_SIZE)
+ VALID_XATTR_BLOCK_SIZE(i))
#define MAX_VALUE_LEN(i) (MIN_OFFSET(i) - \
sizeof(struct f2fs_xattr_header) - \
sizeof(struct f2fs_xattr_entry))
#define MIN_INLINE_XATTR_SIZE (sizeof(struct f2fs_xattr_header) /
sizeof(__le32))
-#define MAX_INLINE_XATTR_SIZE \
- (DEF_ADDRS_PER_INODE - \
+#define MAX_INLINE_XATTR_SIZE_FOR_BLOCKSIZE(blocksize) \
+ (((blocksize) - OFFSET_OF_END_OF_I_EXT - \
+ SIZE_OF_I_NID - sizeof(struct node_footer)) / \
+ sizeof(__le32) - \
F2FS_TOTAL_EXTRA_ATTR_SIZE / sizeof(__le32) - \
DEF_INLINE_RESERVED_SIZE - \
MIN_INLINE_DENTRY_SIZE / sizeof(__le32))
+#define MAX_INLINE_XATTR_SIZE(inode) \
+ MAX_INLINE_XATTR_SIZE_FOR_BLOCKSIZE(i_blocksize(inode))
#define DEFAULT_XATTR_SLAB_SIZE (DEFAULT_INLINE_XATTR_ADDRS * \
sizeof(__le32) + XATTR_PADDING_SIZE)
diff --git a/include/linux/f2fs_fs.h b/include/linux/f2fs_fs.h
index f339f83220f2..18a9abaa879a 100644
--- a/include/linux/f2fs_fs.h
+++ b/include/linux/f2fs_fs.h
@@ -15,6 +15,8 @@
#define F2FS_MIN_LOG_SECTOR_SIZE 9 /* 9 bits for 512 bytes */
#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_MIN_LOG_BLOCKSIZE 12
+#define F2FS_MIN_BLKSIZE 4096UL
#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 */
--
2.53.0