hugetlbfs: Convert to fs_context
authorDavid Howells <dhowells@redhat.com>
Thu, 1 Nov 2018 23:07:26 +0000 (23:07 +0000)
committerAl Viro <viro@zeniv.linux.org.uk>
Thu, 28 Feb 2019 08:29:36 +0000 (03:29 -0500)
Convert the hugetlbfs to use the fs_context during mount.

Signed-off-by: David Howells <dhowells@redhat.com>
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
fs/hugetlbfs/inode.c

index 32920a10100e23fc60f53cf36c882278ae972cee..239c7ca09b7495adb8bc0c78f7f3cf4ef29e1aea 100644 (file)
@@ -27,7 +27,7 @@
 #include <linux/backing-dev.h>
 #include <linux/hugetlb.h>
 #include <linux/pagevec.h>
-#include <linux/parser.h>
+#include <linux/fs_parser.h>
 #include <linux/mman.h>
 #include <linux/slab.h>
 #include <linux/dnotify.h>
@@ -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_inode_operations;
 
-struct hugetlbfs_config {
+enum hugetlbfs_size_type { NO_SIZE, SIZE_STD, SIZE_PERCENT };
+
+struct hugetlbfs_fs_context {
        struct hstate           *hstate;
+       unsigned long long      max_size_opt;
+       unsigned long long      min_size_opt;
        long                    max_hpages;
        long                    nr_inodes;
        long                    min_hpages;
+       enum hugetlbfs_size_type max_val_type;
+       enum hugetlbfs_size_type min_val_type;
        kuid_t                  uid;
        kgid_t                  gid;
        umode_t                 mode;
@@ -57,22 +63,30 @@ struct hugetlbfs_config {
 
 int sysctl_hugetlb_shm_group;
 
-enum {
-       Opt_size, Opt_nr_inodes,
-       Opt_mode, Opt_uid, Opt_gid,
-       Opt_pagesize, Opt_min_size,
-       Opt_err,
+enum hugetlb_param {
+       Opt_gid,
+       Opt_min_size,
+       Opt_mode,
+       Opt_nr_inodes,
+       Opt_pagesize,
+       Opt_size,
+       Opt_uid,
 };
 
-static const match_table_t tokens = {
-       {Opt_size,      "size=%s"},
-       {Opt_nr_inodes, "nr_inodes=%s"},
-       {Opt_mode,      "mode=%o"},
-       {Opt_uid,       "uid=%u"},
-       {Opt_gid,       "gid=%u"},
-       {Opt_pagesize,  "pagesize=%s"},
-       {Opt_min_size,  "min_size=%s"},
-       {Opt_err,       NULL},
+static const struct fs_parameter_spec hugetlb_param_specs[] = {
+       fsparam_u32   ("gid",           Opt_gid),
+       fsparam_string("min_size",      Opt_min_size),
+       fsparam_u32   ("mode",          Opt_mode),
+       fsparam_string("nr_inodes",     Opt_nr_inodes),
+       fsparam_string("pagesize",      Opt_pagesize),
+       fsparam_string("size",          Opt_size),
+       fsparam_u32   ("uid",           Opt_uid),
+       {}
+};
+
+static const struct fs_parameter_description hugetlb_fs_parameters = {
+       .name           = "hugetlbfs",
+       .specs          = hugetlb_param_specs,
 };
 
 #ifdef CONFIG_NUMA
@@ -708,16 +722,16 @@ static int hugetlbfs_setattr(struct dentry *dentry, struct iattr *attr)
 }
 
 static struct inode *hugetlbfs_get_root(struct super_block *sb,
-                                       struct hugetlbfs_config *config)
+                                       struct hugetlbfs_fs_context *ctx)
 {
        struct inode *inode;
 
        inode = new_inode(sb);
        if (inode) {
                inode->i_ino = get_next_ino();
-               inode->i_mode = S_IFDIR | config->mode;
-               inode->i_uid = config->uid;
-               inode->i_gid = config->gid;
+               inode->i_mode = S_IFDIR | ctx->mode;
+               inode->i_uid = ctx->uid;
+               inode->i_gid = ctx->gid;
                inode->i_atime = inode->i_mtime = inode->i_ctime = current_time(inode);
                inode->i_op = &hugetlbfs_dir_inode_operations;
                inode->i_fop = &simple_dir_operations;
@@ -1081,8 +1095,6 @@ static const struct super_operations hugetlbfs_ops = {
        .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
  * 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,
        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;
-       substring_t args[MAX_OPT_ARGS];
-       int option;
-       unsigned long long max_size_opt = 0, min_size_opt = 0;
-       enum hugetlbfs_size_type max_val_type = NO_SIZE, min_val_type = NO_SIZE;
-
-       if (!options)
+       struct hugetlbfs_fs_context *ctx = fc->fs_private;
+       struct fs_parse_result result;
+       char *rest;
+       unsigned long ps;
+       int opt;
+
+       opt = fs_parse(fc, &hugetlb_fs_parameters, param, &result);
+       if (opt < 0)
+               return opt;
+
+       switch (opt) {
+       case Opt_uid:
+               ctx->uid = make_kuid(current_user_ns(), result.uint_32);
+               if (!uid_valid(ctx->uid))
+                       goto bad_val;
                return 0;
 
-       while ((p = strsep(&options, ",")) != NULL) {
-               int token;
-               if (!*p)
-                       continue;
+       case Opt_gid:
+               ctx->gid = make_kgid(current_user_ns(), result.uint_32);
+               if (!gid_valid(ctx->gid))
+                       goto bad_val;
+               return 0;
 
-               token = match_token(p, tokens, args);
-               switch (token) {
-               case Opt_uid:
-                       if (match_int(&args[0], &option))
-                               goto bad_val;
-                       pconfig->uid = make_kuid(current_user_ns(), option);
-                       if (!uid_valid(pconfig->uid))
-                               goto bad_val;
-                       break;
+       case Opt_mode:
+               ctx->mode = result.uint_32 & 01777U;
+               return 0;
 
-               case Opt_gid:
-                       if (match_int(&args[0], &option))
-                               goto bad_val;
-                       pconfig->gid = make_kgid(current_user_ns(), option);
-                       if (!gid_valid(pconfig->gid))
-                               goto bad_val;
-                       break;
+       case Opt_size:
+               /* memparse() will accept a K/M/G without a digit */
+               if (!isdigit(param->string[0]))
+                       goto bad_val;
+               ctx->max_size_opt = memparse(param->string, &rest);
+               ctx->max_val_type = SIZE_STD;
+               if (*rest == '%')
+                       ctx->max_val_type = SIZE_PERCENT;
+               return 0;
 
-               case Opt_mode:
-                       if (match_octal(&args[0], &option))
-                               goto bad_val;
-                       pconfig->mode = option & 01777U;
-                       break;
+       case Opt_nr_inodes:
+               /* memparse() will accept a K/M/G without a digit */
+               if (!isdigit(param->string[0]))
+                       goto bad_val;
+               ctx->nr_inodes = memparse(param->string, &rest);
+               return 0;
 
-               case Opt_size: {
-                       /* memparse() will accept a K/M/G without a digit */
-                       if (!isdigit(*args[0].from))
-                               goto bad_val;
-                       max_size_opt = memparse(args[0].from, &rest);
-                       max_val_type = SIZE_STD;
-                       if (*rest == '%')
-                               max_val_type = SIZE_PERCENT;
-                       break;
+       case Opt_pagesize:
+               ps = memparse(param->string, &rest);
+               ctx->hstate = size_to_hstate(ps);
+               if (!ctx->hstate) {
+                       pr_err("Unsupported page size %lu MB\n", ps >> 20);
+                       return -EINVAL;
                }
+               return 0;
 
-               case Opt_nr_inodes:
-                       /* memparse() will accept a K/M/G without a digit */
-                       if (!isdigit(*args[0].from))
-                               goto bad_val;
-                       pconfig->nr_inodes = memparse(args[0].from, &rest);
-                       break;
+       case Opt_min_size:
+               /* memparse() will accept a K/M/G without a digit */
+               if (!isdigit(param->string[0]))
+                       goto bad_val;
+               ctx->min_size_opt = memparse(param->string, &rest);
+               ctx->min_val_type = SIZE_STD;
+               if (*rest == '%')
+                       ctx->min_val_type = SIZE_PERCENT;
+               return 0;
 
-               case Opt_pagesize: {
-                       unsigned long ps;
-                       ps = memparse(args[0].from, &rest);
-                       pconfig->hstate = size_to_hstate(ps);
-                       if (!pconfig->hstate) {
-                               pr_err("Unsupported page size %lu MB\n",
-                                       ps >> 20);
-                               return -EINVAL;
-                       }
-                       break;
-               }
+       default:
+               return -EINVAL;
+       }
 
-               case Opt_min_size: {
-                       /* memparse() will accept a K/M/G without a digit */
-                       if (!isdigit(*args[0].from))
-                               goto bad_val;
-                       min_size_opt = memparse(args[0].from, &rest);
-                       min_val_type = SIZE_STD;
-                       if (*rest == '%')
-                               min_val_type = SIZE_PERCENT;
-                       break;
-               }
+bad_val:
+       return invalf(fc, "hugetlbfs: Bad value '%s' for mount option '%s'\n",
+                     param->string, param->key);
+}
 
-               default:
-                       pr_err("Bad mount option: \"%s\"\n", p);
-                       return -EINVAL;
-                       break;
-               }
-       }
+/*
+ * 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
         * options to number of huge pages.  If NO_SIZE, -1 is returned.
         */
-       pconfig->max_hpages = hugetlbfs_size_to_hpages(pconfig->hstate,
-                                               max_size_opt, max_val_type);
-       pconfig->min_hpages = hugetlbfs_size_to_hpages(pconfig->hstate,
-                                               min_size_opt, min_val_type);
+       ctx->max_hpages = hugetlbfs_size_to_hpages(ctx->hstate,
+                                                  ctx->max_size_opt,
+                                                  ctx->max_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_val_type > NO_SIZE &&
-           pconfig->min_hpages > pconfig->max_hpages) {
-               pr_err("minimum size can not be greater than maximum size\n");
+       if (ctx->max_val_type > NO_SIZE &&
+           ctx->min_hpages > ctx->max_hpages) {
+               pr_err("Minimum size can not be greater than maximum size\n");
                return -EINVAL;
        }
 
        return 0;
-
-bad_val:
-       pr_err("Bad value '%s' for mount option '%s'\n", args[0].from, p);
-       return -EINVAL;
 }
 
 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_config config;
+       struct hugetlbfs_fs_context *ctx = fc->fs_private;
        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);
        if (!sbinfo)
                return -ENOMEM;
        sb->s_fs_info = sbinfo;
-       sbinfo->hstate = config.hstate;
        spin_lock_init(&sbinfo->stat_lock);
-       sbinfo->max_inodes = config.nr_inodes;
-       sbinfo->free_inodes = config.nr_inodes;
-       sbinfo->spool = NULL;
-       sbinfo->uid = config.uid;
-       sbinfo->gid = config.gid;
-       sbinfo->mode = config.mode;
+       sbinfo->hstate          = ctx->hstate;
+       sbinfo->max_inodes      = ctx->nr_inodes;
+       sbinfo->free_inodes     = ctx->nr_inodes;
+       sbinfo->spool           = NULL;
+       sbinfo->uid             = ctx->uid;
+       sbinfo->gid             = ctx->gid;
+       sbinfo->mode            = ctx->mode;
 
        /*
         * Allocate and initialize subpool if maximum or minimum size is
         * specified.  Any needed reservations (for minimim size) are taken
         * taken when the subpool is created.
         */
-       if (config.max_hpages != -1 || config.min_hpages != -1) {
-               sbinfo->spool = hugepage_new_subpool(config.hstate,
-                                                       config.max_hpages,
-                                                       config.min_hpages);
+       if (ctx->max_hpages != -1 || ctx->min_hpages != -1) {
+               sbinfo->spool = hugepage_new_subpool(ctx->hstate,
+                                                    ctx->max_hpages,
+                                                    ctx->min_hpages);
                if (!sbinfo->spool)
                        goto out_free;
        }
        sb->s_maxbytes = MAX_LFS_FILESIZE;
-       sb->s_blocksize = huge_page_size(config.hstate);
-       sb->s_blocksize_bits = huge_page_shift(config.hstate);
+       sb->s_blocksize = huge_page_size(ctx->hstate);
+       sb->s_blocksize_bits = huge_page_shift(ctx->hstate);
        sb->s_magic = HUGETLBFS_MAGIC;
        sb->s_op = &hugetlbfs_ops;
        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)
                goto out_free;
        return 0;
@@ -1278,16 +1271,52 @@ out_free:
        return -ENOMEM;
 }
 
-static struct dentry *hugetlbfs_mount(struct file_system_type *fs_type,
-       int flags, const char *dev_name, void *data)
+static int hugetlbfs_get_tree(struct fs_context *fc)
+{
+       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 = {
-       .name           = "hugetlbfs",
-       .mount          = hugetlbfs_mount,
-       .kill_sb        = kill_litter_super,
+       .name                   = "hugetlbfs",
+       .init_fs_context        = hugetlbfs_init_fs_context,
+       .parameters             = &hugetlb_fs_parameters,
+       .kill_sb                = kill_litter_super,
 };
 
 static struct vfsmount *hugetlbfs_vfsmount[HUGE_MAX_HSTATE];
@@ -1372,8 +1401,29 @@ out:
        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)
 {
+       struct vfsmount *mnt;
        struct hstate *h;
        int error;
        int i;
@@ -1396,24 +1446,16 @@ static int __init init_hugetlbfs_fs(void)
 
        i = 0;
        for_each_hstate(h) {
-               char buf[50];
-               unsigned ps_kb = 1U << (h->order + PAGE_SHIFT - 10);
-
-               snprintf(buf, sizeof(buf), "pagesize=%uK", ps_kb);
-               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;
+               mnt = mount_one_hugetlbfs(h);
+               if (IS_ERR(mnt) && i == 0) {
+                       error = PTR_ERR(mnt);
+                       goto out;
                }
+               hugetlbfs_vfsmount[i] = mnt;
                i++;
        }
-       /* Non default hstates are optional */
-       if (!IS_ERR_OR_NULL(hugetlbfs_vfsmount[default_hstate_idx]))
-               return 0;
+
+       return 0;
 
  out:
        kmem_cache_destroy(hugetlbfs_inode_cachep);