From 7e30d2a5eb0b2d5853f06cb8a2d44937d80a6bd6 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Sat, 1 Jun 2019 18:56:53 -0400 Subject: make shmem_fill_super() static ... have callers use shmem_mount() Signed-off-by: Al Viro --- mm/shmem.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'mm/shmem.c') diff --git a/mm/shmem.c b/mm/shmem.c index 2bed4761f279..40f574c06375 100644 --- a/mm/shmem.c +++ b/mm/shmem.c @@ -3547,7 +3547,7 @@ static void shmem_put_super(struct super_block *sb) sb->s_fs_info = NULL; } -int shmem_fill_super(struct super_block *sb, void *data, int silent) +static int shmem_fill_super(struct super_block *sb, void *data, int silent) { struct inode *inode; struct shmem_sb_info *sbinfo; @@ -3759,7 +3759,7 @@ static const struct vm_operations_struct shmem_vm_ops = { #endif }; -static struct dentry *shmem_mount(struct file_system_type *fs_type, +struct dentry *shmem_mount(struct file_system_type *fs_type, int flags, const char *dev_name, void *data) { return mount_nodev(fs_type, flags, data, shmem_fill_super); -- cgit v1.2.2 From 0b5071dd323da2e277bce7e68749dc0a5fba4703 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Sun, 8 Sep 2019 18:49:18 -0400 Subject: shmem_parse_options(): use a separate structure to keep the results ... and copy the data from it into sbinfo in the callers. For use by remount we need to keep track whether there'd been options setting max_inodes, max_blocks and huge resp. and do the sanity checks (and copying) only if such options had been seen. uid/gid/mode is ignored by remount and NULL mpol is already explicitly treated as "ignore it", so we don't need to keep track of those. Note: theoretically, mpol_parse_string() may return NULL not in case of error (for default policy), so the assumption that NULL mpol means "change nothing" is incorrect. However, that's the mainline behaviour and any changes belong in a separate patch. If we go for that, we'll need to keep track of having encountered mpol= option too. [changes in remount logics from Hugh Dickins folded] Signed-off-by: Al Viro --- mm/shmem.c | 110 +++++++++++++++++++++++++++++++++++++------------------------ 1 file changed, 67 insertions(+), 43 deletions(-) (limited to 'mm/shmem.c') diff --git a/mm/shmem.c b/mm/shmem.c index 40f574c06375..98aa066c00f7 100644 --- a/mm/shmem.c +++ b/mm/shmem.c @@ -107,6 +107,20 @@ struct shmem_falloc { pgoff_t nr_unswapped; /* how often writepage refused to swap out */ }; +struct shmem_options { + unsigned long long blocks; + unsigned long long inodes; + struct mempolicy *mpol; + kuid_t uid; + kgid_t gid; + umode_t mode; + int huge; + int seen; +#define SHMEM_SEEN_BLOCKS 1 +#define SHMEM_SEEN_INODES 2 +#define SHMEM_SEEN_HUGE 4 +}; + #ifdef CONFIG_TMPFS static unsigned long shmem_default_max_blocks(void) { @@ -3349,8 +3363,7 @@ static const struct export_operations shmem_export_ops = { .fh_to_dentry = shmem_fh_to_dentry, }; -static int shmem_parse_options(char *options, struct shmem_sb_info *sbinfo, - bool remount) +static int shmem_parse_options(char *options, struct shmem_options *ctx) { char *this_char, *value, *rest; struct mempolicy *mpol = NULL; @@ -3395,39 +3408,35 @@ static int shmem_parse_options(char *options, struct shmem_sb_info *sbinfo, } if (*rest) goto bad_val; - sbinfo->max_blocks = - DIV_ROUND_UP(size, PAGE_SIZE); + ctx->blocks = DIV_ROUND_UP(size, PAGE_SIZE); + ctx->seen |= SHMEM_SEEN_BLOCKS; } else if (!strcmp(this_char,"nr_blocks")) { - sbinfo->max_blocks = memparse(value, &rest); + ctx->blocks = memparse(value, &rest); if (*rest) goto bad_val; + ctx->seen |= SHMEM_SEEN_BLOCKS; } else if (!strcmp(this_char,"nr_inodes")) { - sbinfo->max_inodes = memparse(value, &rest); + ctx->inodes = memparse(value, &rest); if (*rest) goto bad_val; + ctx->seen |= SHMEM_SEEN_INODES; } else if (!strcmp(this_char,"mode")) { - if (remount) - continue; - sbinfo->mode = simple_strtoul(value, &rest, 8) & 07777; + ctx->mode = simple_strtoul(value, &rest, 8) & 07777; if (*rest) goto bad_val; } else if (!strcmp(this_char,"uid")) { - if (remount) - continue; uid = simple_strtoul(value, &rest, 0); if (*rest) goto bad_val; - sbinfo->uid = make_kuid(current_user_ns(), uid); - if (!uid_valid(sbinfo->uid)) + ctx->uid = make_kuid(current_user_ns(), uid); + if (!uid_valid(ctx->uid)) goto bad_val; } else if (!strcmp(this_char,"gid")) { - if (remount) - continue; gid = simple_strtoul(value, &rest, 0); if (*rest) goto bad_val; - sbinfo->gid = make_kgid(current_user_ns(), gid); - if (!gid_valid(sbinfo->gid)) + ctx->gid = make_kgid(current_user_ns(), gid); + if (!gid_valid(ctx->gid)) goto bad_val; #ifdef CONFIG_TRANSPARENT_HUGE_PAGECACHE } else if (!strcmp(this_char, "huge")) { @@ -3438,7 +3447,8 @@ static int shmem_parse_options(char *options, struct shmem_sb_info *sbinfo, if (!has_transparent_hugepage() && huge != SHMEM_HUGE_NEVER) goto bad_val; - sbinfo->huge = huge; + ctx->huge = huge; + ctx->seen |= SHMEM_SEEN_HUGE; #endif #ifdef CONFIG_NUMA } else if (!strcmp(this_char,"mpol")) { @@ -3452,7 +3462,7 @@ static int shmem_parse_options(char *options, struct shmem_sb_info *sbinfo, goto error; } } - sbinfo->mpol = mpol; + ctx->mpol = mpol; return 0; bad_val: @@ -3467,42 +3477,50 @@ error: static int shmem_remount_fs(struct super_block *sb, int *flags, char *data) { struct shmem_sb_info *sbinfo = SHMEM_SB(sb); - struct shmem_sb_info config = *sbinfo; + struct shmem_options ctx = {.seen = 0}; unsigned long inodes; int error = -EINVAL; - config.mpol = NULL; - if (shmem_parse_options(data, &config, true)) + if (shmem_parse_options(data, &ctx)) return error; spin_lock(&sbinfo->stat_lock); inodes = sbinfo->max_inodes - sbinfo->free_inodes; - if (percpu_counter_compare(&sbinfo->used_blocks, config.max_blocks) > 0) - goto out; - if (config.max_inodes < inodes) - goto out; /* * Those tests disallow limited->unlimited while any are in use; * but we must separately disallow unlimited->limited, because * in that case we have no record of how much is already in use. */ - if (config.max_blocks && !sbinfo->max_blocks) - goto out; - if (config.max_inodes && !sbinfo->max_inodes) - goto out; + if ((ctx.seen & SHMEM_SEEN_BLOCKS) && ctx.blocks) { + if (!sbinfo->max_blocks) + goto out; + if (percpu_counter_compare(&sbinfo->used_blocks, + ctx.blocks) > 0) + goto out; + } + if ((ctx.seen & SHMEM_SEEN_INODES) && ctx.inodes) { + if (!sbinfo->max_inodes) + goto out; + if (ctx.inodes < inodes) + goto out; + } error = 0; - sbinfo->huge = config.huge; - sbinfo->max_blocks = config.max_blocks; - sbinfo->max_inodes = config.max_inodes; - sbinfo->free_inodes = config.max_inodes - inodes; + if (ctx.seen & SHMEM_SEEN_HUGE) + sbinfo->huge = ctx.huge; + if (ctx.seen & SHMEM_SEEN_BLOCKS) + sbinfo->max_blocks = ctx.blocks; + if (ctx.seen & SHMEM_SEEN_INODES) { + sbinfo->max_inodes = ctx.inodes; + sbinfo->free_inodes = ctx.inodes - inodes; + } /* * Preserve previous mempolicy unless mpol remount option was specified. */ - if (config.mpol) { + if (ctx.mpol) { mpol_put(sbinfo->mpol); - sbinfo->mpol = config.mpol; /* transfers initial ref */ + sbinfo->mpol = ctx.mpol; /* transfers initial ref */ } out: spin_unlock(&sbinfo->stat_lock); @@ -3551,6 +3569,9 @@ static int shmem_fill_super(struct super_block *sb, void *data, int silent) { struct inode *inode; struct shmem_sb_info *sbinfo; + struct shmem_options ctx = {.mode = 0777 | S_ISVTX, + .uid = current_fsuid(), + .gid = current_fsgid()}; int err = -ENOMEM; /* Round up to L1_CACHE_BYTES to resist false sharing */ @@ -3559,9 +3580,6 @@ static int shmem_fill_super(struct super_block *sb, void *data, int silent) if (!sbinfo) return -ENOMEM; - sbinfo->mode = 0777 | S_ISVTX; - sbinfo->uid = current_fsuid(); - sbinfo->gid = current_fsgid(); sb->s_fs_info = sbinfo; #ifdef CONFIG_TMPFS @@ -3571,9 +3589,9 @@ static int shmem_fill_super(struct super_block *sb, void *data, int silent) * but the internal instance is left unlimited. */ if (!(sb->s_flags & SB_KERNMOUNT)) { - sbinfo->max_blocks = shmem_default_max_blocks(); - sbinfo->max_inodes = shmem_default_max_inodes(); - if (shmem_parse_options(data, sbinfo, false)) { + ctx.blocks = shmem_default_max_blocks(); + ctx.inodes = shmem_default_max_inodes(); + if (shmem_parse_options(data, &ctx)) { err = -EINVAL; goto failed; } @@ -3585,11 +3603,17 @@ static int shmem_fill_super(struct super_block *sb, void *data, int silent) #else sb->s_flags |= SB_NOUSER; #endif + sbinfo->max_blocks = ctx.blocks; + sbinfo->free_inodes = sbinfo->max_inodes = ctx.inodes; + sbinfo->uid = ctx.uid; + sbinfo->gid = ctx.gid; + sbinfo->mode = ctx.mode; + sbinfo->huge = ctx.huge; + sbinfo->mpol = ctx.mpol; spin_lock_init(&sbinfo->stat_lock); if (percpu_counter_init(&sbinfo->used_blocks, 0, GFP_KERNEL)) goto failed; - sbinfo->free_inodes = sbinfo->max_inodes; spin_lock_init(&sbinfo->shrinklist_lock); INIT_LIST_HEAD(&sbinfo->shrinklist); -- cgit v1.2.2 From f6490b7fbb8216e15c0b52409b5fd9d035fe3530 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Sun, 8 Sep 2019 19:11:46 -0400 Subject: shmem_parse_options(): don't bother with mpol in separate variable just use ctx->mpol (note that callers always set ctx->mpol to NULL when calling that). Signed-off-by: Al Viro --- mm/shmem.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) (limited to 'mm/shmem.c') diff --git a/mm/shmem.c b/mm/shmem.c index 98aa066c00f7..8bca06c12b9a 100644 --- a/mm/shmem.c +++ b/mm/shmem.c @@ -3366,7 +3366,6 @@ static const struct export_operations shmem_export_ops = { static int shmem_parse_options(char *options, struct shmem_options *ctx) { char *this_char, *value, *rest; - struct mempolicy *mpol = NULL; uid_t uid; gid_t gid; @@ -3452,9 +3451,9 @@ static int shmem_parse_options(char *options, struct shmem_options *ctx) #endif #ifdef CONFIG_NUMA } else if (!strcmp(this_char,"mpol")) { - mpol_put(mpol); - mpol = NULL; - if (mpol_parse_str(value, &mpol)) + mpol_put(ctx->mpol); + ctx->mpol = NULL; + if (mpol_parse_str(value, &ctx->mpol)) goto bad_val; #endif } else { @@ -3462,14 +3461,14 @@ static int shmem_parse_options(char *options, struct shmem_options *ctx) goto error; } } - ctx->mpol = mpol; return 0; bad_val: pr_err("tmpfs: Bad value '%s' for mount option '%s'\n", value, this_char); error: - mpol_put(mpol); + mpol_put(ctx->mpol); + ctx->mpol = NULL; return 1; } -- cgit v1.2.2 From e04dc423ae2c0fc862fef6b43ed9083226375e98 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Sun, 8 Sep 2019 19:20:12 -0400 Subject: shmem_parse_options(): take handling a single option into a helper mechanical move. Signed-off-by: Al Viro --- mm/shmem.c | 148 +++++++++++++++++++++++++++++++++---------------------------- 1 file changed, 79 insertions(+), 69 deletions(-) (limited to 'mm/shmem.c') diff --git a/mm/shmem.c b/mm/shmem.c index 8bca06c12b9a..b392a8263329 100644 --- a/mm/shmem.c +++ b/mm/shmem.c @@ -3363,12 +3363,88 @@ static const struct export_operations shmem_export_ops = { .fh_to_dentry = shmem_fh_to_dentry, }; -static int shmem_parse_options(char *options, struct shmem_options *ctx) +static int shmem_parse_one(struct shmem_options *ctx, char *opt, char *value) { - char *this_char, *value, *rest; + char *rest; uid_t uid; gid_t gid; + if (!strcmp(opt, "size")) { + unsigned long long size; + size = memparse(value,&rest); + if (*rest == '%') { + size <<= PAGE_SHIFT; + size *= totalram_pages(); + do_div(size, 100); + rest++; + } + if (*rest) + goto bad_val; + ctx->blocks = DIV_ROUND_UP(size, PAGE_SIZE); + ctx->seen |= SHMEM_SEEN_BLOCKS; + } else if (!strcmp(opt, "nr_blocks")) { + ctx->blocks = memparse(value, &rest); + if (*rest) + goto bad_val; + ctx->seen |= SHMEM_SEEN_BLOCKS; + } else if (!strcmp(opt, "nr_inodes")) { + ctx->inodes = memparse(value, &rest); + if (*rest) + goto bad_val; + ctx->seen |= SHMEM_SEEN_INODES; + } else if (!strcmp(opt, "mode")) { + ctx->mode = simple_strtoul(value, &rest, 8) & 07777; + if (*rest) + goto bad_val; + } else if (!strcmp(opt, "uid")) { + uid = simple_strtoul(value, &rest, 0); + if (*rest) + goto bad_val; + ctx->uid = make_kuid(current_user_ns(), uid); + if (!uid_valid(ctx->uid)) + goto bad_val; + } else if (!strcmp(opt, "gid")) { + gid = simple_strtoul(value, &rest, 0); + if (*rest) + goto bad_val; + ctx->gid = make_kgid(current_user_ns(), gid); + if (!gid_valid(ctx->gid)) + goto bad_val; +#ifdef CONFIG_TRANSPARENT_HUGE_PAGECACHE + } else if (!strcmp(opt, "huge")) { + int huge; + huge = shmem_parse_huge(value); + if (huge < 0) + goto bad_val; + if (!has_transparent_hugepage() && + huge != SHMEM_HUGE_NEVER) + goto bad_val; + ctx->huge = huge; + ctx->seen |= SHMEM_SEEN_HUGE; +#endif +#ifdef CONFIG_NUMA + } else if (!strcmp(opt, "mpol")) { + mpol_put(ctx->mpol); + ctx->mpol = NULL; + if (mpol_parse_str(value, &ctx->mpol)) + goto bad_val; +#endif + } else { + pr_err("tmpfs: Bad mount option %s\n", opt); + return -EINVAL; + } + return 0; + +bad_val: + pr_err("tmpfs: Bad value '%s' for mount option '%s'\n", + value, opt); + return -EINVAL; +} + +static int shmem_parse_options(char *options, struct shmem_options *ctx) +{ + char *this_char, *value; + while (options != NULL) { this_char = options; for (;;) { @@ -3395,77 +3471,11 @@ static int shmem_parse_options(char *options, struct shmem_options *ctx) this_char); goto error; } - - if (!strcmp(this_char,"size")) { - unsigned long long size; - size = memparse(value,&rest); - if (*rest == '%') { - size <<= PAGE_SHIFT; - size *= totalram_pages(); - do_div(size, 100); - rest++; - } - if (*rest) - goto bad_val; - ctx->blocks = DIV_ROUND_UP(size, PAGE_SIZE); - ctx->seen |= SHMEM_SEEN_BLOCKS; - } else if (!strcmp(this_char,"nr_blocks")) { - ctx->blocks = memparse(value, &rest); - if (*rest) - goto bad_val; - ctx->seen |= SHMEM_SEEN_BLOCKS; - } else if (!strcmp(this_char,"nr_inodes")) { - ctx->inodes = memparse(value, &rest); - if (*rest) - goto bad_val; - ctx->seen |= SHMEM_SEEN_INODES; - } else if (!strcmp(this_char,"mode")) { - ctx->mode = simple_strtoul(value, &rest, 8) & 07777; - if (*rest) - goto bad_val; - } else if (!strcmp(this_char,"uid")) { - uid = simple_strtoul(value, &rest, 0); - if (*rest) - goto bad_val; - ctx->uid = make_kuid(current_user_ns(), uid); - if (!uid_valid(ctx->uid)) - goto bad_val; - } else if (!strcmp(this_char,"gid")) { - gid = simple_strtoul(value, &rest, 0); - if (*rest) - goto bad_val; - ctx->gid = make_kgid(current_user_ns(), gid); - if (!gid_valid(ctx->gid)) - goto bad_val; -#ifdef CONFIG_TRANSPARENT_HUGE_PAGECACHE - } else if (!strcmp(this_char, "huge")) { - int huge; - huge = shmem_parse_huge(value); - if (huge < 0) - goto bad_val; - if (!has_transparent_hugepage() && - huge != SHMEM_HUGE_NEVER) - goto bad_val; - ctx->huge = huge; - ctx->seen |= SHMEM_SEEN_HUGE; -#endif -#ifdef CONFIG_NUMA - } else if (!strcmp(this_char,"mpol")) { - mpol_put(ctx->mpol); - ctx->mpol = NULL; - if (mpol_parse_str(value, &ctx->mpol)) - goto bad_val; -#endif - } else { - pr_err("tmpfs: Bad mount option %s\n", this_char); + if (shmem_parse_one(ctx, this_char, value) < 0) goto error; - } } return 0; -bad_val: - pr_err("tmpfs: Bad value '%s' for mount option '%s'\n", - value, this_char); error: mpol_put(ctx->mpol); ctx->mpol = NULL; -- cgit v1.2.2 From 626c3920aeb4575f53c96b0d4ad4e651a21cbb66 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Sun, 8 Sep 2019 20:28:06 -0400 Subject: shmem_parse_one(): switch to use of fs_parse() This thing will eventually become our ->parse_param(), while shmem_parse_options() - ->parse_monolithic(). At that point shmem_parse_options() will start calling vfs_parse_fs_string(), rather than calling shmem_parse_one() directly. Signed-off-by: Al Viro --- mm/shmem.c | 183 +++++++++++++++++++++++++++++++++++++++---------------------- 1 file changed, 116 insertions(+), 67 deletions(-) (limited to 'mm/shmem.c') diff --git a/mm/shmem.c b/mm/shmem.c index b392a8263329..6a41595dd1b3 100644 --- a/mm/shmem.c +++ b/mm/shmem.c @@ -37,6 +37,7 @@ #include #include #include +#include #include /* for arch/microblaze update_mmu_cache() */ @@ -3363,15 +3364,63 @@ static const struct export_operations shmem_export_ops = { .fh_to_dentry = shmem_fh_to_dentry, }; -static int shmem_parse_one(struct shmem_options *ctx, char *opt, char *value) +enum shmem_param { + Opt_gid, + Opt_huge, + Opt_mode, + Opt_mpol, + Opt_nr_blocks, + Opt_nr_inodes, + Opt_size, + Opt_uid, +}; + +static const struct fs_parameter_spec shmem_param_specs[] = { + fsparam_u32 ("gid", Opt_gid), + fsparam_enum ("huge", Opt_huge), + fsparam_u32oct("mode", Opt_mode), + fsparam_string("mpol", Opt_mpol), + fsparam_string("nr_blocks", Opt_nr_blocks), + fsparam_string("nr_inodes", Opt_nr_inodes), + fsparam_string("size", Opt_size), + fsparam_u32 ("uid", Opt_uid), + {} +}; + +static const struct fs_parameter_enum shmem_param_enums[] = { + { Opt_huge, "never", SHMEM_HUGE_NEVER }, + { Opt_huge, "always", SHMEM_HUGE_ALWAYS }, + { Opt_huge, "within_size", SHMEM_HUGE_WITHIN_SIZE }, + { Opt_huge, "advise", SHMEM_HUGE_ADVISE }, + {} +}; + +const struct fs_parameter_description shmem_fs_parameters = { + .name = "tmpfs", + .specs = shmem_param_specs, + .enums = shmem_param_enums, +}; + +static int shmem_parse_one(struct shmem_options *ctx, + struct fs_parameter *param) { + struct fs_context *fc = NULL; + struct fs_parse_result result; + unsigned long long size; char *rest; - uid_t uid; - gid_t gid; + int opt; + + opt = fs_parse(fc, &shmem_fs_parameters, param, &result); + if (opt < 0) { + if (opt == -ENOPARAM) + return invalf(fc, "tmpfs: Unknown parameter '%s'", + param->key); + return opt; + } - if (!strcmp(opt, "size")) { - unsigned long long size; - size = memparse(value,&rest); + switch (opt) { + case Opt_size: + size = memparse(param->string, &rest); if (*rest == '%') { size <<= PAGE_SHIFT; size *= totalram_pages(); @@ -3379,74 +3428,65 @@ static int shmem_parse_one(struct shmem_options *ctx, char *opt, char *value) rest++; } if (*rest) - goto bad_val; + goto bad_value; ctx->blocks = DIV_ROUND_UP(size, PAGE_SIZE); ctx->seen |= SHMEM_SEEN_BLOCKS; - } else if (!strcmp(opt, "nr_blocks")) { - ctx->blocks = memparse(value, &rest); + break; + case Opt_nr_blocks: + ctx->blocks = memparse(param->string, &rest); if (*rest) - goto bad_val; + goto bad_value; ctx->seen |= SHMEM_SEEN_BLOCKS; - } else if (!strcmp(opt, "nr_inodes")) { - ctx->inodes = memparse(value, &rest); + break; + case Opt_nr_inodes: + ctx->inodes = memparse(param->string, &rest); if (*rest) - goto bad_val; + goto bad_value; ctx->seen |= SHMEM_SEEN_INODES; - } else if (!strcmp(opt, "mode")) { - ctx->mode = simple_strtoul(value, &rest, 8) & 07777; - if (*rest) - goto bad_val; - } else if (!strcmp(opt, "uid")) { - uid = simple_strtoul(value, &rest, 0); - if (*rest) - goto bad_val; - ctx->uid = make_kuid(current_user_ns(), uid); + break; + case Opt_mode: + ctx->mode = result.uint_32 & 07777; + break; + case Opt_uid: + ctx->uid = make_kuid(current_user_ns(), result.uint_32); if (!uid_valid(ctx->uid)) - goto bad_val; - } else if (!strcmp(opt, "gid")) { - gid = simple_strtoul(value, &rest, 0); - if (*rest) - goto bad_val; - ctx->gid = make_kgid(current_user_ns(), gid); + goto bad_value; + break; + case Opt_gid: + ctx->gid = make_kgid(current_user_ns(), result.uint_32); if (!gid_valid(ctx->gid)) - goto bad_val; -#ifdef CONFIG_TRANSPARENT_HUGE_PAGECACHE - } else if (!strcmp(opt, "huge")) { - int huge; - huge = shmem_parse_huge(value); - if (huge < 0) - goto bad_val; - if (!has_transparent_hugepage() && - huge != SHMEM_HUGE_NEVER) - goto bad_val; - ctx->huge = huge; + goto bad_value; + break; + case Opt_huge: + ctx->huge = result.uint_32; + if (ctx->huge != SHMEM_HUGE_NEVER && + !(IS_ENABLED(CONFIG_TRANSPARENT_HUGE_PAGECACHE) && + has_transparent_hugepage())) + goto unsupported_parameter; ctx->seen |= SHMEM_SEEN_HUGE; -#endif -#ifdef CONFIG_NUMA - } else if (!strcmp(opt, "mpol")) { - mpol_put(ctx->mpol); - ctx->mpol = NULL; - if (mpol_parse_str(value, &ctx->mpol)) - goto bad_val; -#endif - } else { - pr_err("tmpfs: Bad mount option %s\n", opt); - return -EINVAL; + break; + case Opt_mpol: + if (IS_ENABLED(CONFIG_NUMA)) { + mpol_put(ctx->mpol); + ctx->mpol = NULL; + if (mpol_parse_str(param->string, &ctx->mpol)) + goto bad_value; + break; + } + goto unsupported_parameter; } return 0; -bad_val: - pr_err("tmpfs: Bad value '%s' for mount option '%s'\n", - value, opt); - return -EINVAL; +unsupported_parameter: + return invalf(fc, "tmpfs: Unsupported parameter '%s'", param->key); +bad_value: + return invalf(fc, "tmpfs: Bad value for '%s'", param->key); } static int shmem_parse_options(char *options, struct shmem_options *ctx) { - char *this_char, *value; - while (options != NULL) { - this_char = options; + char *this_char = options; for (;;) { /* * NUL-terminate this option: unfortunately, @@ -3462,17 +3502,26 @@ static int shmem_parse_options(char *options, struct shmem_options *ctx) break; } } - if (!*this_char) - continue; - if ((value = strchr(this_char,'=')) != NULL) { - *value++ = 0; - } else { - pr_err("tmpfs: No value for mount option '%s'\n", - this_char); - goto error; + if (*this_char) { + char *value = strchr(this_char,'='); + struct fs_parameter param = { + .key = this_char, + .type = fs_value_is_string, + }; + int err; + + if (value) { + *value++ = '\0'; + param.size = strlen(value); + param.string = kstrdup(value, GFP_KERNEL); + if (!param.string) + goto error; + } + err = shmem_parse_one(ctx, ¶m); + kfree(param.string); + if (err) + goto error; } - if (shmem_parse_one(ctx, this_char, value) < 0) - goto error; } return 0; -- cgit v1.2.2 From f32356261d44d580649a7abce1156d15d49cf20f Mon Sep 17 00:00:00 2001 From: David Howells Date: Mon, 25 Mar 2019 16:38:31 +0000 Subject: vfs: Convert ramfs, shmem, tmpfs, devtmpfs, rootfs to use the new mount API Convert the ramfs, shmem, tmpfs, devtmpfs and rootfs filesystems to the new internal mount API as the old one will be obsoleted and removed. This allows greater flexibility in communication of mount parameters between userspace, the VFS and the filesystem. See Documentation/filesystems/mount_api.txt for more information. Note that tmpfs is slightly tricky as it can contain embedded commas, so it can't be trivially split up using strsep() to break on commas in generic_parse_monolithic(). Instead, tmpfs has to supply its own generic parser. However, if tmpfs changes, then devtmpfs and rootfs, which are wrappers around tmpfs or ramfs, must change too - and thus so must ramfs, so these had to be converted also. [AV: rewritten] Signed-off-by: David Howells cc: Hugh Dickins cc: linux-mm@kvack.org Signed-off-by: Al Viro --- mm/shmem.c | 187 +++++++++++++++++++++++++++++++++++-------------------------- 1 file changed, 108 insertions(+), 79 deletions(-) (limited to 'mm/shmem.c') diff --git a/mm/shmem.c b/mm/shmem.c index 6a41595dd1b3..0f7fd4a85db6 100644 --- a/mm/shmem.c +++ b/mm/shmem.c @@ -3401,22 +3401,17 @@ const struct fs_parameter_description shmem_fs_parameters = { .enums = shmem_param_enums, }; -static int shmem_parse_one(struct shmem_options *ctx, - struct fs_parameter *param) +static int shmem_parse_one(struct fs_context *fc, struct fs_parameter *param) { - struct fs_context *fc = NULL; + struct shmem_options *ctx = fc->fs_private; struct fs_parse_result result; unsigned long long size; char *rest; int opt; opt = fs_parse(fc, &shmem_fs_parameters, param, &result); - if (opt < 0) { - if (opt == -ENOPARAM) - return invalf(fc, "tmpfs: Unknown parameter '%s'", - param->key); + if (opt < 0) return opt; - } switch (opt) { case Opt_size: @@ -3483,8 +3478,10 @@ bad_value: return invalf(fc, "tmpfs: Bad value for '%s'", param->key); } -static int shmem_parse_options(char *options, struct shmem_options *ctx) +static int shmem_parse_options(struct fs_context *fc, void *data) { + char *options = data; + while (options != NULL) { char *this_char = options; for (;;) { @@ -3504,85 +3501,81 @@ static int shmem_parse_options(char *options, struct shmem_options *ctx) } if (*this_char) { char *value = strchr(this_char,'='); - struct fs_parameter param = { - .key = this_char, - .type = fs_value_is_string, - }; + size_t len = 0; int err; if (value) { *value++ = '\0'; - param.size = strlen(value); - param.string = kstrdup(value, GFP_KERNEL); - if (!param.string) - goto error; + len = strlen(value); } - err = shmem_parse_one(ctx, ¶m); - kfree(param.string); - if (err) - goto error; + err = vfs_parse_fs_string(fc, this_char, value, len); + if (err < 0) + return err; } } return 0; - -error: - mpol_put(ctx->mpol); - ctx->mpol = NULL; - return 1; - } -static int shmem_remount_fs(struct super_block *sb, int *flags, char *data) +/* + * Reconfigure a shmem filesystem. + * + * Note that we disallow change from limited->unlimited blocks/inodes while any + * are in use; but we must separately disallow unlimited->limited, because in + * that case we have no record of how much is already in use. + */ +static int shmem_reconfigure(struct fs_context *fc) { - struct shmem_sb_info *sbinfo = SHMEM_SB(sb); - struct shmem_options ctx = {.seen = 0}; + struct shmem_options *ctx = fc->fs_private; + struct shmem_sb_info *sbinfo = SHMEM_SB(fc->root->d_sb); unsigned long inodes; - int error = -EINVAL; - - if (shmem_parse_options(data, &ctx)) - return error; + const char *err; spin_lock(&sbinfo->stat_lock); inodes = sbinfo->max_inodes - sbinfo->free_inodes; - /* - * Those tests disallow limited->unlimited while any are in use; - * but we must separately disallow unlimited->limited, because - * in that case we have no record of how much is already in use. - */ - if ((ctx.seen & SHMEM_SEEN_BLOCKS) && ctx.blocks) { - if (!sbinfo->max_blocks) + if ((ctx->seen & SHMEM_SEEN_BLOCKS) && ctx->blocks) { + if (!sbinfo->max_blocks) { + err = "Cannot retroactively limit size"; goto out; + } if (percpu_counter_compare(&sbinfo->used_blocks, - ctx.blocks) > 0) + ctx->blocks) > 0) { + err = "Too small a size for current use"; goto out; + } } - if ((ctx.seen & SHMEM_SEEN_INODES) && ctx.inodes) { - if (!sbinfo->max_inodes) + if ((ctx->seen & SHMEM_SEEN_INODES) && ctx->inodes) { + if (!sbinfo->max_inodes) { + err = "Cannot retroactively limit inodes"; goto out; - if (ctx.inodes < inodes) + } + if (ctx->inodes < inodes) { + err = "Too few inodes for current use"; goto out; + } } - error = 0; - if (ctx.seen & SHMEM_SEEN_HUGE) - sbinfo->huge = ctx.huge; - if (ctx.seen & SHMEM_SEEN_BLOCKS) - sbinfo->max_blocks = ctx.blocks; - if (ctx.seen & SHMEM_SEEN_INODES) { - sbinfo->max_inodes = ctx.inodes; - sbinfo->free_inodes = ctx.inodes - inodes; + if (ctx->seen & SHMEM_SEEN_HUGE) + sbinfo->huge = ctx->huge; + if (ctx->seen & SHMEM_SEEN_BLOCKS) + sbinfo->max_blocks = ctx->blocks; + if (ctx->seen & SHMEM_SEEN_INODES) { + sbinfo->max_inodes = ctx->inodes; + sbinfo->free_inodes = ctx->inodes - inodes; } /* * Preserve previous mempolicy unless mpol remount option was specified. */ - if (ctx.mpol) { + if (ctx->mpol) { mpol_put(sbinfo->mpol); - sbinfo->mpol = ctx.mpol; /* transfers initial ref */ + sbinfo->mpol = ctx->mpol; /* transfers initial ref */ + ctx->mpol = NULL; } + spin_unlock(&sbinfo->stat_lock); + return 0; out: spin_unlock(&sbinfo->stat_lock); - return error; + return invalf(fc, "tmpfs: %s", err); } static int shmem_show_options(struct seq_file *seq, struct dentry *root) @@ -3623,13 +3616,11 @@ static void shmem_put_super(struct super_block *sb) sb->s_fs_info = NULL; } -static int shmem_fill_super(struct super_block *sb, void *data, int silent) +static int shmem_fill_super(struct super_block *sb, struct fs_context *fc) { + struct shmem_options *ctx = fc->fs_private; struct inode *inode; struct shmem_sb_info *sbinfo; - struct shmem_options ctx = {.mode = 0777 | S_ISVTX, - .uid = current_fsuid(), - .gid = current_fsgid()}; int err = -ENOMEM; /* Round up to L1_CACHE_BYTES to resist false sharing */ @@ -3647,12 +3638,10 @@ static int shmem_fill_super(struct super_block *sb, void *data, int silent) * but the internal instance is left unlimited. */ if (!(sb->s_flags & SB_KERNMOUNT)) { - ctx.blocks = shmem_default_max_blocks(); - ctx.inodes = shmem_default_max_inodes(); - if (shmem_parse_options(data, &ctx)) { - err = -EINVAL; - goto failed; - } + if (!(ctx->seen & SHMEM_SEEN_BLOCKS)) + ctx->blocks = shmem_default_max_blocks(); + if (!(ctx->seen & SHMEM_SEEN_INODES)) + ctx->inodes = shmem_default_max_inodes(); } else { sb->s_flags |= SB_NOUSER; } @@ -3661,13 +3650,14 @@ static int shmem_fill_super(struct super_block *sb, void *data, int silent) #else sb->s_flags |= SB_NOUSER; #endif - sbinfo->max_blocks = ctx.blocks; - sbinfo->free_inodes = sbinfo->max_inodes = ctx.inodes; - sbinfo->uid = ctx.uid; - sbinfo->gid = ctx.gid; - sbinfo->mode = ctx.mode; - sbinfo->huge = ctx.huge; - sbinfo->mpol = ctx.mpol; + sbinfo->max_blocks = ctx->blocks; + sbinfo->free_inodes = sbinfo->max_inodes = ctx->inodes; + sbinfo->uid = ctx->uid; + sbinfo->gid = ctx->gid; + sbinfo->mode = ctx->mode; + sbinfo->huge = ctx->huge; + sbinfo->mpol = ctx->mpol; + ctx->mpol = NULL; spin_lock_init(&sbinfo->stat_lock); if (percpu_counter_init(&sbinfo->used_blocks, 0, GFP_KERNEL)) @@ -3704,6 +3694,31 @@ failed: return err; } +static int shmem_get_tree(struct fs_context *fc) +{ + return get_tree_nodev(fc, shmem_fill_super); +} + +static void shmem_free_fc(struct fs_context *fc) +{ + struct shmem_options *ctx = fc->fs_private; + + if (ctx) { + mpol_put(ctx->mpol); + kfree(ctx); + } +} + +static const struct fs_context_operations shmem_fs_context_ops = { + .free = shmem_free_fc, + .get_tree = shmem_get_tree, +#ifdef CONFIG_TMPFS + .parse_monolithic = shmem_parse_options, + .parse_param = shmem_parse_one, + .reconfigure = shmem_reconfigure, +#endif +}; + static struct kmem_cache *shmem_inode_cachep; static struct inode *shmem_alloc_inode(struct super_block *sb) @@ -3820,7 +3835,6 @@ static const struct super_operations shmem_ops = { .destroy_inode = shmem_destroy_inode, #ifdef CONFIG_TMPFS .statfs = shmem_statfs, - .remount_fs = shmem_remount_fs, .show_options = shmem_show_options, #endif .evict_inode = shmem_evict_inode, @@ -3841,16 +3855,30 @@ static const struct vm_operations_struct shmem_vm_ops = { #endif }; -struct dentry *shmem_mount(struct file_system_type *fs_type, - int flags, const char *dev_name, void *data) +int shmem_init_fs_context(struct fs_context *fc) { - return mount_nodev(fs_type, flags, data, shmem_fill_super); + struct shmem_options *ctx; + + ctx = kzalloc(sizeof(struct shmem_options), GFP_KERNEL); + if (!ctx) + return -ENOMEM; + + ctx->mode = 0777 | S_ISVTX; + ctx->uid = current_fsuid(); + ctx->gid = current_fsgid(); + + fc->fs_private = ctx; + fc->ops = &shmem_fs_context_ops; + return 0; } static struct file_system_type shmem_fs_type = { .owner = THIS_MODULE, .name = "tmpfs", - .mount = shmem_mount, + .init_fs_context = shmem_init_fs_context, +#ifdef CONFIG_TMPFS + .parameters = &shmem_fs_parameters, +#endif .kill_sb = kill_litter_super, .fs_flags = FS_USERNS_MOUNT, }; @@ -3994,7 +4022,8 @@ bool shmem_huge_enabled(struct vm_area_struct *vma) static struct file_system_type shmem_fs_type = { .name = "tmpfs", - .mount = ramfs_mount, + .init_fs_context = ramfs_init_fs_context, + .parameters = &ramfs_fs_parameters, .kill_sb = kill_litter_super, .fs_flags = FS_USERNS_MOUNT, }; -- cgit v1.2.2