[<prev] [next>] [<thread-prev] [day] [month] [year] [list]
Message-ID: <39297371db2b8622513fcff1d7506841585f7121.camel@kernel.org>
Date: Sat, 27 Apr 2019 08:27:16 -0400
From: Jeff Layton <jlayton@...nel.org>
To: David Howells <dhowells@...hat.com>, viro@...iv.linux.org.uk
Cc: "Yan, Zheng" <zyan@...hat.com>, Ilya Dryomov <idryomov@...il.com>,
Sage Weil <sage@...hat.com>, ceph-devel@...r.kernel.org,
linux-fsdevel@...r.kernel.org, linux-kernel@...r.kernel.org
Subject: Re: [RFC PATCH 52/68] vfs: Convert ceph to use the new mount API
On Wed, 2019-03-27 at 23:47 +0000, David Howells wrote:
> Convert the ceph filesystem to the new internal mount API as the old
> one will be obsoleted and removed. This allows greater flexibility in
> communication of mount parameters between userspace, the VFS and the
> filesystem.
>
> See Documentation/filesystems/mount_api.txt for more information.
>
> Signed-off-by: David Howells <dhowells@...hat.com>
> Reviewed-by: "Yan, Zheng" <zyan@...hat.com>
> cc: Ilya Dryomov <idryomov@...il.com>
> cc: Sage Weil <sage@...hat.com>
> cc: ceph-devel@...r.kernel.org
> ---
>
> drivers/block/rbd.c | 363 +++++++++++---------
> fs/ceph/cache.c | 9 -
> fs/ceph/cache.h | 5
> fs/ceph/super.c | 697 +++++++++++++++++++--------------------
> fs/ceph/super.h | 1
> fs/fs_context.c | 2
> fs/fs_parser.c | 2
> include/linux/ceph/ceph_debug.h | 1
> include/linux/ceph/libceph.h | 17 +
> net/ceph/ceph_common.c | 410 ++++++++++-------------
> 10 files changed, 729 insertions(+), 778 deletions(-)
>
> diff --git a/drivers/block/rbd.c b/drivers/block/rbd.c
> index 2210c1b9491b..1970af223d4b 100644
> --- a/drivers/block/rbd.c
> +++ b/drivers/block/rbd.c
> @@ -34,7 +34,7 @@
> #include <linux/ceph/cls_lock_client.h>
> #include <linux/ceph/striper.h>
> #include <linux/ceph/decode.h>
> -#include <linux/parser.h>
> +#include <linux/fs_parser.h>
> #include <linux/bsearch.h>
>
> #include <linux/kernel.h>
> @@ -747,40 +747,6 @@ static struct rbd_client *rbd_client_find(struct ceph_options *ceph_opts)
> /*
> * (Per device) rbd map options
> */
> -enum {
> - Opt_queue_depth,
> - Opt_alloc_size,
> - Opt_lock_timeout,
> - Opt_last_int,
> - /* int args above */
> - Opt_pool_ns,
> - Opt_last_string,
> - /* string args above */
> - Opt_read_only,
> - Opt_read_write,
> - Opt_lock_on_read,
> - Opt_exclusive,
> - Opt_notrim,
> - Opt_err
> -};
> -
> -static match_table_t rbd_opts_tokens = {
> - {Opt_queue_depth, "queue_depth=%d"},
> - {Opt_alloc_size, "alloc_size=%d"},
> - {Opt_lock_timeout, "lock_timeout=%d"},
> - /* int args above */
> - {Opt_pool_ns, "_pool_ns=%s"},
> - /* string args above */
> - {Opt_read_only, "read_only"},
> - {Opt_read_only, "ro"}, /* Alternate spelling */
> - {Opt_read_write, "read_write"},
> - {Opt_read_write, "rw"}, /* Alternate spelling */
> - {Opt_lock_on_read, "lock_on_read"},
> - {Opt_exclusive, "exclusive"},
> - {Opt_notrim, "notrim"},
> - {Opt_err, NULL}
> -};
> -
> struct rbd_options {
> int queue_depth;
> int alloc_size;
> @@ -799,85 +765,98 @@ struct rbd_options {
> #define RBD_EXCLUSIVE_DEFAULT false
> #define RBD_TRIM_DEFAULT true
>
> -struct parse_rbd_opts_ctx {
> - struct rbd_spec *spec;
> - struct rbd_options *opts;
> +enum {
> + Opt_alloc_size,
> + Opt_exclusive,
> + Opt_lock_on_read,
> + Opt_lock_timeout,
> + Opt_notrim,
> + Opt_pool_ns,
> + Opt_queue_depth,
> + Opt_read_only,
> + Opt_read_write,
> +};
> +
> +static const struct fs_parameter_spec rbd_param_specs[] = {
> + fsparam_u32 ("alloc_size", Opt_alloc_size),
> + fsparam_flag ("exclusive", Opt_exclusive),
> + fsparam_flag ("lock_on_read", Opt_lock_on_read),
> + fsparam_u32 ("lock_timeout", Opt_lock_timeout),
> + fsparam_flag ("notrim", Opt_notrim),
> + fsparam_string ("_pool_ns", Opt_pool_ns),
> + fsparam_u32 ("queue_depth", Opt_queue_depth),
> + fsparam_flag ("ro", Opt_read_only),
> + fsparam_flag ("rw", Opt_read_write),
> + {}
> +};
> +
> +static const struct fs_parameter_description rbd_parameters = {
> + .name = "rbd",
> + .specs = rbd_param_specs,
> };
>
> -static int parse_rbd_opts_token(char *c, void *private)
> +static int rbd_parse_param(struct ceph_config_context *ctx, struct fs_parameter *param)
> {
> - struct parse_rbd_opts_ctx *pctx = private;
> - substring_t argstr[MAX_OPT_ARGS];
> - int token, intval, ret;
> + struct rbd_options *opts = ctx->rbd_opts;
> + struct rbd_spec *spec = ctx->rbd_spec;
> + struct fs_parse_result result;
> + int ret, opt;
>
> - token = match_token(c, rbd_opts_tokens, argstr);
> - if (token < Opt_last_int) {
> - ret = match_int(&argstr[0], &intval);
> - if (ret < 0) {
> - pr_err("bad option arg (not int) at '%s'\n", c);
> - return ret;
> - }
> - dout("got int token %d val %d\n", token, intval);
> - } else if (token > Opt_last_int && token < Opt_last_string) {
> - dout("got string token %d val %s\n", token, argstr[0].from);
> - } else {
> - dout("got token %d\n", token);
> - }
> + ret = ceph_parse_option(ctx->opt, NULL, param);
> + if (ret != -ENOPARAM)
> + return ret;
> +
> + opt = fs_parse(NULL, &rbd_parameters, param, &result);
> + if (opt < 0)
> + return opt;
>
> - switch (token) {
> + switch (opt) {
> case Opt_queue_depth:
> - if (intval < 1) {
> - pr_err("queue_depth out of range\n");
> - return -EINVAL;
> - }
> - pctx->opts->queue_depth = intval;
> + if (result.uint_32 < 1)
> + goto out_of_range;
> + opts->queue_depth = result.uint_32;
> break;
> case Opt_alloc_size:
> - if (intval < SECTOR_SIZE) {
> - pr_err("alloc_size out of range\n");
> - return -EINVAL;
> - }
> - if (!is_power_of_2(intval)) {
> - pr_err("alloc_size must be a power of 2\n");
> - return -EINVAL;
> - }
> - pctx->opts->alloc_size = intval;
> + if (result.uint_32 < SECTOR_SIZE)
> + goto out_of_range;
> + if (!is_power_of_2(result.uint_32))
> + return invalf(NULL, "alloc_size must be a power of 2\n");
> + opts->alloc_size = result.uint_32;
> break;
> case Opt_lock_timeout:
> /* 0 is "wait forever" (i.e. infinite timeout) */
> - if (intval < 0 || intval > INT_MAX / 1000) {
> - pr_err("lock_timeout out of range\n");
> - return -EINVAL;
> - }
> - pctx->opts->lock_timeout = msecs_to_jiffies(intval * 1000);
> + if (result.uint_32 > INT_MAX / 1000)
> + goto out_of_range;
> + opts->lock_timeout = msecs_to_jiffies(result.uint_32 * 1000);
> break;
> case Opt_pool_ns:
> - kfree(pctx->spec->pool_ns);
> - pctx->spec->pool_ns = match_strdup(argstr);
> - if (!pctx->spec->pool_ns)
> - return -ENOMEM;
> + kfree(spec->pool_ns);
> + spec->pool_ns = param->string;
> + param->string = NULL;
> break;
> case Opt_read_only:
> - pctx->opts->read_only = true;
> + opts->read_only = true;
> break;
> case Opt_read_write:
> - pctx->opts->read_only = false;
> + opts->read_only = false;
> break;
> case Opt_lock_on_read:
> - pctx->opts->lock_on_read = true;
> + opts->lock_on_read = true;
> break;
> case Opt_exclusive:
> - pctx->opts->exclusive = true;
> + opts->exclusive = true;
> break;
> case Opt_notrim:
> - pctx->opts->trim = false;
> + opts->trim = false;
> break;
> default:
> - /* libceph prints "bad option" msg */
> return -EINVAL;
> }
>
> return 0;
> +
> +out_of_range:
> + return invalf(NULL, "ceph: %s out of range", param->key);
> }
>
> static char* obj_op_name(enum obj_operation_type op_type)
> @@ -5428,22 +5407,80 @@ static inline size_t next_token(const char **buf)
> *
> * Note: uses GFP_KERNEL for allocation.
> */
> -static inline char *dup_token(const char **buf, size_t *lenp)
> +static inline char *dup_token(const char **buf)
> {
> char *dup;
> size_t len;
>
> len = next_token(buf);
> - dup = kmemdup(*buf, len + 1, GFP_KERNEL);
> - if (!dup)
> - return NULL;
> - *(dup + len) = '\0';
> - *buf += len;
> + dup = kmemdup_nul(*buf, len, GFP_KERNEL);
> + if (dup)
> + *buf += len;
> + return dup;
> +}
>
> - if (lenp)
> - *lenp = len;
> +/*
> + * Parse the parameter string.
> + */
> +static int rbd_parse_monolithic(struct ceph_config_context *ctx, const char *data)
> +{
> + const char *sep, *key, *eq, *value;
> + char key_buf[32];
> + size_t size, klen;
> + int ret = 0;
>
> - return dup;
> + struct fs_parameter param = {
> + .key = key_buf,
> + .type = fs_value_is_string,
> + };
> +
> + do {
> + key = data;
> + sep = strchr(data, ',');
> + if (sep) {
> + data = sep + 1;
> + size = sep - key;
> + } else {
> + data = NULL;
> + size = strlen(key);
> + }
> +
> + if (!size)
> + continue;
> +
> + eq = memchr(key, '=', sep - key);
> + if (eq) {
> + klen = eq - key;
> + if (klen == 0)
> + return invalf(NULL, "Invalid option \"\"");
> + value = eq + 1;
> + param.size = sep - value;
> + } else {
> + klen = size;
> + value = NULL;
> + param.size = 0;
> + }
> +
> + if (klen >= sizeof(key_buf))
> + return invalf(NULL, "Unknown option %*.*s",
> + (int)klen, (int)klen, key);
> + memcpy(key_buf, key, klen);
> + key_buf[klen] = 0;
> +
> + if (param.size > 0) {
> + param.string = kmemdup_nul(value, param.size,
> + GFP_KERNEL);
> + if (!param.string)
> + return -ENOMEM;
> + }
> +
> + ret = rbd_parse_param(ctx, ¶m);
> + kfree(param.string);
> + if (ret < 0)
> + break;
> + } while (data);
> +
> + return ret;
> }
>
> /*
> @@ -5487,18 +5524,11 @@ static inline char *dup_token(const char **buf, size_t *lenp)
> * created. The image head is used if no snapshot id is
> * provided. Snapshot mappings are always read-only.
> */
> -static int rbd_add_parse_args(const char *buf,
> - struct ceph_options **ceph_opts,
> - struct rbd_options **opts,
> - struct rbd_spec **rbd_spec)
> +static int rbd_add_parse_args(const char *buf, struct ceph_config_context *ctx)
> {
> - size_t len;
> - char *options;
> - const char *mon_addrs;
> + const char *options, *mon_addrs;
> + size_t len, options_len, mon_addrs_size;
> char *snap_name;
> - size_t mon_addrs_size;
> - struct parse_rbd_opts_ctx pctx = { 0 };
> - struct ceph_options *copts;
> int ret;
>
> /* The first four tokens are required */
> @@ -5509,36 +5539,35 @@ static int rbd_add_parse_args(const char *buf,
> return -EINVAL;
> }
> mon_addrs = buf;
> - mon_addrs_size = len + 1;
> + mon_addrs_size = len;
> buf += len;
>
> - ret = -EINVAL;
> - options = dup_token(&buf, NULL);
> - if (!options)
> - return -ENOMEM;
> - if (!*options) {
> + options_len = next_token(&buf);
> + if (options_len == 0) {
> rbd_warn(NULL, "no options provided");
> - goto out_err;
> + return -EINVAL;
> }
> + options = buf;
> + buf += len;
>
> - pctx.spec = rbd_spec_alloc();
> - if (!pctx.spec)
> - goto out_mem;
> + ctx->rbd_spec = rbd_spec_alloc();
> + if (!ctx->rbd_spec)
> + return -ENOMEM;
>
> - pctx.spec->pool_name = dup_token(&buf, NULL);
> - if (!pctx.spec->pool_name)
> - goto out_mem;
> - if (!*pctx.spec->pool_name) {
> + ctx->rbd_spec->pool_name = dup_token(&buf);
> + if (!ctx->rbd_spec->pool_name)
> + return -ENOMEM;
> + if (!*ctx->rbd_spec->pool_name) {
> rbd_warn(NULL, "no pool name provided");
> - goto out_err;
> + return -EINVAL;
> }
>
> - pctx.spec->image_name = dup_token(&buf, NULL);
> - if (!pctx.spec->image_name)
> - goto out_mem;
> - if (!*pctx.spec->image_name) {
> + ctx->rbd_spec->image_name = dup_token(&buf);
> + if (!ctx->rbd_spec->image_name)
> + return -ENOMEM;
> + if (!*ctx->rbd_spec->image_name) {
> rbd_warn(NULL, "no image name provided");
> - goto out_err;
> + return -EINVAL;
> }
>
> /*
> @@ -5550,51 +5579,37 @@ static int rbd_add_parse_args(const char *buf,
> buf = RBD_SNAP_HEAD_NAME; /* No snapshot supplied */
> len = sizeof (RBD_SNAP_HEAD_NAME) - 1;
> } else if (len > RBD_MAX_SNAP_NAME_LEN) {
> - ret = -ENAMETOOLONG;
> - goto out_err;
> + return -ENAMETOOLONG;
> }
> - snap_name = kmemdup(buf, len + 1, GFP_KERNEL);
> +
> + snap_name = kmemdup_nul(buf, len, GFP_KERNEL);
> if (!snap_name)
> - goto out_mem;
> - *(snap_name + len) = '\0';
> - pctx.spec->snap_name = snap_name;
> + return -ENOMEM;
> + ctx->rbd_spec->snap_name = snap_name;
>
> /* Initialize all rbd options to the defaults */
>
> - pctx.opts = kzalloc(sizeof(*pctx.opts), GFP_KERNEL);
> - if (!pctx.opts)
> - goto out_mem;
> -
> - pctx.opts->read_only = RBD_READ_ONLY_DEFAULT;
> - pctx.opts->queue_depth = RBD_QUEUE_DEPTH_DEFAULT;
> - pctx.opts->alloc_size = RBD_ALLOC_SIZE_DEFAULT;
> - pctx.opts->lock_timeout = RBD_LOCK_TIMEOUT_DEFAULT;
> - pctx.opts->lock_on_read = RBD_LOCK_ON_READ_DEFAULT;
> - pctx.opts->exclusive = RBD_EXCLUSIVE_DEFAULT;
> - pctx.opts->trim = RBD_TRIM_DEFAULT;
> -
> - copts = ceph_parse_options(options, mon_addrs,
> - mon_addrs + mon_addrs_size - 1,
> - parse_rbd_opts_token, &pctx);
> - if (IS_ERR(copts)) {
> - ret = PTR_ERR(copts);
> - goto out_err;
> - }
> - kfree(options);
> + ctx->rbd_opts = kzalloc(sizeof(*ctx->rbd_opts), GFP_KERNEL);
> + if (!ctx->rbd_opts)
> + return -ENOMEM;
>
> - *ceph_opts = copts;
> - *opts = pctx.opts;
> - *rbd_spec = pctx.spec;
> + ctx->rbd_opts->read_only = RBD_READ_ONLY_DEFAULT;
> + ctx->rbd_opts->queue_depth = RBD_QUEUE_DEPTH_DEFAULT;
> + ctx->rbd_opts->alloc_size = RBD_ALLOC_SIZE_DEFAULT;
> + ctx->rbd_opts->lock_timeout = RBD_LOCK_TIMEOUT_DEFAULT;
> + ctx->rbd_opts->lock_on_read = RBD_LOCK_ON_READ_DEFAULT;
> + ctx->rbd_opts->exclusive = RBD_EXCLUSIVE_DEFAULT;
> + ctx->rbd_opts->trim = RBD_TRIM_DEFAULT;
>
> - return 0;
> -out_mem:
> - ret = -ENOMEM;
> -out_err:
> - kfree(pctx.opts);
> - rbd_spec_put(pctx.spec);
> - kfree(options);
> + ctx->opt = ceph_alloc_options();
> + if (!ctx->opt)
> + return -ENOMEM;
>
> - return ret;
> + ret = ceph_parse_server_specs(ctx->opt, NULL, mon_addrs, mon_addrs_size);
> + if (ret < 0)
> + return ret;
> +
> + return rbd_parse_monolithic(ctx, options);
> }
>
> static void rbd_dev_image_unlock(struct rbd_device *rbd_dev)
> @@ -6001,10 +6016,8 @@ static ssize_t do_rbd_add(struct bus_type *bus,
> const char *buf,
> size_t count)
> {
> + struct ceph_config_context ctx = {};
> struct rbd_device *rbd_dev = NULL;
> - struct ceph_options *ceph_opts = NULL;
> - struct rbd_options *rbd_opts = NULL;
> - struct rbd_spec *spec = NULL;
> struct rbd_client *rbdc;
> int rc;
>
> @@ -6012,33 +6025,34 @@ static ssize_t do_rbd_add(struct bus_type *bus,
> return -ENODEV;
>
> /* parse add command */
> - rc = rbd_add_parse_args(buf, &ceph_opts, &rbd_opts, &spec);
> + rc = rbd_add_parse_args(buf, &ctx);
> if (rc < 0)
> goto out;
>
> - rbdc = rbd_get_client(ceph_opts);
> + rbdc = rbd_get_client(ctx.opt);
> if (IS_ERR(rbdc)) {
> rc = PTR_ERR(rbdc);
> goto err_out_args;
> }
>
> /* pick the pool */
> - rc = ceph_pg_poolid_by_name(rbdc->client->osdc.osdmap, spec->pool_name);
> + rc = ceph_pg_poolid_by_name(rbdc->client->osdc.osdmap,
> + ctx.rbd_spec->pool_name);
> if (rc < 0) {
> if (rc == -ENOENT)
> - pr_info("pool %s does not exist\n", spec->pool_name);
> + pr_info("pool %s does not exist\n", ctx.rbd_spec->pool_name);
> goto err_out_client;
> }
> - spec->pool_id = (u64)rc;
> + ctx.rbd_spec->pool_id = (u64)rc;
>
> - rbd_dev = rbd_dev_create(rbdc, spec, rbd_opts);
> + rbd_dev = rbd_dev_create(rbdc, ctx.rbd_spec, ctx.rbd_opts);
> if (!rbd_dev) {
> rc = -ENOMEM;
> goto err_out_client;
> }
> rbdc = NULL; /* rbd_dev now owns this */
> - spec = NULL; /* rbd_dev now owns this */
> - rbd_opts = NULL; /* rbd_dev now owns this */
> + ctx.rbd_spec = NULL; /* rbd_dev now owns this */
> + ctx.rbd_opts = NULL; /* rbd_dev now owns this */
>
> rbd_dev->config_info = kstrdup(buf, GFP_KERNEL);
> if (!rbd_dev->config_info) {
> @@ -6106,8 +6120,9 @@ static ssize_t do_rbd_add(struct bus_type *bus,
> err_out_client:
> rbd_put_client(rbdc);
> err_out_args:
> - rbd_spec_put(spec);
> - kfree(rbd_opts);
> + rbd_spec_put(ctx.rbd_spec);
> + kfree(ctx.rbd_opts);
> + ceph_destroy_options(ctx.opt);
> goto out;
> }
>
> diff --git a/fs/ceph/cache.c b/fs/ceph/cache.c
> index 1bf3502bdd6f..890d914ff5d5 100644
> --- a/fs/ceph/cache.c
> +++ b/fs/ceph/cache.c
> @@ -21,6 +21,7 @@
> *
> */
>
> +#include <linux/fs_context.h>
> #include "super.h"
> #include "cache.h"
>
> @@ -62,7 +63,7 @@ void ceph_fscache_unregister(void)
> fscache_unregister_netfs(&ceph_cache_netfs);
> }
>
> -int ceph_fscache_register_fs(struct ceph_fs_client* fsc)
> +int ceph_fscache_register_fs(struct fs_context *fc, struct ceph_fs_client* fsc)
> {
> const struct ceph_fsid *fsid = &fsc->client->fsid;
> const char *fscache_uniq = fsc->mount_options->fscache_uniq;
> @@ -79,8 +80,8 @@ int ceph_fscache_register_fs(struct ceph_fs_client* fsc)
> if (uniq_len && memcmp(ent->uniquifier, fscache_uniq, uniq_len))
> continue;
>
> - pr_err("fscache cookie already registered for fsid %pU\n", fsid);
> - pr_err(" use fsc=%%s mount option to specify a uniquifier\n");
> + errorf(fc, "fscache cookie already registered for fsid %pU\n", fsid);
> + errorf(fc, " use fsc=%%s mount option to specify a uniquifier\n");
> err = -EBUSY;
> goto out_unlock;
> }
> @@ -108,7 +109,7 @@ int ceph_fscache_register_fs(struct ceph_fs_client* fsc)
> list_add_tail(&ent->list, &ceph_fscache_list);
> } else {
> kfree(ent);
> - pr_err("unable to register fscache cookie for fsid %pU\n",
> + errorf(fc, "unable to register fscache cookie for fsid %pU\n",
> fsid);
> /* all other fs ignore this error */
> }
> diff --git a/fs/ceph/cache.h b/fs/ceph/cache.h
> index 7e72c7594f0c..0df0eb9e7c33 100644
> --- a/fs/ceph/cache.h
> +++ b/fs/ceph/cache.h
> @@ -31,7 +31,7 @@ extern struct fscache_netfs ceph_cache_netfs;
> int ceph_fscache_register(void);
> void ceph_fscache_unregister(void);
>
> -int ceph_fscache_register_fs(struct ceph_fs_client* fsc);
> +int ceph_fscache_register_fs(struct fs_context *fc, struct ceph_fs_client* fsc);
> void ceph_fscache_unregister_fs(struct ceph_fs_client* fsc);
>
> void ceph_fscache_register_inode_cookie(struct inode *inode);
> @@ -103,7 +103,8 @@ static inline void ceph_fscache_unregister(void)
> {
> }
>
> -static inline int ceph_fscache_register_fs(struct ceph_fs_client* fsc)
> +static inline int ceph_fscache_register_fs(struct fs_context *fc,
> + struct ceph_fs_client *fsc)
> {
> return 0;
> }
> diff --git a/fs/ceph/super.c b/fs/ceph/super.c
> index 6d5bb2f74612..edfe9cf08fe5 100644
> --- a/fs/ceph/super.c
> +++ b/fs/ceph/super.c
> @@ -8,7 +8,8 @@
> #include <linux/in6.h>
> #include <linux/module.h>
> #include <linux/mount.h>
> -#include <linux/parser.h>
> +#include <linux/fs_context.h>
> +#include <linux/fs_parser.h>
> #include <linux/sched.h>
> #include <linux/seq_file.h>
> #include <linux/slab.h>
> @@ -128,272 +129,290 @@ static int ceph_sync_fs(struct super_block *sb, int wait)
> * mount options
> */
> enum {
> - Opt_wsize,
> - Opt_rsize,
> - Opt_rasize,
> - Opt_caps_wanted_delay_min,
> - Opt_caps_wanted_delay_max,
> + Opt_acl,
> + Opt_asyncreaddir,
> Opt_caps_max,
> - Opt_readdir_max_entries,
> - Opt_readdir_max_bytes,
> + Opt_caps_wanted_delay_max,
> + Opt_caps_wanted_delay_min,
> Opt_congestion_kb,
> - Opt_last_int,
> - /* int args above */
> - Opt_snapdirname,
> - Opt_mds_namespace,
> - Opt_fscache_uniq,
> - Opt_last_string,
> - /* string args above */
> - Opt_dirstat,
> - Opt_nodirstat,
> - Opt_rbytes,
> - Opt_norbytes,
> - Opt_asyncreaddir,
> - Opt_noasyncreaddir,
> + Opt_copyfrom,
> Opt_dcache,
> - Opt_nodcache,
> - Opt_ino32,
> - Opt_noino32,
> + Opt_dirstat,
> Opt_fscache,
> - Opt_nofscache,
> + Opt_ino32,
> + Opt_mds_namespace,
> Opt_poolperm,
> - Opt_nopoolperm,
> - Opt_require_active_mds,
> - Opt_norequire_active_mds,
> -#ifdef CONFIG_CEPH_FS_POSIX_ACL
> - Opt_acl,
> -#endif
> - Opt_noacl,
> Opt_quotadf,
> - Opt_noquotadf,
> - Opt_copyfrom,
> - Opt_nocopyfrom,
> + Opt_rasize,
> + Opt_rbytes,
> + Opt_readdir_max_bytes,
> + Opt_readdir_max_entries,
> + Opt_require_active_mds,
> + Opt_rsize,
> + Opt_snapdirname,
> + Opt_source,
> + Opt_wsize,
> };
>
> -static match_table_t fsopt_tokens = {
> - {Opt_wsize, "wsize=%d"},
> - {Opt_rsize, "rsize=%d"},
> - {Opt_rasize, "rasize=%d"},
> - {Opt_caps_wanted_delay_min, "caps_wanted_delay_min=%d"},
> - {Opt_caps_wanted_delay_max, "caps_wanted_delay_max=%d"},
> - {Opt_caps_max, "caps_max=%d"},
> - {Opt_readdir_max_entries, "readdir_max_entries=%d"},
> - {Opt_readdir_max_bytes, "readdir_max_bytes=%d"},
> - {Opt_congestion_kb, "write_congestion_kb=%d"},
> - /* int args above */
> - {Opt_snapdirname, "snapdirname=%s"},
> - {Opt_mds_namespace, "mds_namespace=%s"},
> - {Opt_fscache_uniq, "fsc=%s"},
> - /* string args above */
> - {Opt_dirstat, "dirstat"},
> - {Opt_nodirstat, "nodirstat"},
> - {Opt_rbytes, "rbytes"},
> - {Opt_norbytes, "norbytes"},
> - {Opt_asyncreaddir, "asyncreaddir"},
> - {Opt_noasyncreaddir, "noasyncreaddir"},
> - {Opt_dcache, "dcache"},
> - {Opt_nodcache, "nodcache"},
> - {Opt_ino32, "ino32"},
> - {Opt_noino32, "noino32"},
> - {Opt_fscache, "fsc"},
> - {Opt_nofscache, "nofsc"},
> - {Opt_poolperm, "poolperm"},
> - {Opt_nopoolperm, "nopoolperm"},
> - {Opt_require_active_mds, "require_active_mds"},
> - {Opt_norequire_active_mds, "norequire_active_mds"},
> -#ifdef CONFIG_CEPH_FS_POSIX_ACL
> - {Opt_acl, "acl"},
> -#endif
> - {Opt_noacl, "noacl"},
> - {Opt_quotadf, "quotadf"},
> - {Opt_noquotadf, "noquotadf"},
> - {Opt_copyfrom, "copyfrom"},
> - {Opt_nocopyfrom, "nocopyfrom"},
> - {-1, NULL}
> +static const struct fs_parameter_spec ceph_param_specs[] = {
> + fsparam_flag_no ("acl", Opt_acl),
> + fsparam_flag_no ("asyncreaddir", Opt_asyncreaddir),
> + fsparam_u32 ("caps_max", Opt_caps_max),
> + fsparam_u32 ("caps_wanted_delay_max", Opt_caps_wanted_delay_max),
> + fsparam_u32 ("caps_wanted_delay_min", Opt_caps_wanted_delay_min),
> + fsparam_s32 ("write_congestion_kb", Opt_congestion_kb),
> + fsparam_flag_no ("copyfrom", Opt_copyfrom),
> + fsparam_flag_no ("dcache", Opt_dcache),
> + fsparam_flag_no ("dirstat", Opt_dirstat),
> + __fsparam (fs_param_is_string, "fsc", Opt_fscache,
> + fs_param_neg_with_no | fs_param_v_optional),
> + fsparam_flag_no ("ino32", Opt_ino32),
> + fsparam_string ("mds_namespace", Opt_mds_namespace),
> + fsparam_flag_no ("poolperm", Opt_poolperm),
> + fsparam_flag_no ("quotadf", Opt_quotadf),
> + fsparam_u32 ("rasize", Opt_rasize),
> + fsparam_flag_no ("rbytes", Opt_rbytes),
> + fsparam_s32 ("readdir_max_bytes", Opt_readdir_max_bytes),
> + fsparam_s32 ("readdir_max_entries", Opt_readdir_max_entries),
> + fsparam_flag_no ("require_active_mds", Opt_require_active_mds),
> + fsparam_u32 ("rsize", Opt_rsize),
> + fsparam_string ("snapdirname", Opt_snapdirname),
> + fsparam_string ("source", Opt_source),
> + fsparam_u32 ("wsize", Opt_wsize),
> + {}
> };
>
> -static int parse_fsopt_token(char *c, void *private)
> +static const struct fs_parameter_description ceph_fs_parameters = {
> + .name = "ceph",
> + .specs = ceph_param_specs,
> +};
> +
> +/*
> + * Parse the source parameter. Distinguish the server list from the path.
> + * Internally we do not include the leading '/' in the path.
> + *
> + * The source will look like:
> + * <server_spec>[,<server_spec>...]:[<path>]
> + * where
> + * <server_spec> is <ip>[:<port>]
> + * <path> is optional, but if present must begin with '/'
> + */
> +static int ceph_parse_source(struct fs_context *fc, struct fs_parameter *param)
> {
> - struct ceph_mount_options *fsopt = private;
> - substring_t argstr[MAX_OPT_ARGS];
> - int token, intval, ret;
> -
> - token = match_token((char *)c, fsopt_tokens, argstr);
> - if (token < 0)
> - return -EINVAL;
> -
> - if (token < Opt_last_int) {
> - ret = match_int(&argstr[0], &intval);
> - if (ret < 0) {
> - pr_err("bad option arg (not int) at '%s'\n", c);
> - return ret;
> + struct ceph_config_context *ctx = fc->fs_private;
> + struct ceph_mount_options *fsopt = ctx->mount_options;
> + char *dev_name = param->string, *dev_name_end;
> + int ret;
> +
> + dout("parse_mount_options %p, dev_name '%s'\n", fsopt, dev_name);
> +
> + if (fc->source)
> + return invalf(fc, "Multiple sources specified");
> + if (!dev_name || !*dev_name)
> + return invalf(fc, "Empty source");
> + if (dev_name[0] == '/')
> + return invalf(fc, "Missing colon");
> +
> + dev_name_end = strchr(dev_name + 1, '/');
> + if (dev_name_end) {
> + if (strlen(dev_name_end) > 1) {
> + kfree(fsopt->server_path);
> + fsopt->server_path = kstrdup(dev_name_end, GFP_KERNEL);
> + if (!fsopt->server_path)
> + return -ENOMEM;
> }
> - dout("got int token %d val %d\n", token, intval);
> - } else if (token > Opt_last_int && token < Opt_last_string) {
> - dout("got string token %d val %s\n", token,
> - argstr[0].from);
> } else {
> - dout("got token %d\n", token);
> + dev_name_end = dev_name + strlen(dev_name);
> + }
> +
> + /* Trim off the path and the colon separator */
> + dev_name_end--;
> + if (*dev_name_end != ':')
> + return invalf(fc, "device name is missing path (no : separator in %s)\n",
> + dev_name);
> + *dev_name_end = 0;
> +
> + dout("device name '%s'\n", dev_name);
> + if (fsopt->server_path)
> + dout("server path '%s'\n", fsopt->server_path);
> +
> + param->size = dev_name_end - dev_name;
> + ret = ceph_parse_server_specs(ctx->opt, fc,
> + param->string, dev_name_end - dev_name);
> + if (ret == 0) {
> + fc->source = param->string;
> + param->string = NULL;
> }
>
> - switch (token) {
> + return 0;
> +}
> +
> +static int ceph_parse_param(struct fs_context *fc, struct fs_parameter *param)
> +{
> + struct ceph_config_context *ctx = fc->fs_private;
> + struct ceph_mount_options *fsopt = ctx->mount_options;
> + struct fs_parse_result result;
> + int ret, opt;
> +
> + ret = ceph_parse_option(ctx->opt, fc, param);
> + if (ret != -ENOPARAM)
> + return ret;
> +
> + opt = fs_parse(fc, &ceph_fs_parameters, param, &result);
> + if (opt < 0)
> + return opt;
> +
> + switch (opt) {
> + case Opt_source:
> + return ceph_parse_source(fc, param);
> case Opt_snapdirname:
> kfree(fsopt->snapdir_name);
> - fsopt->snapdir_name = kstrndup(argstr[0].from,
> - argstr[0].to-argstr[0].from,
> - GFP_KERNEL);
> - if (!fsopt->snapdir_name)
> - return -ENOMEM;
> + fsopt->snapdir_name = param->string;
> + param->string = NULL;
> break;
> case Opt_mds_namespace:
> kfree(fsopt->mds_namespace);
> - fsopt->mds_namespace = kstrndup(argstr[0].from,
> - argstr[0].to-argstr[0].from,
> - GFP_KERNEL);
> - if (!fsopt->mds_namespace)
> - return -ENOMEM;
> + fsopt->mds_namespace = param->string;
> + param->string = NULL;
> break;
> - case Opt_fscache_uniq:
> - kfree(fsopt->fscache_uniq);
> - fsopt->fscache_uniq = kstrndup(argstr[0].from,
> - argstr[0].to-argstr[0].from,
> - GFP_KERNEL);
> - if (!fsopt->fscache_uniq)
> - return -ENOMEM;
> - fsopt->flags |= CEPH_MOUNT_OPT_FSCACHE;
> - break;
> - /* misc */
> case Opt_wsize:
> - if (intval < (int)PAGE_SIZE || intval > CEPH_MAX_WRITE_SIZE)
> - return -EINVAL;
> - fsopt->wsize = ALIGN(intval, PAGE_SIZE);
> + if (result.uint_32 < (int)PAGE_SIZE || result.uint_32 > CEPH_MAX_WRITE_SIZE)
> + goto invalid_value;
> + fsopt->wsize = ALIGN(result.uint_32, PAGE_SIZE);
> break;
> case Opt_rsize:
> - if (intval < (int)PAGE_SIZE || intval > CEPH_MAX_READ_SIZE)
> - return -EINVAL;
> - fsopt->rsize = ALIGN(intval, PAGE_SIZE);
> + if (result.uint_32 < (int)PAGE_SIZE || result.uint_32 > CEPH_MAX_READ_SIZE)
> + goto invalid_value;
> + fsopt->rsize = ALIGN(result.uint_32, PAGE_SIZE);
> break;
> case Opt_rasize:
> - if (intval < 0)
> - return -EINVAL;
> - fsopt->rasize = ALIGN(intval, PAGE_SIZE);
> + fsopt->rasize = ALIGN(result.uint_32, PAGE_SIZE);
> break;
> case Opt_caps_wanted_delay_min:
> - if (intval < 1)
> - return -EINVAL;
> - fsopt->caps_wanted_delay_min = intval;
> + if (result.uint_32 < 1)
> + goto invalid_value;
> + fsopt->caps_wanted_delay_min = result.uint_32;
> break;
> case Opt_caps_wanted_delay_max:
> - if (intval < 1)
> - return -EINVAL;
> - fsopt->caps_wanted_delay_max = intval;
> + if (result.uint_32 < 1)
> + goto invalid_value;
> + fsopt->caps_wanted_delay_max = result.uint_32;
> break;
> case Opt_caps_max:
> - if (intval < 0)
> - return -EINVAL;
> - fsopt->caps_max = intval;
> + fsopt->caps_max = result.uint_32;
> break;
> case Opt_readdir_max_entries:
> - if (intval < 1)
> - return -EINVAL;
> - fsopt->max_readdir = intval;
> + if (result.uint_32 < 1)
> + goto invalid_value;
> + fsopt->max_readdir = result.uint_32;
> break;
> case Opt_readdir_max_bytes:
> - if (intval < (int)PAGE_SIZE && intval != 0)
> - return -EINVAL;
> - fsopt->max_readdir_bytes = intval;
> + if (result.uint_32 < (int)PAGE_SIZE && result.uint_32 != 0)
> + goto invalid_value;
> + fsopt->max_readdir_bytes = result.uint_32;
> break;
> case Opt_congestion_kb:
> - if (intval < 1024) /* at least 1M */
> - return -EINVAL;
> - fsopt->congestion_kb = intval;
> + if (result.uint_32 < 1024) /* at least 1M */
> + goto invalid_value;
> + fsopt->congestion_kb = result.uint_32;
> break;
> case Opt_dirstat:
> - fsopt->flags |= CEPH_MOUNT_OPT_DIRSTAT;
> - break;
> - case Opt_nodirstat:
> - fsopt->flags &= ~CEPH_MOUNT_OPT_DIRSTAT;
> + if (!result.negated)
> + fsopt->flags |= CEPH_MOUNT_OPT_DIRSTAT;
> + else
> + fsopt->flags &= ~CEPH_MOUNT_OPT_DIRSTAT;
> break;
> case Opt_rbytes:
> - fsopt->flags |= CEPH_MOUNT_OPT_RBYTES;
> - break;
> - case Opt_norbytes:
> - fsopt->flags &= ~CEPH_MOUNT_OPT_RBYTES;
> + if (!result.negated)
> + fsopt->flags |= CEPH_MOUNT_OPT_RBYTES;
> + else
> + fsopt->flags &= ~CEPH_MOUNT_OPT_RBYTES;
> break;
> case Opt_asyncreaddir:
> - fsopt->flags &= ~CEPH_MOUNT_OPT_NOASYNCREADDIR;
> - break;
> - case Opt_noasyncreaddir:
> - fsopt->flags |= CEPH_MOUNT_OPT_NOASYNCREADDIR;
> + if (!result.negated)
> + fsopt->flags &= ~CEPH_MOUNT_OPT_NOASYNCREADDIR;
> + else
> + fsopt->flags |= CEPH_MOUNT_OPT_NOASYNCREADDIR;
> break;
> case Opt_dcache:
> - fsopt->flags |= CEPH_MOUNT_OPT_DCACHE;
> - break;
> - case Opt_nodcache:
> - fsopt->flags &= ~CEPH_MOUNT_OPT_DCACHE;
> + if (!result.negated)
> + fsopt->flags |= CEPH_MOUNT_OPT_DCACHE;
> + else
> + fsopt->flags &= ~CEPH_MOUNT_OPT_DCACHE;
> break;
> case Opt_ino32:
> - fsopt->flags |= CEPH_MOUNT_OPT_INO32;
> - break;
> - case Opt_noino32:
> - fsopt->flags &= ~CEPH_MOUNT_OPT_INO32;
> + if (!result.negated)
> + fsopt->flags |= CEPH_MOUNT_OPT_INO32;
> + else
> + fsopt->flags &= ~CEPH_MOUNT_OPT_INO32;
> break;
> +
> case Opt_fscache:
> - fsopt->flags |= CEPH_MOUNT_OPT_FSCACHE;
> - kfree(fsopt->fscache_uniq);
> - fsopt->fscache_uniq = NULL;
> - break;
> - case Opt_nofscache:
> - fsopt->flags &= ~CEPH_MOUNT_OPT_FSCACHE;
> kfree(fsopt->fscache_uniq);
> fsopt->fscache_uniq = NULL;
> + if (result.negated) {
> + fsopt->flags &= ~CEPH_MOUNT_OPT_FSCACHE;
> + } else {
> + fsopt->flags |= CEPH_MOUNT_OPT_FSCACHE;
> + fsopt->fscache_uniq = param->string;
> + param->string = NULL;
> + }
> break;
> +
> case Opt_poolperm:
> - fsopt->flags &= ~CEPH_MOUNT_OPT_NOPOOLPERM;
> - break;
> - case Opt_nopoolperm:
> - fsopt->flags |= CEPH_MOUNT_OPT_NOPOOLPERM;
> + if (!result.negated)
> + fsopt->flags &= ~CEPH_MOUNT_OPT_NOPOOLPERM;
> + else
> + fsopt->flags |= CEPH_MOUNT_OPT_NOPOOLPERM;
> break;
> case Opt_require_active_mds:
> - fsopt->flags &= ~CEPH_MOUNT_OPT_MOUNTWAIT;
> - break;
> - case Opt_norequire_active_mds:
> - fsopt->flags |= CEPH_MOUNT_OPT_MOUNTWAIT;
> + if (!result.negated)
> + fsopt->flags &= ~CEPH_MOUNT_OPT_MOUNTWAIT;
> + else
> + fsopt->flags |= CEPH_MOUNT_OPT_MOUNTWAIT;
> break;
> case Opt_quotadf:
> - fsopt->flags &= ~CEPH_MOUNT_OPT_NOQUOTADF;
> - break;
> - case Opt_noquotadf:
> - fsopt->flags |= CEPH_MOUNT_OPT_NOQUOTADF;
> + if (!result.negated)
> + fsopt->flags &= ~CEPH_MOUNT_OPT_NOQUOTADF;
> + else
> + fsopt->flags |= CEPH_MOUNT_OPT_NOQUOTADF;
> break;
> case Opt_copyfrom:
> - fsopt->flags &= ~CEPH_MOUNT_OPT_NOCOPYFROM;
> + if (!result.negated)
> + fsopt->flags &= ~CEPH_MOUNT_OPT_NOCOPYFROM;
> + else
> + fsopt->flags |= CEPH_MOUNT_OPT_NOCOPYFROM;
> break;
> - case Opt_nocopyfrom:
> - fsopt->flags |= CEPH_MOUNT_OPT_NOCOPYFROM;
> - break;
> -#ifdef CONFIG_CEPH_FS_POSIX_ACL
> case Opt_acl:
> - fsopt->sb_flags |= SB_POSIXACL;
> - break;
> + if (!result.negated) {
> +#ifdef CONFIG_CEPH_FS_POSIX_ACL
> + fc->sb_flags |= SB_POSIXACL;
> +#else
> + return invalf(fc, "POSIX ACL support is disabled");
> #endif
> - case Opt_noacl:
> - fsopt->sb_flags &= ~SB_POSIXACL;
> + } else {
> + fc->sb_flags &= ~SB_POSIXACL;
> + }
> break;
> default:
> - BUG_ON(token);
> + BUG();
> }
> return 0;
> +
> +invalid_value:
> + return invalf(fc, "ceph: Invalid value for %s", param->key);
> }
>
> static void destroy_mount_options(struct ceph_mount_options *args)
> {
> - dout("destroy_mount_options %p\n", args);
> - kfree(args->snapdir_name);
> - kfree(args->mds_namespace);
> - kfree(args->server_path);
> - kfree(args->fscache_uniq);
> - kfree(args);
> + if (args) {
> + dout("destroy_mount_options %p\n", args);
> + kfree(args->snapdir_name);
> + kfree(args->mds_namespace);
> + kfree(args->server_path);
> + kfree(args->fscache_uniq);
> + kfree(args);
> + }
> }
>
> static int strcmp_null(const char *s1, const char *s2)
> @@ -436,91 +455,6 @@ static int compare_mount_options(struct ceph_mount_options *new_fsopt,
> return ceph_compare_options(new_opt, fsc->client);
> }
>
> -static int parse_mount_options(struct ceph_mount_options **pfsopt,
> - struct ceph_options **popt,
> - int flags, char *options,
> - const char *dev_name)
> -{
> - struct ceph_mount_options *fsopt;
> - const char *dev_name_end;
> - int err;
> -
> - if (!dev_name || !*dev_name)
> - return -EINVAL;
> -
> - fsopt = kzalloc(sizeof(*fsopt), GFP_KERNEL);
> - if (!fsopt)
> - return -ENOMEM;
> -
> - dout("parse_mount_options %p, dev_name '%s'\n", fsopt, dev_name);
> -
> - fsopt->sb_flags = flags;
> - fsopt->flags = CEPH_MOUNT_OPT_DEFAULT;
> -
> - fsopt->wsize = CEPH_MAX_WRITE_SIZE;
> - fsopt->rsize = CEPH_MAX_READ_SIZE;
> - fsopt->rasize = CEPH_RASIZE_DEFAULT;
> - fsopt->snapdir_name = kstrdup(CEPH_SNAPDIRNAME_DEFAULT, GFP_KERNEL);
> - if (!fsopt->snapdir_name) {
> - err = -ENOMEM;
> - goto out;
> - }
> -
> - fsopt->caps_wanted_delay_min = CEPH_CAPS_WANTED_DELAY_MIN_DEFAULT;
> - fsopt->caps_wanted_delay_max = CEPH_CAPS_WANTED_DELAY_MAX_DEFAULT;
> - fsopt->max_readdir = CEPH_MAX_READDIR_DEFAULT;
> - fsopt->max_readdir_bytes = CEPH_MAX_READDIR_BYTES_DEFAULT;
> - fsopt->congestion_kb = default_congestion_kb();
> -
> - /*
> - * Distinguish the server list from the path in "dev_name".
> - * Internally we do not include the leading '/' in the path.
> - *
> - * "dev_name" will look like:
> - * <server_spec>[,<server_spec>...]:[<path>]
> - * where
> - * <server_spec> is <ip>[:<port>]
> - * <path> is optional, but if present must begin with '/'
> - */
> - dev_name_end = strchr(dev_name, '/');
> - if (dev_name_end) {
> - if (strlen(dev_name_end) > 1) {
> - fsopt->server_path = kstrdup(dev_name_end, GFP_KERNEL);
> - if (!fsopt->server_path) {
> - err = -ENOMEM;
> - goto out;
> - }
> - }
> - } else {
> - dev_name_end = dev_name + strlen(dev_name);
> - }
> - err = -EINVAL;
> - dev_name_end--; /* back up to ':' separator */
> - if (dev_name_end < dev_name || *dev_name_end != ':') {
> - pr_err("device name is missing path (no : separator in %s)\n",
> - dev_name);
> - goto out;
> - }
> - dout("device name '%.*s'\n", (int)(dev_name_end - dev_name), dev_name);
> - if (fsopt->server_path)
> - dout("server path '%s'\n", fsopt->server_path);
> -
> - *popt = ceph_parse_options(options, dev_name, dev_name_end,
> - parse_fsopt_token, (void *)fsopt);
> - if (IS_ERR(*popt)) {
> - err = PTR_ERR(*popt);
> - goto out;
> - }
> -
> - /* success */
> - *pfsopt = fsopt;
> - return 0;
> -
> -out:
> - destroy_mount_options(fsopt);
> - return err;
> -}
> -
> /**
> * ceph_show_options - Show mount options in /proc/mounts
> * @m: seq_file to write to
> @@ -564,7 +498,7 @@ static int ceph_show_options(struct seq_file *m, struct dentry *root)
> seq_puts(m, ",noquotadf");
>
> #ifdef CONFIG_CEPH_FS_POSIX_ACL
> - if (fsopt->sb_flags & SB_POSIXACL)
> + if (root->d_sb->s_flags & SB_POSIXACL)
> seq_puts(m, ",acl");
> else
> seq_puts(m, ",noacl");
> @@ -624,12 +558,10 @@ static int extra_mon_dispatch(struct ceph_client *client, struct ceph_msg *msg)
>
> /*
> * create a new fs client
> - *
> - * Success or not, this function consumes @fsopt and @opt.
> */
> -static struct ceph_fs_client *create_fs_client(struct ceph_mount_options *fsopt,
> - struct ceph_options *opt)
> +static struct ceph_fs_client *create_fs_client(struct fs_context *fc)
> {
> + struct ceph_config_context *ctx = fc->fs_private;
> struct ceph_fs_client *fsc;
> int page_count;
> size_t size;
> @@ -641,17 +573,18 @@ static struct ceph_fs_client *create_fs_client(struct ceph_mount_options *fsopt,
> goto fail;
> }
>
> - fsc->client = ceph_create_client(opt, fsc);
> + fsc->client = ceph_create_client(ctx->opt, fsc);
> if (IS_ERR(fsc->client)) {
> + errorf(fc, "ceph: Failed to create client");
> err = PTR_ERR(fsc->client);
> goto fail;
> }
> - opt = NULL; /* fsc->client now owns this */
> + ctx->opt = NULL; /* fsc->client now owns this */
>
> fsc->client->extra_mon_dispatch = extra_mon_dispatch;
> ceph_set_opt(fsc->client, ABORT_ON_FULL);
>
> - if (!fsopt->mds_namespace) {
> + if (!ctx->mount_options->mds_namespace) {
> ceph_monc_want_map(&fsc->client->monc, CEPH_SUB_MDSMAP,
> 0, true);
> } else {
> @@ -659,7 +592,8 @@ static struct ceph_fs_client *create_fs_client(struct ceph_mount_options *fsopt,
> 0, false);
> }
>
> - fsc->mount_options = fsopt;
> + fsc->mount_options = ctx->mount_options;
> + ctx->mount_options = NULL;
>
> fsc->sb = NULL;
> fsc->mount_state = CEPH_MOUNT_MOUNTING;
> @@ -706,9 +640,6 @@ static struct ceph_fs_client *create_fs_client(struct ceph_mount_options *fsopt,
> ceph_destroy_client(fsc->client);
> fail:
> kfree(fsc);
> - if (opt)
> - ceph_destroy_options(opt);
> - destroy_mount_options(fsopt);
> return ERR_PTR(err);
> }
>
> @@ -912,7 +843,7 @@ static struct dentry *open_root_dentry(struct ceph_fs_client *fsc,
> /*
> * mount: join the ceph cluster, and open root directory.
> */
> -static struct dentry *ceph_real_mount(struct ceph_fs_client *fsc)
> +static int ceph_real_mount(struct fs_context *fc, struct ceph_fs_client *fsc)
> {
> int err;
> unsigned long started = jiffies; /* note the start time */
> @@ -929,7 +860,7 @@ static struct dentry *ceph_real_mount(struct ceph_fs_client *fsc)
>
> /* setup fscache */
> if (fsc->mount_options->flags & CEPH_MOUNT_OPT_FSCACHE) {
> - err = ceph_fscache_register_fs(fsc);
> + err = ceph_fscache_register_fs(fc, fsc);
> if (err < 0)
> goto out;
> }
> @@ -943,41 +874,41 @@ static struct dentry *ceph_real_mount(struct ceph_fs_client *fsc)
> }
>
> err = ceph_fs_debugfs_init(fsc);
> - if (err < 0)
> + if (err < 0) {
> + errorf(fc, "ceph: Can't create debugfs entries: %d",
> + err);
> goto out;
> + }
>
> root = open_root_dentry(fsc, path, started);
> if (IS_ERR(root)) {
> err = PTR_ERR(root);
> goto out;
> }
> - fsc->sb->s_root = dget(root);
> - } else {
> - root = dget(fsc->sb->s_root);
> + fsc->sb->s_root = root;
> }
>
> + fc->root = dget(fsc->sb->s_root);
> fsc->mount_state = CEPH_MOUNT_MOUNTED;
> dout("mount success\n");
> mutex_unlock(&fsc->client->mount_mutex);
> - return root;
> + return err;
>
> out:
> mutex_unlock(&fsc->client->mount_mutex);
> - return ERR_PTR(err);
> + return err;
> }
>
> -static int ceph_set_super(struct super_block *s, void *data)
> +static int ceph_set_super(struct super_block *s, struct fs_context *fc)
> {
> - struct ceph_fs_client *fsc = data;
> + struct ceph_fs_client *fsc = s->s_fs_info;
> int ret;
>
> - dout("set_super %p data %p\n", s, data);
> + dout("set_super %p\n", s);
>
> - s->s_flags = fsc->mount_options->sb_flags;
> s->s_maxbytes = MAX_LFS_FILESIZE;
>
> s->s_xattr = ceph_xattr_handlers;
> - s->s_fs_info = fsc;
> fsc->sb = s;
> fsc->max_file_size = 1ULL << 40; /* temp value until we get mdsmap */
>
> @@ -987,24 +918,18 @@ static int ceph_set_super(struct super_block *s, void *data)
>
> s->s_time_gran = 1000; /* 1000 ns == 1 us */
>
> - ret = set_anon_super(s, NULL); /* what is that second arg for? */
> + ret = set_anon_super_fc(s, fc);
> if (ret != 0)
> - goto fail;
> -
> - return ret;
> -
> -fail:
> - s->s_fs_info = NULL;
> - fsc->sb = NULL;
> + fsc->sb = NULL;
> return ret;
> }
>
> /*
> * share superblock if same fs AND options
> */
> -static int ceph_compare_super(struct super_block *sb, void *data)
> +static int ceph_compare_super(struct super_block *sb, struct fs_context *fc)
> {
> - struct ceph_fs_client *new = data;
> + struct ceph_fs_client *new = fc->s_fs_info;
> struct ceph_mount_options *fsopt = new->mount_options;
> struct ceph_options *opt = new->client->options;
> struct ceph_fs_client *other = ceph_sb_to_client(sb);
> @@ -1020,7 +945,7 @@ static int ceph_compare_super(struct super_block *sb, void *data)
> dout("fsid doesn't match\n");
> return 0;
> }
> - if (fsopt->sb_flags != other->mount_options->sb_flags) {
> + if (fc->sb_flags != (sb->s_flags & ~SB_BORN)) {
> dout("flags differ\n");
> return 0;
> }
> @@ -1050,46 +975,41 @@ static int ceph_setup_bdi(struct super_block *sb, struct ceph_fs_client *fsc)
> return 0;
> }
>
> -static struct dentry *ceph_mount(struct file_system_type *fs_type,
> - int flags, const char *dev_name, void *data)
> +static int ceph_get_tree(struct fs_context *fc)
> {
> - struct super_block *sb;
> struct ceph_fs_client *fsc;
> - struct dentry *res;
> + struct super_block *sb;
> int err;
> - int (*compare_super)(struct super_block *, void *) = ceph_compare_super;
> - struct ceph_mount_options *fsopt = NULL;
> - struct ceph_options *opt = NULL;
> + int (*compare_super)(struct super_block *, struct fs_context *) =
> + ceph_compare_super;
> +
> + dout("ceph_get_tree\n");
>
> - dout("ceph_mount\n");
> + if (!fc->source)
> + return invalf(fc, "source parameter not specified");
>
> #ifdef CONFIG_CEPH_FS_POSIX_ACL
> - flags |= SB_POSIXACL;
> + fc->sb_flags |= SB_POSIXACL;
> #endif
> - err = parse_mount_options(&fsopt, &opt, flags, data, dev_name);
> - if (err < 0) {
> - res = ERR_PTR(err);
> - goto out_final;
> - }
>
> /* create client (which we may/may not use) */
> - fsc = create_fs_client(fsopt, opt);
> + fsc = create_fs_client(fc);
> if (IS_ERR(fsc)) {
> - res = ERR_CAST(fsc);
> - goto out_final;
> + err = PTR_ERR(fsc);
> + goto out;
> }
>
> err = ceph_mdsc_init(fsc);
> - if (err < 0) {
> - res = ERR_PTR(err);
> + if (err < 0)
> goto out;
> - }
>
> if (ceph_test_opt(fsc->client, NOSHARE))
> compare_super = NULL;
> - sb = sget(fs_type, compare_super, ceph_set_super, flags, fsc);
> +
> + fc->s_fs_info = fsc;
> + sb = sget_fc(fc, compare_super, ceph_set_super);
> if (IS_ERR(sb)) {
> - res = ERR_CAST(sb);
> + err = PTR_ERR(sb);
> goto out;
> }
>
> @@ -1101,30 +1021,97 @@ static struct dentry *ceph_mount(struct file_system_type *fs_type,
> } else {
> dout("get_sb using new client %p\n", fsc);
> err = ceph_setup_bdi(sb, fsc);
> - if (err < 0) {
> - res = ERR_PTR(err);
> + if (err < 0)
> goto out_splat;
> - }
> }
>
> - res = ceph_real_mount(fsc);
> - if (IS_ERR(res))
> + err = ceph_real_mount(fc, fsc);
> + if (err < 0)
> goto out_splat;
> - dout("root %p inode %p ino %llx.%llx\n", res,
> - d_inode(res), ceph_vinop(d_inode(res)));
> - return res;
> + dout("root %p inode %p ino %llx.%llx\n",
> + fc->root, d_inode(fc->root), ceph_vinop(d_inode(fc->root)));
> + return 0;
>
> out_splat:
> ceph_mdsc_close_sessions(fsc->mdsc);
> deactivate_locked_super(sb);
> - goto out_final;
> -
> out:
> - ceph_mdsc_destroy(fsc);
> - destroy_fs_client(fsc);
> -out_final:
> - dout("ceph_mount fail %ld\n", PTR_ERR(res));
> - return res;
> + dout("ceph_mount fail %d\n", err);
> + return err;
> +}
> +
> +static void ceph_free_fc(struct fs_context *fc)
> +{
> + struct ceph_config_context *ctx = fc->fs_private;
> + struct ceph_fs_client *fsc = fc->s_fs_info;
> +
> + if (fsc) {
> + ceph_mdsc_destroy(fsc);
> + destroy_fs_client(fsc);
> + }
> +
> + if (ctx) {
> + destroy_mount_options(ctx->mount_options);
> + ceph_destroy_options(ctx->opt);
> + kfree(ctx);
> + }
> +}
> +
> +static const struct fs_context_operations ceph_context_ops = {
> + .free = ceph_free_fc,
> + .parse_param = ceph_parse_param,
> + .get_tree = ceph_get_tree,
> +};
> +
> +/*
> + * Set up the filesystem mount context.
> + */
> +static int ceph_init_fs_context(struct fs_context *fc)
> +{
> + struct ceph_config_context *ctx;
> + struct ceph_mount_options *fsopt;
> +
> + ctx = kzalloc(sizeof(struct ceph_config_context), GFP_KERNEL);
> + if (!ctx)
> + goto nomem;
> +
> + ctx->mount_options = kzalloc(sizeof(struct ceph_mount_options), GFP_KERNEL);
> + if (!ctx->mount_options)
> + goto nomem_ctx;
> +
> + ctx->mount_options->snapdir_name = kstrdup(CEPH_SNAPDIRNAME_DEFAULT, GFP_KERNEL);
> + if (!ctx->mount_options->snapdir_name)
> + goto nomem_mo;
> +
> + ctx->opt = ceph_alloc_options();
> + if (!ctx->opt)
> + goto nomem_snap;
> +
> + fsopt = ctx->mount_options;
> + fsopt->flags = CEPH_MOUNT_OPT_DEFAULT;
> +
> + fsopt->wsize = CEPH_MAX_WRITE_SIZE;
> + fsopt->rsize = CEPH_MAX_READ_SIZE;
> + fsopt->rasize = CEPH_RASIZE_DEFAULT;
> +
> + fsopt->caps_wanted_delay_min = CEPH_CAPS_WANTED_DELAY_MIN_DEFAULT;
> + fsopt->caps_wanted_delay_max = CEPH_CAPS_WANTED_DELAY_MAX_DEFAULT;
> + fsopt->max_readdir = CEPH_MAX_READDIR_DEFAULT;
> + fsopt->max_readdir_bytes = CEPH_MAX_READDIR_BYTES_DEFAULT;
> + fsopt->congestion_kb = default_congestion_kb();
> +
> + fc->fs_private = ctx;
> + fc->ops = &ceph_context_ops;
> + return 0;
> +
> +nomem_snap:
> + kfree(ctx->mount_options->snapdir_name);
> +nomem_mo:
> + kfree(ctx->mount_options);
> +nomem_ctx:
> + kfree(ctx);
> +nomem:
> + return -ENOMEM;
> }
>
> static void ceph_kill_sb(struct super_block *s)
> @@ -1153,7 +1140,7 @@ static void ceph_kill_sb(struct super_block *s)
> static struct file_system_type ceph_fs_type = {
> .owner = THIS_MODULE,
> .name = "ceph",
> - .mount = ceph_mount,
> + .init_fs_context = ceph_init_fs_context,
> .kill_sb = ceph_kill_sb,
> .fs_flags = FS_RENAME_DOES_D_MOVE,
> };
> diff --git a/fs/ceph/super.h b/fs/ceph/super.h
> index 16c03188578e..47e0e4b4f7f8 100644
> --- a/fs/ceph/super.h
> +++ b/fs/ceph/super.h
> @@ -72,7 +72,6 @@
>
> struct ceph_mount_options {
> int flags;
> - int sb_flags;
>
> int wsize; /* max write size */
> int rsize; /* max read size */
> diff --git a/fs/fs_context.c b/fs/fs_context.c
> index f9ad2554c2b8..68822d126f50 100644
> --- a/fs/fs_context.c
> +++ b/fs/fs_context.c
> @@ -195,7 +195,7 @@ EXPORT_SYMBOL(vfs_parse_fs_string);
>
> /**
> * generic_parse_monolithic - Parse key[=val][,key[=val]]* mount data
> - * @ctx: The superblock configuration to fill in.
> + * @fc: The filesystem configuration to fill in.
> * @data: The data to parse
> *
> * Parse a blob of data that's in key[=val][,key[=val]]* form. This can be
> diff --git a/fs/fs_parser.c b/fs/fs_parser.c
> index bd59e1725c50..a74f1c5f3599 100644
> --- a/fs/fs_parser.c
> +++ b/fs/fs_parser.c
> @@ -64,7 +64,7 @@ static const struct fs_parameter_spec *fs_lookup_key(
>
> /*
> * fs_parse - Parse a filesystem configuration parameter
> - * @fc: The filesystem context to log errors through.
> + * @fc: The filesystem context to log errors through (or NULL).
> * @desc: The parameter description to use.
> * @param: The parameter.
> * @result: Where to place the result of the parse
> diff --git a/include/linux/ceph/ceph_debug.h b/include/linux/ceph/ceph_debug.h
> index d5a5da838caf..fa4a84e0e018 100644
> --- a/include/linux/ceph/ceph_debug.h
> +++ b/include/linux/ceph/ceph_debug.h
> @@ -2,6 +2,7 @@
> #ifndef _FS_CEPH_DEBUG_H
> #define _FS_CEPH_DEBUG_H
>
> +#undef pr_fmt
> #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
>
> #include <linux/string.h>
> diff --git a/include/linux/ceph/libceph.h b/include/linux/ceph/libceph.h
> index 337d5049ff93..82fdb821f19d 100644
> --- a/include/linux/ceph/libceph.h
> +++ b/include/linux/ceph/libceph.h
> @@ -66,6 +66,13 @@ struct ceph_options {
> struct ceph_crypto_key *key;
> };
>
> +struct ceph_config_context {
> + struct ceph_options *opt;
> + struct ceph_mount_options *mount_options;
> + struct rbd_spec *rbd_spec;
> + struct rbd_options *rbd_opts;
> +};
> +
> /*
> * defaults
> */
> @@ -278,10 +285,12 @@ extern const char *ceph_msg_type_name(int type);
> extern int ceph_check_fsid(struct ceph_client *client, struct ceph_fsid *fsid);
> extern void *ceph_kvmalloc(size_t size, gfp_t flags);
>
> -extern struct ceph_options *ceph_parse_options(char *options,
> - const char *dev_name, const char *dev_name_end,
> - int (*parse_extra_token)(char *c, void *private),
> - void *private);
> +struct fs_parameter;
> +extern struct ceph_options *ceph_alloc_options(void);
> +extern int ceph_parse_server_specs(struct ceph_options *opt, struct fs_context *fc,
> + const char *data, size_t size);
> +extern int ceph_parse_option(struct ceph_options *opt, struct fs_context *fc,
> + struct fs_parameter *param);
> int ceph_print_client_options(struct seq_file *m, struct ceph_client *client,
> bool show_all);
> extern void ceph_destroy_options(struct ceph_options *opt);
> diff --git a/net/ceph/ceph_common.c b/net/ceph/ceph_common.c
> index 79eac465ec65..40387d7d30ef 100644
> --- a/net/ceph/ceph_common.c
> +++ b/net/ceph/ceph_common.c
> @@ -10,7 +10,7 @@
> #include <linux/module.h>
> #include <linux/mount.h>
> #include <linux/nsproxy.h>
> -#include <linux/parser.h>
> +#include <linux/fs_parser.h>
> #include <linux/sched.h>
> #include <linux/seq_file.h>
> #include <linux/slab.h>
> @@ -231,70 +231,84 @@ static int parse_fsid(const char *str, struct ceph_fsid *fsid)
> * ceph options
> */
> enum {
> - Opt_osdtimeout,
> - Opt_osdkeepalivetimeout,
> + Opt_abort_on_full,
> + Opt_cephx_require_signatures,
> + Opt_cephx_sign_messages,
> + Opt_crc,
> + Opt_fsid,
> + Opt_ip,
> + Opt_key,
> Opt_mount_timeout,
> + Opt_name,
> Opt_osd_idle_ttl,
> Opt_osd_request_timeout,
> - Opt_last_int,
> - /* int args above */
> - Opt_fsid,
> - Opt_name,
> + Opt_osdkeepalivetimeout,
> + Opt_osdtimeout,
> Opt_secret,
> - Opt_key,
> - Opt_ip,
> - Opt_last_string,
> - /* string args above */
> Opt_share,
> - Opt_noshare,
> - Opt_crc,
> - Opt_nocrc,
> - Opt_cephx_require_signatures,
> - Opt_nocephx_require_signatures,
> - Opt_cephx_sign_messages,
> - Opt_nocephx_sign_messages,
> Opt_tcp_nodelay,
> - Opt_notcp_nodelay,
> - Opt_abort_on_full,
> };
>
> -static match_table_t opt_tokens = {
> - {Opt_osdtimeout, "osdtimeout=%d"},
> - {Opt_osdkeepalivetimeout, "osdkeepalive=%d"},
> - {Opt_mount_timeout, "mount_timeout=%d"},
> - {Opt_osd_idle_ttl, "osd_idle_ttl=%d"},
> - {Opt_osd_request_timeout, "osd_request_timeout=%d"},
> - /* int args above */
> - {Opt_fsid, "fsid=%s"},
> - {Opt_name, "name=%s"},
> - {Opt_secret, "secret=%s"},
> - {Opt_key, "key=%s"},
> - {Opt_ip, "ip=%s"},
> - /* string args above */
> - {Opt_share, "share"},
> - {Opt_noshare, "noshare"},
> - {Opt_crc, "crc"},
> - {Opt_nocrc, "nocrc"},
> - {Opt_cephx_require_signatures, "cephx_require_signatures"},
> - {Opt_nocephx_require_signatures, "nocephx_require_signatures"},
> - {Opt_cephx_sign_messages, "cephx_sign_messages"},
> - {Opt_nocephx_sign_messages, "nocephx_sign_messages"},
> - {Opt_tcp_nodelay, "tcp_nodelay"},
> - {Opt_notcp_nodelay, "notcp_nodelay"},
> - {Opt_abort_on_full, "abort_on_full"},
> - {-1, NULL}
> +static const struct fs_parameter_spec ceph_option_specs[] = {
> + fsparam_flag ("abort_on_full", Opt_abort_on_full),
> + fsparam_flag_no ("cephx_require_signatures", Opt_cephx_require_signatures),
> + fsparam_flag_no ("cephx_sign_messages", Opt_cephx_sign_messages),
> + fsparam_flag_no ("crc", Opt_crc),
> + fsparam_string ("fsid", Opt_fsid),
> + fsparam_string ("ip", Opt_ip),
> + fsparam_string ("key", Opt_key),
> + fsparam_u32 ("mount_timeout", Opt_mount_timeout),
> + fsparam_string ("name", Opt_name),
> + fsparam_u32 ("osd_idle_ttl", Opt_osd_idle_ttl),
> + fsparam_u32 ("osd_request_timeout", Opt_osd_request_timeout),
> + fsparam_u32 ("osdkeepalive", Opt_osdkeepalivetimeout),
> + __fsparam (fs_param_is_s32, "osdtimeout", Opt_osdtimeout, fs_param_deprecated),
> + fsparam_string ("secret", Opt_secret),
> + fsparam_flag_no ("share", Opt_share),
> + fsparam_flag_no ("tcp_nodelay", Opt_tcp_nodelay),
> + {}
> +};
> +
> +static const struct fs_parameter_description ceph_options = {
> + .name = "ceph",
> + .specs = ceph_option_specs,
> };
>
> +struct ceph_options *ceph_alloc_options(void)
> +{
> + struct ceph_options *opt;
> +
> + opt = kzalloc(sizeof(struct ceph_options), GFP_KERNEL);
> + if (!opt)
> + return NULL;
> +
> + opt->mon_addr = kcalloc(CEPH_MAX_MON, sizeof(*opt->mon_addr), GFP_KERNEL);
> + if (!opt->mon_addr) {
> + kfree(opt);
> + return NULL;
> + }
> +
> + opt->flags = CEPH_OPT_DEFAULT;
> + opt->osd_keepalive_timeout = CEPH_OSD_KEEPALIVE_DEFAULT;
> + opt->mount_timeout = CEPH_MOUNT_TIMEOUT_DEFAULT;
> + opt->osd_idle_ttl = CEPH_OSD_IDLE_TTL_DEFAULT;
> + opt->osd_request_timeout = CEPH_OSD_REQUEST_TIMEOUT_DEFAULT;
> + return opt;
> +}
> +EXPORT_SYMBOL(ceph_alloc_options);
> +
> void ceph_destroy_options(struct ceph_options *opt)
> {
> - dout("destroy_options %p\n", opt);
> - kfree(opt->name);
> - if (opt->key) {
> - ceph_crypto_key_destroy(opt->key);
> - kfree(opt->key);
> + if (opt) {
> + dout("destroy_options %p\n", opt);
> + kfree(opt->name);
> + if (opt->key) {
> + ceph_crypto_key_destroy(opt->key);
> + kfree(opt->key);
> + }
> + kfree(opt->mon_addr);
> + kfree(opt);
> }
> - kfree(opt->mon_addr);
> - kfree(opt);
> }
> EXPORT_SYMBOL(ceph_destroy_options);
>
> @@ -343,217 +357,141 @@ static int get_secret(struct ceph_crypto_key *dst, const char *name) {
> return err;
> }
>
> -struct ceph_options *
> -ceph_parse_options(char *options, const char *dev_name,
> - const char *dev_name_end,
> - int (*parse_extra_token)(char *c, void *private),
> - void *private)
> +int ceph_parse_server_specs(struct ceph_options *opt, struct fs_context *fc,
> + const char *data, size_t size)
> {
> - struct ceph_options *opt;
> - const char *c;
> - int err = -ENOMEM;
> - substring_t argstr[MAX_OPT_ARGS];
> -
> - opt = kzalloc(sizeof(*opt), GFP_KERNEL);
> - if (!opt)
> - return ERR_PTR(-ENOMEM);
> - opt->mon_addr = kcalloc(CEPH_MAX_MON, sizeof(*opt->mon_addr),
> - GFP_KERNEL);
> - if (!opt->mon_addr)
> - goto out;
> -
> - dout("parse_options %p options '%s' dev_name '%s'\n", opt, options,
> - dev_name);
> -
> - /* start with defaults */
> - opt->flags = CEPH_OPT_DEFAULT;
> - opt->osd_keepalive_timeout = CEPH_OSD_KEEPALIVE_DEFAULT;
> - opt->mount_timeout = CEPH_MOUNT_TIMEOUT_DEFAULT;
> - opt->osd_idle_ttl = CEPH_OSD_IDLE_TTL_DEFAULT;
> - opt->osd_request_timeout = CEPH_OSD_REQUEST_TIMEOUT_DEFAULT;
> -
> /* get mon ip(s) */
> /* ip1[:port1][,ip2[:port2]...] */
> - err = ceph_parse_ips(dev_name, dev_name_end, opt->mon_addr,
> - CEPH_MAX_MON, &opt->num_mon);
> - if (err < 0)
> - goto out;
> + return ceph_parse_ips(data, data + size,
> + opt->mon_addr, CEPH_MAX_MON, &opt->num_mon);
> +}
> +EXPORT_SYMBOL(ceph_parse_server_specs);
>
> - /* parse mount options */
> - while ((c = strsep(&options, ",")) != NULL) {
> - int token, intval;
> - if (!*c)
> - continue;
> - err = -EINVAL;
> - token = match_token((char *)c, opt_tokens, argstr);
> - if (token < 0 && parse_extra_token) {
> - /* extra? */
> - err = parse_extra_token((char *)c, private);
> - if (err < 0) {
> - pr_err("bad option at '%s'\n", c);
> - goto out;
> - }
> - continue;
> - }
> - if (token < Opt_last_int) {
> - err = match_int(&argstr[0], &intval);
> - if (err < 0) {
> - pr_err("bad option arg (not int) at '%s'\n", c);
> - goto out;
> - }
> - dout("got int token %d val %d\n", token, intval);
> - } else if (token > Opt_last_int && token < Opt_last_string) {
> - dout("got string token %d val %s\n", token,
> - argstr[0].from);
> - } else {
> - dout("got token %d\n", token);
> - }
> - switch (token) {
> - case Opt_ip:
> - err = ceph_parse_ips(argstr[0].from,
> - argstr[0].to,
> - &opt->my_addr,
> - 1, NULL);
> - if (err < 0)
> - goto out;
> +int ceph_parse_option(struct ceph_options *opt, struct fs_context *fc,
> + struct fs_parameter *param)
> +{
> + struct fs_parse_result result;
> + int token, err;
> +
> + dout("parse_option '%s'\n", param->key);
> +
> + token = fs_parse(fc, &ceph_options, param, &result);
> + if (token < 0)
> + return token;
> +
> + switch (token) {
> + case Opt_ip:
> + err = ceph_parse_ips(param->string,
> + param->string + param->size,
> + &opt->my_addr,
> + 1, NULL);
> + if (err == 0)
> opt->flags |= CEPH_OPT_MYIP;
> - break;
> + break;
>
> - case Opt_fsid:
> - err = parse_fsid(argstr[0].from, &opt->fsid);
> - if (err == 0)
> - opt->flags |= CEPH_OPT_FSID;
> - break;
> - case Opt_name:
> - kfree(opt->name);
> - opt->name = kstrndup(argstr[0].from,
> - argstr[0].to-argstr[0].from,
> - GFP_KERNEL);
> - if (!opt->name) {
> - err = -ENOMEM;
> - goto out;
> - }
> - break;
> - case Opt_secret:
> - ceph_crypto_key_destroy(opt->key);
> - kfree(opt->key);
> -
> - opt->key = kzalloc(sizeof(*opt->key), GFP_KERNEL);
> - if (!opt->key) {
> - err = -ENOMEM;
> - goto out;
> - }
> - err = ceph_crypto_key_unarmor(opt->key, argstr[0].from);
> - if (err < 0)
> - goto out;
> - break;
> - case Opt_key:
> - ceph_crypto_key_destroy(opt->key);
> - kfree(opt->key);
> -
> - opt->key = kzalloc(sizeof(*opt->key), GFP_KERNEL);
> - if (!opt->key) {
> - err = -ENOMEM;
> - goto out;
> - }
> - err = get_secret(opt->key, argstr[0].from);
> - if (err < 0)
> - goto out;
> - break;
> + case Opt_fsid:
> + err = parse_fsid(param->string, &opt->fsid);
> + if (err < 0)
> + return invalf(fc, "Invalid fsid");
> + opt->flags |= CEPH_OPT_FSID;
> + break;
> + case Opt_name:
> + kfree(opt->name);
> + opt->name = param->string;
> + param->string = NULL;
> + break;
> + case Opt_secret:
> + ceph_crypto_key_destroy(opt->key);
> + kfree(opt->key);
>
> - /* misc */
> - case Opt_osdtimeout:
> - pr_warn("ignoring deprecated osdtimeout option\n");
> - break;
> - case Opt_osdkeepalivetimeout:
> - /* 0 isn't well defined right now, reject it */
> - if (intval < 1 || intval > INT_MAX / 1000) {
> - pr_err("osdkeepalive out of range\n");
> - err = -EINVAL;
> - goto out;
> - }
> - opt->osd_keepalive_timeout =
> - msecs_to_jiffies(intval * 1000);
> - break;
> - case Opt_osd_idle_ttl:
> - /* 0 isn't well defined right now, reject it */
> - if (intval < 1 || intval > INT_MAX / 1000) {
> - pr_err("osd_idle_ttl out of range\n");
> - err = -EINVAL;
> - goto out;
> - }
> - opt->osd_idle_ttl = msecs_to_jiffies(intval * 1000);
> - break;
> - case Opt_mount_timeout:
> - /* 0 is "wait forever" (i.e. infinite timeout) */
> - if (intval < 0 || intval > INT_MAX / 1000) {
> - pr_err("mount_timeout out of range\n");
> - err = -EINVAL;
> - goto out;
> - }
> - opt->mount_timeout = msecs_to_jiffies(intval * 1000);
> - break;
> - case Opt_osd_request_timeout:
> - /* 0 is "wait forever" (i.e. infinite timeout) */
> - if (intval < 0 || intval > INT_MAX / 1000) {
> - pr_err("osd_request_timeout out of range\n");
> - err = -EINVAL;
> - goto out;
> - }
> - opt->osd_request_timeout = msecs_to_jiffies(intval * 1000);
> - break;
> + opt->key = kzalloc(sizeof(*opt->key), GFP_KERNEL);
> + if (!opt->key)
> + return -ENOMEM;
> + return ceph_crypto_key_unarmor(opt->key, param->string);
> + case Opt_key:
> + ceph_crypto_key_destroy(opt->key);
> + kfree(opt->key);
>
> - case Opt_share:
> + opt->key = kzalloc(sizeof(*opt->key), GFP_KERNEL);
> + if (!opt->key)
> + return -ENOMEM;
> + return get_secret(opt->key, param->string);
> +
> + /* misc */
> + case Opt_osdkeepalivetimeout:
> + /* 0 isn't well defined right now, reject it */
> + if (result.uint_32 < 1 || result.uint_32 > INT_MAX / 1000)
> + goto out_of_range;
> + opt->osd_keepalive_timeout =
> + msecs_to_jiffies(result.uint_32 * 1000);
> + break;
> + case Opt_osd_idle_ttl:
> + /* 0 isn't well defined right now, reject it */
> + if (result.uint_32 < 1 || result.uint_32 > INT_MAX / 1000)
> + goto out_of_range;
> + opt->osd_idle_ttl = msecs_to_jiffies(result.uint_32 * 1000);
> + break;
> + case Opt_mount_timeout:
> + /* 0 is "wait forever" (i.e. infinite timeout) */
> + if (result.uint_32 > INT_MAX / 1000)
> + goto out_of_range;
> + opt->mount_timeout = msecs_to_jiffies(result.uint_32 * 1000);
> + break;
> + case Opt_osd_request_timeout:
> + /* 0 is "wait forever" (i.e. infinite timeout) */
> + if (result.uint_32 > INT_MAX / 1000)
> + goto out_of_range;
> + opt->osd_request_timeout = msecs_to_jiffies(result.uint_32 * 1000);
> + break;
> +
> + case Opt_share:
> + if (!result.negated)
> opt->flags &= ~CEPH_OPT_NOSHARE;
> - break;
> - case Opt_noshare:
> + else
> opt->flags |= CEPH_OPT_NOSHARE;
> - break;
> + break;
>
> - case Opt_crc:
> + case Opt_crc:
> + if (!result.negated)
> opt->flags &= ~CEPH_OPT_NOCRC;
> - break;
> - case Opt_nocrc:
> + else
> opt->flags |= CEPH_OPT_NOCRC;
> - break;
> + break;
>
> - case Opt_cephx_require_signatures:
> + case Opt_cephx_require_signatures:
> + if (!result.negated)
> opt->flags &= ~CEPH_OPT_NOMSGAUTH;
> - break;
> - case Opt_nocephx_require_signatures:
> + else
> opt->flags |= CEPH_OPT_NOMSGAUTH;
> - break;
> - case Opt_cephx_sign_messages:
> + break;
> + case Opt_cephx_sign_messages:
> + if (!result.negated)
> opt->flags &= ~CEPH_OPT_NOMSGSIGN;
> - break;
> - case Opt_nocephx_sign_messages:
> + else
> opt->flags |= CEPH_OPT_NOMSGSIGN;
> - break;
> + break;
>
> - case Opt_tcp_nodelay:
> + case Opt_tcp_nodelay:
> + if (!result.negated)
> opt->flags |= CEPH_OPT_TCP_NODELAY;
> - break;
> - case Opt_notcp_nodelay:
> + else
> opt->flags &= ~CEPH_OPT_TCP_NODELAY;
> - break;
> + break;
>
> - case Opt_abort_on_full:
> - opt->flags |= CEPH_OPT_ABORT_ON_FULL;
> - break;
> + case Opt_abort_on_full:
> + opt->flags |= CEPH_OPT_ABORT_ON_FULL;
> + break;
>
> - default:
> - BUG_ON(token);
> - }
> + default:
> + BUG();
> }
>
> - /* success */
> - return opt;
> + return 0;
>
> -out:
> - ceph_destroy_options(opt);
> - return ERR_PTR(err);
> +out_of_range:
> + return invalf(fc, "ceph: %s out of range", param->key);
> }
> -EXPORT_SYMBOL(ceph_parse_options);
> +EXPORT_SYMBOL(ceph_parse_option);
>
> int ceph_print_client_options(struct seq_file *m, struct ceph_client *client,
> bool show_all)
>
Reviewed-by: Jeff Layton <jlayton@...nel.org>
Powered by blists - more mailing lists