summaryrefslogtreecommitdiffstats
path: root/mm/shmem.c
diff options
context:
space:
mode:
Diffstat (limited to 'mm/shmem.c')
-rw-r--r--mm/shmem.c183
1 files changed, 116 insertions, 67 deletions
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 <linux/khugepaged.h>
#include <linux/hugetlb.h>
#include <linux/frontswap.h>
+#include <linux/fs_parser.h>
#include <asm/tlbflush.h> /* 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, &param);
+ kfree(param.string);
+ if (err)
+ goto error;
}
- if (shmem_parse_one(ctx, this_char, value) < 0)
- goto error;
}
return 0;