summaryrefslogtreecommitdiff
path: root/mm/shmem.c
diff options
context:
space:
mode:
Diffstat (limited to 'mm/shmem.c')
-rw-r--r--mm/shmem.c187
1 files changed, 108 insertions, 79 deletions
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, &param);
- 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,
};