* [PATCH v4] f2fs: add reserved nodes for privileged users
@ 2025-07-31 7:57 Chunhai Guo
2025-07-31 8:46 ` Chao Yu
0 siblings, 1 reply; 7+ messages in thread
From: Chunhai Guo @ 2025-07-31 7:57 UTC (permalink / raw)
To: chao, jaegeuk; +Cc: linux-f2fs-devel, linux-kernel, Chunhai Guo
This patch allows privileged users to reserve nodes via the
'reserve_node' mount option, which is similar to the existing
'reserve_root' option.
"-o reserve_node=<N>" means <N> nodes are reserved for privileged
users only.
Signed-off-by: Chunhai Guo <guochunhai@vivo.com>
---
v3->v4: Rebase this patch on https://lore.kernel.org/linux-f2fs-devel/20250731060338.1136086-1-chao@kernel.org
v2->v3: Apply Chao's suggestion from v2.
v1->v2: Add two missing handling parts.
v1: https://lore.kernel.org/linux-f2fs-devel/20250729095238.607433-1-guochunhai@vivo.com/
---
Documentation/filesystems/f2fs.rst | 9 ++++---
fs/f2fs/f2fs.h | 14 +++++++---
fs/f2fs/super.c | 43 +++++++++++++++++++++++++-----
3 files changed, 52 insertions(+), 14 deletions(-)
diff --git a/Documentation/filesystems/f2fs.rst b/Documentation/filesystems/f2fs.rst
index 03b1efa6d3b2..95dbcd7ac9a8 100644
--- a/Documentation/filesystems/f2fs.rst
+++ b/Documentation/filesystems/f2fs.rst
@@ -173,9 +173,12 @@ data_flush Enable data flushing before checkpoint in order to
persist data of regular and symlink.
reserve_root=%d Support configuring reserved space which is used for
allocation from a privileged user with specified uid or
- gid, unit: 4KB, the default limit is 0.2% of user blocks.
-resuid=%d The user ID which may use the reserved blocks.
-resgid=%d The group ID which may use the reserved blocks.
+ gid, unit: 4KB, the default limit is 12.5% of user blocks.
+reserve_node=%d Support configuring reserved nodes which are used for
+ allocation from a privileged user with specified uid or
+ gid, the default limit is 12.5% of all nodes.
+resuid=%d The user ID which may use the reserved blocks and nodes.
+resgid=%d The group ID which may use the reserved blocks and nodes.
fault_injection=%d Enable fault injection in all supported types with
specified injection rate.
fault_type=%d Support configuring fault injection type, should be
diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h
index eb372af22edc..b9676ef16246 100644
--- a/fs/f2fs/f2fs.h
+++ b/fs/f2fs/f2fs.h
@@ -131,6 +131,7 @@ extern const char *f2fs_fault_name[FAULT_MAX];
* string rather than using the MS_LAZYTIME flag, so this must remain.
*/
#define F2FS_MOUNT_LAZYTIME 0x40000000
+#define F2FS_MOUNT_RESERVE_NODE 0x80000000
#define F2FS_OPTION(sbi) ((sbi)->mount_opt)
#define clear_opt(sbi, option) (F2FS_OPTION(sbi).opt &= ~F2FS_MOUNT_##option)
@@ -172,6 +173,7 @@ struct f2fs_rwsem {
struct f2fs_mount_info {
unsigned int opt;
block_t root_reserved_blocks; /* root reserved blocks */
+ block_t root_reserved_nodes; /* root reserved nodes */
kuid_t s_resuid; /* reserved blocks for uid */
kgid_t s_resgid; /* reserved blocks for gid */
int active_logs; /* # of active logs */
@@ -2355,7 +2357,7 @@ static inline bool f2fs_has_xattr_block(unsigned int ofs)
return ofs == XATTR_NODE_OFFSET;
}
-static inline bool __allow_reserved_blocks(struct f2fs_sb_info *sbi,
+static inline bool __allow_reserved_root(struct f2fs_sb_info *sbi,
struct inode *inode, bool cap)
{
if (!inode)
@@ -2380,7 +2382,7 @@ static inline unsigned int get_available_block_count(struct f2fs_sb_info *sbi,
avail_user_block_count = sbi->user_block_count -
sbi->current_reserved_blocks;
- if (test_opt(sbi, RESERVE_ROOT) && !__allow_reserved_blocks(sbi, inode, cap))
+ if (test_opt(sbi, RESERVE_ROOT) && !__allow_reserved_root(sbi, inode, cap))
avail_user_block_count -= F2FS_OPTION(sbi).root_reserved_blocks;
if (unlikely(is_sbi_flag_set(sbi, SBI_CP_DISABLED))) {
@@ -2738,7 +2740,7 @@ static inline int inc_valid_node_count(struct f2fs_sb_info *sbi,
struct inode *inode, bool is_inode)
{
block_t valid_block_count;
- unsigned int valid_node_count;
+ unsigned int valid_node_count, avail_user_node_count;
unsigned int avail_user_block_count;
int err;
@@ -2767,8 +2769,12 @@ static inline int inc_valid_node_count(struct f2fs_sb_info *sbi,
goto enospc;
}
+ avail_user_node_count = sbi->total_node_count - F2FS_RESERVED_NODE_NUM;
+ if (test_opt(sbi, RESERVE_NODE) &&
+ !__allow_reserved_root(sbi, inode, false))
+ avail_user_node_count -= F2FS_OPTION(sbi).root_reserved_nodes;
valid_node_count = sbi->total_valid_node_count + 1;
- if (unlikely(valid_node_count > sbi->total_node_count)) {
+ if (unlikely(valid_node_count > avail_user_node_count)) {
spin_unlock(&sbi->stat_lock);
goto enospc;
}
diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c
index 30c038413040..a24e855a38ed 100644
--- a/fs/f2fs/super.c
+++ b/fs/f2fs/super.c
@@ -143,6 +143,7 @@ enum {
Opt_extent_cache,
Opt_data_flush,
Opt_reserve_root,
+ Opt_reserve_node,
Opt_resgid,
Opt_resuid,
Opt_mode,
@@ -265,6 +266,7 @@ static const struct fs_parameter_spec f2fs_param_specs[] = {
fsparam_flag_no("extent_cache", Opt_extent_cache),
fsparam_flag("data_flush", Opt_data_flush),
fsparam_u32("reserve_root", Opt_reserve_root),
+ fsparam_u32("reserve_node", Opt_reserve_node),
fsparam_gid("resgid", Opt_resgid),
fsparam_uid("resuid", Opt_resuid),
fsparam_enum("mode", Opt_mode, f2fs_param_mode),
@@ -336,6 +338,7 @@ static match_table_t f2fs_checkpoint_tokens = {
#define F2FS_SPEC_discard_unit (1 << 21)
#define F2FS_SPEC_memory_mode (1 << 22)
#define F2FS_SPEC_errors (1 << 23)
+#define F2FS_SPEC_reserve_node (1 << 24)
struct f2fs_fs_context {
struct f2fs_mount_info info;
@@ -437,22 +440,30 @@ static void f2fs_destroy_casefold_cache(void) { }
static inline void limit_reserve_root(struct f2fs_sb_info *sbi)
{
- block_t limit = min((sbi->user_block_count >> 3),
+ block_t block_limit = min((sbi->user_block_count >> 3),
sbi->user_block_count - sbi->reserved_blocks);
+ block_t node_limit = sbi->total_node_count >> 3;
/* limit is 12.5% */
if (test_opt(sbi, RESERVE_ROOT) &&
- F2FS_OPTION(sbi).root_reserved_blocks > limit) {
- F2FS_OPTION(sbi).root_reserved_blocks = limit;
+ F2FS_OPTION(sbi).root_reserved_blocks > block_limit) {
+ F2FS_OPTION(sbi).root_reserved_blocks = block_limit;
f2fs_info(sbi, "Reduce reserved blocks for root = %u",
F2FS_OPTION(sbi).root_reserved_blocks);
}
- if (!test_opt(sbi, RESERVE_ROOT) &&
+ if (test_opt(sbi, RESERVE_NODE) &&
+ F2FS_OPTION(sbi).root_reserved_nodes > node_limit) {
+ F2FS_OPTION(sbi).root_reserved_nodes = node_limit;
+ f2fs_info(sbi, "Reduce reserved nodes for root = %u",
+ F2FS_OPTION(sbi).root_reserved_nodes);
+ }
+ if (!test_opt(sbi, RESERVE_ROOT) && !test_opt(sbi, RESERVE_NODE) &&
(!uid_eq(F2FS_OPTION(sbi).s_resuid,
make_kuid(&init_user_ns, F2FS_DEF_RESUID)) ||
!gid_eq(F2FS_OPTION(sbi).s_resgid,
make_kgid(&init_user_ns, F2FS_DEF_RESGID))))
- f2fs_info(sbi, "Ignore s_resuid=%u, s_resgid=%u w/o reserve_root",
+ f2fs_info(sbi, "Ignore s_resuid=%u, s_resgid=%u w/o reserve_root"
+ " and reserve_node",
from_kuid_munged(&init_user_ns,
F2FS_OPTION(sbi).s_resuid),
from_kgid_munged(&init_user_ns,
@@ -841,6 +852,11 @@ static int f2fs_parse_param(struct fs_context *fc, struct fs_parameter *param)
F2FS_CTX_INFO(ctx).root_reserved_blocks = result.uint_32;
ctx->spec_mask |= F2FS_SPEC_reserve_root;
break;
+ case Opt_reserve_node:
+ ctx_set_opt(ctx, F2FS_MOUNT_RESERVE_NODE);
+ F2FS_CTX_INFO(ctx).root_reserved_nodes = result.uint_32;
+ ctx->spec_mask |= F2FS_SPEC_reserve_node;
+ break;
case Opt_resuid:
F2FS_CTX_INFO(ctx).s_resuid = result.uid;
ctx->spec_mask |= F2FS_SPEC_resuid;
@@ -1424,6 +1440,14 @@ static int f2fs_check_opt_consistency(struct fs_context *fc,
ctx_clear_opt(ctx, F2FS_MOUNT_RESERVE_ROOT);
ctx->opt_mask &= ~F2FS_MOUNT_RESERVE_ROOT;
}
+ if (test_opt(sbi, RESERVE_NODE) &&
+ (ctx->opt_mask & F2FS_MOUNT_RESERVE_NODE) &&
+ ctx_test_opt(ctx, F2FS_MOUNT_RESERVE_NODE)) {
+ f2fs_info(sbi, "Preserve previous reserve_node=%u",
+ F2FS_OPTION(sbi).root_reserved_nodes);
+ ctx_clear_opt(ctx, F2FS_MOUNT_RESERVE_NODE);
+ ctx->opt_mask &= ~F2FS_MOUNT_RESERVE_NODE;
+ }
err = f2fs_check_test_dummy_encryption(fc, sb);
if (err)
@@ -1623,6 +1647,9 @@ static void f2fs_apply_options(struct fs_context *fc, struct super_block *sb)
if (ctx->spec_mask & F2FS_SPEC_reserve_root)
F2FS_OPTION(sbi).root_reserved_blocks =
F2FS_CTX_INFO(ctx).root_reserved_blocks;
+ if (ctx->spec_mask & F2FS_SPEC_reserve_node)
+ F2FS_OPTION(sbi).root_reserved_nodes =
+ F2FS_CTX_INFO(ctx).root_reserved_nodes;
if (ctx->spec_mask & F2FS_SPEC_resgid)
F2FS_OPTION(sbi).s_resgid = F2FS_CTX_INFO(ctx).s_resgid;
if (ctx->spec_mask & F2FS_SPEC_resuid)
@@ -2342,9 +2369,11 @@ static int f2fs_show_options(struct seq_file *seq, struct dentry *root)
else if (F2FS_OPTION(sbi).fs_mode == FS_MODE_FRAGMENT_BLK)
seq_puts(seq, "fragment:block");
seq_printf(seq, ",active_logs=%u", F2FS_OPTION(sbi).active_logs);
- if (test_opt(sbi, RESERVE_ROOT))
- seq_printf(seq, ",reserve_root=%u,resuid=%u,resgid=%u",
+ if (test_opt(sbi, RESERVE_ROOT) || test_opt(sbi, RESERVE_NODE))
+ seq_printf(seq, ",reserve_root=%u,reserve_node=%u,resuid=%u,"
+ "resgid=%u",
F2FS_OPTION(sbi).root_reserved_blocks,
+ F2FS_OPTION(sbi).root_reserved_nodes,
from_kuid_munged(&init_user_ns,
F2FS_OPTION(sbi).s_resuid),
from_kgid_munged(&init_user_ns,
--
2.34.1
^ permalink raw reply related [flat|nested] 7+ messages in thread
* Re: [PATCH v4] f2fs: add reserved nodes for privileged users
2025-07-31 7:57 [PATCH v4] f2fs: add reserved nodes for privileged users Chunhai Guo
@ 2025-07-31 8:46 ` Chao Yu
2025-07-31 10:53 ` Chunhai Guo
2025-08-05 8:56 ` Chao Yu
0 siblings, 2 replies; 7+ messages in thread
From: Chao Yu @ 2025-07-31 8:46 UTC (permalink / raw)
To: Chunhai Guo, jaegeuk; +Cc: chao, linux-f2fs-devel, linux-kernel
On 7/31/25 15:57, Chunhai Guo wrote:
> This patch allows privileged users to reserve nodes via the
> 'reserve_node' mount option, which is similar to the existing
> 'reserve_root' option.
>
> "-o reserve_node=<N>" means <N> nodes are reserved for privileged
> users only.
>
> Signed-off-by: Chunhai Guo <guochunhai@vivo.com>
> ---
> v3->v4: Rebase this patch on https://lore.kernel.org/linux-f2fs-devel/20250731060338.1136086-1-chao@kernel.org
> v2->v3: Apply Chao's suggestion from v2.
> v1->v2: Add two missing handling parts.
> v1: https://lore.kernel.org/linux-f2fs-devel/20250729095238.607433-1-guochunhai@vivo.com/
> ---
> Documentation/filesystems/f2fs.rst | 9 ++++---
> fs/f2fs/f2fs.h | 14 +++++++---
> fs/f2fs/super.c | 43 +++++++++++++++++++++++++-----
> 3 files changed, 52 insertions(+), 14 deletions(-)
>
> diff --git a/Documentation/filesystems/f2fs.rst b/Documentation/filesystems/f2fs.rst
> index 03b1efa6d3b2..95dbcd7ac9a8 100644
> --- a/Documentation/filesystems/f2fs.rst
> +++ b/Documentation/filesystems/f2fs.rst
> @@ -173,9 +173,12 @@ data_flush Enable data flushing before checkpoint in order to
> persist data of regular and symlink.
> reserve_root=%d Support configuring reserved space which is used for
> allocation from a privileged user with specified uid or
> - gid, unit: 4KB, the default limit is 0.2% of user blocks.
> -resuid=%d The user ID which may use the reserved blocks.
> -resgid=%d The group ID which may use the reserved blocks.
> + gid, unit: 4KB, the default limit is 12.5% of user blocks.
> +reserve_node=%d Support configuring reserved nodes which are used for
> + allocation from a privileged user with specified uid or
> + gid, the default limit is 12.5% of all nodes.
> +resuid=%d The user ID which may use the reserved blocks and nodes.
> +resgid=%d The group ID which may use the reserved blocks and nodes.
> fault_injection=%d Enable fault injection in all supported types with
> specified injection rate.
> fault_type=%d Support configuring fault injection type, should be
> diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h
> index eb372af22edc..b9676ef16246 100644
> --- a/fs/f2fs/f2fs.h
> +++ b/fs/f2fs/f2fs.h
> @@ -131,6 +131,7 @@ extern const char *f2fs_fault_name[FAULT_MAX];
> * string rather than using the MS_LAZYTIME flag, so this must remain.
> */
> #define F2FS_MOUNT_LAZYTIME 0x40000000
> +#define F2FS_MOUNT_RESERVE_NODE 0x80000000
>
> #define F2FS_OPTION(sbi) ((sbi)->mount_opt)
> #define clear_opt(sbi, option) (F2FS_OPTION(sbi).opt &= ~F2FS_MOUNT_##option)
> @@ -172,6 +173,7 @@ struct f2fs_rwsem {
> struct f2fs_mount_info {
> unsigned int opt;
> block_t root_reserved_blocks; /* root reserved blocks */
> + block_t root_reserved_nodes; /* root reserved nodes */
> kuid_t s_resuid; /* reserved blocks for uid */
> kgid_t s_resgid; /* reserved blocks for gid */
> int active_logs; /* # of active logs */
> @@ -2355,7 +2357,7 @@ static inline bool f2fs_has_xattr_block(unsigned int ofs)
> return ofs == XATTR_NODE_OFFSET;
> }
>
> -static inline bool __allow_reserved_blocks(struct f2fs_sb_info *sbi,
> +static inline bool __allow_reserved_root(struct f2fs_sb_info *sbi,
> struct inode *inode, bool cap)
> {
> if (!inode)
> @@ -2380,7 +2382,7 @@ static inline unsigned int get_available_block_count(struct f2fs_sb_info *sbi,
> avail_user_block_count = sbi->user_block_count -
> sbi->current_reserved_blocks;
>
> - if (test_opt(sbi, RESERVE_ROOT) && !__allow_reserved_blocks(sbi, inode, cap))
> + if (test_opt(sbi, RESERVE_ROOT) && !__allow_reserved_root(sbi, inode, cap))
> avail_user_block_count -= F2FS_OPTION(sbi).root_reserved_blocks;
>
> if (unlikely(is_sbi_flag_set(sbi, SBI_CP_DISABLED))) {
> @@ -2738,7 +2740,7 @@ static inline int inc_valid_node_count(struct f2fs_sb_info *sbi,
> struct inode *inode, bool is_inode)
> {
> block_t valid_block_count;
> - unsigned int valid_node_count;
> + unsigned int valid_node_count, avail_user_node_count;
> unsigned int avail_user_block_count;
> int err;
>
> @@ -2767,8 +2769,12 @@ static inline int inc_valid_node_count(struct f2fs_sb_info *sbi,
> goto enospc;
> }
>
> + avail_user_node_count = sbi->total_node_count - F2FS_RESERVED_NODE_NUM;
> + if (test_opt(sbi, RESERVE_NODE) &&
> + !__allow_reserved_root(sbi, inode, false))
Chunhai,
Do we need to pass cap=true to __allow_reserved_root()?
In addition, do we need to change cap as well for below statement?
avail_user_block_count = get_available_block_count(sbi, inode, false);
Thanks,
> + avail_user_node_count -= F2FS_OPTION(sbi).root_reserved_nodes;
> valid_node_count = sbi->total_valid_node_count + 1;
> - if (unlikely(valid_node_count > sbi->total_node_count)) {
> + if (unlikely(valid_node_count > avail_user_node_count)) {
> spin_unlock(&sbi->stat_lock);
> goto enospc;
> }
> diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c
> index 30c038413040..a24e855a38ed 100644
> --- a/fs/f2fs/super.c
> +++ b/fs/f2fs/super.c
> @@ -143,6 +143,7 @@ enum {
> Opt_extent_cache,
> Opt_data_flush,
> Opt_reserve_root,
> + Opt_reserve_node,
> Opt_resgid,
> Opt_resuid,
> Opt_mode,
> @@ -265,6 +266,7 @@ static const struct fs_parameter_spec f2fs_param_specs[] = {
> fsparam_flag_no("extent_cache", Opt_extent_cache),
> fsparam_flag("data_flush", Opt_data_flush),
> fsparam_u32("reserve_root", Opt_reserve_root),
> + fsparam_u32("reserve_node", Opt_reserve_node),
> fsparam_gid("resgid", Opt_resgid),
> fsparam_uid("resuid", Opt_resuid),
> fsparam_enum("mode", Opt_mode, f2fs_param_mode),
> @@ -336,6 +338,7 @@ static match_table_t f2fs_checkpoint_tokens = {
> #define F2FS_SPEC_discard_unit (1 << 21)
> #define F2FS_SPEC_memory_mode (1 << 22)
> #define F2FS_SPEC_errors (1 << 23)
> +#define F2FS_SPEC_reserve_node (1 << 24)
>
> struct f2fs_fs_context {
> struct f2fs_mount_info info;
> @@ -437,22 +440,30 @@ static void f2fs_destroy_casefold_cache(void) { }
>
> static inline void limit_reserve_root(struct f2fs_sb_info *sbi)
> {
> - block_t limit = min((sbi->user_block_count >> 3),
> + block_t block_limit = min((sbi->user_block_count >> 3),
> sbi->user_block_count - sbi->reserved_blocks);
> + block_t node_limit = sbi->total_node_count >> 3;
>
> /* limit is 12.5% */
> if (test_opt(sbi, RESERVE_ROOT) &&
> - F2FS_OPTION(sbi).root_reserved_blocks > limit) {
> - F2FS_OPTION(sbi).root_reserved_blocks = limit;
> + F2FS_OPTION(sbi).root_reserved_blocks > block_limit) {
> + F2FS_OPTION(sbi).root_reserved_blocks = block_limit;
> f2fs_info(sbi, "Reduce reserved blocks for root = %u",
> F2FS_OPTION(sbi).root_reserved_blocks);
> }
> - if (!test_opt(sbi, RESERVE_ROOT) &&
> + if (test_opt(sbi, RESERVE_NODE) &&
> + F2FS_OPTION(sbi).root_reserved_nodes > node_limit) {
> + F2FS_OPTION(sbi).root_reserved_nodes = node_limit;
> + f2fs_info(sbi, "Reduce reserved nodes for root = %u",
> + F2FS_OPTION(sbi).root_reserved_nodes);
> + }
> + if (!test_opt(sbi, RESERVE_ROOT) && !test_opt(sbi, RESERVE_NODE) &&
> (!uid_eq(F2FS_OPTION(sbi).s_resuid,
> make_kuid(&init_user_ns, F2FS_DEF_RESUID)) ||
> !gid_eq(F2FS_OPTION(sbi).s_resgid,
> make_kgid(&init_user_ns, F2FS_DEF_RESGID))))
> - f2fs_info(sbi, "Ignore s_resuid=%u, s_resgid=%u w/o reserve_root",
> + f2fs_info(sbi, "Ignore s_resuid=%u, s_resgid=%u w/o reserve_root"
> + " and reserve_node",
> from_kuid_munged(&init_user_ns,
> F2FS_OPTION(sbi).s_resuid),
> from_kgid_munged(&init_user_ns,
> @@ -841,6 +852,11 @@ static int f2fs_parse_param(struct fs_context *fc, struct fs_parameter *param)
> F2FS_CTX_INFO(ctx).root_reserved_blocks = result.uint_32;
> ctx->spec_mask |= F2FS_SPEC_reserve_root;
> break;
> + case Opt_reserve_node:
> + ctx_set_opt(ctx, F2FS_MOUNT_RESERVE_NODE);
> + F2FS_CTX_INFO(ctx).root_reserved_nodes = result.uint_32;
> + ctx->spec_mask |= F2FS_SPEC_reserve_node;
> + break;
> case Opt_resuid:
> F2FS_CTX_INFO(ctx).s_resuid = result.uid;
> ctx->spec_mask |= F2FS_SPEC_resuid;
> @@ -1424,6 +1440,14 @@ static int f2fs_check_opt_consistency(struct fs_context *fc,
> ctx_clear_opt(ctx, F2FS_MOUNT_RESERVE_ROOT);
> ctx->opt_mask &= ~F2FS_MOUNT_RESERVE_ROOT;
> }
> + if (test_opt(sbi, RESERVE_NODE) &&
> + (ctx->opt_mask & F2FS_MOUNT_RESERVE_NODE) &&
> + ctx_test_opt(ctx, F2FS_MOUNT_RESERVE_NODE)) {
> + f2fs_info(sbi, "Preserve previous reserve_node=%u",
> + F2FS_OPTION(sbi).root_reserved_nodes);
> + ctx_clear_opt(ctx, F2FS_MOUNT_RESERVE_NODE);
> + ctx->opt_mask &= ~F2FS_MOUNT_RESERVE_NODE;
> + }
>
> err = f2fs_check_test_dummy_encryption(fc, sb);
> if (err)
> @@ -1623,6 +1647,9 @@ static void f2fs_apply_options(struct fs_context *fc, struct super_block *sb)
> if (ctx->spec_mask & F2FS_SPEC_reserve_root)
> F2FS_OPTION(sbi).root_reserved_blocks =
> F2FS_CTX_INFO(ctx).root_reserved_blocks;
> + if (ctx->spec_mask & F2FS_SPEC_reserve_node)
> + F2FS_OPTION(sbi).root_reserved_nodes =
> + F2FS_CTX_INFO(ctx).root_reserved_nodes;
> if (ctx->spec_mask & F2FS_SPEC_resgid)
> F2FS_OPTION(sbi).s_resgid = F2FS_CTX_INFO(ctx).s_resgid;
> if (ctx->spec_mask & F2FS_SPEC_resuid)
> @@ -2342,9 +2369,11 @@ static int f2fs_show_options(struct seq_file *seq, struct dentry *root)
> else if (F2FS_OPTION(sbi).fs_mode == FS_MODE_FRAGMENT_BLK)
> seq_puts(seq, "fragment:block");
> seq_printf(seq, ",active_logs=%u", F2FS_OPTION(sbi).active_logs);
> - if (test_opt(sbi, RESERVE_ROOT))
> - seq_printf(seq, ",reserve_root=%u,resuid=%u,resgid=%u",
> + if (test_opt(sbi, RESERVE_ROOT) || test_opt(sbi, RESERVE_NODE))
> + seq_printf(seq, ",reserve_root=%u,reserve_node=%u,resuid=%u,"
> + "resgid=%u",
> F2FS_OPTION(sbi).root_reserved_blocks,
> + F2FS_OPTION(sbi).root_reserved_nodes,
> from_kuid_munged(&init_user_ns,
> F2FS_OPTION(sbi).s_resuid),
> from_kgid_munged(&init_user_ns,
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [PATCH v4] f2fs: add reserved nodes for privileged users
2025-07-31 8:46 ` Chao Yu
@ 2025-07-31 10:53 ` Chunhai Guo
2025-08-05 2:38 ` Jaegeuk Kim
2025-08-05 8:56 ` Chao Yu
1 sibling, 1 reply; 7+ messages in thread
From: Chunhai Guo @ 2025-07-31 10:53 UTC (permalink / raw)
To: Chao Yu, Chunhai Guo, jaegeuk@kernel.org
Cc: linux-f2fs-devel@lists.sourceforge.net,
linux-kernel@vger.kernel.org
在 7/31/2025 4:46 PM, Chao Yu 写道:
> On 7/31/25 15:57, Chunhai Guo wrote:
>> This patch allows privileged users to reserve nodes via the
>> 'reserve_node' mount option, which is similar to the existing
>> 'reserve_root' option.
>>
>> "-o reserve_node=<N>" means <N> nodes are reserved for privileged
>> users only.
>>
>> Signed-off-by: Chunhai Guo <guochunhai@vivo.com>
>> ---
>> v3->v4: Rebase this patch on https://lore.kernel.org/linux-f2fs-devel/20250731060338.1136086-1-chao@kernel.org
>> v2->v3: Apply Chao's suggestion from v2.
>> v1->v2: Add two missing handling parts.
>> v1: https://lore.kernel.org/linux-f2fs-devel/20250729095238.607433-1-guochunhai@vivo.com/
>> ---
>> Documentation/filesystems/f2fs.rst | 9 ++++---
>> fs/f2fs/f2fs.h | 14 +++++++---
>> fs/f2fs/super.c | 43 +++++++++++++++++++++++++-----
>> 3 files changed, 52 insertions(+), 14 deletions(-)
>>
>> diff --git a/Documentation/filesystems/f2fs.rst b/Documentation/filesystems/f2fs.rst
>> index 03b1efa6d3b2..95dbcd7ac9a8 100644
>> --- a/Documentation/filesystems/f2fs.rst
>> +++ b/Documentation/filesystems/f2fs.rst
>> @@ -173,9 +173,12 @@ data_flush Enable data flushing before checkpoint in order to
>> persist data of regular and symlink.
>> reserve_root=%d Support configuring reserved space which is used for
>> allocation from a privileged user with specified uid or
>> - gid, unit: 4KB, the default limit is 0.2% of user blocks.
>> -resuid=%d The user ID which may use the reserved blocks.
>> -resgid=%d The group ID which may use the reserved blocks.
>> + gid, unit: 4KB, the default limit is 12.5% of user blocks.
>> +reserve_node=%d Support configuring reserved nodes which are used for
>> + allocation from a privileged user with specified uid or
>> + gid, the default limit is 12.5% of all nodes.
>> +resuid=%d The user ID which may use the reserved blocks and nodes.
>> +resgid=%d The group ID which may use the reserved blocks and nodes.
>> fault_injection=%d Enable fault injection in all supported types with
>> specified injection rate.
>> fault_type=%d Support configuring fault injection type, should be
>> diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h
>> index eb372af22edc..b9676ef16246 100644
>> --- a/fs/f2fs/f2fs.h
>> +++ b/fs/f2fs/f2fs.h
>> @@ -131,6 +131,7 @@ extern const char *f2fs_fault_name[FAULT_MAX];
>> * string rather than using the MS_LAZYTIME flag, so this must remain.
>> */
>> #define F2FS_MOUNT_LAZYTIME 0x40000000
>> +#define F2FS_MOUNT_RESERVE_NODE 0x80000000
>>
>> #define F2FS_OPTION(sbi) ((sbi)->mount_opt)
>> #define clear_opt(sbi, option) (F2FS_OPTION(sbi).opt &= ~F2FS_MOUNT_##option)
>> @@ -172,6 +173,7 @@ struct f2fs_rwsem {
>> struct f2fs_mount_info {
>> unsigned int opt;
>> block_t root_reserved_blocks; /* root reserved blocks */
>> + block_t root_reserved_nodes; /* root reserved nodes */
>> kuid_t s_resuid; /* reserved blocks for uid */
>> kgid_t s_resgid; /* reserved blocks for gid */
>> int active_logs; /* # of active logs */
>> @@ -2355,7 +2357,7 @@ static inline bool f2fs_has_xattr_block(unsigned int ofs)
>> return ofs == XATTR_NODE_OFFSET;
>> }
>>
>> -static inline bool __allow_reserved_blocks(struct f2fs_sb_info *sbi,
>> +static inline bool __allow_reserved_root(struct f2fs_sb_info *sbi,
>> struct inode *inode, bool cap)
>> {
>> if (!inode)
>> @@ -2380,7 +2382,7 @@ static inline unsigned int get_available_block_count(struct f2fs_sb_info *sbi,
>> avail_user_block_count = sbi->user_block_count -
>> sbi->current_reserved_blocks;
>>
>> - if (test_opt(sbi, RESERVE_ROOT) && !__allow_reserved_blocks(sbi, inode, cap))
>> + if (test_opt(sbi, RESERVE_ROOT) && !__allow_reserved_root(sbi, inode, cap))
>> avail_user_block_count -= F2FS_OPTION(sbi).root_reserved_blocks;
>>
>> if (unlikely(is_sbi_flag_set(sbi, SBI_CP_DISABLED))) {
>> @@ -2738,7 +2740,7 @@ static inline int inc_valid_node_count(struct f2fs_sb_info *sbi,
>> struct inode *inode, bool is_inode)
>> {
>> block_t valid_block_count;
>> - unsigned int valid_node_count;
>> + unsigned int valid_node_count, avail_user_node_count;
>> unsigned int avail_user_block_count;
>> int err;
>>
>> @@ -2767,8 +2769,12 @@ static inline int inc_valid_node_count(struct f2fs_sb_info *sbi,
>> goto enospc;
>> }
>>
>> + avail_user_node_count = sbi->total_node_count - F2FS_RESERVED_NODE_NUM;
>> + if (test_opt(sbi, RESERVE_NODE) &&
>> + !__allow_reserved_root(sbi, inode, false))
> Chunhai,
>
> Do we need to pass cap=true to __allow_reserved_root()?
>
> In addition, do we need to change cap as well for below statement?
>
> avail_user_block_count = get_available_block_count(sbi, inode, false);
Hi Jaegeuk,
Based on the description in commit a90a0884ac75 ("f2fs: check
cap_resource only for data blocks"), it seems that inode or node blocks
don't need to perform a check on the cap_resource. I'm not certain about
the reasoning behind this. Could you please help to clarify it?
Thanks,
>
> Thanks,
>
>> + avail_user_node_count -= F2FS_OPTION(sbi).root_reserved_nodes;
>> valid_node_count = sbi->total_valid_node_count + 1;
>> - if (unlikely(valid_node_count > sbi->total_node_count)) {
>> + if (unlikely(valid_node_count > avail_user_node_count)) {
>> spin_unlock(&sbi->stat_lock);
>> goto enospc;
>> }
>> diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c
>> index 30c038413040..a24e855a38ed 100644
>> --- a/fs/f2fs/super.c
>> +++ b/fs/f2fs/super.c
>> @@ -143,6 +143,7 @@ enum {
>> Opt_extent_cache,
>> Opt_data_flush,
>> Opt_reserve_root,
>> + Opt_reserve_node,
>> Opt_resgid,
>> Opt_resuid,
>> Opt_mode,
>> @@ -265,6 +266,7 @@ static const struct fs_parameter_spec f2fs_param_specs[] = {
>> fsparam_flag_no("extent_cache", Opt_extent_cache),
>> fsparam_flag("data_flush", Opt_data_flush),
>> fsparam_u32("reserve_root", Opt_reserve_root),
>> + fsparam_u32("reserve_node", Opt_reserve_node),
>> fsparam_gid("resgid", Opt_resgid),
>> fsparam_uid("resuid", Opt_resuid),
>> fsparam_enum("mode", Opt_mode, f2fs_param_mode),
>> @@ -336,6 +338,7 @@ static match_table_t f2fs_checkpoint_tokens = {
>> #define F2FS_SPEC_discard_unit (1 << 21)
>> #define F2FS_SPEC_memory_mode (1 << 22)
>> #define F2FS_SPEC_errors (1 << 23)
>> +#define F2FS_SPEC_reserve_node (1 << 24)
>>
>> struct f2fs_fs_context {
>> struct f2fs_mount_info info;
>> @@ -437,22 +440,30 @@ static void f2fs_destroy_casefold_cache(void) { }
>>
>> static inline void limit_reserve_root(struct f2fs_sb_info *sbi)
>> {
>> - block_t limit = min((sbi->user_block_count >> 3),
>> + block_t block_limit = min((sbi->user_block_count >> 3),
>> sbi->user_block_count - sbi->reserved_blocks);
>> + block_t node_limit = sbi->total_node_count >> 3;
>>
>> /* limit is 12.5% */
>> if (test_opt(sbi, RESERVE_ROOT) &&
>> - F2FS_OPTION(sbi).root_reserved_blocks > limit) {
>> - F2FS_OPTION(sbi).root_reserved_blocks = limit;
>> + F2FS_OPTION(sbi).root_reserved_blocks > block_limit) {
>> + F2FS_OPTION(sbi).root_reserved_blocks = block_limit;
>> f2fs_info(sbi, "Reduce reserved blocks for root = %u",
>> F2FS_OPTION(sbi).root_reserved_blocks);
>> }
>> - if (!test_opt(sbi, RESERVE_ROOT) &&
>> + if (test_opt(sbi, RESERVE_NODE) &&
>> + F2FS_OPTION(sbi).root_reserved_nodes > node_limit) {
>> + F2FS_OPTION(sbi).root_reserved_nodes = node_limit;
>> + f2fs_info(sbi, "Reduce reserved nodes for root = %u",
>> + F2FS_OPTION(sbi).root_reserved_nodes);
>> + }
>> + if (!test_opt(sbi, RESERVE_ROOT) && !test_opt(sbi, RESERVE_NODE) &&
>> (!uid_eq(F2FS_OPTION(sbi).s_resuid,
>> make_kuid(&init_user_ns, F2FS_DEF_RESUID)) ||
>> !gid_eq(F2FS_OPTION(sbi).s_resgid,
>> make_kgid(&init_user_ns, F2FS_DEF_RESGID))))
>> - f2fs_info(sbi, "Ignore s_resuid=%u, s_resgid=%u w/o reserve_root",
>> + f2fs_info(sbi, "Ignore s_resuid=%u, s_resgid=%u w/o reserve_root"
>> + " and reserve_node",
>> from_kuid_munged(&init_user_ns,
>> F2FS_OPTION(sbi).s_resuid),
>> from_kgid_munged(&init_user_ns,
>> @@ -841,6 +852,11 @@ static int f2fs_parse_param(struct fs_context *fc, struct fs_parameter *param)
>> F2FS_CTX_INFO(ctx).root_reserved_blocks = result.uint_32;
>> ctx->spec_mask |= F2FS_SPEC_reserve_root;
>> break;
>> + case Opt_reserve_node:
>> + ctx_set_opt(ctx, F2FS_MOUNT_RESERVE_NODE);
>> + F2FS_CTX_INFO(ctx).root_reserved_nodes = result.uint_32;
>> + ctx->spec_mask |= F2FS_SPEC_reserve_node;
>> + break;
>> case Opt_resuid:
>> F2FS_CTX_INFO(ctx).s_resuid = result.uid;
>> ctx->spec_mask |= F2FS_SPEC_resuid;
>> @@ -1424,6 +1440,14 @@ static int f2fs_check_opt_consistency(struct fs_context *fc,
>> ctx_clear_opt(ctx, F2FS_MOUNT_RESERVE_ROOT);
>> ctx->opt_mask &= ~F2FS_MOUNT_RESERVE_ROOT;
>> }
>> + if (test_opt(sbi, RESERVE_NODE) &&
>> + (ctx->opt_mask & F2FS_MOUNT_RESERVE_NODE) &&
>> + ctx_test_opt(ctx, F2FS_MOUNT_RESERVE_NODE)) {
>> + f2fs_info(sbi, "Preserve previous reserve_node=%u",
>> + F2FS_OPTION(sbi).root_reserved_nodes);
>> + ctx_clear_opt(ctx, F2FS_MOUNT_RESERVE_NODE);
>> + ctx->opt_mask &= ~F2FS_MOUNT_RESERVE_NODE;
>> + }
>>
>> err = f2fs_check_test_dummy_encryption(fc, sb);
>> if (err)
>> @@ -1623,6 +1647,9 @@ static void f2fs_apply_options(struct fs_context *fc, struct super_block *sb)
>> if (ctx->spec_mask & F2FS_SPEC_reserve_root)
>> F2FS_OPTION(sbi).root_reserved_blocks =
>> F2FS_CTX_INFO(ctx).root_reserved_blocks;
>> + if (ctx->spec_mask & F2FS_SPEC_reserve_node)
>> + F2FS_OPTION(sbi).root_reserved_nodes =
>> + F2FS_CTX_INFO(ctx).root_reserved_nodes;
>> if (ctx->spec_mask & F2FS_SPEC_resgid)
>> F2FS_OPTION(sbi).s_resgid = F2FS_CTX_INFO(ctx).s_resgid;
>> if (ctx->spec_mask & F2FS_SPEC_resuid)
>> @@ -2342,9 +2369,11 @@ static int f2fs_show_options(struct seq_file *seq, struct dentry *root)
>> else if (F2FS_OPTION(sbi).fs_mode == FS_MODE_FRAGMENT_BLK)
>> seq_puts(seq, "fragment:block");
>> seq_printf(seq, ",active_logs=%u", F2FS_OPTION(sbi).active_logs);
>> - if (test_opt(sbi, RESERVE_ROOT))
>> - seq_printf(seq, ",reserve_root=%u,resuid=%u,resgid=%u",
>> + if (test_opt(sbi, RESERVE_ROOT) || test_opt(sbi, RESERVE_NODE))
>> + seq_printf(seq, ",reserve_root=%u,reserve_node=%u,resuid=%u,"
>> + "resgid=%u",
>> F2FS_OPTION(sbi).root_reserved_blocks,
>> + F2FS_OPTION(sbi).root_reserved_nodes,
>> from_kuid_munged(&init_user_ns,
>> F2FS_OPTION(sbi).s_resuid),
>> from_kgid_munged(&init_user_ns,
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [PATCH v4] f2fs: add reserved nodes for privileged users
2025-07-31 10:53 ` Chunhai Guo
@ 2025-08-05 2:38 ` Jaegeuk Kim
2025-08-06 8:56 ` Chunhai Guo
0 siblings, 1 reply; 7+ messages in thread
From: Jaegeuk Kim @ 2025-08-05 2:38 UTC (permalink / raw)
To: Chunhai Guo
Cc: Chao Yu, linux-f2fs-devel@lists.sourceforge.net,
linux-kernel@vger.kernel.org
On 07/31, Chunhai Guo wrote:
> 在 7/31/2025 4:46 PM, Chao Yu 写道:
> > On 7/31/25 15:57, Chunhai Guo wrote:
> >> This patch allows privileged users to reserve nodes via the
> >> 'reserve_node' mount option, which is similar to the existing
> >> 'reserve_root' option.
> >>
> >> "-o reserve_node=<N>" means <N> nodes are reserved for privileged
> >> users only.
> >>
> >> Signed-off-by: Chunhai Guo <guochunhai@vivo.com>
> >> ---
> >> v3->v4: Rebase this patch on https://lore.kernel.org/linux-f2fs-devel/20250731060338.1136086-1-chao@kernel.org
> >> v2->v3: Apply Chao's suggestion from v2.
> >> v1->v2: Add two missing handling parts.
> >> v1: https://lore.kernel.org/linux-f2fs-devel/20250729095238.607433-1-guochunhai@vivo.com/
> >> ---
> >> Documentation/filesystems/f2fs.rst | 9 ++++---
> >> fs/f2fs/f2fs.h | 14 +++++++---
> >> fs/f2fs/super.c | 43 +++++++++++++++++++++++++-----
> >> 3 files changed, 52 insertions(+), 14 deletions(-)
> >>
> >> diff --git a/Documentation/filesystems/f2fs.rst b/Documentation/filesystems/f2fs.rst
> >> index 03b1efa6d3b2..95dbcd7ac9a8 100644
> >> --- a/Documentation/filesystems/f2fs.rst
> >> +++ b/Documentation/filesystems/f2fs.rst
> >> @@ -173,9 +173,12 @@ data_flush Enable data flushing before checkpoint in order to
> >> persist data of regular and symlink.
> >> reserve_root=%d Support configuring reserved space which is used for
> >> allocation from a privileged user with specified uid or
> >> - gid, unit: 4KB, the default limit is 0.2% of user blocks.
> >> -resuid=%d The user ID which may use the reserved blocks.
> >> -resgid=%d The group ID which may use the reserved blocks.
> >> + gid, unit: 4KB, the default limit is 12.5% of user blocks.
> >> +reserve_node=%d Support configuring reserved nodes which are used for
> >> + allocation from a privileged user with specified uid or
> >> + gid, the default limit is 12.5% of all nodes.
> >> +resuid=%d The user ID which may use the reserved blocks and nodes.
> >> +resgid=%d The group ID which may use the reserved blocks and nodes.
> >> fault_injection=%d Enable fault injection in all supported types with
> >> specified injection rate.
> >> fault_type=%d Support configuring fault injection type, should be
> >> diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h
> >> index eb372af22edc..b9676ef16246 100644
> >> --- a/fs/f2fs/f2fs.h
> >> +++ b/fs/f2fs/f2fs.h
> >> @@ -131,6 +131,7 @@ extern const char *f2fs_fault_name[FAULT_MAX];
> >> * string rather than using the MS_LAZYTIME flag, so this must remain.
> >> */
> >> #define F2FS_MOUNT_LAZYTIME 0x40000000
> >> +#define F2FS_MOUNT_RESERVE_NODE 0x80000000
> >>
> >> #define F2FS_OPTION(sbi) ((sbi)->mount_opt)
> >> #define clear_opt(sbi, option) (F2FS_OPTION(sbi).opt &= ~F2FS_MOUNT_##option)
> >> @@ -172,6 +173,7 @@ struct f2fs_rwsem {
> >> struct f2fs_mount_info {
> >> unsigned int opt;
> >> block_t root_reserved_blocks; /* root reserved blocks */
> >> + block_t root_reserved_nodes; /* root reserved nodes */
> >> kuid_t s_resuid; /* reserved blocks for uid */
> >> kgid_t s_resgid; /* reserved blocks for gid */
> >> int active_logs; /* # of active logs */
> >> @@ -2355,7 +2357,7 @@ static inline bool f2fs_has_xattr_block(unsigned int ofs)
> >> return ofs == XATTR_NODE_OFFSET;
> >> }
> >>
> >> -static inline bool __allow_reserved_blocks(struct f2fs_sb_info *sbi,
> >> +static inline bool __allow_reserved_root(struct f2fs_sb_info *sbi,
> >> struct inode *inode, bool cap)
> >> {
> >> if (!inode)
> >> @@ -2380,7 +2382,7 @@ static inline unsigned int get_available_block_count(struct f2fs_sb_info *sbi,
> >> avail_user_block_count = sbi->user_block_count -
> >> sbi->current_reserved_blocks;
> >>
> >> - if (test_opt(sbi, RESERVE_ROOT) && !__allow_reserved_blocks(sbi, inode, cap))
> >> + if (test_opt(sbi, RESERVE_ROOT) && !__allow_reserved_root(sbi, inode, cap))
> >> avail_user_block_count -= F2FS_OPTION(sbi).root_reserved_blocks;
> >>
> >> if (unlikely(is_sbi_flag_set(sbi, SBI_CP_DISABLED))) {
> >> @@ -2738,7 +2740,7 @@ static inline int inc_valid_node_count(struct f2fs_sb_info *sbi,
> >> struct inode *inode, bool is_inode)
> >> {
> >> block_t valid_block_count;
> >> - unsigned int valid_node_count;
> >> + unsigned int valid_node_count, avail_user_node_count;
> >> unsigned int avail_user_block_count;
> >> int err;
> >>
> >> @@ -2767,8 +2769,12 @@ static inline int inc_valid_node_count(struct f2fs_sb_info *sbi,
> >> goto enospc;
> >> }
> >>
> >> + avail_user_node_count = sbi->total_node_count - F2FS_RESERVED_NODE_NUM;
> >> + if (test_opt(sbi, RESERVE_NODE) &&
> >> + !__allow_reserved_root(sbi, inode, false))
> > Chunhai,
> >
> > Do we need to pass cap=true to __allow_reserved_root()?
> >
> > In addition, do we need to change cap as well for below statement?
> >
> > avail_user_block_count = get_available_block_count(sbi, inode, false);
>
>
> Hi Jaegeuk,
>
>
> Based on the description in commit a90a0884ac75 ("f2fs: check
> cap_resource only for data blocks"), it seems that inode or node blocks
> don't need to perform a check on the cap_resource. I'm not certain about
> the reasoning behind this. Could you please help to clarify it?
I don't quite remember as I had to fix the selinx issue long long time ago.
>
>
> Thanks,
>
> >
> > Thanks,
> >
> >> + avail_user_node_count -= F2FS_OPTION(sbi).root_reserved_nodes;
> >> valid_node_count = sbi->total_valid_node_count + 1;
> >> - if (unlikely(valid_node_count > sbi->total_node_count)) {
> >> + if (unlikely(valid_node_count > avail_user_node_count)) {
> >> spin_unlock(&sbi->stat_lock);
> >> goto enospc;
> >> }
> >> diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c
> >> index 30c038413040..a24e855a38ed 100644
> >> --- a/fs/f2fs/super.c
> >> +++ b/fs/f2fs/super.c
> >> @@ -143,6 +143,7 @@ enum {
> >> Opt_extent_cache,
> >> Opt_data_flush,
> >> Opt_reserve_root,
> >> + Opt_reserve_node,
> >> Opt_resgid,
> >> Opt_resuid,
> >> Opt_mode,
> >> @@ -265,6 +266,7 @@ static const struct fs_parameter_spec f2fs_param_specs[] = {
> >> fsparam_flag_no("extent_cache", Opt_extent_cache),
> >> fsparam_flag("data_flush", Opt_data_flush),
> >> fsparam_u32("reserve_root", Opt_reserve_root),
> >> + fsparam_u32("reserve_node", Opt_reserve_node),
> >> fsparam_gid("resgid", Opt_resgid),
> >> fsparam_uid("resuid", Opt_resuid),
> >> fsparam_enum("mode", Opt_mode, f2fs_param_mode),
> >> @@ -336,6 +338,7 @@ static match_table_t f2fs_checkpoint_tokens = {
> >> #define F2FS_SPEC_discard_unit (1 << 21)
> >> #define F2FS_SPEC_memory_mode (1 << 22)
> >> #define F2FS_SPEC_errors (1 << 23)
> >> +#define F2FS_SPEC_reserve_node (1 << 24)
> >>
> >> struct f2fs_fs_context {
> >> struct f2fs_mount_info info;
> >> @@ -437,22 +440,30 @@ static void f2fs_destroy_casefold_cache(void) { }
> >>
> >> static inline void limit_reserve_root(struct f2fs_sb_info *sbi)
> >> {
> >> - block_t limit = min((sbi->user_block_count >> 3),
> >> + block_t block_limit = min((sbi->user_block_count >> 3),
> >> sbi->user_block_count - sbi->reserved_blocks);
> >> + block_t node_limit = sbi->total_node_count >> 3;
> >>
> >> /* limit is 12.5% */
> >> if (test_opt(sbi, RESERVE_ROOT) &&
> >> - F2FS_OPTION(sbi).root_reserved_blocks > limit) {
> >> - F2FS_OPTION(sbi).root_reserved_blocks = limit;
> >> + F2FS_OPTION(sbi).root_reserved_blocks > block_limit) {
> >> + F2FS_OPTION(sbi).root_reserved_blocks = block_limit;
> >> f2fs_info(sbi, "Reduce reserved blocks for root = %u",
> >> F2FS_OPTION(sbi).root_reserved_blocks);
> >> }
> >> - if (!test_opt(sbi, RESERVE_ROOT) &&
> >> + if (test_opt(sbi, RESERVE_NODE) &&
> >> + F2FS_OPTION(sbi).root_reserved_nodes > node_limit) {
> >> + F2FS_OPTION(sbi).root_reserved_nodes = node_limit;
> >> + f2fs_info(sbi, "Reduce reserved nodes for root = %u",
> >> + F2FS_OPTION(sbi).root_reserved_nodes);
> >> + }
> >> + if (!test_opt(sbi, RESERVE_ROOT) && !test_opt(sbi, RESERVE_NODE) &&
> >> (!uid_eq(F2FS_OPTION(sbi).s_resuid,
> >> make_kuid(&init_user_ns, F2FS_DEF_RESUID)) ||
> >> !gid_eq(F2FS_OPTION(sbi).s_resgid,
> >> make_kgid(&init_user_ns, F2FS_DEF_RESGID))))
> >> - f2fs_info(sbi, "Ignore s_resuid=%u, s_resgid=%u w/o reserve_root",
> >> + f2fs_info(sbi, "Ignore s_resuid=%u, s_resgid=%u w/o reserve_root"
> >> + " and reserve_node",
> >> from_kuid_munged(&init_user_ns,
> >> F2FS_OPTION(sbi).s_resuid),
> >> from_kgid_munged(&init_user_ns,
> >> @@ -841,6 +852,11 @@ static int f2fs_parse_param(struct fs_context *fc, struct fs_parameter *param)
> >> F2FS_CTX_INFO(ctx).root_reserved_blocks = result.uint_32;
> >> ctx->spec_mask |= F2FS_SPEC_reserve_root;
> >> break;
> >> + case Opt_reserve_node:
> >> + ctx_set_opt(ctx, F2FS_MOUNT_RESERVE_NODE);
> >> + F2FS_CTX_INFO(ctx).root_reserved_nodes = result.uint_32;
> >> + ctx->spec_mask |= F2FS_SPEC_reserve_node;
> >> + break;
> >> case Opt_resuid:
> >> F2FS_CTX_INFO(ctx).s_resuid = result.uid;
> >> ctx->spec_mask |= F2FS_SPEC_resuid;
> >> @@ -1424,6 +1440,14 @@ static int f2fs_check_opt_consistency(struct fs_context *fc,
> >> ctx_clear_opt(ctx, F2FS_MOUNT_RESERVE_ROOT);
> >> ctx->opt_mask &= ~F2FS_MOUNT_RESERVE_ROOT;
> >> }
> >> + if (test_opt(sbi, RESERVE_NODE) &&
> >> + (ctx->opt_mask & F2FS_MOUNT_RESERVE_NODE) &&
> >> + ctx_test_opt(ctx, F2FS_MOUNT_RESERVE_NODE)) {
> >> + f2fs_info(sbi, "Preserve previous reserve_node=%u",
> >> + F2FS_OPTION(sbi).root_reserved_nodes);
> >> + ctx_clear_opt(ctx, F2FS_MOUNT_RESERVE_NODE);
> >> + ctx->opt_mask &= ~F2FS_MOUNT_RESERVE_NODE;
> >> + }
> >>
> >> err = f2fs_check_test_dummy_encryption(fc, sb);
> >> if (err)
> >> @@ -1623,6 +1647,9 @@ static void f2fs_apply_options(struct fs_context *fc, struct super_block *sb)
> >> if (ctx->spec_mask & F2FS_SPEC_reserve_root)
> >> F2FS_OPTION(sbi).root_reserved_blocks =
> >> F2FS_CTX_INFO(ctx).root_reserved_blocks;
> >> + if (ctx->spec_mask & F2FS_SPEC_reserve_node)
> >> + F2FS_OPTION(sbi).root_reserved_nodes =
> >> + F2FS_CTX_INFO(ctx).root_reserved_nodes;
> >> if (ctx->spec_mask & F2FS_SPEC_resgid)
> >> F2FS_OPTION(sbi).s_resgid = F2FS_CTX_INFO(ctx).s_resgid;
> >> if (ctx->spec_mask & F2FS_SPEC_resuid)
> >> @@ -2342,9 +2369,11 @@ static int f2fs_show_options(struct seq_file *seq, struct dentry *root)
> >> else if (F2FS_OPTION(sbi).fs_mode == FS_MODE_FRAGMENT_BLK)
> >> seq_puts(seq, "fragment:block");
> >> seq_printf(seq, ",active_logs=%u", F2FS_OPTION(sbi).active_logs);
> >> - if (test_opt(sbi, RESERVE_ROOT))
> >> - seq_printf(seq, ",reserve_root=%u,resuid=%u,resgid=%u",
> >> + if (test_opt(sbi, RESERVE_ROOT) || test_opt(sbi, RESERVE_NODE))
> >> + seq_printf(seq, ",reserve_root=%u,reserve_node=%u,resuid=%u,"
> >> + "resgid=%u",
> >> F2FS_OPTION(sbi).root_reserved_blocks,
> >> + F2FS_OPTION(sbi).root_reserved_nodes,
> >> from_kuid_munged(&init_user_ns,
> >> F2FS_OPTION(sbi).s_resuid),
> >> from_kgid_munged(&init_user_ns,
>
>
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [PATCH v4] f2fs: add reserved nodes for privileged users
2025-07-31 8:46 ` Chao Yu
2025-07-31 10:53 ` Chunhai Guo
@ 2025-08-05 8:56 ` Chao Yu
2025-08-06 8:56 ` Chunhai Guo
1 sibling, 1 reply; 7+ messages in thread
From: Chao Yu @ 2025-08-05 8:56 UTC (permalink / raw)
To: Chunhai Guo, jaegeuk; +Cc: chao, linux-f2fs-devel, linux-kernel
On 7/31/25 16:46, Chao Yu wrote:
> On 7/31/25 15:57, Chunhai Guo wrote:
>> This patch allows privileged users to reserve nodes via the
>> 'reserve_node' mount option, which is similar to the existing
>> 'reserve_root' option.
>>
>> "-o reserve_node=<N>" means <N> nodes are reserved for privileged
>> users only.
>>
>> Signed-off-by: Chunhai Guo <guochunhai@vivo.com>
>> ---
>> v3->v4: Rebase this patch on https://lore.kernel.org/linux-f2fs-devel/20250731060338.1136086-1-chao@kernel.org
>> v2->v3: Apply Chao's suggestion from v2.
>> v1->v2: Add two missing handling parts.
>> v1: https://lore.kernel.org/linux-f2fs-devel/20250729095238.607433-1-guochunhai@vivo.com/
>> ---
>> Documentation/filesystems/f2fs.rst | 9 ++++---
>> fs/f2fs/f2fs.h | 14 +++++++---
>> fs/f2fs/super.c | 43 +++++++++++++++++++++++++-----
>> 3 files changed, 52 insertions(+), 14 deletions(-)
>>
>> diff --git a/Documentation/filesystems/f2fs.rst b/Documentation/filesystems/f2fs.rst
>> index 03b1efa6d3b2..95dbcd7ac9a8 100644
>> --- a/Documentation/filesystems/f2fs.rst
>> +++ b/Documentation/filesystems/f2fs.rst
>> @@ -173,9 +173,12 @@ data_flush Enable data flushing before checkpoint in order to
>> persist data of regular and symlink.
>> reserve_root=%d Support configuring reserved space which is used for
>> allocation from a privileged user with specified uid or
>> - gid, unit: 4KB, the default limit is 0.2% of user blocks.
>> -resuid=%d The user ID which may use the reserved blocks.
>> -resgid=%d The group ID which may use the reserved blocks.
>> + gid, unit: 4KB, the default limit is 12.5% of user blocks.
>> +reserve_node=%d Support configuring reserved nodes which are used for
>> + allocation from a privileged user with specified uid or
>> + gid, the default limit is 12.5% of all nodes.
>> +resuid=%d The user ID which may use the reserved blocks and nodes.
>> +resgid=%d The group ID which may use the reserved blocks and nodes.
>> fault_injection=%d Enable fault injection in all supported types with
>> specified injection rate.
>> fault_type=%d Support configuring fault injection type, should be
>> diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h
>> index eb372af22edc..b9676ef16246 100644
>> --- a/fs/f2fs/f2fs.h
>> +++ b/fs/f2fs/f2fs.h
>> @@ -131,6 +131,7 @@ extern const char *f2fs_fault_name[FAULT_MAX];
>> * string rather than using the MS_LAZYTIME flag, so this must remain.
>> */
>> #define F2FS_MOUNT_LAZYTIME 0x40000000
>> +#define F2FS_MOUNT_RESERVE_NODE 0x80000000
>>
>> #define F2FS_OPTION(sbi) ((sbi)->mount_opt)
>> #define clear_opt(sbi, option) (F2FS_OPTION(sbi).opt &= ~F2FS_MOUNT_##option)
>> @@ -172,6 +173,7 @@ struct f2fs_rwsem {
>> struct f2fs_mount_info {
>> unsigned int opt;
>> block_t root_reserved_blocks; /* root reserved blocks */
>> + block_t root_reserved_nodes; /* root reserved nodes */
>> kuid_t s_resuid; /* reserved blocks for uid */
>> kgid_t s_resgid; /* reserved blocks for gid */
>> int active_logs; /* # of active logs */
>> @@ -2355,7 +2357,7 @@ static inline bool f2fs_has_xattr_block(unsigned int ofs)
>> return ofs == XATTR_NODE_OFFSET;
>> }
>>
>> -static inline bool __allow_reserved_blocks(struct f2fs_sb_info *sbi,
>> +static inline bool __allow_reserved_root(struct f2fs_sb_info *sbi,
>> struct inode *inode, bool cap)
>> {
>> if (!inode)
>> @@ -2380,7 +2382,7 @@ static inline unsigned int get_available_block_count(struct f2fs_sb_info *sbi,
>> avail_user_block_count = sbi->user_block_count -
>> sbi->current_reserved_blocks;
>>
>> - if (test_opt(sbi, RESERVE_ROOT) && !__allow_reserved_blocks(sbi, inode, cap))
>> + if (test_opt(sbi, RESERVE_ROOT) && !__allow_reserved_root(sbi, inode, cap))
>> avail_user_block_count -= F2FS_OPTION(sbi).root_reserved_blocks;
>>
>> if (unlikely(is_sbi_flag_set(sbi, SBI_CP_DISABLED))) {
>> @@ -2738,7 +2740,7 @@ static inline int inc_valid_node_count(struct f2fs_sb_info *sbi,
>> struct inode *inode, bool is_inode)
>> {
>> block_t valid_block_count;
>> - unsigned int valid_node_count;
>> + unsigned int valid_node_count, avail_user_node_count;
>> unsigned int avail_user_block_count;
>> int err;
>>
>> @@ -2767,8 +2769,12 @@ static inline int inc_valid_node_count(struct f2fs_sb_info *sbi,
>> goto enospc;
>> }
>>
>> + avail_user_node_count = sbi->total_node_count - F2FS_RESERVED_NODE_NUM;
>> + if (test_opt(sbi, RESERVE_NODE) &&
>> + !__allow_reserved_root(sbi, inode, false))
>
> Chunhai,
>
> Do we need to pass cap=true to __allow_reserved_root()?
>
> In addition, do we need to change cap as well for below statement?
>
> avail_user_block_count = get_available_block_count(sbi, inode, false);
I meant something like this? not sure.
---
fs/f2fs/f2fs.h | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h
index 3804b70e5a28..d0aa5766ea66 100644
--- a/fs/f2fs/f2fs.h
+++ b/fs/f2fs/f2fs.h
@@ -2805,7 +2805,8 @@ static inline int inc_valid_node_count(struct f2fs_sb_info *sbi,
spin_lock(&sbi->stat_lock);
valid_block_count = sbi->total_valid_block_count + 1;
- avail_user_block_count = get_available_block_count(sbi, inode, false);
+ avail_user_block_count = get_available_block_count(sbi, inode,
+ test_opt(sbi, RESERVE_NODE));
if (unlikely(valid_block_count > avail_user_block_count)) {
spin_unlock(&sbi->stat_lock);
@@ -2814,7 +2815,7 @@ static inline int inc_valid_node_count(struct f2fs_sb_info *sbi,
avail_user_node_count = sbi->total_node_count - F2FS_RESERVED_NODE_NUM;
if (test_opt(sbi, RESERVE_NODE) &&
- !__allow_reserved_root(sbi, inode, false))
+ !__allow_reserved_root(sbi, inode, true))
avail_user_node_count -= F2FS_OPTION(sbi).root_reserved_nodes;
valid_node_count = sbi->total_valid_node_count + 1;
if (unlikely(valid_node_count > avail_user_node_count)) {
--
2.49.0
Thanks,
>
> Thanks,
>
>> + avail_user_node_count -= F2FS_OPTION(sbi).root_reserved_nodes;
>> valid_node_count = sbi->total_valid_node_count + 1;
>> - if (unlikely(valid_node_count > sbi->total_node_count)) {
>> + if (unlikely(valid_node_count > avail_user_node_count)) {
>> spin_unlock(&sbi->stat_lock);
>> goto enospc;
>> }
>> diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c
>> index 30c038413040..a24e855a38ed 100644
>> --- a/fs/f2fs/super.c
>> +++ b/fs/f2fs/super.c
>> @@ -143,6 +143,7 @@ enum {
>> Opt_extent_cache,
>> Opt_data_flush,
>> Opt_reserve_root,
>> + Opt_reserve_node,
>> Opt_resgid,
>> Opt_resuid,
>> Opt_mode,
>> @@ -265,6 +266,7 @@ static const struct fs_parameter_spec f2fs_param_specs[] = {
>> fsparam_flag_no("extent_cache", Opt_extent_cache),
>> fsparam_flag("data_flush", Opt_data_flush),
>> fsparam_u32("reserve_root", Opt_reserve_root),
>> + fsparam_u32("reserve_node", Opt_reserve_node),
>> fsparam_gid("resgid", Opt_resgid),
>> fsparam_uid("resuid", Opt_resuid),
>> fsparam_enum("mode", Opt_mode, f2fs_param_mode),
>> @@ -336,6 +338,7 @@ static match_table_t f2fs_checkpoint_tokens = {
>> #define F2FS_SPEC_discard_unit (1 << 21)
>> #define F2FS_SPEC_memory_mode (1 << 22)
>> #define F2FS_SPEC_errors (1 << 23)
>> +#define F2FS_SPEC_reserve_node (1 << 24)
>>
>> struct f2fs_fs_context {
>> struct f2fs_mount_info info;
>> @@ -437,22 +440,30 @@ static void f2fs_destroy_casefold_cache(void) { }
>>
>> static inline void limit_reserve_root(struct f2fs_sb_info *sbi)
>> {
>> - block_t limit = min((sbi->user_block_count >> 3),
>> + block_t block_limit = min((sbi->user_block_count >> 3),
>> sbi->user_block_count - sbi->reserved_blocks);
>> + block_t node_limit = sbi->total_node_count >> 3;
>>
>> /* limit is 12.5% */
>> if (test_opt(sbi, RESERVE_ROOT) &&
>> - F2FS_OPTION(sbi).root_reserved_blocks > limit) {
>> - F2FS_OPTION(sbi).root_reserved_blocks = limit;
>> + F2FS_OPTION(sbi).root_reserved_blocks > block_limit) {
>> + F2FS_OPTION(sbi).root_reserved_blocks = block_limit;
>> f2fs_info(sbi, "Reduce reserved blocks for root = %u",
>> F2FS_OPTION(sbi).root_reserved_blocks);
>> }
>> - if (!test_opt(sbi, RESERVE_ROOT) &&
>> + if (test_opt(sbi, RESERVE_NODE) &&
>> + F2FS_OPTION(sbi).root_reserved_nodes > node_limit) {
>> + F2FS_OPTION(sbi).root_reserved_nodes = node_limit;
>> + f2fs_info(sbi, "Reduce reserved nodes for root = %u",
>> + F2FS_OPTION(sbi).root_reserved_nodes);
>> + }
>> + if (!test_opt(sbi, RESERVE_ROOT) && !test_opt(sbi, RESERVE_NODE) &&
>> (!uid_eq(F2FS_OPTION(sbi).s_resuid,
>> make_kuid(&init_user_ns, F2FS_DEF_RESUID)) ||
>> !gid_eq(F2FS_OPTION(sbi).s_resgid,
>> make_kgid(&init_user_ns, F2FS_DEF_RESGID))))
>> - f2fs_info(sbi, "Ignore s_resuid=%u, s_resgid=%u w/o reserve_root",
>> + f2fs_info(sbi, "Ignore s_resuid=%u, s_resgid=%u w/o reserve_root"
>> + " and reserve_node",
>> from_kuid_munged(&init_user_ns,
>> F2FS_OPTION(sbi).s_resuid),
>> from_kgid_munged(&init_user_ns,
>> @@ -841,6 +852,11 @@ static int f2fs_parse_param(struct fs_context *fc, struct fs_parameter *param)
>> F2FS_CTX_INFO(ctx).root_reserved_blocks = result.uint_32;
>> ctx->spec_mask |= F2FS_SPEC_reserve_root;
>> break;
>> + case Opt_reserve_node:
>> + ctx_set_opt(ctx, F2FS_MOUNT_RESERVE_NODE);
>> + F2FS_CTX_INFO(ctx).root_reserved_nodes = result.uint_32;
>> + ctx->spec_mask |= F2FS_SPEC_reserve_node;
>> + break;
>> case Opt_resuid:
>> F2FS_CTX_INFO(ctx).s_resuid = result.uid;
>> ctx->spec_mask |= F2FS_SPEC_resuid;
>> @@ -1424,6 +1440,14 @@ static int f2fs_check_opt_consistency(struct fs_context *fc,
>> ctx_clear_opt(ctx, F2FS_MOUNT_RESERVE_ROOT);
>> ctx->opt_mask &= ~F2FS_MOUNT_RESERVE_ROOT;
>> }
>> + if (test_opt(sbi, RESERVE_NODE) &&
>> + (ctx->opt_mask & F2FS_MOUNT_RESERVE_NODE) &&
>> + ctx_test_opt(ctx, F2FS_MOUNT_RESERVE_NODE)) {
>> + f2fs_info(sbi, "Preserve previous reserve_node=%u",
>> + F2FS_OPTION(sbi).root_reserved_nodes);
>> + ctx_clear_opt(ctx, F2FS_MOUNT_RESERVE_NODE);
>> + ctx->opt_mask &= ~F2FS_MOUNT_RESERVE_NODE;
>> + }
>>
>> err = f2fs_check_test_dummy_encryption(fc, sb);
>> if (err)
>> @@ -1623,6 +1647,9 @@ static void f2fs_apply_options(struct fs_context *fc, struct super_block *sb)
>> if (ctx->spec_mask & F2FS_SPEC_reserve_root)
>> F2FS_OPTION(sbi).root_reserved_blocks =
>> F2FS_CTX_INFO(ctx).root_reserved_blocks;
>> + if (ctx->spec_mask & F2FS_SPEC_reserve_node)
>> + F2FS_OPTION(sbi).root_reserved_nodes =
>> + F2FS_CTX_INFO(ctx).root_reserved_nodes;
>> if (ctx->spec_mask & F2FS_SPEC_resgid)
>> F2FS_OPTION(sbi).s_resgid = F2FS_CTX_INFO(ctx).s_resgid;
>> if (ctx->spec_mask & F2FS_SPEC_resuid)
>> @@ -2342,9 +2369,11 @@ static int f2fs_show_options(struct seq_file *seq, struct dentry *root)
>> else if (F2FS_OPTION(sbi).fs_mode == FS_MODE_FRAGMENT_BLK)
>> seq_puts(seq, "fragment:block");
>> seq_printf(seq, ",active_logs=%u", F2FS_OPTION(sbi).active_logs);
>> - if (test_opt(sbi, RESERVE_ROOT))
>> - seq_printf(seq, ",reserve_root=%u,resuid=%u,resgid=%u",
>> + if (test_opt(sbi, RESERVE_ROOT) || test_opt(sbi, RESERVE_NODE))
>> + seq_printf(seq, ",reserve_root=%u,reserve_node=%u,resuid=%u,"
>> + "resgid=%u",
>> F2FS_OPTION(sbi).root_reserved_blocks,
>> + F2FS_OPTION(sbi).root_reserved_nodes,
>> from_kuid_munged(&init_user_ns,
>> F2FS_OPTION(sbi).s_resuid),
>> from_kgid_munged(&init_user_ns,
>
^ permalink raw reply related [flat|nested] 7+ messages in thread
* Re: [PATCH v4] f2fs: add reserved nodes for privileged users
2025-08-05 2:38 ` Jaegeuk Kim
@ 2025-08-06 8:56 ` Chunhai Guo
0 siblings, 0 replies; 7+ messages in thread
From: Chunhai Guo @ 2025-08-06 8:56 UTC (permalink / raw)
To: Jaegeuk Kim, Chunhai Guo
Cc: Chao Yu, linux-f2fs-devel@lists.sourceforge.net,
linux-kernel@vger.kernel.org
在 8/5/2025 10:38 AM, Jaegeuk Kim 写道:
> On 07/31, Chunhai Guo wrote:
>> 在 7/31/2025 4:46 PM, Chao Yu 写道:
>>> On 7/31/25 15:57, Chunhai Guo wrote:
>>>> This patch allows privileged users to reserve nodes via the
>>>> 'reserve_node' mount option, which is similar to the existing
>>>> 'reserve_root' option.
>>>>
>>>> "-o reserve_node=<N>" means <N> nodes are reserved for privileged
>>>> users only.
>>>>
>>>> Signed-off-by: Chunhai Guo <guochunhai@vivo.com>
>>>> ---
>>>> v3->v4: Rebase this patch on https://lore.kernel.org/linux-f2fs-devel/20250731060338.1136086-1-chao@kernel.org
>>>> v2->v3: Apply Chao's suggestion from v2.
>>>> v1->v2: Add two missing handling parts.
>>>> v1: https://lore.kernel.org/linux-f2fs-devel/20250729095238.607433-1-guochunhai@vivo.com/
>>>> ---
>>>> Documentation/filesystems/f2fs.rst | 9 ++++---
>>>> fs/f2fs/f2fs.h | 14 +++++++---
>>>> fs/f2fs/super.c | 43 +++++++++++++++++++++++++-----
>>>> 3 files changed, 52 insertions(+), 14 deletions(-)
>>>>
>>>> diff --git a/Documentation/filesystems/f2fs.rst b/Documentation/filesystems/f2fs.rst
>>>> index 03b1efa6d3b2..95dbcd7ac9a8 100644
>>>> --- a/Documentation/filesystems/f2fs.rst
>>>> +++ b/Documentation/filesystems/f2fs.rst
>>>> @@ -173,9 +173,12 @@ data_flush Enable data flushing before checkpoint in order to
>>>> persist data of regular and symlink.
>>>> reserve_root=%d Support configuring reserved space which is used for
>>>> allocation from a privileged user with specified uid or
>>>> - gid, unit: 4KB, the default limit is 0.2% of user blocks.
>>>> -resuid=%d The user ID which may use the reserved blocks.
>>>> -resgid=%d The group ID which may use the reserved blocks.
>>>> + gid, unit: 4KB, the default limit is 12.5% of user blocks.
>>>> +reserve_node=%d Support configuring reserved nodes which are used for
>>>> + allocation from a privileged user with specified uid or
>>>> + gid, the default limit is 12.5% of all nodes.
>>>> +resuid=%d The user ID which may use the reserved blocks and nodes.
>>>> +resgid=%d The group ID which may use the reserved blocks and nodes.
>>>> fault_injection=%d Enable fault injection in all supported types with
>>>> specified injection rate.
>>>> fault_type=%d Support configuring fault injection type, should be
>>>> diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h
>>>> index eb372af22edc..b9676ef16246 100644
>>>> --- a/fs/f2fs/f2fs.h
>>>> +++ b/fs/f2fs/f2fs.h
>>>> @@ -131,6 +131,7 @@ extern const char *f2fs_fault_name[FAULT_MAX];
>>>> * string rather than using the MS_LAZYTIME flag, so this must remain.
>>>> */
>>>> #define F2FS_MOUNT_LAZYTIME 0x40000000
>>>> +#define F2FS_MOUNT_RESERVE_NODE 0x80000000
>>>>
>>>> #define F2FS_OPTION(sbi) ((sbi)->mount_opt)
>>>> #define clear_opt(sbi, option) (F2FS_OPTION(sbi).opt &= ~F2FS_MOUNT_##option)
>>>> @@ -172,6 +173,7 @@ struct f2fs_rwsem {
>>>> struct f2fs_mount_info {
>>>> unsigned int opt;
>>>> block_t root_reserved_blocks; /* root reserved blocks */
>>>> + block_t root_reserved_nodes; /* root reserved nodes */
>>>> kuid_t s_resuid; /* reserved blocks for uid */
>>>> kgid_t s_resgid; /* reserved blocks for gid */
>>>> int active_logs; /* # of active logs */
>>>> @@ -2355,7 +2357,7 @@ static inline bool f2fs_has_xattr_block(unsigned int ofs)
>>>> return ofs == XATTR_NODE_OFFSET;
>>>> }
>>>>
>>>> -static inline bool __allow_reserved_blocks(struct f2fs_sb_info *sbi,
>>>> +static inline bool __allow_reserved_root(struct f2fs_sb_info *sbi,
>>>> struct inode *inode, bool cap)
>>>> {
>>>> if (!inode)
>>>> @@ -2380,7 +2382,7 @@ static inline unsigned int get_available_block_count(struct f2fs_sb_info *sbi,
>>>> avail_user_block_count = sbi->user_block_count -
>>>> sbi->current_reserved_blocks;
>>>>
>>>> - if (test_opt(sbi, RESERVE_ROOT) && !__allow_reserved_blocks(sbi, inode, cap))
>>>> + if (test_opt(sbi, RESERVE_ROOT) && !__allow_reserved_root(sbi, inode, cap))
>>>> avail_user_block_count -= F2FS_OPTION(sbi).root_reserved_blocks;
>>>>
>>>> if (unlikely(is_sbi_flag_set(sbi, SBI_CP_DISABLED))) {
>>>> @@ -2738,7 +2740,7 @@ static inline int inc_valid_node_count(struct f2fs_sb_info *sbi,
>>>> struct inode *inode, bool is_inode)
>>>> {
>>>> block_t valid_block_count;
>>>> - unsigned int valid_node_count;
>>>> + unsigned int valid_node_count, avail_user_node_count;
>>>> unsigned int avail_user_block_count;
>>>> int err;
>>>>
>>>> @@ -2767,8 +2769,12 @@ static inline int inc_valid_node_count(struct f2fs_sb_info *sbi,
>>>> goto enospc;
>>>> }
>>>>
>>>> + avail_user_node_count = sbi->total_node_count - F2FS_RESERVED_NODE_NUM;
>>>> + if (test_opt(sbi, RESERVE_NODE) &&
>>>> + !__allow_reserved_root(sbi, inode, false))
>>> Chunhai,
>>>
>>> Do we need to pass cap=true to __allow_reserved_root()?
>>>
>>> In addition, do we need to change cap as well for below statement?
>>>
>>> avail_user_block_count = get_available_block_count(sbi, inode, false);
>>
>> Hi Jaegeuk,
>>
>>
>> Based on the description in commit a90a0884ac75 ("f2fs: check
>> cap_resource only for data blocks"), it seems that inode or node blocks
>> don't need to perform a check on the cap_resource. I'm not certain about
>> the reasoning behind this. Could you please help to clarify it?
> I don't quite remember as I had to fix the selinx issue long long time ago.
Got it. It seems hard to clarify the reason back then.
Thanks
>>
>> Thanks,
>>
>>> Thanks,
>>>
>>>> + avail_user_node_count -= F2FS_OPTION(sbi).root_reserved_nodes;
>>>> valid_node_count = sbi->total_valid_node_count + 1;
>>>> - if (unlikely(valid_node_count > sbi->total_node_count)) {
>>>> + if (unlikely(valid_node_count > avail_user_node_count)) {
>>>> spin_unlock(&sbi->stat_lock);
>>>> goto enospc;
>>>> }
>>>> diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c
>>>> index 30c038413040..a24e855a38ed 100644
>>>> --- a/fs/f2fs/super.c
>>>> +++ b/fs/f2fs/super.c
>>>> @@ -143,6 +143,7 @@ enum {
>>>> Opt_extent_cache,
>>>> Opt_data_flush,
>>>> Opt_reserve_root,
>>>> + Opt_reserve_node,
>>>> Opt_resgid,
>>>> Opt_resuid,
>>>> Opt_mode,
>>>> @@ -265,6 +266,7 @@ static const struct fs_parameter_spec f2fs_param_specs[] = {
>>>> fsparam_flag_no("extent_cache", Opt_extent_cache),
>>>> fsparam_flag("data_flush", Opt_data_flush),
>>>> fsparam_u32("reserve_root", Opt_reserve_root),
>>>> + fsparam_u32("reserve_node", Opt_reserve_node),
>>>> fsparam_gid("resgid", Opt_resgid),
>>>> fsparam_uid("resuid", Opt_resuid),
>>>> fsparam_enum("mode", Opt_mode, f2fs_param_mode),
>>>> @@ -336,6 +338,7 @@ static match_table_t f2fs_checkpoint_tokens = {
>>>> #define F2FS_SPEC_discard_unit (1 << 21)
>>>> #define F2FS_SPEC_memory_mode (1 << 22)
>>>> #define F2FS_SPEC_errors (1 << 23)
>>>> +#define F2FS_SPEC_reserve_node (1 << 24)
>>>>
>>>> struct f2fs_fs_context {
>>>> struct f2fs_mount_info info;
>>>> @@ -437,22 +440,30 @@ static void f2fs_destroy_casefold_cache(void) { }
>>>>
>>>> static inline void limit_reserve_root(struct f2fs_sb_info *sbi)
>>>> {
>>>> - block_t limit = min((sbi->user_block_count >> 3),
>>>> + block_t block_limit = min((sbi->user_block_count >> 3),
>>>> sbi->user_block_count - sbi->reserved_blocks);
>>>> + block_t node_limit = sbi->total_node_count >> 3;
>>>>
>>>> /* limit is 12.5% */
>>>> if (test_opt(sbi, RESERVE_ROOT) &&
>>>> - F2FS_OPTION(sbi).root_reserved_blocks > limit) {
>>>> - F2FS_OPTION(sbi).root_reserved_blocks = limit;
>>>> + F2FS_OPTION(sbi).root_reserved_blocks > block_limit) {
>>>> + F2FS_OPTION(sbi).root_reserved_blocks = block_limit;
>>>> f2fs_info(sbi, "Reduce reserved blocks for root = %u",
>>>> F2FS_OPTION(sbi).root_reserved_blocks);
>>>> }
>>>> - if (!test_opt(sbi, RESERVE_ROOT) &&
>>>> + if (test_opt(sbi, RESERVE_NODE) &&
>>>> + F2FS_OPTION(sbi).root_reserved_nodes > node_limit) {
>>>> + F2FS_OPTION(sbi).root_reserved_nodes = node_limit;
>>>> + f2fs_info(sbi, "Reduce reserved nodes for root = %u",
>>>> + F2FS_OPTION(sbi).root_reserved_nodes);
>>>> + }
>>>> + if (!test_opt(sbi, RESERVE_ROOT) && !test_opt(sbi, RESERVE_NODE) &&
>>>> (!uid_eq(F2FS_OPTION(sbi).s_resuid,
>>>> make_kuid(&init_user_ns, F2FS_DEF_RESUID)) ||
>>>> !gid_eq(F2FS_OPTION(sbi).s_resgid,
>>>> make_kgid(&init_user_ns, F2FS_DEF_RESGID))))
>>>> - f2fs_info(sbi, "Ignore s_resuid=%u, s_resgid=%u w/o reserve_root",
>>>> + f2fs_info(sbi, "Ignore s_resuid=%u, s_resgid=%u w/o reserve_root"
>>>> + " and reserve_node",
>>>> from_kuid_munged(&init_user_ns,
>>>> F2FS_OPTION(sbi).s_resuid),
>>>> from_kgid_munged(&init_user_ns,
>>>> @@ -841,6 +852,11 @@ static int f2fs_parse_param(struct fs_context *fc, struct fs_parameter *param)
>>>> F2FS_CTX_INFO(ctx).root_reserved_blocks = result.uint_32;
>>>> ctx->spec_mask |= F2FS_SPEC_reserve_root;
>>>> break;
>>>> + case Opt_reserve_node:
>>>> + ctx_set_opt(ctx, F2FS_MOUNT_RESERVE_NODE);
>>>> + F2FS_CTX_INFO(ctx).root_reserved_nodes = result.uint_32;
>>>> + ctx->spec_mask |= F2FS_SPEC_reserve_node;
>>>> + break;
>>>> case Opt_resuid:
>>>> F2FS_CTX_INFO(ctx).s_resuid = result.uid;
>>>> ctx->spec_mask |= F2FS_SPEC_resuid;
>>>> @@ -1424,6 +1440,14 @@ static int f2fs_check_opt_consistency(struct fs_context *fc,
>>>> ctx_clear_opt(ctx, F2FS_MOUNT_RESERVE_ROOT);
>>>> ctx->opt_mask &= ~F2FS_MOUNT_RESERVE_ROOT;
>>>> }
>>>> + if (test_opt(sbi, RESERVE_NODE) &&
>>>> + (ctx->opt_mask & F2FS_MOUNT_RESERVE_NODE) &&
>>>> + ctx_test_opt(ctx, F2FS_MOUNT_RESERVE_NODE)) {
>>>> + f2fs_info(sbi, "Preserve previous reserve_node=%u",
>>>> + F2FS_OPTION(sbi).root_reserved_nodes);
>>>> + ctx_clear_opt(ctx, F2FS_MOUNT_RESERVE_NODE);
>>>> + ctx->opt_mask &= ~F2FS_MOUNT_RESERVE_NODE;
>>>> + }
>>>>
>>>> err = f2fs_check_test_dummy_encryption(fc, sb);
>>>> if (err)
>>>> @@ -1623,6 +1647,9 @@ static void f2fs_apply_options(struct fs_context *fc, struct super_block *sb)
>>>> if (ctx->spec_mask & F2FS_SPEC_reserve_root)
>>>> F2FS_OPTION(sbi).root_reserved_blocks =
>>>> F2FS_CTX_INFO(ctx).root_reserved_blocks;
>>>> + if (ctx->spec_mask & F2FS_SPEC_reserve_node)
>>>> + F2FS_OPTION(sbi).root_reserved_nodes =
>>>> + F2FS_CTX_INFO(ctx).root_reserved_nodes;
>>>> if (ctx->spec_mask & F2FS_SPEC_resgid)
>>>> F2FS_OPTION(sbi).s_resgid = F2FS_CTX_INFO(ctx).s_resgid;
>>>> if (ctx->spec_mask & F2FS_SPEC_resuid)
>>>> @@ -2342,9 +2369,11 @@ static int f2fs_show_options(struct seq_file *seq, struct dentry *root)
>>>> else if (F2FS_OPTION(sbi).fs_mode == FS_MODE_FRAGMENT_BLK)
>>>> seq_puts(seq, "fragment:block");
>>>> seq_printf(seq, ",active_logs=%u", F2FS_OPTION(sbi).active_logs);
>>>> - if (test_opt(sbi, RESERVE_ROOT))
>>>> - seq_printf(seq, ",reserve_root=%u,resuid=%u,resgid=%u",
>>>> + if (test_opt(sbi, RESERVE_ROOT) || test_opt(sbi, RESERVE_NODE))
>>>> + seq_printf(seq, ",reserve_root=%u,reserve_node=%u,resuid=%u,"
>>>> + "resgid=%u",
>>>> F2FS_OPTION(sbi).root_reserved_blocks,
>>>> + F2FS_OPTION(sbi).root_reserved_nodes,
>>>> from_kuid_munged(&init_user_ns,
>>>> F2FS_OPTION(sbi).s_resuid),
>>>> from_kgid_munged(&init_user_ns,
>>
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [PATCH v4] f2fs: add reserved nodes for privileged users
2025-08-05 8:56 ` Chao Yu
@ 2025-08-06 8:56 ` Chunhai Guo
0 siblings, 0 replies; 7+ messages in thread
From: Chunhai Guo @ 2025-08-06 8:56 UTC (permalink / raw)
To: Chao Yu, Chunhai Guo, jaegeuk@kernel.org
Cc: linux-f2fs-devel@lists.sourceforge.net,
linux-kernel@vger.kernel.org
在 8/5/2025 4:56 PM, Chao Yu 写道:
> On 7/31/25 16:46, Chao Yu wrote:
>> On 7/31/25 15:57, Chunhai Guo wrote:
>>> This patch allows privileged users to reserve nodes via the
>>> 'reserve_node' mount option, which is similar to the existing
>>> 'reserve_root' option.
>>>
>>> "-o reserve_node=<N>" means <N> nodes are reserved for privileged
>>> users only.
>>>
>>> Signed-off-by: Chunhai Guo <guochunhai@vivo.com>
>>> ---
>>> v3->v4: Rebase this patch on https://lore.kernel.org/linux-f2fs-devel/20250731060338.1136086-1-chao@kernel.org
>>> v2->v3: Apply Chao's suggestion from v2.
>>> v1->v2: Add two missing handling parts.
>>> v1: https://lore.kernel.org/linux-f2fs-devel/20250729095238.607433-1-guochunhai@vivo.com/
>>> ---
>>> Documentation/filesystems/f2fs.rst | 9 ++++---
>>> fs/f2fs/f2fs.h | 14 +++++++---
>>> fs/f2fs/super.c | 43 +++++++++++++++++++++++++-----
>>> 3 files changed, 52 insertions(+), 14 deletions(-)
>>>
>>> diff --git a/Documentation/filesystems/f2fs.rst b/Documentation/filesystems/f2fs.rst
>>> index 03b1efa6d3b2..95dbcd7ac9a8 100644
>>> --- a/Documentation/filesystems/f2fs.rst
>>> +++ b/Documentation/filesystems/f2fs.rst
>>> @@ -173,9 +173,12 @@ data_flush Enable data flushing before checkpoint in order to
>>> persist data of regular and symlink.
>>> reserve_root=%d Support configuring reserved space which is used for
>>> allocation from a privileged user with specified uid or
>>> - gid, unit: 4KB, the default limit is 0.2% of user blocks.
>>> -resuid=%d The user ID which may use the reserved blocks.
>>> -resgid=%d The group ID which may use the reserved blocks.
>>> + gid, unit: 4KB, the default limit is 12.5% of user blocks.
>>> +reserve_node=%d Support configuring reserved nodes which are used for
>>> + allocation from a privileged user with specified uid or
>>> + gid, the default limit is 12.5% of all nodes.
>>> +resuid=%d The user ID which may use the reserved blocks and nodes.
>>> +resgid=%d The group ID which may use the reserved blocks and nodes.
>>> fault_injection=%d Enable fault injection in all supported types with
>>> specified injection rate.
>>> fault_type=%d Support configuring fault injection type, should be
>>> diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h
>>> index eb372af22edc..b9676ef16246 100644
>>> --- a/fs/f2fs/f2fs.h
>>> +++ b/fs/f2fs/f2fs.h
>>> @@ -131,6 +131,7 @@ extern const char *f2fs_fault_name[FAULT_MAX];
>>> * string rather than using the MS_LAZYTIME flag, so this must remain.
>>> */
>>> #define F2FS_MOUNT_LAZYTIME 0x40000000
>>> +#define F2FS_MOUNT_RESERVE_NODE 0x80000000
>>>
>>> #define F2FS_OPTION(sbi) ((sbi)->mount_opt)
>>> #define clear_opt(sbi, option) (F2FS_OPTION(sbi).opt &= ~F2FS_MOUNT_##option)
>>> @@ -172,6 +173,7 @@ struct f2fs_rwsem {
>>> struct f2fs_mount_info {
>>> unsigned int opt;
>>> block_t root_reserved_blocks; /* root reserved blocks */
>>> + block_t root_reserved_nodes; /* root reserved nodes */
>>> kuid_t s_resuid; /* reserved blocks for uid */
>>> kgid_t s_resgid; /* reserved blocks for gid */
>>> int active_logs; /* # of active logs */
>>> @@ -2355,7 +2357,7 @@ static inline bool f2fs_has_xattr_block(unsigned int ofs)
>>> return ofs == XATTR_NODE_OFFSET;
>>> }
>>>
>>> -static inline bool __allow_reserved_blocks(struct f2fs_sb_info *sbi,
>>> +static inline bool __allow_reserved_root(struct f2fs_sb_info *sbi,
>>> struct inode *inode, bool cap)
>>> {
>>> if (!inode)
>>> @@ -2380,7 +2382,7 @@ static inline unsigned int get_available_block_count(struct f2fs_sb_info *sbi,
>>> avail_user_block_count = sbi->user_block_count -
>>> sbi->current_reserved_blocks;
>>>
>>> - if (test_opt(sbi, RESERVE_ROOT) && !__allow_reserved_blocks(sbi, inode, cap))
>>> + if (test_opt(sbi, RESERVE_ROOT) && !__allow_reserved_root(sbi, inode, cap))
>>> avail_user_block_count -= F2FS_OPTION(sbi).root_reserved_blocks;
>>>
>>> if (unlikely(is_sbi_flag_set(sbi, SBI_CP_DISABLED))) {
>>> @@ -2738,7 +2740,7 @@ static inline int inc_valid_node_count(struct f2fs_sb_info *sbi,
>>> struct inode *inode, bool is_inode)
>>> {
>>> block_t valid_block_count;
>>> - unsigned int valid_node_count;
>>> + unsigned int valid_node_count, avail_user_node_count;
>>> unsigned int avail_user_block_count;
>>> int err;
>>>
>>> @@ -2767,8 +2769,12 @@ static inline int inc_valid_node_count(struct f2fs_sb_info *sbi,
>>> goto enospc;
>>> }
>>>
>>> + avail_user_node_count = sbi->total_node_count - F2FS_RESERVED_NODE_NUM;
>>> + if (test_opt(sbi, RESERVE_NODE) &&
>>> + !__allow_reserved_root(sbi, inode, false))
>> Chunhai,
>>
>> Do we need to pass cap=true to __allow_reserved_root()?
>>
>> In addition, do we need to change cap as well for below statement?
>>
>> avail_user_block_count = get_available_block_count(sbi, inode, false);
> I meant something like this? not sure.
I think this is fine. I have tested with your modifications on an
Android device and no selinx issue has been found. I will send the v5
patch with your modifications.
Thanks,
>
> ---
> fs/f2fs/f2fs.h | 5 +++--
> 1 file changed, 3 insertions(+), 2 deletions(-)
>
> diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h
> index 3804b70e5a28..d0aa5766ea66 100644
> --- a/fs/f2fs/f2fs.h
> +++ b/fs/f2fs/f2fs.h
> @@ -2805,7 +2805,8 @@ static inline int inc_valid_node_count(struct f2fs_sb_info *sbi,
> spin_lock(&sbi->stat_lock);
>
> valid_block_count = sbi->total_valid_block_count + 1;
> - avail_user_block_count = get_available_block_count(sbi, inode, false);
> + avail_user_block_count = get_available_block_count(sbi, inode,
> + test_opt(sbi, RESERVE_NODE));
>
> if (unlikely(valid_block_count > avail_user_block_count)) {
> spin_unlock(&sbi->stat_lock);
> @@ -2814,7 +2815,7 @@ static inline int inc_valid_node_count(struct f2fs_sb_info *sbi,
>
> avail_user_node_count = sbi->total_node_count - F2FS_RESERVED_NODE_NUM;
> if (test_opt(sbi, RESERVE_NODE) &&
> - !__allow_reserved_root(sbi, inode, false))
> + !__allow_reserved_root(sbi, inode, true))
> avail_user_node_count -= F2FS_OPTION(sbi).root_reserved_nodes;
> valid_node_count = sbi->total_valid_node_count + 1;
> if (unlikely(valid_node_count > avail_user_node_count)) {
^ permalink raw reply [flat|nested] 7+ messages in thread
end of thread, other threads:[~2025-08-06 8:56 UTC | newest]
Thread overview: 7+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-07-31 7:57 [PATCH v4] f2fs: add reserved nodes for privileged users Chunhai Guo
2025-07-31 8:46 ` Chao Yu
2025-07-31 10:53 ` Chunhai Guo
2025-08-05 2:38 ` Jaegeuk Kim
2025-08-06 8:56 ` Chunhai Guo
2025-08-05 8:56 ` Chao Yu
2025-08-06 8:56 ` Chunhai Guo
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).