[PATCH 38/38] tmpfs, devtmpfs, ramfs, rootfs: Convert to fs_context

From: David Howells
Date: Thu Mar 14 2019 - 12:14:06 EST


Convert tmpfs to fs_context as the mpol= option can be properly handled.
The issue is that 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 must 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 convert these
also.

Signed-off-by: David Howells <dhowells@xxxxxxxxxx>
cc: Hugh Dickins <hughd@xxxxxxxxxx>
cc: linux-mm@xxxxxxxxx
---

drivers/base/devtmpfs.c | 16 +-
fs/ramfs/inode.c | 104 +++++++-----
include/linux/ramfs.h | 6 -
include/linux/shmem_fs.h | 4
init/do_mounts.c | 12 +
mm/shmem.c | 396 ++++++++++++++++++++++++++++++----------------
6 files changed, 341 insertions(+), 197 deletions(-)

diff --git a/drivers/base/devtmpfs.c b/drivers/base/devtmpfs.c
index 0dbc43068eeb..1f50c844c2ab 100644
--- a/drivers/base/devtmpfs.c
+++ b/drivers/base/devtmpfs.c
@@ -56,19 +56,15 @@ static int __init mount_param(char *str)
}
__setup("devtmpfs.mount=", mount_param);

-static struct dentry *dev_mount(struct file_system_type *fs_type, int flags,
- const char *dev_name, void *data)
-{
+static struct file_system_type dev_fs_type = {
+ .name = "devtmpfs",
#ifdef CONFIG_TMPFS
- return mount_single(fs_type, flags, data, shmem_fill_super);
+ .init_fs_context = shmem_init_fs_context,
+ .parameters = &shmem_fs_parameters,
#else
- return mount_single(fs_type, flags, data, ramfs_fill_super);
+ .init_fs_context = ramfs_init_fs_context,
+ .parameters = &ramfs_fs_parameters,
#endif
-}
-
-static struct file_system_type dev_fs_type = {
- .name = "devtmpfs",
- .mount = dev_mount,
.kill_sb = kill_litter_super,
};

diff --git a/fs/ramfs/inode.c b/fs/ramfs/inode.c
index 11201b2d06b9..01170dd6c95f 100644
--- a/fs/ramfs/inode.c
+++ b/fs/ramfs/inode.c
@@ -36,6 +36,8 @@
#include <linux/magic.h>
#include <linux/slab.h>
#include <linux/uaccess.h>
+#include <linux/fs_context.h>
+#include <linux/fs_parser.h>
#include "internal.h"

struct ramfs_mount_opts {
@@ -175,62 +177,52 @@ static const struct super_operations ramfs_ops = {
.show_options = ramfs_show_options,
};

-enum {
+enum ramfs_param {
Opt_mode,
- Opt_err
};

-static const match_table_t tokens = {
- {Opt_mode, "mode=%o"},
- {Opt_err, NULL}
+static const struct fs_parameter_spec ramfs_param_specs[] = {
+ fsparam_u32oct("mode", Opt_mode),
+ {}
};

-static int ramfs_parse_options(char *data, struct ramfs_mount_opts *opts)
+const struct fs_parameter_description ramfs_fs_parameters = {
+ .name = "ramfs",
+ .specs = ramfs_param_specs,
+};
+
+static int ramfs_parse_param(struct fs_context *fc, struct fs_parameter *param)
{
- substring_t args[MAX_OPT_ARGS];
- int option;
- int token;
- char *p;
-
- opts->mode = RAMFS_DEFAULT_MODE;
-
- while ((p = strsep(&data, ",")) != NULL) {
- if (!*p)
- continue;
-
- token = match_token(p, tokens, args);
- switch (token) {
- case Opt_mode:
- if (match_octal(&args[0], &option))
- return -EINVAL;
- opts->mode = option & S_IALLUGO;
- break;
+ struct fs_parse_result result;
+ struct ramfs_fs_info *fsi = fc->s_fs_info;
+ int opt;
+
+ opt = fs_parse(fc, &ramfs_fs_parameters, param, &result);
+ if (opt < 0) {
/*
* We might like to report bad mount options here;
* but traditionally ramfs has ignored all mount options,
* and as it is used as a !CONFIG_SHMEM simple substitute
* for tmpfs, better continue to ignore other mount options.
*/
- }
+ if (opt == -ENOPARAM)
+ opt = 0;
+ return opt;
+ }
+
+ switch (opt) {
+ case Opt_mode:
+ fsi->mount_opts.mode = result.uint_32 & S_IALLUGO;
+ break;
}

return 0;
}

-int ramfs_fill_super(struct super_block *sb, void *data, int silent)
+static int ramfs_fill_super(struct super_block *sb, struct fs_context *fc)
{
- struct ramfs_fs_info *fsi;
+ struct ramfs_fs_info *fsi = sb->s_fs_info;
struct inode *inode;
- int err;
-
- fsi = kzalloc(sizeof(struct ramfs_fs_info), GFP_KERNEL);
- sb->s_fs_info = fsi;
- if (!fsi)
- return -ENOMEM;
-
- err = ramfs_parse_options(data, &fsi->mount_opts);
- if (err)
- return err;

sb->s_maxbytes = MAX_LFS_FILESIZE;
sb->s_blocksize = PAGE_SIZE;
@@ -247,10 +239,39 @@ int ramfs_fill_super(struct super_block *sb, void *data, int silent)
return 0;
}

-struct dentry *ramfs_mount(struct file_system_type *fs_type,
- int flags, const char *dev_name, void *data)
+static int ramfs_get_tree(struct fs_context *fc)
{
- return mount_nodev(fs_type, flags, data, ramfs_fill_super);
+ enum vfs_get_super_keying keying = vfs_get_independent_super;
+
+ if (strcmp(fc->fs_type->name, "devtmpfs") == 0)
+ keying = vfs_get_single_super;
+
+ return vfs_get_super(fc, keying, ramfs_fill_super);
+}
+
+static void ramfs_free_fc(struct fs_context *fc)
+{
+ kfree(fc->s_fs_info);
+}
+
+static const struct fs_context_operations ramfs_context_ops = {
+ .free = ramfs_free_fc,
+ .parse_param = ramfs_parse_param,
+ .get_tree = ramfs_get_tree,
+};
+
+int ramfs_init_fs_context(struct fs_context *fc)
+{
+ struct ramfs_fs_info *fsi;
+
+ fsi = kzalloc(sizeof(*fsi), GFP_KERNEL);
+ if (!fsi)
+ return -ENOMEM;
+
+ fsi->mount_opts.mode = RAMFS_DEFAULT_MODE;
+ fc->s_fs_info = fsi;
+ fc->ops = &ramfs_context_ops;
+ return 0;
}

static void ramfs_kill_sb(struct super_block *sb)
@@ -261,7 +282,8 @@ static void ramfs_kill_sb(struct super_block *sb)

static struct file_system_type ramfs_fs_type = {
.name = "ramfs",
- .mount = ramfs_mount,
+ .init_fs_context = ramfs_init_fs_context,
+ .parameters = &ramfs_fs_parameters,
.kill_sb = ramfs_kill_sb,
.fs_flags = FS_USERNS_MOUNT,
};
diff --git a/include/linux/ramfs.h b/include/linux/ramfs.h
index 5ef7d54caac2..94b407424cb7 100644
--- a/include/linux/ramfs.h
+++ b/include/linux/ramfs.h
@@ -4,8 +4,7 @@

struct inode *ramfs_get_inode(struct super_block *sb, const struct inode *dir,
umode_t mode, dev_t dev);
-extern struct dentry *ramfs_mount(struct file_system_type *fs_type,
- int flags, const char *dev_name, void *data);
+extern int ramfs_init_fs_context(struct fs_context *fc);

#ifdef CONFIG_MMU
static inline int
@@ -17,10 +16,9 @@ ramfs_nommu_expand_for_mapping(struct inode *inode, size_t newsize)
extern int ramfs_nommu_expand_for_mapping(struct inode *inode, size_t newsize);
#endif

+extern const struct fs_parameter_description ramfs_fs_parameters;
extern const struct file_operations ramfs_file_operations;
extern const struct vm_operations_struct generic_file_vm_ops;
extern int __init init_ramfs_fs(void);

-int ramfs_fill_super(struct super_block *sb, void *data, int silent);
-
#endif
diff --git a/include/linux/shmem_fs.h b/include/linux/shmem_fs.h
index f3fb1edb3526..3f73c00081e8 100644
--- a/include/linux/shmem_fs.h
+++ b/include/linux/shmem_fs.h
@@ -48,8 +48,10 @@ static inline struct shmem_inode_info *SHMEM_I(struct inode *inode)
/*
* Functions in mm/shmem.c called directly from elsewhere:
*/
+extern const struct fs_parameter_description shmem_fs_parameters;
+
extern int shmem_init(void);
-extern int shmem_fill_super(struct super_block *sb, void *data, int silent);
+extern int shmem_init_fs_context(struct fs_context *fc);
extern struct file *shmem_file_setup(const char *name,
loff_t size, unsigned long flags);
extern struct file *shmem_kernel_file_setup(const char *name, loff_t size,
diff --git a/init/do_mounts.c b/init/do_mounts.c
index f8c230c77035..8242abd47e3f 100644
--- a/init/do_mounts.c
+++ b/init/do_mounts.c
@@ -626,24 +626,22 @@ void __init prepare_namespace(void)
}

static bool is_tmpfs;
-static struct dentry *rootfs_mount(struct file_system_type *fs_type,
- int flags, const char *dev_name, void *data)
+static int rootfs_init_fs_context(struct fs_context *fc)
{
static unsigned long once;
- void *fill = ramfs_fill_super;

if (test_and_set_bit(0, &once))
- return ERR_PTR(-ENODEV);
+ return -ENODEV;

if (IS_ENABLED(CONFIG_TMPFS) && is_tmpfs)
- fill = shmem_fill_super;
+ return shmem_init_fs_context(fc);

- return mount_nodev(fs_type, flags, data, fill);
+ return ramfs_init_fs_context(fc);
}

static struct file_system_type rootfs_fs_type = {
.name = "rootfs",
- .mount = rootfs_mount,
+ .init_fs_context = rootfs_init_fs_context,
.kill_sb = kill_litter_super,
};

diff --git a/mm/shmem.c b/mm/shmem.c
index b3db3779a30a..5f45a710ee04 100644
--- a/mm/shmem.c
+++ b/mm/shmem.c
@@ -37,6 +37,8 @@
#include <linux/khugepaged.h>
#include <linux/hugetlb.h>
#include <linux/frontswap.h>
+#include <linux/fs_context.h>
+#include <linux/fs_parser.h>

#include <asm/tlbflush.h> /* for arch/microblaze update_mmu_cache() */

@@ -85,6 +87,17 @@ static struct vfsmount *shm_mnt;

#include "internal.h"

+struct shmem_fs_context {
+ unsigned long changes;
+ unsigned long max_blocks; /* How many blocks are allowed */
+ unsigned long max_inodes; /* How many inodes are allowed */
+ kuid_t uid;
+ kgid_t gid;
+ int huge;
+ umode_t mode;
+ struct mempolicy *mpol; /* default memory policy for mappings */
+};
+
#define BLOCKS_PER_PAGE (PAGE_SIZE/512)
#define VM_ACCT(size) (PAGE_ALIGN(size) >> PAGE_SHIFT)

@@ -3351,16 +3364,13 @@ 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_monolithic(struct fs_context *fc, void *data)
{
- char *this_char, *value, *rest;
- struct mempolicy *mpol = NULL;
- uid_t uid;
- gid_t gid;
+ char *options = data, *key;
+ int ret = 0;

while (options != NULL) {
- this_char = options;
+ key = options;
for (;;) {
/*
* NUL-terminate this option: unfortunately,
@@ -3376,139 +3386,219 @@ static int shmem_parse_options(char *options, struct shmem_sb_info *sbinfo,
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 (!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 (*key) {
+ size_t v_len = 0;
+ char *value = strchr(key, '=');
+
+ if (value) {
+ if (value == key)
+ continue;
+ *value++ = 0;
+ v_len = strlen(value);
}
- if (*rest)
- goto bad_val;
- sbinfo->max_blocks =
- DIV_ROUND_UP(size, PAGE_SIZE);
- } else if (!strcmp(this_char,"nr_blocks")) {
- sbinfo->max_blocks = memparse(value, &rest);
- if (*rest)
- goto bad_val;
- } else if (!strcmp(this_char,"nr_inodes")) {
- sbinfo->max_inodes = memparse(value, &rest);
- if (*rest)
- goto bad_val;
- } else if (!strcmp(this_char,"mode")) {
- if (remount)
- continue;
- sbinfo->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))
- 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))
- goto bad_val;
+ ret = vfs_parse_fs_string(fc, key, value, v_len);
+ if (ret < 0)
+ break;
+ }
+ }
+
+ return ret;
+}
+
+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 },
+ { Opt_huge, "deny", SHMEM_HUGE_DENY },
+ { Opt_huge, "force", SHMEM_HUGE_FORCE },
+ {}
+};
+
+const struct fs_parameter_description shmem_fs_parameters = {
+ .name = "shmem",
+ .specs = shmem_param_specs,
+ .enums = shmem_param_enums,
+};
+
+static void shmem_apply_options(struct shmem_sb_info *sbinfo,
+ struct fs_context *fc,
+ unsigned long inodes_in_use)
+{
+ struct shmem_fs_context *ctx = fc->fs_private;
+ struct mempolicy *old = NULL;
+
+ if (test_bit(Opt_nr_blocks, &ctx->changes))
+ sbinfo->max_blocks = ctx->max_blocks;
+ if (test_bit(Opt_nr_inodes, &ctx->changes)) {
+ sbinfo->max_inodes = ctx->max_inodes;
+ sbinfo->free_inodes = ctx->max_inodes - inodes_in_use;
+ }
+ if (test_bit(Opt_huge, &ctx->changes))
+ sbinfo->huge = ctx->huge;
+ if (test_bit(Opt_mpol, &ctx->changes)) {
+ old = sbinfo->mpol;
+ sbinfo->mpol = ctx->mpol;
+ }
+
+ if (fc->purpose != FS_CONTEXT_FOR_RECONFIGURE) {
+ if (test_bit(Opt_uid, &ctx->changes))
+ sbinfo->uid = ctx->uid;
+ if (test_bit(Opt_gid, &ctx->changes))
+ sbinfo->gid = ctx->gid;
+ if (test_bit(Opt_mode, &ctx->changes))
+ sbinfo->mode = ctx->mode;
+ }
+
+ mpol_put(old);
+}
+
+static int shmem_parse_param(struct fs_context *fc, struct fs_parameter *param)
+{
+ struct shmem_fs_context *ctx = fc->fs_private;
+ struct fs_parse_result result;
+ unsigned long long size;
+ struct mempolicy *mpol;
+ char *rest;
+ int opt;
+
+ opt = fs_parse(fc, &shmem_fs_parameters, param, &result);
+ if (opt < 0)
+ return opt;
+
+ switch (opt) {
+ case Opt_size:
+ rest = param->string;
+ size = memparse(param->string, &rest);
+ if (*rest == '%') {
+ size <<= PAGE_SHIFT;
+ size *= totalram_pages();
+ do_div(size, 100);
+ rest++;
+ }
+ if (*rest)
+ return invalf(fc, "shmem: Invalid size");
+ ctx->max_blocks = DIV_ROUND_UP(size, PAGE_SIZE);
+ break;
+
+ case Opt_nr_blocks:
+ rest = param->string;
+ ctx->max_blocks = memparse(param->string, &rest);
+ if (*rest)
+ return invalf(fc, "shmem: Invalid nr_blocks");
+ break;
+ case Opt_nr_inodes:
+ rest = param->string;
+ ctx->max_inodes = memparse(param->string, &rest);
+ if (*rest)
+ return invalf(fc, "shmem: Invalid nr_inodes");
+ 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))
+ return invalf(fc, "shmem: Invalid uid");
+ break;
+
+ case Opt_gid:
+ ctx->gid = make_kgid(current_user_ns(), result.uint_32);
+ if (!gid_valid(ctx->gid))
+ return invalf(fc, "shmem: Invalid gid");
+ break;
+
+ case Opt_huge:
#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;
- sbinfo->huge = huge;
+ if (!has_transparent_hugepage() &&
+ result.uint_32 != SHMEM_HUGE_NEVER)
+ return invalf(fc, "shmem: Huge pages disabled");
+
+ ctx->huge = result.uint_32;
+ break;
+#else
+ return invalf(fc, "shmem: huge= option disabled");
#endif
+
+ case Opt_mpol:
#ifdef CONFIG_NUMA
- } else if (!strcmp(this_char,"mpol")) {
- mpol_put(mpol);
- mpol = NULL;
- if (mpol_parse_str(value, &mpol))
- goto bad_val;
+ if (mpol_parse_str(param->string, &mpol))
+ return invalf(fc, "shmem: Invalid mpol=");
+ mpol_put(ctx->mpol);
+ ctx->mpol = mpol;
#endif
- } else {
- pr_err("tmpfs: Bad mount option %s\n", this_char);
- goto error;
- }
+ break;
}
- sbinfo->mpol = mpol;
- return 0;
-
-bad_val:
- pr_err("tmpfs: Bad value '%s' for mount option '%s'\n",
- value, this_char);
-error:
- mpol_put(mpol);
- return 1;

+ __set_bit(opt, &ctx->changes);
+ return 0;
}

-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_fs_context *ctx = fc->fs_private;
+ struct super_block *sb = fc->root->d_sb;
struct shmem_sb_info *sbinfo = SHMEM_SB(sb);
- struct shmem_sb_info config = *sbinfo;
- unsigned long inodes;
- int error = -EINVAL;
-
- config.mpol = NULL;
- if (shmem_parse_options(data, &config, true))
- return error;
+ unsigned long inodes_in_use;

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;
-
- 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 (test_bit(Opt_nr_blocks, &ctx->changes)) {
+ if (ctx->max_blocks && !sbinfo->max_blocks) {
+ spin_unlock(&sbinfo->stat_lock);
+ return invalf(fc, "shmem: Can't retroactively limit nr_blocks");
+ }
+ if (percpu_counter_compare(&sbinfo->used_blocks, ctx->max_blocks) > 0) {
+ spin_unlock(&sbinfo->stat_lock);
+ return invalf(fc, "shmem: Too few blocks for current use");
+ }
+ }

- /*
- * Preserve previous mempolicy unless mpol remount option was specified.
- */
- if (config.mpol) {
- mpol_put(sbinfo->mpol);
- sbinfo->mpol = config.mpol; /* transfers initial ref */
+ inodes_in_use = sbinfo->max_inodes - sbinfo->free_inodes;
+ if (test_bit(Opt_nr_inodes, &ctx->changes)) {
+ if (ctx->max_inodes && !sbinfo->max_inodes) {
+ spin_unlock(&sbinfo->stat_lock);
+ return invalf(fc, "shmem: Can't retroactively limit nr_inodes");
+ }
+ if (ctx->max_inodes < inodes_in_use) {
+ spin_unlock(&sbinfo->stat_lock);
+ return invalf(fc, "shmem: Too few inodes for current use");
+ }
}
-out:
+
+ shmem_apply_options(sbinfo, fc, inodes_in_use);
spin_unlock(&sbinfo->stat_lock);
- return error;
+ return 0;
}

static int shmem_show_options(struct seq_file *seq, struct dentry *root)
@@ -3549,7 +3639,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, struct fs_context *fc)
{
struct inode *inode;
struct shmem_sb_info *sbinfo;
@@ -3575,10 +3665,7 @@ int shmem_fill_super(struct super_block *sb, void *data, int silent)
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)) {
- err = -EINVAL;
- goto failed;
- }
+ shmem_apply_options(sbinfo, fc, 0);
} else {
sb->s_flags |= SB_NOUSER;
}
@@ -3624,6 +3711,36 @@ int shmem_fill_super(struct super_block *sb, void *data, int silent)
return err;
}

+static int shmem_get_tree(struct fs_context *fc)
+{
+ enum vfs_get_super_keying keying = vfs_get_independent_super;
+
+ if (strcmp(fc->fs_type->name, "devtmpfs") == 0)
+ keying = vfs_get_single_super;
+
+ return vfs_get_super(fc, keying, shmem_fill_super);
+}
+
+static void shmem_free_fc(struct fs_context *fc)
+{
+ struct shmem_fs_context *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_monolithic,
+ .parse_param = shmem_parse_param,
+ .reconfigure = shmem_reconfigure,
+#endif
+};
+
static struct kmem_cache *shmem_inode_cachep;

static struct inode *shmem_alloc_inode(struct super_block *sb)
@@ -3741,7 +3858,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,
@@ -3762,16 +3878,26 @@ static const struct vm_operations_struct shmem_vm_ops = {
#endif
};

-static 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_fs_context *ctx;
+
+ ctx = kzalloc(sizeof(struct shmem_fs_context), GFP_KERNEL);
+ if (!ctx)
+ return -ENOMEM;
+
+ 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,
};
@@ -3916,7 +4042,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,
};
@@ -4117,3 +4244,4 @@ struct page *shmem_read_mapping_page_gfp(struct address_space *mapping,
#endif
}
EXPORT_SYMBOL_GPL(shmem_read_mapping_page_gfp);
+