On Thu, 2019-09-05 at 17:17 +0200, Ilya Dryomov wrote: > On Thu, Sep 5, 2019 at 12:24 PM Jeff Layton <jlayton@xxxxxxxxxx> wrote: > > From: David Howells <dhowells@xxxxxxxxxx> > > > > 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@xxxxxxxxxx> > > Reviewed-by: "Yan, Zheng" <zyan@xxxxxxxxxx> > > cc: Ilya Dryomov <idryomov@xxxxxxxxx> > > cc: Sage Weil <sage@xxxxxxxxxx> > > cc: ceph-devel@xxxxxxxxxxxxxxx > > Signed-off-by: Al Viro <viro@xxxxxxxxxxxxxxxxxx> > > Signed-off-by: Jeff Layton <jlayton@xxxxxxxxxx> > > --- > > drivers/block/rbd.c | 339 ++++++++-------- > > fs/ceph/cache.c | 9 +- > > fs/ceph/cache.h | 5 +- > > fs/ceph/super.c | 687 ++++++++++++++++---------------- > > 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, 716 insertions(+), 757 deletions(-) > > > > This patch is essentially the same as the patch in Al's work.mount1 > > branch, but rebased on top of some patches that we have queued up for > > cephfs in v5.4 that add a new mount option. > > > > I think it'd be best to take this via the ceph tree to avoid the need > > for a merge commit, but if you'd rather merge it via the vfs tree, > > let me know. > > Hi David, > > I see Zheng's Reviewed-by, but I'm going to have to NACK this patch, at > least as posted. Attempting to map an rbd image generates a bad memory > access in strspn(). I haven't looked at it in detail yet. > Interesting. I don't see an oops here, but the mapping fails with this: $ sudo rbd --pool rbd device map foo rbd: sysfs write failed In some cases useful info is found in syslog - try "dmesg | tail". rbd: map failed: (22) Invalid argument ...dmesg shows this: [ 45.536578] rbd: no pool name provided So something is definitely off with the parsing here. I'll dig deeper. > A couple of comments below. > > > diff --git a/drivers/block/rbd.c b/drivers/block/rbd.c > > index c3df76a862d2..3ad63d684238 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> > > @@ -823,34 +823,12 @@ 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 { > > @@ -871,85 +849,86 @@ 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; > > +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; > > > > - switch (token) { > > + opt = fs_parse(NULL, &rbd_parameters, param, &result); > > + if (opt < 0) > > + return opt; > > + > > + 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); > > This should be "rbd: ...". > > > } > > > > static char* obj_op_name(enum obj_operation_type op_type) > > @@ -6438,22 +6417,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; > > +} > > + > > +/* > > + * 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; > > > > - if (lenp) > > - *lenp = len; > > + struct fs_parameter param = { > > + .key = key_buf, > > + .type = fs_value_is_string, > > + }; > > > > - return dup; > > + 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; > > } > > I'm surprised to see this. Does the new framework not have a generic > helper for parsing a comma-separated key=value string? > > There is plenty of similar non-filesystem strsep/match_token loops in > the kernel besides rbd. A quick grep lists infiniband, lio, nvme and > smack. Is each of them going to be replaced with a 60 line long ad-hoc > parser, needed to call into the new framework? > > > /* > > @@ -6497,18 +6534,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 */ > > @@ -6519,36 +6549,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; > > } > > > > /* > > @@ -6560,51 +6589,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) > > @@ -7037,10 +7052,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; > > > > @@ -7048,33 +7061,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) { > > @@ -7139,8 +7153,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 bc90cf6ad7ed..f43e284ed64c 100644 > > --- a/fs/ceph/cache.c > > +++ b/fs/ceph/cache.c > > @@ -6,6 +6,7 @@ > > * Written by Milosz Tanski (milosz@xxxxxxxxx) > > */ > > > > +#include <linux/fs_context.h> > > #include "super.h" > > #include "cache.h" > > > > @@ -47,7 +48,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; > > @@ -64,8 +65,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; > > } > > @@ -93,7 +94,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 e486fac3434d..f72328fd357b 100644 > > --- a/fs/ceph/cache.h > > +++ b/fs/ceph/cache.h > > @@ -16,7 +16,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); > > @@ -88,7 +88,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 03b63b1cd32c..393fbb6bb95d 100644 > > --- a/fs/ceph/super.c > > +++ b/fs/ceph/super.c > > @@ -9,7 +9,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> > > @@ -138,276 +139,305 @@ enum { > > Opt_readdir_max_entries, > > Opt_readdir_max_bytes, > > Opt_congestion_kb, > > - Opt_last_int, > > - /* int args above */ > > Opt_snapdirname, > > Opt_mds_namespace, > > - Opt_fscache_uniq, > > Opt_recover_session, > > - Opt_last_string, > > - /* string args above */ > > Opt_dirstat, > > - Opt_nodirstat, > > Opt_rbytes, > > - Opt_norbytes, > > Opt_asyncreaddir, > > - Opt_noasyncreaddir, > > Opt_dcache, > > - Opt_nodcache, > > Opt_ino32, > > - Opt_noino32, > > Opt_fscache, > > - Opt_nofscache, > > 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_source, > > }; > > > > -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_recover_session, "recover_session=%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} > > +enum ceph_recover_session_mode { > > + ceph_recover_session_no, > > + ceph_recover_session_clean > > +}; > > + > > +static const struct fs_parameter_enum ceph_param_enums[] = { > > + { Opt_recover_session, "no", ceph_recover_session_no }, > > + { Opt_recover_session, "clean", ceph_recover_session_clean }, > > + {} > > }; > > > > -static int parse_fsopt_token(char *c, void *private) > > +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_enum ("recover_session", Opt_recover_session), > > + 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 const struct fs_parameter_description ceph_fs_parameters = { > > + .name = "ceph", > > + .specs = ceph_param_specs, > > + .enums = ceph_param_enums, > > +}; > > + > > +/* > > + * 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); > > } > > > > - switch (token) { > > + /* 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; > > + } > > + > > + 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; > > + unsigned int mode; > > + 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_recover_session: > > - if (!strncmp(argstr[0].from, "no", > > - argstr[0].to - argstr[0].from)) { > > + mode = result.uint_32; > > + if (mode == ceph_recover_session_no) > > fsopt->flags &= ~CEPH_MOUNT_OPT_CLEANRECOVER; > > - } else if (!strncmp(argstr[0].from, "clean", > > - argstr[0].to - argstr[0].from)) { > > + else if (mode == ceph_recover_session_clean) > > fsopt->flags |= CEPH_MOUNT_OPT_CLEANRECOVER; > > - } else { > > + else > > return -EINVAL; > > - } > > - 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; > > - break; > > - case Opt_nocopyfrom: > > - fsopt->flags |= CEPH_MOUNT_OPT_NOCOPYFROM; > > + if (!result.negated) > > + fsopt->flags &= ~CEPH_MOUNT_OPT_NOCOPYFROM; > > + else > > + 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) > > @@ -450,91 +480,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 > > @@ -578,7 +523,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"); > > @@ -642,12 +587,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; > > @@ -659,17 +602,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 { > > @@ -677,7 +621,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; > > @@ -715,9 +660,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); > > } > > > > @@ -925,9 +867,9 @@ 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; > > + int err = 0; > > unsigned long started = jiffies; /* note the start time */ > > struct dentry *root; > > > > @@ -942,7 +884,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; > > } > > @@ -962,33 +904,30 @@ static struct dentry *ceph_real_mount(struct ceph_fs_client *fsc) > > 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 */ > > > > @@ -998,24 +937,18 @@ static int ceph_set_super(struct super_block *s, void *data) > > > > s->s_time_gran = 1; > > > > - 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); > > @@ -1031,7 +964,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; > > } > > @@ -1061,46 +994,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; > > } > > > > @@ -1112,30 +1040,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) > > @@ -1164,7 +1159,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 f98d9247f9cb..a8d8d59155d8 100644 > > --- a/fs/ceph/super.h > > +++ b/fs/ceph/super.h > > @@ -74,7 +74,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 103643c68e3f..64e2b6324d55 100644 > > --- a/fs/fs_context.c > > +++ b/fs/fs_context.c > > @@ -193,7 +193,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 > > This hunk probably belongs to a different patch. (No objection to > keeping it if all outstanding patches are per-subsystem conversions, of > course.) > > > diff --git a/fs/fs_parser.c b/fs/fs_parser.c > > index 460ea4206fa2..65545db9dbea 100644 > > --- a/fs/fs_parser.c > > +++ b/fs/fs_parser.c > > @@ -60,7 +60,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 > > Ditto. > > > 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 > > This shouldn't be needed. Instead, since cache.c uses dout(), it > should include ceph_debug.h at the top. I'll fix that in our testing > branch. > > Thanks, > > Ilya -- Jeff Layton <jlayton@xxxxxxxxxx>