Documentation/filesystems/f2fs.rst | 9 ++++--- fs/f2fs/f2fs.h | 17 +++++++----- fs/f2fs/super.c | 43 +++++++++++++++++++++++++----- 3 files changed, 53 insertions(+), 16 deletions(-)
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>
---
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 | 17 +++++++-----
fs/f2fs/super.c | 43 +++++++++++++++++++++++++-----
3 files changed, 53 insertions(+), 16 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 97c1a2a3fbd7..70f32c118dac 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,13 +2357,11 @@ 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)
return true;
- if (!test_opt(sbi, RESERVE_ROOT))
- return false;
if (IS_NOQUOTA(inode))
return true;
if (uid_eq(F2FS_OPTION(sbi).s_resuid, current_fsuid()))
@@ -2382,7 +2382,8 @@ 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 (!__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))) {
@@ -2740,7 +2741,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;
@@ -2769,8 +2770,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
On 7/30/25 22:15, 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> > --- > 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 | 17 +++++++----- > fs/f2fs/super.c | 43 +++++++++++++++++++++++++----- > 3 files changed, 53 insertions(+), 16 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 97c1a2a3fbd7..70f32c118dac 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,13 +2357,11 @@ 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) > return true; > - if (!test_opt(sbi, RESERVE_ROOT)) > - return false; Oh, I just notice this condition is wrong, let me submit a patch to fix this, as I hope it can go into stable correctly. Thanks, > if (IS_NOQUOTA(inode)) > return true; > if (uid_eq(F2FS_OPTION(sbi).s_resuid, current_fsuid())) > @@ -2382,7 +2382,8 @@ 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 (!__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))) { > @@ -2740,7 +2741,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; > > @@ -2769,8 +2770,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,
在 7/31/2025 10:56 AM, Chao Yu 写道: > On 7/30/25 22:15, 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> >> --- >> 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 | 17 +++++++----- >> fs/f2fs/super.c | 43 +++++++++++++++++++++++++----- >> 3 files changed, 53 insertions(+), 16 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 97c1a2a3fbd7..70f32c118dac 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,13 +2357,11 @@ 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) >> return true; >> - if (!test_opt(sbi, RESERVE_ROOT)) >> - return false; > Oh, I just notice this condition is wrong, let me submit a patch > to fix this, as I hope it can go into stable correctly. Got it. I have send the v4 patch base on your patch. Please have a check. Thanks, > > Thanks, > >> if (IS_NOQUOTA(inode)) >> return true; >> if (uid_eq(F2FS_OPTION(sbi).s_resuid, current_fsuid())) >> @@ -2382,7 +2382,8 @@ 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 (!__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))) { >> @@ -2740,7 +2741,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; >> >> @@ -2769,8 +2770,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,
© 2016 - 2025 Red Hat, Inc.