Commit 32021982 authored by David Howells's avatar David Howells Committed by Al Viro

hugetlbfs: Convert to fs_context

Convert the hugetlbfs to use the fs_context during mount.
Signed-off-by: default avatarDavid Howells <dhowells@redhat.com>
Signed-off-by: default avatarAl Viro <viro@zeniv.linux.org.uk>
parent a1875374
...@@ -27,7 +27,7 @@ ...@@ -27,7 +27,7 @@
#include <linux/backing-dev.h> #include <linux/backing-dev.h>
#include <linux/hugetlb.h> #include <linux/hugetlb.h>
#include <linux/pagevec.h> #include <linux/pagevec.h>
#include <linux/parser.h> #include <linux/fs_parser.h>
#include <linux/mman.h> #include <linux/mman.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/dnotify.h> #include <linux/dnotify.h>
...@@ -45,11 +45,17 @@ const struct file_operations hugetlbfs_file_operations; ...@@ -45,11 +45,17 @@ const struct file_operations hugetlbfs_file_operations;
static const struct inode_operations hugetlbfs_dir_inode_operations; static const struct inode_operations hugetlbfs_dir_inode_operations;
static const struct inode_operations hugetlbfs_inode_operations; static const struct inode_operations hugetlbfs_inode_operations;
struct hugetlbfs_config { enum hugetlbfs_size_type { NO_SIZE, SIZE_STD, SIZE_PERCENT };
struct hugetlbfs_fs_context {
struct hstate *hstate; struct hstate *hstate;
unsigned long long max_size_opt;
unsigned long long min_size_opt;
long max_hpages; long max_hpages;
long nr_inodes; long nr_inodes;
long min_hpages; long min_hpages;
enum hugetlbfs_size_type max_val_type;
enum hugetlbfs_size_type min_val_type;
kuid_t uid; kuid_t uid;
kgid_t gid; kgid_t gid;
umode_t mode; umode_t mode;
...@@ -57,22 +63,30 @@ struct hugetlbfs_config { ...@@ -57,22 +63,30 @@ struct hugetlbfs_config {
int sysctl_hugetlb_shm_group; int sysctl_hugetlb_shm_group;
enum { enum hugetlb_param {
Opt_size, Opt_nr_inodes, Opt_gid,
Opt_mode, Opt_uid, Opt_gid, Opt_min_size,
Opt_pagesize, Opt_min_size, Opt_mode,
Opt_err, Opt_nr_inodes,
Opt_pagesize,
Opt_size,
Opt_uid,
}; };
static const match_table_t tokens = { static const struct fs_parameter_spec hugetlb_param_specs[] = {
{Opt_size, "size=%s"}, fsparam_u32 ("gid", Opt_gid),
{Opt_nr_inodes, "nr_inodes=%s"}, fsparam_string("min_size", Opt_min_size),
{Opt_mode, "mode=%o"}, fsparam_u32 ("mode", Opt_mode),
{Opt_uid, "uid=%u"}, fsparam_string("nr_inodes", Opt_nr_inodes),
{Opt_gid, "gid=%u"}, fsparam_string("pagesize", Opt_pagesize),
{Opt_pagesize, "pagesize=%s"}, fsparam_string("size", Opt_size),
{Opt_min_size, "min_size=%s"}, fsparam_u32 ("uid", Opt_uid),
{Opt_err, NULL}, {}
};
static const struct fs_parameter_description hugetlb_fs_parameters = {
.name = "hugetlbfs",
.specs = hugetlb_param_specs,
}; };
#ifdef CONFIG_NUMA #ifdef CONFIG_NUMA
...@@ -708,16 +722,16 @@ static int hugetlbfs_setattr(struct dentry *dentry, struct iattr *attr) ...@@ -708,16 +722,16 @@ static int hugetlbfs_setattr(struct dentry *dentry, struct iattr *attr)
} }
static struct inode *hugetlbfs_get_root(struct super_block *sb, static struct inode *hugetlbfs_get_root(struct super_block *sb,
struct hugetlbfs_config *config) struct hugetlbfs_fs_context *ctx)
{ {
struct inode *inode; struct inode *inode;
inode = new_inode(sb); inode = new_inode(sb);
if (inode) { if (inode) {
inode->i_ino = get_next_ino(); inode->i_ino = get_next_ino();
inode->i_mode = S_IFDIR | config->mode; inode->i_mode = S_IFDIR | ctx->mode;
inode->i_uid = config->uid; inode->i_uid = ctx->uid;
inode->i_gid = config->gid; inode->i_gid = ctx->gid;
inode->i_atime = inode->i_mtime = inode->i_ctime = current_time(inode); inode->i_atime = inode->i_mtime = inode->i_ctime = current_time(inode);
inode->i_op = &hugetlbfs_dir_inode_operations; inode->i_op = &hugetlbfs_dir_inode_operations;
inode->i_fop = &simple_dir_operations; inode->i_fop = &simple_dir_operations;
...@@ -1081,8 +1095,6 @@ static const struct super_operations hugetlbfs_ops = { ...@@ -1081,8 +1095,6 @@ static const struct super_operations hugetlbfs_ops = {
.show_options = hugetlbfs_show_options, .show_options = hugetlbfs_show_options,
}; };
enum hugetlbfs_size_type { NO_SIZE, SIZE_STD, SIZE_PERCENT };
/* /*
* Convert size option passed from command line to number of huge pages * Convert size option passed from command line to number of huge pages
* in the pool specified by hstate. Size option could be in bytes * in the pool specified by hstate. Size option could be in bytes
...@@ -1105,170 +1117,151 @@ hugetlbfs_size_to_hpages(struct hstate *h, unsigned long long size_opt, ...@@ -1105,170 +1117,151 @@ hugetlbfs_size_to_hpages(struct hstate *h, unsigned long long size_opt,
return size_opt; return size_opt;
} }
static int /*
hugetlbfs_parse_options(char *options, struct hugetlbfs_config *pconfig) * Parse one mount parameter.
*/
static int hugetlbfs_parse_param(struct fs_context *fc, struct fs_parameter *param)
{ {
char *p, *rest; struct hugetlbfs_fs_context *ctx = fc->fs_private;
substring_t args[MAX_OPT_ARGS]; struct fs_parse_result result;
int option; char *rest;
unsigned long long max_size_opt = 0, min_size_opt = 0; unsigned long ps;
enum hugetlbfs_size_type max_val_type = NO_SIZE, min_val_type = NO_SIZE; int opt;
if (!options)
return 0;
while ((p = strsep(&options, ",")) != NULL) { opt = fs_parse(fc, &hugetlb_fs_parameters, param, &result);
int token; if (opt < 0)
if (!*p) return opt;
continue;
token = match_token(p, tokens, args); switch (opt) {
switch (token) {
case Opt_uid: case Opt_uid:
if (match_int(&args[0], &option)) ctx->uid = make_kuid(current_user_ns(), result.uint_32);
if (!uid_valid(ctx->uid))
goto bad_val; goto bad_val;
pconfig->uid = make_kuid(current_user_ns(), option); return 0;
if (!uid_valid(pconfig->uid))
goto bad_val;
break;
case Opt_gid: case Opt_gid:
if (match_int(&args[0], &option)) ctx->gid = make_kgid(current_user_ns(), result.uint_32);
goto bad_val; if (!gid_valid(ctx->gid))
pconfig->gid = make_kgid(current_user_ns(), option);
if (!gid_valid(pconfig->gid))
goto bad_val; goto bad_val;
break; return 0;
case Opt_mode: case Opt_mode:
if (match_octal(&args[0], &option)) ctx->mode = result.uint_32 & 01777U;
goto bad_val; return 0;
pconfig->mode = option & 01777U;
break;
case Opt_size: { case Opt_size:
/* memparse() will accept a K/M/G without a digit */ /* memparse() will accept a K/M/G without a digit */
if (!isdigit(*args[0].from)) if (!isdigit(param->string[0]))
goto bad_val; goto bad_val;
max_size_opt = memparse(args[0].from, &rest); ctx->max_size_opt = memparse(param->string, &rest);
max_val_type = SIZE_STD; ctx->max_val_type = SIZE_STD;
if (*rest == '%') if (*rest == '%')
max_val_type = SIZE_PERCENT; ctx->max_val_type = SIZE_PERCENT;
break; return 0;
}
case Opt_nr_inodes: case Opt_nr_inodes:
/* memparse() will accept a K/M/G without a digit */ /* memparse() will accept a K/M/G without a digit */
if (!isdigit(*args[0].from)) if (!isdigit(param->string[0]))
goto bad_val; goto bad_val;
pconfig->nr_inodes = memparse(args[0].from, &rest); ctx->nr_inodes = memparse(param->string, &rest);
break; return 0;
case Opt_pagesize: { case Opt_pagesize:
unsigned long ps; ps = memparse(param->string, &rest);
ps = memparse(args[0].from, &rest); ctx->hstate = size_to_hstate(ps);
pconfig->hstate = size_to_hstate(ps); if (!ctx->hstate) {
if (!pconfig->hstate) { pr_err("Unsupported page size %lu MB\n", ps >> 20);
pr_err("Unsupported page size %lu MB\n",
ps >> 20);
return -EINVAL; return -EINVAL;
} }
break; return 0;
}
case Opt_min_size: { case Opt_min_size:
/* memparse() will accept a K/M/G without a digit */ /* memparse() will accept a K/M/G without a digit */
if (!isdigit(*args[0].from)) if (!isdigit(param->string[0]))
goto bad_val; goto bad_val;
min_size_opt = memparse(args[0].from, &rest); ctx->min_size_opt = memparse(param->string, &rest);
min_val_type = SIZE_STD; ctx->min_val_type = SIZE_STD;
if (*rest == '%') if (*rest == '%')
min_val_type = SIZE_PERCENT; ctx->min_val_type = SIZE_PERCENT;
break; return 0;
}
default: default:
pr_err("Bad mount option: \"%s\"\n", p);
return -EINVAL; return -EINVAL;
break;
}
} }
bad_val:
return invalf(fc, "hugetlbfs: Bad value '%s' for mount option '%s'\n",
param->string, param->key);
}
/*
* Validate the parsed options.
*/
static int hugetlbfs_validate(struct fs_context *fc)
{
struct hugetlbfs_fs_context *ctx = fc->fs_private;
/* /*
* Use huge page pool size (in hstate) to convert the size * Use huge page pool size (in hstate) to convert the size
* options to number of huge pages. If NO_SIZE, -1 is returned. * options to number of huge pages. If NO_SIZE, -1 is returned.
*/ */
pconfig->max_hpages = hugetlbfs_size_to_hpages(pconfig->hstate, ctx->max_hpages = hugetlbfs_size_to_hpages(ctx->hstate,
max_size_opt, max_val_type); ctx->max_size_opt,
pconfig->min_hpages = hugetlbfs_size_to_hpages(pconfig->hstate, ctx->max_val_type);
min_size_opt, min_val_type); ctx->min_hpages = hugetlbfs_size_to_hpages(ctx->hstate,
ctx->min_size_opt,
ctx->min_val_type);
/* /*
* If max_size was specified, then min_size must be smaller * If max_size was specified, then min_size must be smaller
*/ */
if (max_val_type > NO_SIZE && if (ctx->max_val_type > NO_SIZE &&
pconfig->min_hpages > pconfig->max_hpages) { ctx->min_hpages > ctx->max_hpages) {
pr_err("minimum size can not be greater than maximum size\n"); pr_err("Minimum size can not be greater than maximum size\n");
return -EINVAL; return -EINVAL;
} }
return 0; return 0;
bad_val:
pr_err("Bad value '%s' for mount option '%s'\n", args[0].from, p);
return -EINVAL;
} }
static int static int
hugetlbfs_fill_super(struct super_block *sb, void *data, int silent) hugetlbfs_fill_super(struct super_block *sb, struct fs_context *fc)
{ {
int ret; struct hugetlbfs_fs_context *ctx = fc->fs_private;
struct hugetlbfs_config config;
struct hugetlbfs_sb_info *sbinfo; struct hugetlbfs_sb_info *sbinfo;
config.max_hpages = -1; /* No limit on size by default */
config.nr_inodes = -1; /* No limit on number of inodes by default */
config.uid = current_fsuid();
config.gid = current_fsgid();
config.mode = 0755;
config.hstate = &default_hstate;
config.min_hpages = -1; /* No default minimum size */
ret = hugetlbfs_parse_options(data, &config);
if (ret)
return ret;
sbinfo = kmalloc(sizeof(struct hugetlbfs_sb_info), GFP_KERNEL); sbinfo = kmalloc(sizeof(struct hugetlbfs_sb_info), GFP_KERNEL);
if (!sbinfo) if (!sbinfo)
return -ENOMEM; return -ENOMEM;
sb->s_fs_info = sbinfo; sb->s_fs_info = sbinfo;
sbinfo->hstate = config.hstate;
spin_lock_init(&sbinfo->stat_lock); spin_lock_init(&sbinfo->stat_lock);
sbinfo->max_inodes = config.nr_inodes; sbinfo->hstate = ctx->hstate;
sbinfo->free_inodes = config.nr_inodes; sbinfo->max_inodes = ctx->nr_inodes;
sbinfo->free_inodes = ctx->nr_inodes;
sbinfo->spool = NULL; sbinfo->spool = NULL;
sbinfo->uid = config.uid; sbinfo->uid = ctx->uid;
sbinfo->gid = config.gid; sbinfo->gid = ctx->gid;
sbinfo->mode = config.mode; sbinfo->mode = ctx->mode;
/* /*
* Allocate and initialize subpool if maximum or minimum size is * Allocate and initialize subpool if maximum or minimum size is
* specified. Any needed reservations (for minimim size) are taken * specified. Any needed reservations (for minimim size) are taken
* taken when the subpool is created. * taken when the subpool is created.
*/ */
if (config.max_hpages != -1 || config.min_hpages != -1) { if (ctx->max_hpages != -1 || ctx->min_hpages != -1) {
sbinfo->spool = hugepage_new_subpool(config.hstate, sbinfo->spool = hugepage_new_subpool(ctx->hstate,
config.max_hpages, ctx->max_hpages,
config.min_hpages); ctx->min_hpages);
if (!sbinfo->spool) if (!sbinfo->spool)
goto out_free; goto out_free;
} }
sb->s_maxbytes = MAX_LFS_FILESIZE; sb->s_maxbytes = MAX_LFS_FILESIZE;
sb->s_blocksize = huge_page_size(config.hstate); sb->s_blocksize = huge_page_size(ctx->hstate);
sb->s_blocksize_bits = huge_page_shift(config.hstate); sb->s_blocksize_bits = huge_page_shift(ctx->hstate);
sb->s_magic = HUGETLBFS_MAGIC; sb->s_magic = HUGETLBFS_MAGIC;
sb->s_op = &hugetlbfs_ops; sb->s_op = &hugetlbfs_ops;
sb->s_time_gran = 1; sb->s_time_gran = 1;
sb->s_root = d_make_root(hugetlbfs_get_root(sb, &config)); sb->s_root = d_make_root(hugetlbfs_get_root(sb, ctx));
if (!sb->s_root) if (!sb->s_root)
goto out_free; goto out_free;
return 0; return 0;
...@@ -1278,15 +1271,51 @@ hugetlbfs_fill_super(struct super_block *sb, void *data, int silent) ...@@ -1278,15 +1271,51 @@ hugetlbfs_fill_super(struct super_block *sb, void *data, int silent)
return -ENOMEM; return -ENOMEM;
} }
static struct dentry *hugetlbfs_mount(struct file_system_type *fs_type, static int hugetlbfs_get_tree(struct fs_context *fc)
int flags, const char *dev_name, void *data) {
int err = hugetlbfs_validate(fc);
if (err)
return err;
return vfs_get_super(fc, vfs_get_independent_super, hugetlbfs_fill_super);
}
static void hugetlbfs_fs_context_free(struct fs_context *fc)
{
kfree(fc->fs_private);
}
static const struct fs_context_operations hugetlbfs_fs_context_ops = {
.free = hugetlbfs_fs_context_free,
.parse_param = hugetlbfs_parse_param,
.get_tree = hugetlbfs_get_tree,
};
static int hugetlbfs_init_fs_context(struct fs_context *fc)
{ {
return mount_nodev(fs_type, flags, data, hugetlbfs_fill_super); struct hugetlbfs_fs_context *ctx;
ctx = kzalloc(sizeof(struct hugetlbfs_fs_context), GFP_KERNEL);
if (!ctx)
return -ENOMEM;
ctx->max_hpages = -1; /* No limit on size by default */
ctx->nr_inodes = -1; /* No limit on number of inodes by default */
ctx->uid = current_fsuid();
ctx->gid = current_fsgid();
ctx->mode = 0755;
ctx->hstate = &default_hstate;
ctx->min_hpages = -1; /* No default minimum size */
ctx->max_val_type = NO_SIZE;
ctx->min_val_type = NO_SIZE;
fc->fs_private = ctx;
fc->ops = &hugetlbfs_fs_context_ops;
return 0;
} }
static struct file_system_type hugetlbfs_fs_type = { static struct file_system_type hugetlbfs_fs_type = {
.name = "hugetlbfs", .name = "hugetlbfs",
.mount = hugetlbfs_mount, .init_fs_context = hugetlbfs_init_fs_context,
.parameters = &hugetlb_fs_parameters,
.kill_sb = kill_litter_super, .kill_sb = kill_litter_super,
}; };
...@@ -1372,8 +1401,29 @@ struct file *hugetlb_file_setup(const char *name, size_t size, ...@@ -1372,8 +1401,29 @@ struct file *hugetlb_file_setup(const char *name, size_t size,
return file; return file;
} }
static struct vfsmount *__init mount_one_hugetlbfs(struct hstate *h)
{
struct fs_context *fc;
struct vfsmount *mnt;
fc = fs_context_for_mount(&hugetlbfs_fs_type, SB_KERNMOUNT);
if (IS_ERR(fc)) {
mnt = ERR_CAST(fc);
} else {
struct hugetlbfs_fs_context *ctx = fc->fs_private;
ctx->hstate = h;
mnt = fc_mount(fc);
put_fs_context(fc);
}
if (IS_ERR(mnt))
pr_err("Cannot mount internal hugetlbfs for page size %uK",
1U << (h->order + PAGE_SHIFT - 10));
return mnt;
}
static int __init init_hugetlbfs_fs(void) static int __init init_hugetlbfs_fs(void)
{ {
struct vfsmount *mnt;
struct hstate *h; struct hstate *h;
int error; int error;
int i; int i;
...@@ -1396,23 +1446,15 @@ static int __init init_hugetlbfs_fs(void) ...@@ -1396,23 +1446,15 @@ static int __init init_hugetlbfs_fs(void)
i = 0; i = 0;
for_each_hstate(h) { for_each_hstate(h) {
char buf[50]; mnt = mount_one_hugetlbfs(h);
unsigned ps_kb = 1U << (h->order + PAGE_SHIFT - 10); if (IS_ERR(mnt) && i == 0) {
error = PTR_ERR(mnt);
snprintf(buf, sizeof(buf), "pagesize=%uK", ps_kb); goto out;
hugetlbfs_vfsmount[i] = kern_mount_data(&hugetlbfs_fs_type,
buf);
if (IS_ERR(hugetlbfs_vfsmount[i])) {
pr_err("Cannot mount internal hugetlbfs for "
"page size %uK", ps_kb);
error = PTR_ERR(hugetlbfs_vfsmount[i]);
hugetlbfs_vfsmount[i] = NULL;
} }
hugetlbfs_vfsmount[i] = mnt;
i++; i++;
} }
/* Non default hstates are optional */
if (!IS_ERR_OR_NULL(hugetlbfs_vfsmount[default_hstate_idx]))
return 0; return 0;
out: out:
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment