Re: [f2fs-dev] [PATCH v7 07/11] f2fs: describe xattr block layout dynamically
From: Daeho Jeong
Date: Mon Aug 31 2026 - 21:10:40 EST
On Mon, Aug 31, 2026 at 3:09 PM Kelvin Zhang <zhangxp1998@xxxxxxxxx> wrote:
>
> 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))
Regarding MAX_INLINE_XATTR_SIZE and MAX_INLINE_XATTR_SIZE_FOR_BLOCKSIZE:
Currently, two separate macros are defined in fs/f2fs/xattr.h:
1. MAX_INLINE_XATTR_SIZE_FOR_BLOCKSIZE(blocksize) - taking a blocksize value
2. MAX_INLINE_XATTR_SIZE(inode) - a wrapper calling the above with
i_blocksize(inode)
This duplication was added because in super.c (during mount option parsing in
f2fs_parse_param), there is no struct inode instance yet, so F2FS_MIN_BLKSIZE
had to be passed explicitly.
Instead of maintaining the verbose `_FOR_BLOCKSIZE` duplicate macro, we can
simply define a single unified macro that takes the blocksize:
#define MAX_INLINE_XATTR_SIZE(bs) \
(F2FS_DEF_ADDRS_PER_INODE(bs) - \
F2FS_TOTAL_EXTRA_ATTR_SIZE / sizeof(__le32) - \
DEF_INLINE_RESERVED_SIZE - \
MIN_INLINE_DENTRY_SIZE / sizeof(__le32))
Call sites can then pass the blocksize directly:
- In super.c:
MAX_INLINE_XATTR_SIZE(F2FS_MIN_BLKSIZE)
- In inode.c:
MAX_INLINE_XATTR_SIZE(i_blocksize(inode))
(or MAX_INLINE_XATTR_SIZE(F2FS_I_SB(inode)->blocksize))
Thanks,
> #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 3d0fe2c78e12..d4151970ae86 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
>
>
>
> _______________________________________________
> Linux-f2fs-devel mailing list
> Linux-f2fs-devel@xxxxxxxxxxxxxxxxxxxxx
> https://lists.sourceforge.net/lists/listinfo/linux-f2fs-devel