forked from Minki/linux
The two highlights are a set of improvements to how rbd read-only
mappings are handled and a conversion to the new mount API (slightly complicated by the fact that we had a common option parsing framework that called out into rbd and the filesystem instead of them calling into it). Also included a few scattered fixes and a MAINTAINERS update for rbd, adding Dongsheng as a reviewer. -----BEGIN PGP SIGNATURE----- iQFHBAABCAAxFiEEydHwtzie9C7TfviiSn/eOAIR84sFAl3oDqwTHGlkcnlvbW92 QGdtYWlsLmNvbQAKCRBKf944AhHzi8/OCACAhPEoSkG8J0XOgP0NlFQGRvikugKq wlUfpNhkJOVmyM1t9LgiHHirTa7/kA76wPo/iHtnvjIZuZoaX3+NoZX5DwgKVCo1 SCQdXR4ohVPiYxUpK+z/fDXxpYHhaO2SAww+RRHSDxnlN5CHqFBcBhRBPfhraZT5 dwiQt7++UOnp/hfk1Dqg5EogmSdLxqWyjClKf2lliZkzbU9YXmGapqQsur6sBk+e cLRmRBmMw4cDAKLL1taCympN0AxNMcePs1njvdwQ7XabNWrT061yFyt1ZNwAV/Nu 0nCyh/9IwQcsR0EvK7FCdUEJPy88Reufd+GleS4nkEZpbxQBzo0aGow0 =Egtk -----END PGP SIGNATURE----- Merge tag 'ceph-for-5.5-rc1' of git://github.com/ceph/ceph-client Pull ceph updates from Ilya Dryomov: "The two highlights are a set of improvements to how rbd read-only mappings are handled and a conversion to the new mount API (slightly complicated by the fact that we had a common option parsing framework that called out into rbd and the filesystem instead of them calling into it). Also included a few scattered fixes and a MAINTAINERS update for rbd, adding Dongsheng as a reviewer" * tag 'ceph-for-5.5-rc1' of git://github.com/ceph/ceph-client: libceph, rbd, ceph: convert to use the new mount API rbd: ask for a weaker incompat mask for read-only mappings rbd: don't query snapshot features rbd: remove snapshot existence validation code rbd: don't establish watch for read-only mappings rbd: don't acquire exclusive lock for read-only mappings rbd: disallow read-write partitions on images mapped read-only rbd: treat images mapped read-only seriously rbd: introduce RBD_DEV_FLAG_READONLY rbd: introduce rbd_is_snap() ceph: don't leave ino field in ceph_mds_request_head uninitialized ceph: tone down loglevel on ceph_mdsc_build_path warning rbd: update MAINTAINERS info ceph: fix geting random mds from mdsmap rbd: fix spelling mistake "requeueing" -> "requeuing" ceph: make several helper accessors take const pointers libceph: drop unnecessary check from dispatch() in mon_client.c
This commit is contained in:
commit
a231582359
@ -13773,7 +13773,7 @@ F: drivers/media/radio/radio-tea5777.c
|
||||
RADOS BLOCK DEVICE (RBD)
|
||||
M: Ilya Dryomov <idryomov@gmail.com>
|
||||
M: Sage Weil <sage@redhat.com>
|
||||
M: Alex Elder <elder@kernel.org>
|
||||
R: Dongsheng Yang <dongsheng.yang@easystack.cn>
|
||||
L: ceph-devel@vger.kernel.org
|
||||
W: http://ceph.com/
|
||||
T: git git://git.kernel.org/pub/scm/linux/kernel/git/sage/ceph-client.git
|
||||
|
@ -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>
|
||||
@ -377,7 +377,6 @@ struct rbd_client_id {
|
||||
|
||||
struct rbd_mapping {
|
||||
u64 size;
|
||||
u64 features;
|
||||
};
|
||||
|
||||
/*
|
||||
@ -462,8 +461,9 @@ struct rbd_device {
|
||||
* by rbd_dev->lock
|
||||
*/
|
||||
enum rbd_dev_flags {
|
||||
RBD_DEV_FLAG_EXISTS, /* mapped snapshot has not been deleted */
|
||||
RBD_DEV_FLAG_EXISTS, /* rbd_dev_device_setup() ran */
|
||||
RBD_DEV_FLAG_REMOVING, /* this mapping is being removed */
|
||||
RBD_DEV_FLAG_READONLY, /* -o ro or snapshot */
|
||||
};
|
||||
|
||||
static DEFINE_MUTEX(client_mutex); /* Serialize client creation */
|
||||
@ -514,6 +514,16 @@ static int minor_to_rbd_dev_id(int minor)
|
||||
return minor >> RBD_SINGLE_MAJOR_PART_SHIFT;
|
||||
}
|
||||
|
||||
static bool rbd_is_ro(struct rbd_device *rbd_dev)
|
||||
{
|
||||
return test_bit(RBD_DEV_FLAG_READONLY, &rbd_dev->flags);
|
||||
}
|
||||
|
||||
static bool rbd_is_snap(struct rbd_device *rbd_dev)
|
||||
{
|
||||
return rbd_dev->spec->snap_id != CEPH_NOSNAP;
|
||||
}
|
||||
|
||||
static bool __rbd_is_lock_owner(struct rbd_device *rbd_dev)
|
||||
{
|
||||
lockdep_assert_held(&rbd_dev->lock_rwsem);
|
||||
@ -633,8 +643,6 @@ static const char *rbd_dev_v2_snap_name(struct rbd_device *rbd_dev,
|
||||
u64 snap_id);
|
||||
static int _rbd_dev_v2_snap_size(struct rbd_device *rbd_dev, u64 snap_id,
|
||||
u8 *order, u64 *snap_size);
|
||||
static int _rbd_dev_v2_snap_features(struct rbd_device *rbd_dev, u64 snap_id,
|
||||
u64 *snap_features);
|
||||
static int rbd_dev_v2_get_flags(struct rbd_device *rbd_dev);
|
||||
|
||||
static void rbd_obj_handle_request(struct rbd_obj_request *obj_req, int result);
|
||||
@ -695,9 +703,16 @@ static int rbd_ioctl_set_ro(struct rbd_device *rbd_dev, unsigned long arg)
|
||||
if (get_user(ro, (int __user *)arg))
|
||||
return -EFAULT;
|
||||
|
||||
/* Snapshots can't be marked read-write */
|
||||
if (rbd_dev->spec->snap_id != CEPH_NOSNAP && !ro)
|
||||
return -EROFS;
|
||||
/*
|
||||
* Both images mapped read-only and snapshots can't be marked
|
||||
* read-write.
|
||||
*/
|
||||
if (!ro) {
|
||||
if (rbd_is_ro(rbd_dev))
|
||||
return -EROFS;
|
||||
|
||||
rbd_assert(!rbd_is_snap(rbd_dev));
|
||||
}
|
||||
|
||||
/* Let blkdev_roset() handle it */
|
||||
return -ENOTTY;
|
||||
@ -823,34 +838,34 @@ 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}
|
||||
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 ("read_only", Opt_read_only),
|
||||
fsparam_flag ("read_write", Opt_read_write),
|
||||
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,
|
||||
};
|
||||
|
||||
struct rbd_options {
|
||||
@ -871,87 +886,12 @@ struct rbd_options {
|
||||
#define RBD_EXCLUSIVE_DEFAULT false
|
||||
#define RBD_TRIM_DEFAULT true
|
||||
|
||||
struct parse_rbd_opts_ctx {
|
||||
struct rbd_parse_opts_ctx {
|
||||
struct rbd_spec *spec;
|
||||
struct ceph_options *copts;
|
||||
struct rbd_options *opts;
|
||||
};
|
||||
|
||||
static int parse_rbd_opts_token(char *c, void *private)
|
||||
{
|
||||
struct parse_rbd_opts_ctx *pctx = private;
|
||||
substring_t argstr[MAX_OPT_ARGS];
|
||||
int token, intval, ret;
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
switch (token) {
|
||||
case Opt_queue_depth:
|
||||
if (intval < 1) {
|
||||
pr_err("queue_depth out of range\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
pctx->opts->queue_depth = intval;
|
||||
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;
|
||||
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);
|
||||
break;
|
||||
case Opt_pool_ns:
|
||||
kfree(pctx->spec->pool_ns);
|
||||
pctx->spec->pool_ns = match_strdup(argstr);
|
||||
if (!pctx->spec->pool_ns)
|
||||
return -ENOMEM;
|
||||
break;
|
||||
case Opt_read_only:
|
||||
pctx->opts->read_only = true;
|
||||
break;
|
||||
case Opt_read_write:
|
||||
pctx->opts->read_only = false;
|
||||
break;
|
||||
case Opt_lock_on_read:
|
||||
pctx->opts->lock_on_read = true;
|
||||
break;
|
||||
case Opt_exclusive:
|
||||
pctx->opts->exclusive = true;
|
||||
break;
|
||||
case Opt_notrim:
|
||||
pctx->opts->trim = false;
|
||||
break;
|
||||
default:
|
||||
/* libceph prints "bad option" msg */
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static char* obj_op_name(enum obj_operation_type op_type)
|
||||
{
|
||||
switch (op_type) {
|
||||
@ -1302,51 +1242,23 @@ static int rbd_snap_size(struct rbd_device *rbd_dev, u64 snap_id,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rbd_snap_features(struct rbd_device *rbd_dev, u64 snap_id,
|
||||
u64 *snap_features)
|
||||
{
|
||||
rbd_assert(rbd_image_format_valid(rbd_dev->image_format));
|
||||
if (snap_id == CEPH_NOSNAP) {
|
||||
*snap_features = rbd_dev->header.features;
|
||||
} else if (rbd_dev->image_format == 1) {
|
||||
*snap_features = 0; /* No features for format 1 */
|
||||
} else {
|
||||
u64 features = 0;
|
||||
int ret;
|
||||
|
||||
ret = _rbd_dev_v2_snap_features(rbd_dev, snap_id, &features);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
*snap_features = features;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rbd_dev_mapping_set(struct rbd_device *rbd_dev)
|
||||
{
|
||||
u64 snap_id = rbd_dev->spec->snap_id;
|
||||
u64 size = 0;
|
||||
u64 features = 0;
|
||||
int ret;
|
||||
|
||||
ret = rbd_snap_size(rbd_dev, snap_id, &size);
|
||||
if (ret)
|
||||
return ret;
|
||||
ret = rbd_snap_features(rbd_dev, snap_id, &features);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
rbd_dev->mapping.size = size;
|
||||
rbd_dev->mapping.features = features;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void rbd_dev_mapping_clear(struct rbd_device *rbd_dev)
|
||||
{
|
||||
rbd_dev->mapping.size = 0;
|
||||
rbd_dev->mapping.features = 0;
|
||||
}
|
||||
|
||||
static void zero_bvec(struct bio_vec *bv)
|
||||
@ -1832,6 +1744,17 @@ static u8 rbd_object_map_get(struct rbd_device *rbd_dev, u64 objno)
|
||||
|
||||
static bool use_object_map(struct rbd_device *rbd_dev)
|
||||
{
|
||||
/*
|
||||
* An image mapped read-only can't use the object map -- it isn't
|
||||
* loaded because the header lock isn't acquired. Someone else can
|
||||
* write to the image and update the object map behind our back.
|
||||
*
|
||||
* A snapshot can't be written to, so using the object map is always
|
||||
* safe.
|
||||
*/
|
||||
if (!rbd_is_snap(rbd_dev) && rbd_is_ro(rbd_dev))
|
||||
return false;
|
||||
|
||||
return ((rbd_dev->header.features & RBD_FEATURE_OBJECT_MAP) &&
|
||||
!(rbd_dev->object_map_flags & RBD_FLAG_OBJECT_MAP_INVALID));
|
||||
}
|
||||
@ -3555,7 +3478,7 @@ static bool need_exclusive_lock(struct rbd_img_request *img_req)
|
||||
if (!(rbd_dev->header.features & RBD_FEATURE_EXCLUSIVE_LOCK))
|
||||
return false;
|
||||
|
||||
if (rbd_dev->spec->snap_id != CEPH_NOSNAP)
|
||||
if (rbd_is_ro(rbd_dev))
|
||||
return false;
|
||||
|
||||
rbd_assert(!test_bit(IMG_REQ_CHILD, &img_req->flags));
|
||||
@ -4230,7 +4153,7 @@ again:
|
||||
* lock owner acked, but resend if we don't see them
|
||||
* release the lock
|
||||
*/
|
||||
dout("%s rbd_dev %p requeueing lock_dwork\n", __func__,
|
||||
dout("%s rbd_dev %p requeuing lock_dwork\n", __func__,
|
||||
rbd_dev);
|
||||
mod_delayed_work(rbd_dev->task_wq, &rbd_dev->lock_dwork,
|
||||
msecs_to_jiffies(2 * RBD_NOTIFY_TIMEOUT * MSEC_PER_SEC));
|
||||
@ -4826,24 +4749,14 @@ static void rbd_queue_workfn(struct work_struct *work)
|
||||
goto err_rq;
|
||||
}
|
||||
|
||||
if (op_type != OBJ_OP_READ && rbd_dev->spec->snap_id != CEPH_NOSNAP) {
|
||||
rbd_warn(rbd_dev, "%s on read-only snapshot",
|
||||
obj_op_name(op_type));
|
||||
result = -EIO;
|
||||
goto err;
|
||||
}
|
||||
|
||||
/*
|
||||
* Quit early if the mapped snapshot no longer exists. It's
|
||||
* still possible the snapshot will have disappeared by the
|
||||
* time our request arrives at the osd, but there's no sense in
|
||||
* sending it if we already know.
|
||||
*/
|
||||
if (!test_bit(RBD_DEV_FLAG_EXISTS, &rbd_dev->flags)) {
|
||||
dout("request for non-existent snapshot");
|
||||
rbd_assert(rbd_dev->spec->snap_id != CEPH_NOSNAP);
|
||||
result = -ENXIO;
|
||||
goto err_rq;
|
||||
if (op_type != OBJ_OP_READ) {
|
||||
if (rbd_is_ro(rbd_dev)) {
|
||||
rbd_warn(rbd_dev, "%s on read-only mapping",
|
||||
obj_op_name(op_type));
|
||||
result = -EIO;
|
||||
goto err;
|
||||
}
|
||||
rbd_assert(!rbd_is_snap(rbd_dev));
|
||||
}
|
||||
|
||||
if (offset && length > U64_MAX - offset + 1) {
|
||||
@ -5025,25 +4938,6 @@ out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Clear the rbd device's EXISTS flag if the snapshot it's mapped to
|
||||
* has disappeared from the (just updated) snapshot context.
|
||||
*/
|
||||
static void rbd_exists_validate(struct rbd_device *rbd_dev)
|
||||
{
|
||||
u64 snap_id;
|
||||
|
||||
if (!test_bit(RBD_DEV_FLAG_EXISTS, &rbd_dev->flags))
|
||||
return;
|
||||
|
||||
snap_id = rbd_dev->spec->snap_id;
|
||||
if (snap_id == CEPH_NOSNAP)
|
||||
return;
|
||||
|
||||
if (rbd_dev_snap_index(rbd_dev, snap_id) == BAD_SNAP_INDEX)
|
||||
clear_bit(RBD_DEV_FLAG_EXISTS, &rbd_dev->flags);
|
||||
}
|
||||
|
||||
static void rbd_dev_update_size(struct rbd_device *rbd_dev)
|
||||
{
|
||||
sector_t size;
|
||||
@ -5084,12 +4978,8 @@ static int rbd_dev_refresh(struct rbd_device *rbd_dev)
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (rbd_dev->spec->snap_id == CEPH_NOSNAP) {
|
||||
rbd_dev->mapping.size = rbd_dev->header.image_size;
|
||||
} else {
|
||||
/* validate mapped snapshot's EXISTS flag */
|
||||
rbd_exists_validate(rbd_dev);
|
||||
}
|
||||
rbd_assert(!rbd_is_snap(rbd_dev));
|
||||
rbd_dev->mapping.size = rbd_dev->header.image_size;
|
||||
|
||||
out:
|
||||
up_write(&rbd_dev->header_rwsem);
|
||||
@ -5211,17 +5101,12 @@ static ssize_t rbd_size_show(struct device *dev,
|
||||
(unsigned long long)rbd_dev->mapping.size);
|
||||
}
|
||||
|
||||
/*
|
||||
* Note this shows the features for whatever's mapped, which is not
|
||||
* necessarily the base image.
|
||||
*/
|
||||
static ssize_t rbd_features_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct rbd_device *rbd_dev = dev_to_rbd_dev(dev);
|
||||
|
||||
return sprintf(buf, "0x%016llx\n",
|
||||
(unsigned long long)rbd_dev->mapping.features);
|
||||
return sprintf(buf, "0x%016llx\n", rbd_dev->header.features);
|
||||
}
|
||||
|
||||
static ssize_t rbd_major_show(struct device *dev,
|
||||
@ -5709,9 +5594,12 @@ out:
|
||||
}
|
||||
|
||||
static int _rbd_dev_v2_snap_features(struct rbd_device *rbd_dev, u64 snap_id,
|
||||
u64 *snap_features)
|
||||
bool read_only, u64 *snap_features)
|
||||
{
|
||||
__le64 snapid = cpu_to_le64(snap_id);
|
||||
struct {
|
||||
__le64 snap_id;
|
||||
u8 read_only;
|
||||
} features_in;
|
||||
struct {
|
||||
__le64 features;
|
||||
__le64 incompat;
|
||||
@ -5719,9 +5607,12 @@ static int _rbd_dev_v2_snap_features(struct rbd_device *rbd_dev, u64 snap_id,
|
||||
u64 unsup;
|
||||
int ret;
|
||||
|
||||
features_in.snap_id = cpu_to_le64(snap_id);
|
||||
features_in.read_only = read_only;
|
||||
|
||||
ret = rbd_obj_method_sync(rbd_dev, &rbd_dev->header_oid,
|
||||
&rbd_dev->header_oloc, "get_features",
|
||||
&snapid, sizeof(snapid),
|
||||
&features_in, sizeof(features_in),
|
||||
&features_buf, sizeof(features_buf));
|
||||
dout("%s: rbd_obj_method_sync returned %d\n", __func__, ret);
|
||||
if (ret < 0)
|
||||
@ -5749,7 +5640,8 @@ static int _rbd_dev_v2_snap_features(struct rbd_device *rbd_dev, u64 snap_id,
|
||||
static int rbd_dev_v2_features(struct rbd_device *rbd_dev)
|
||||
{
|
||||
return _rbd_dev_v2_snap_features(rbd_dev, CEPH_NOSNAP,
|
||||
&rbd_dev->header.features);
|
||||
rbd_is_ro(rbd_dev),
|
||||
&rbd_dev->header.features);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -6456,6 +6348,122 @@ static inline char *dup_token(const char **buf, size_t *lenp)
|
||||
return dup;
|
||||
}
|
||||
|
||||
static int rbd_parse_param(struct fs_parameter *param,
|
||||
struct rbd_parse_opts_ctx *pctx)
|
||||
{
|
||||
struct rbd_options *opt = pctx->opts;
|
||||
struct fs_parse_result result;
|
||||
int token, ret;
|
||||
|
||||
ret = ceph_parse_param(param, pctx->copts, NULL);
|
||||
if (ret != -ENOPARAM)
|
||||
return ret;
|
||||
|
||||
token = fs_parse(NULL, &rbd_parameters, param, &result);
|
||||
dout("%s fs_parse '%s' token %d\n", __func__, param->key, token);
|
||||
if (token < 0) {
|
||||
if (token == -ENOPARAM) {
|
||||
return invalf(NULL, "rbd: Unknown parameter '%s'",
|
||||
param->key);
|
||||
}
|
||||
return token;
|
||||
}
|
||||
|
||||
switch (token) {
|
||||
case Opt_queue_depth:
|
||||
if (result.uint_32 < 1)
|
||||
goto out_of_range;
|
||||
opt->queue_depth = result.uint_32;
|
||||
break;
|
||||
case Opt_alloc_size:
|
||||
if (result.uint_32 < SECTOR_SIZE)
|
||||
goto out_of_range;
|
||||
if (!is_power_of_2(result.uint_32)) {
|
||||
return invalf(NULL, "rbd: alloc_size must be a power of 2");
|
||||
}
|
||||
opt->alloc_size = result.uint_32;
|
||||
break;
|
||||
case Opt_lock_timeout:
|
||||
/* 0 is "wait forever" (i.e. infinite timeout) */
|
||||
if (result.uint_32 > INT_MAX / 1000)
|
||||
goto out_of_range;
|
||||
opt->lock_timeout = msecs_to_jiffies(result.uint_32 * 1000);
|
||||
break;
|
||||
case Opt_pool_ns:
|
||||
kfree(pctx->spec->pool_ns);
|
||||
pctx->spec->pool_ns = param->string;
|
||||
param->string = NULL;
|
||||
break;
|
||||
case Opt_read_only:
|
||||
opt->read_only = true;
|
||||
break;
|
||||
case Opt_read_write:
|
||||
opt->read_only = false;
|
||||
break;
|
||||
case Opt_lock_on_read:
|
||||
opt->lock_on_read = true;
|
||||
break;
|
||||
case Opt_exclusive:
|
||||
opt->exclusive = true;
|
||||
break;
|
||||
case Opt_notrim:
|
||||
opt->trim = false;
|
||||
break;
|
||||
default:
|
||||
BUG();
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
out_of_range:
|
||||
return invalf(NULL, "rbd: %s out of range", param->key);
|
||||
}
|
||||
|
||||
/*
|
||||
* This duplicates most of generic_parse_monolithic(), untying it from
|
||||
* fs_context and skipping standard superblock and security options.
|
||||
*/
|
||||
static int rbd_parse_options(char *options, struct rbd_parse_opts_ctx *pctx)
|
||||
{
|
||||
char *key;
|
||||
int ret = 0;
|
||||
|
||||
dout("%s '%s'\n", __func__, options);
|
||||
while ((key = strsep(&options, ",")) != NULL) {
|
||||
if (*key) {
|
||||
struct fs_parameter param = {
|
||||
.key = key,
|
||||
.type = fs_value_is_string,
|
||||
};
|
||||
char *value = strchr(key, '=');
|
||||
size_t v_len = 0;
|
||||
|
||||
if (value) {
|
||||
if (value == key)
|
||||
continue;
|
||||
*value++ = 0;
|
||||
v_len = strlen(value);
|
||||
}
|
||||
|
||||
|
||||
if (v_len > 0) {
|
||||
param.string = kmemdup_nul(value, v_len,
|
||||
GFP_KERNEL);
|
||||
if (!param.string)
|
||||
return -ENOMEM;
|
||||
}
|
||||
param.size = v_len;
|
||||
|
||||
ret = rbd_parse_param(¶m, pctx);
|
||||
kfree(param.string);
|
||||
if (ret)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Parse the options provided for an "rbd add" (i.e., rbd image
|
||||
* mapping) request. These arrive via a write to /sys/bus/rbd/add,
|
||||
@ -6507,8 +6515,7 @@ static int rbd_add_parse_args(const char *buf,
|
||||
const char *mon_addrs;
|
||||
char *snap_name;
|
||||
size_t mon_addrs_size;
|
||||
struct parse_rbd_opts_ctx pctx = { 0 };
|
||||
struct ceph_options *copts;
|
||||
struct rbd_parse_opts_ctx pctx = { 0 };
|
||||
int ret;
|
||||
|
||||
/* The first four tokens are required */
|
||||
@ -6519,7 +6526,7 @@ 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;
|
||||
@ -6569,6 +6576,10 @@ static int rbd_add_parse_args(const char *buf,
|
||||
*(snap_name + len) = '\0';
|
||||
pctx.spec->snap_name = snap_name;
|
||||
|
||||
pctx.copts = ceph_alloc_options();
|
||||
if (!pctx.copts)
|
||||
goto out_mem;
|
||||
|
||||
/* Initialize all rbd options to the defaults */
|
||||
|
||||
pctx.opts = kzalloc(sizeof(*pctx.opts), GFP_KERNEL);
|
||||
@ -6583,27 +6594,27 @@ static int rbd_add_parse_args(const char *buf,
|
||||
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);
|
||||
ret = ceph_parse_mon_ips(mon_addrs, mon_addrs_size, pctx.copts, NULL);
|
||||
if (ret)
|
||||
goto out_err;
|
||||
}
|
||||
kfree(options);
|
||||
|
||||
*ceph_opts = copts;
|
||||
ret = rbd_parse_options(options, &pctx);
|
||||
if (ret)
|
||||
goto out_err;
|
||||
|
||||
*ceph_opts = pctx.copts;
|
||||
*opts = pctx.opts;
|
||||
*rbd_spec = pctx.spec;
|
||||
|
||||
kfree(options);
|
||||
return 0;
|
||||
|
||||
out_mem:
|
||||
ret = -ENOMEM;
|
||||
out_err:
|
||||
kfree(pctx.opts);
|
||||
ceph_destroy_options(pctx.copts);
|
||||
rbd_spec_put(pctx.spec);
|
||||
kfree(options);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -6632,7 +6643,7 @@ static int rbd_add_acquire_lock(struct rbd_device *rbd_dev)
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (rbd_dev->spec->snap_id != CEPH_NOSNAP)
|
||||
if (rbd_is_ro(rbd_dev))
|
||||
return 0;
|
||||
|
||||
rbd_assert(!rbd_is_lock_owner(rbd_dev));
|
||||
@ -6838,6 +6849,8 @@ static int rbd_dev_probe_parent(struct rbd_device *rbd_dev, int depth)
|
||||
__rbd_get_client(rbd_dev->rbd_client);
|
||||
rbd_spec_get(rbd_dev->parent_spec);
|
||||
|
||||
__set_bit(RBD_DEV_FLAG_READONLY, &parent->flags);
|
||||
|
||||
ret = rbd_dev_image_probe(parent, depth);
|
||||
if (ret < 0)
|
||||
goto out_err;
|
||||
@ -6889,7 +6902,7 @@ static int rbd_dev_device_setup(struct rbd_device *rbd_dev)
|
||||
goto err_out_blkdev;
|
||||
|
||||
set_capacity(rbd_dev->disk, rbd_dev->mapping.size / SECTOR_SIZE);
|
||||
set_disk_ro(rbd_dev->disk, rbd_dev->opts->read_only);
|
||||
set_disk_ro(rbd_dev->disk, rbd_is_ro(rbd_dev));
|
||||
|
||||
ret = dev_set_name(&rbd_dev->dev, "%d", rbd_dev->dev_id);
|
||||
if (ret)
|
||||
@ -6927,6 +6940,24 @@ static int rbd_dev_header_name(struct rbd_device *rbd_dev)
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void rbd_print_dne(struct rbd_device *rbd_dev, bool is_snap)
|
||||
{
|
||||
if (!is_snap) {
|
||||
pr_info("image %s/%s%s%s does not exist\n",
|
||||
rbd_dev->spec->pool_name,
|
||||
rbd_dev->spec->pool_ns ?: "",
|
||||
rbd_dev->spec->pool_ns ? "/" : "",
|
||||
rbd_dev->spec->image_name);
|
||||
} else {
|
||||
pr_info("snap %s/%s%s%s@%s does not exist\n",
|
||||
rbd_dev->spec->pool_name,
|
||||
rbd_dev->spec->pool_ns ?: "",
|
||||
rbd_dev->spec->pool_ns ? "/" : "",
|
||||
rbd_dev->spec->image_name,
|
||||
rbd_dev->spec->snap_name);
|
||||
}
|
||||
}
|
||||
|
||||
static void rbd_dev_image_release(struct rbd_device *rbd_dev)
|
||||
{
|
||||
rbd_dev_unprobe(rbd_dev);
|
||||
@ -6945,6 +6976,7 @@ static void rbd_dev_image_release(struct rbd_device *rbd_dev)
|
||||
*/
|
||||
static int rbd_dev_image_probe(struct rbd_device *rbd_dev, int depth)
|
||||
{
|
||||
bool need_watch = !rbd_is_ro(rbd_dev);
|
||||
int ret;
|
||||
|
||||
/*
|
||||
@ -6961,22 +6993,21 @@ static int rbd_dev_image_probe(struct rbd_device *rbd_dev, int depth)
|
||||
if (ret)
|
||||
goto err_out_format;
|
||||
|
||||
if (!depth) {
|
||||
if (need_watch) {
|
||||
ret = rbd_register_watch(rbd_dev);
|
||||
if (ret) {
|
||||
if (ret == -ENOENT)
|
||||
pr_info("image %s/%s%s%s does not exist\n",
|
||||
rbd_dev->spec->pool_name,
|
||||
rbd_dev->spec->pool_ns ?: "",
|
||||
rbd_dev->spec->pool_ns ? "/" : "",
|
||||
rbd_dev->spec->image_name);
|
||||
rbd_print_dne(rbd_dev, false);
|
||||
goto err_out_format;
|
||||
}
|
||||
}
|
||||
|
||||
ret = rbd_dev_header_info(rbd_dev);
|
||||
if (ret)
|
||||
if (ret) {
|
||||
if (ret == -ENOENT && !need_watch)
|
||||
rbd_print_dne(rbd_dev, false);
|
||||
goto err_out_watch;
|
||||
}
|
||||
|
||||
/*
|
||||
* If this image is the one being mapped, we have pool name and
|
||||
@ -6990,12 +7021,7 @@ static int rbd_dev_image_probe(struct rbd_device *rbd_dev, int depth)
|
||||
ret = rbd_spec_fill_names(rbd_dev);
|
||||
if (ret) {
|
||||
if (ret == -ENOENT)
|
||||
pr_info("snap %s/%s%s%s@%s does not exist\n",
|
||||
rbd_dev->spec->pool_name,
|
||||
rbd_dev->spec->pool_ns ?: "",
|
||||
rbd_dev->spec->pool_ns ? "/" : "",
|
||||
rbd_dev->spec->image_name,
|
||||
rbd_dev->spec->snap_name);
|
||||
rbd_print_dne(rbd_dev, true);
|
||||
goto err_out_probe;
|
||||
}
|
||||
|
||||
@ -7003,7 +7029,7 @@ static int rbd_dev_image_probe(struct rbd_device *rbd_dev, int depth)
|
||||
if (ret)
|
||||
goto err_out_probe;
|
||||
|
||||
if (rbd_dev->spec->snap_id != CEPH_NOSNAP &&
|
||||
if (rbd_is_snap(rbd_dev) &&
|
||||
(rbd_dev->header.features & RBD_FEATURE_OBJECT_MAP)) {
|
||||
ret = rbd_object_map_load(rbd_dev);
|
||||
if (ret)
|
||||
@ -7027,7 +7053,7 @@ static int rbd_dev_image_probe(struct rbd_device *rbd_dev, int depth)
|
||||
err_out_probe:
|
||||
rbd_dev_unprobe(rbd_dev);
|
||||
err_out_watch:
|
||||
if (!depth)
|
||||
if (need_watch)
|
||||
rbd_unregister_watch(rbd_dev);
|
||||
err_out_format:
|
||||
rbd_dev->image_format = 0;
|
||||
@ -7079,6 +7105,11 @@ static ssize_t do_rbd_add(struct bus_type *bus,
|
||||
spec = NULL; /* rbd_dev now owns this */
|
||||
rbd_opts = NULL; /* rbd_dev now owns this */
|
||||
|
||||
/* if we are mapping a snapshot it will be a read-only mapping */
|
||||
if (rbd_dev->opts->read_only ||
|
||||
strcmp(rbd_dev->spec->snap_name, RBD_SNAP_HEAD_NAME))
|
||||
__set_bit(RBD_DEV_FLAG_READONLY, &rbd_dev->flags);
|
||||
|
||||
rbd_dev->config_info = kstrdup(buf, GFP_KERNEL);
|
||||
if (!rbd_dev->config_info) {
|
||||
rc = -ENOMEM;
|
||||
@ -7092,10 +7123,6 @@ static ssize_t do_rbd_add(struct bus_type *bus,
|
||||
goto err_out_rbd_dev;
|
||||
}
|
||||
|
||||
/* If we are mapping a snapshot it must be marked read-only */
|
||||
if (rbd_dev->spec->snap_id != CEPH_NOSNAP)
|
||||
rbd_dev->opts->read_only = true;
|
||||
|
||||
if (rbd_dev->opts->alloc_size > rbd_dev->layout.object_size) {
|
||||
rbd_warn(rbd_dev, "alloc_size adjusted to %u",
|
||||
rbd_dev->layout.object_size);
|
||||
|
@ -8,6 +8,7 @@
|
||||
|
||||
#include <linux/ceph/ceph_debug.h>
|
||||
|
||||
#include <linux/fs_context.h>
|
||||
#include "super.h"
|
||||
#include "cache.h"
|
||||
|
||||
@ -49,7 +50,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 ceph_fs_client* fsc, struct fs_context *fc)
|
||||
{
|
||||
const struct ceph_fsid *fsid = &fsc->client->fsid;
|
||||
const char *fscache_uniq = fsc->mount_options->fscache_uniq;
|
||||
@ -66,8 +67,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, "ceph: fscache cookie already registered for fsid %pU, use fsc=<uniquifier> option",
|
||||
fsid);
|
||||
err = -EBUSY;
|
||||
goto out_unlock;
|
||||
}
|
||||
@ -95,7 +96,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, "ceph: unable to register fscache cookie for fsid %pU",
|
||||
fsid);
|
||||
/* all other fs ignore this error */
|
||||
}
|
||||
|
@ -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 ceph_fs_client* fsc, struct fs_context *fc);
|
||||
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 ceph_fs_client* fsc,
|
||||
struct fs_context *fc)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
@ -2182,13 +2182,17 @@ retry:
|
||||
}
|
||||
base = ceph_ino(d_inode(temp));
|
||||
rcu_read_unlock();
|
||||
if (pos < 0 || read_seqretry(&rename_lock, seq)) {
|
||||
pr_err("build_path did not end path lookup where "
|
||||
"expected, pos is %d\n", pos);
|
||||
/* presumably this is only possible if racing with a
|
||||
rename of one of the parent directories (we can not
|
||||
lock the dentries above us to prevent this, but
|
||||
retrying should be harmless) */
|
||||
|
||||
if (read_seqretry(&rename_lock, seq))
|
||||
goto retry;
|
||||
|
||||
if (pos < 0) {
|
||||
/*
|
||||
* A rename didn't occur, but somehow we didn't end up where
|
||||
* we thought we would. Throw a warning and try again.
|
||||
*/
|
||||
pr_warn("build_path did not end path lookup where "
|
||||
"expected, pos is %d\n", pos);
|
||||
goto retry;
|
||||
}
|
||||
|
||||
@ -2345,6 +2349,7 @@ static struct ceph_msg *create_request_message(struct ceph_mds_client *mdsc,
|
||||
head->op = cpu_to_le32(req->r_op);
|
||||
head->caller_uid = cpu_to_le32(from_kuid(&init_user_ns, req->r_uid));
|
||||
head->caller_gid = cpu_to_le32(from_kgid(&init_user_ns, req->r_gid));
|
||||
head->ino = 0;
|
||||
head->args = req->r_args;
|
||||
|
||||
ceph_encode_filepath(&p, end, ino1, path1);
|
||||
|
@ -20,7 +20,7 @@
|
||||
int ceph_mdsmap_get_random_mds(struct ceph_mdsmap *m)
|
||||
{
|
||||
int n = 0;
|
||||
int i;
|
||||
int i, j;
|
||||
|
||||
/* special case for one mds */
|
||||
if (1 == m->m_num_mds && m->m_info[0].state > 0)
|
||||
@ -35,9 +35,12 @@ int ceph_mdsmap_get_random_mds(struct ceph_mdsmap *m)
|
||||
|
||||
/* pick */
|
||||
n = prandom_u32() % n;
|
||||
for (i = 0; n > 0; i++, n--)
|
||||
while (m->m_info[i].state <= 0)
|
||||
i++;
|
||||
for (j = 0, i = 0; i < m->m_num_mds; i++) {
|
||||
if (m->m_info[i].state > 0)
|
||||
j++;
|
||||
if (j > n)
|
||||
break;
|
||||
}
|
||||
|
||||
return i;
|
||||
}
|
||||
|
646
fs/ceph/super.c
646
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,280 +139,308 @@ 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,
|
||||
Opt_source,
|
||||
/* 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,
|
||||
};
|
||||
|
||||
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 int parse_fsopt_token(char *c, void *private)
|
||||
static const struct fs_parameter_enum ceph_mount_param_enums[] = {
|
||||
{ Opt_recover_session, "no", ceph_recover_session_no },
|
||||
{ Opt_recover_session, "clean", ceph_recover_session_clean },
|
||||
{}
|
||||
};
|
||||
|
||||
static const struct fs_parameter_spec ceph_mount_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_enum ("recover_session", Opt_recover_session),
|
||||
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_mount_parameters = {
|
||||
.name = "ceph",
|
||||
.specs = ceph_mount_param_specs,
|
||||
.enums = ceph_mount_param_enums,
|
||||
};
|
||||
|
||||
struct ceph_parse_opts_ctx {
|
||||
struct ceph_options *copts;
|
||||
struct ceph_mount_options *opts;
|
||||
};
|
||||
|
||||
/*
|
||||
* 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_parameter *param, struct fs_context *fc)
|
||||
{
|
||||
struct ceph_mount_options *fsopt = private;
|
||||
substring_t argstr[MAX_OPT_ARGS];
|
||||
int token, intval, ret;
|
||||
struct ceph_parse_opts_ctx *pctx = fc->fs_private;
|
||||
struct ceph_mount_options *fsopt = pctx->opts;
|
||||
char *dev_name = param->string, *dev_name_end;
|
||||
int ret;
|
||||
|
||||
token = match_token((char *)c, fsopt_tokens, argstr);
|
||||
if (token < 0)
|
||||
return -EINVAL;
|
||||
dout("%s '%s'\n", __func__, dev_name);
|
||||
if (!dev_name || !*dev_name)
|
||||
return invalf(fc, "ceph: Empty source");
|
||||
|
||||
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;
|
||||
dev_name_end = strchr(dev_name, '/');
|
||||
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);
|
||||
}
|
||||
|
||||
dev_name_end--; /* back up to ':' separator */
|
||||
if (dev_name_end < dev_name || *dev_name_end != ':')
|
||||
return invalf(fc, "ceph: No path or : separator in source");
|
||||
|
||||
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);
|
||||
|
||||
ret = ceph_parse_mon_ips(param->string, dev_name_end - dev_name,
|
||||
pctx->copts, fc);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
fc->source = param->string;
|
||||
param->string = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ceph_parse_mount_param(struct fs_context *fc,
|
||||
struct fs_parameter *param)
|
||||
{
|
||||
struct ceph_parse_opts_ctx *pctx = fc->fs_private;
|
||||
struct ceph_mount_options *fsopt = pctx->opts;
|
||||
struct fs_parse_result result;
|
||||
unsigned int mode;
|
||||
int token, ret;
|
||||
|
||||
ret = ceph_parse_param(param, pctx->copts, fc);
|
||||
if (ret != -ENOPARAM)
|
||||
return ret;
|
||||
|
||||
token = fs_parse(fc, &ceph_mount_parameters, param, &result);
|
||||
dout("%s fs_parse '%s' token %d\n", __func__, param->key, token);
|
||||
if (token < 0)
|
||||
return token;
|
||||
|
||||
switch (token) {
|
||||
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 {
|
||||
return -EINVAL;
|
||||
}
|
||||
else
|
||||
BUG();
|
||||
break;
|
||||
case Opt_fscache_uniq:
|
||||
#ifdef CONFIG_CEPH_FSCACHE
|
||||
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;
|
||||
#else
|
||||
pr_err("fscache support is disabled\n");
|
||||
return -EINVAL;
|
||||
#endif
|
||||
case Opt_source:
|
||||
if (fc->source)
|
||||
return invalf(fc, "ceph: Multiple sources specified");
|
||||
return ceph_parse_source(param, fc);
|
||||
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 < PAGE_SIZE ||
|
||||
result.uint_32 > CEPH_MAX_WRITE_SIZE)
|
||||
goto out_of_range;
|
||||
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 < PAGE_SIZE ||
|
||||
result.uint_32 > CEPH_MAX_READ_SIZE)
|
||||
goto out_of_range;
|
||||
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 out_of_range;
|
||||
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 out_of_range;
|
||||
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 out_of_range;
|
||||
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 < PAGE_SIZE && result.uint_32 != 0)
|
||||
goto out_of_range;
|
||||
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 out_of_range;
|
||||
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:
|
||||
#ifdef CONFIG_CEPH_FSCACHE
|
||||
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;
|
||||
#else
|
||||
pr_err("fscache support is disabled\n");
|
||||
return -EINVAL;
|
||||
return invalf(fc, "ceph: fscache support is disabled");
|
||||
#endif
|
||||
case Opt_nofscache:
|
||||
fsopt->flags &= ~CEPH_MOUNT_OPT_FSCACHE;
|
||||
kfree(fsopt->fscache_uniq);
|
||||
fsopt->fscache_uniq = 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, "ceph: 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;
|
||||
|
||||
out_of_range:
|
||||
return invalf(fc, "ceph: %s out of range", param->key);
|
||||
}
|
||||
|
||||
static void destroy_mount_options(struct ceph_mount_options *args)
|
||||
{
|
||||
dout("destroy_mount_options %p\n", args);
|
||||
if (!args)
|
||||
return;
|
||||
|
||||
kfree(args->snapdir_name);
|
||||
kfree(args->mds_namespace);
|
||||
kfree(args->server_path);
|
||||
@ -459,91 +488,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
|
||||
@ -587,7 +531,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");
|
||||
@ -860,12 +804,6 @@ static void ceph_umount_begin(struct super_block *sb)
|
||||
fsc->filp_gen++; // invalidate open files
|
||||
}
|
||||
|
||||
static int ceph_remount(struct super_block *sb, int *flags, char *data)
|
||||
{
|
||||
sync_filesystem(sb);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct super_operations ceph_super_ops = {
|
||||
.alloc_inode = ceph_alloc_inode,
|
||||
.free_inode = ceph_free_inode,
|
||||
@ -874,7 +812,6 @@ static const struct super_operations ceph_super_ops = {
|
||||
.evict_inode = ceph_evict_inode,
|
||||
.sync_fs = ceph_sync_fs,
|
||||
.put_super = ceph_put_super,
|
||||
.remount_fs = ceph_remount,
|
||||
.show_options = ceph_show_options,
|
||||
.statfs = ceph_statfs,
|
||||
.umount_begin = ceph_umount_begin,
|
||||
@ -935,7 +872,8 @@ out:
|
||||
/*
|
||||
* mount: join the ceph cluster, and open root directory.
|
||||
*/
|
||||
static struct dentry *ceph_real_mount(struct ceph_fs_client *fsc)
|
||||
static struct dentry *ceph_real_mount(struct ceph_fs_client *fsc,
|
||||
struct fs_context *fc)
|
||||
{
|
||||
int err;
|
||||
unsigned long started = jiffies; /* note the start time */
|
||||
@ -952,7 +890,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(fsc, fc);
|
||||
if (err < 0)
|
||||
goto out;
|
||||
}
|
||||
@ -987,18 +925,16 @@ out:
|
||||
return ERR_PTR(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 */
|
||||
|
||||
@ -1010,24 +946,18 @@ static int ceph_set_super(struct super_block *s, void *data)
|
||||
s->s_time_min = 0;
|
||||
s->s_time_max = U32_MAX;
|
||||
|
||||
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);
|
||||
@ -1043,7 +973,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;
|
||||
}
|
||||
@ -1073,46 +1003,46 @@ 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 ceph_parse_opts_ctx *pctx = fc->fs_private;
|
||||
struct super_block *sb;
|
||||
struct ceph_fs_client *fsc;
|
||||
struct dentry *res;
|
||||
int (*compare_super)(struct super_block *, struct fs_context *) =
|
||||
ceph_compare_super;
|
||||
int err;
|
||||
int (*compare_super)(struct super_block *, void *) = ceph_compare_super;
|
||||
struct ceph_mount_options *fsopt = NULL;
|
||||
struct ceph_options *opt = NULL;
|
||||
|
||||
dout("ceph_mount\n");
|
||||
dout("ceph_get_tree\n");
|
||||
|
||||
if (!fc->source)
|
||||
return invalf(fc, "ceph: No source");
|
||||
|
||||
#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(pctx->opts, pctx->copts);
|
||||
pctx->opts = NULL;
|
||||
pctx->copts = NULL;
|
||||
if (IS_ERR(fsc)) {
|
||||
res = ERR_CAST(fsc);
|
||||
err = PTR_ERR(fsc);
|
||||
goto out_final;
|
||||
}
|
||||
|
||||
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);
|
||||
fc->s_fs_info = NULL;
|
||||
if (IS_ERR(sb)) {
|
||||
res = ERR_CAST(sb);
|
||||
err = PTR_ERR(sb);
|
||||
goto out;
|
||||
}
|
||||
|
||||
@ -1123,18 +1053,19 @@ 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))
|
||||
res = ceph_real_mount(fsc, fc);
|
||||
if (IS_ERR(res)) {
|
||||
err = PTR_ERR(res);
|
||||
goto out_splat;
|
||||
}
|
||||
dout("root %p inode %p ino %llx.%llx\n", res,
|
||||
d_inode(res), ceph_vinop(d_inode(res)));
|
||||
return res;
|
||||
fc->root = fsc->sb->s_root;
|
||||
return 0;
|
||||
|
||||
out_splat:
|
||||
ceph_mdsc_close_sessions(fsc->mdsc);
|
||||
@ -1144,8 +1075,79 @@ out_splat:
|
||||
out:
|
||||
destroy_fs_client(fsc);
|
||||
out_final:
|
||||
dout("ceph_mount fail %ld\n", PTR_ERR(res));
|
||||
return res;
|
||||
dout("ceph_get_tree fail %d\n", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
static void ceph_free_fc(struct fs_context *fc)
|
||||
{
|
||||
struct ceph_parse_opts_ctx *pctx = fc->fs_private;
|
||||
|
||||
if (pctx) {
|
||||
destroy_mount_options(pctx->opts);
|
||||
ceph_destroy_options(pctx->copts);
|
||||
kfree(pctx);
|
||||
}
|
||||
}
|
||||
|
||||
static int ceph_reconfigure_fc(struct fs_context *fc)
|
||||
{
|
||||
sync_filesystem(fc->root->d_sb);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct fs_context_operations ceph_context_ops = {
|
||||
.free = ceph_free_fc,
|
||||
.parse_param = ceph_parse_mount_param,
|
||||
.get_tree = ceph_get_tree,
|
||||
.reconfigure = ceph_reconfigure_fc,
|
||||
};
|
||||
|
||||
/*
|
||||
* Set up the filesystem mount context.
|
||||
*/
|
||||
static int ceph_init_fs_context(struct fs_context *fc)
|
||||
{
|
||||
struct ceph_parse_opts_ctx *pctx;
|
||||
struct ceph_mount_options *fsopt;
|
||||
|
||||
pctx = kzalloc(sizeof(*pctx), GFP_KERNEL);
|
||||
if (!pctx)
|
||||
return -ENOMEM;
|
||||
|
||||
pctx->copts = ceph_alloc_options();
|
||||
if (!pctx->copts)
|
||||
goto nomem;
|
||||
|
||||
pctx->opts = kzalloc(sizeof(*pctx->opts), GFP_KERNEL);
|
||||
if (!pctx->opts)
|
||||
goto nomem;
|
||||
|
||||
fsopt = pctx->opts;
|
||||
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)
|
||||
goto nomem;
|
||||
|
||||
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 = pctx;
|
||||
fc->ops = &ceph_context_ops;
|
||||
return 0;
|
||||
|
||||
nomem:
|
||||
destroy_mount_options(pctx->opts);
|
||||
ceph_destroy_options(pctx->copts);
|
||||
kfree(pctx);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
static void ceph_kill_sb(struct super_block *s)
|
||||
@ -1172,7 +1174,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,
|
||||
};
|
||||
|
@ -74,7 +74,6 @@
|
||||
|
||||
struct ceph_mount_options {
|
||||
int flags;
|
||||
int sb_flags;
|
||||
|
||||
int wsize; /* max write size */
|
||||
int rsize; /* max read size */
|
||||
@ -407,22 +406,26 @@ struct ceph_inode_info {
|
||||
struct inode vfs_inode; /* at end */
|
||||
};
|
||||
|
||||
static inline struct ceph_inode_info *ceph_inode(struct inode *inode)
|
||||
static inline struct ceph_inode_info *
|
||||
ceph_inode(const struct inode *inode)
|
||||
{
|
||||
return container_of(inode, struct ceph_inode_info, vfs_inode);
|
||||
}
|
||||
|
||||
static inline struct ceph_fs_client *ceph_inode_to_client(struct inode *inode)
|
||||
static inline struct ceph_fs_client *
|
||||
ceph_inode_to_client(const struct inode *inode)
|
||||
{
|
||||
return (struct ceph_fs_client *)inode->i_sb->s_fs_info;
|
||||
}
|
||||
|
||||
static inline struct ceph_fs_client *ceph_sb_to_client(struct super_block *sb)
|
||||
static inline struct ceph_fs_client *
|
||||
ceph_sb_to_client(const struct super_block *sb)
|
||||
{
|
||||
return (struct ceph_fs_client *)sb->s_fs_info;
|
||||
}
|
||||
|
||||
static inline struct ceph_vino ceph_vino(struct inode *inode)
|
||||
static inline struct ceph_vino
|
||||
ceph_vino(const struct inode *inode)
|
||||
{
|
||||
return ceph_inode(inode)->i_vino;
|
||||
}
|
||||
|
@ -280,10 +280,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;
|
||||
struct ceph_options *ceph_alloc_options(void);
|
||||
int ceph_parse_mon_ips(const char *buf, size_t len, struct ceph_options *opt,
|
||||
struct fs_context *fc);
|
||||
int ceph_parse_param(struct fs_parameter *param, struct ceph_options *opt,
|
||||
struct fs_context *fc);
|
||||
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);
|
||||
|
@ -11,7 +11,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/sched/mm.h>
|
||||
#include <linux/seq_file.h>
|
||||
@ -254,58 +254,77 @@ enum {
|
||||
Opt_mount_timeout,
|
||||
Opt_osd_idle_ttl,
|
||||
Opt_osd_request_timeout,
|
||||
Opt_last_int,
|
||||
/* int args above */
|
||||
Opt_fsid,
|
||||
Opt_name,
|
||||
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_param_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_parameters = {
|
||||
.name = "libceph",
|
||||
.specs = ceph_param_specs,
|
||||
};
|
||||
|
||||
struct ceph_options *ceph_alloc_options(void)
|
||||
{
|
||||
struct ceph_options *opt;
|
||||
|
||||
opt = kzalloc(sizeof(*opt), 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);
|
||||
if (!opt)
|
||||
return;
|
||||
|
||||
kfree(opt->name);
|
||||
if (opt->key) {
|
||||
ceph_crypto_key_destroy(opt->key);
|
||||
@ -317,7 +336,9 @@ void ceph_destroy_options(struct ceph_options *opt)
|
||||
EXPORT_SYMBOL(ceph_destroy_options);
|
||||
|
||||
/* get secret from key store */
|
||||
static int get_secret(struct ceph_crypto_key *dst, const char *name) {
|
||||
static int get_secret(struct ceph_crypto_key *dst, const char *name,
|
||||
struct fs_context *fc)
|
||||
{
|
||||
struct key *ukey;
|
||||
int key_err;
|
||||
int err = 0;
|
||||
@ -330,20 +351,20 @@ static int get_secret(struct ceph_crypto_key *dst, const char *name) {
|
||||
key_err = PTR_ERR(ukey);
|
||||
switch (key_err) {
|
||||
case -ENOKEY:
|
||||
pr_warn("ceph: Mount failed due to key not found: %s\n",
|
||||
name);
|
||||
errorf(fc, "libceph: Failed due to key not found: %s",
|
||||
name);
|
||||
break;
|
||||
case -EKEYEXPIRED:
|
||||
pr_warn("ceph: Mount failed due to expired key: %s\n",
|
||||
name);
|
||||
errorf(fc, "libceph: Failed due to expired key: %s",
|
||||
name);
|
||||
break;
|
||||
case -EKEYREVOKED:
|
||||
pr_warn("ceph: Mount failed due to revoked key: %s\n",
|
||||
name);
|
||||
errorf(fc, "libceph: Failed due to revoked key: %s",
|
||||
name);
|
||||
break;
|
||||
default:
|
||||
pr_warn("ceph: Mount failed due to unknown key error %d: %s\n",
|
||||
key_err, name);
|
||||
errorf(fc, "libceph: Failed due to key error %d: %s",
|
||||
key_err, name);
|
||||
}
|
||||
err = -EPERM;
|
||||
goto out;
|
||||
@ -361,217 +382,157 @@ out:
|
||||
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_mon_ips(const char *buf, size_t len, struct ceph_options *opt,
|
||||
struct fs_context *fc)
|
||||
{
|
||||
struct ceph_options *opt;
|
||||
const char *c;
|
||||
int err = -ENOMEM;
|
||||
substring_t argstr[MAX_OPT_ARGS];
|
||||
int ret;
|
||||
|
||||
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;
|
||||
|
||||
/* 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;
|
||||
opt->flags |= CEPH_OPT_MYIP;
|
||||
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;
|
||||
|
||||
/* 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;
|
||||
|
||||
case Opt_share:
|
||||
opt->flags &= ~CEPH_OPT_NOSHARE;
|
||||
break;
|
||||
case Opt_noshare:
|
||||
opt->flags |= CEPH_OPT_NOSHARE;
|
||||
break;
|
||||
|
||||
case Opt_crc:
|
||||
opt->flags &= ~CEPH_OPT_NOCRC;
|
||||
break;
|
||||
case Opt_nocrc:
|
||||
opt->flags |= CEPH_OPT_NOCRC;
|
||||
break;
|
||||
|
||||
case Opt_cephx_require_signatures:
|
||||
opt->flags &= ~CEPH_OPT_NOMSGAUTH;
|
||||
break;
|
||||
case Opt_nocephx_require_signatures:
|
||||
opt->flags |= CEPH_OPT_NOMSGAUTH;
|
||||
break;
|
||||
case Opt_cephx_sign_messages:
|
||||
opt->flags &= ~CEPH_OPT_NOMSGSIGN;
|
||||
break;
|
||||
case Opt_nocephx_sign_messages:
|
||||
opt->flags |= CEPH_OPT_NOMSGSIGN;
|
||||
break;
|
||||
|
||||
case Opt_tcp_nodelay:
|
||||
opt->flags |= CEPH_OPT_TCP_NODELAY;
|
||||
break;
|
||||
case Opt_notcp_nodelay:
|
||||
opt->flags &= ~CEPH_OPT_TCP_NODELAY;
|
||||
break;
|
||||
|
||||
case Opt_abort_on_full:
|
||||
opt->flags |= CEPH_OPT_ABORT_ON_FULL;
|
||||
break;
|
||||
|
||||
default:
|
||||
BUG_ON(token);
|
||||
}
|
||||
ret = ceph_parse_ips(buf, buf + len, opt->mon_addr, CEPH_MAX_MON,
|
||||
&opt->num_mon);
|
||||
if (ret) {
|
||||
errorf(fc, "libceph: Failed to parse monitor IPs: %d", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* success */
|
||||
return opt;
|
||||
|
||||
out:
|
||||
ceph_destroy_options(opt);
|
||||
return ERR_PTR(err);
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(ceph_parse_options);
|
||||
EXPORT_SYMBOL(ceph_parse_mon_ips);
|
||||
|
||||
int ceph_parse_param(struct fs_parameter *param, struct ceph_options *opt,
|
||||
struct fs_context *fc)
|
||||
{
|
||||
struct fs_parse_result result;
|
||||
int token, err;
|
||||
|
||||
token = fs_parse(fc, &ceph_parameters, param, &result);
|
||||
dout("%s fs_parse '%s' token %d\n", __func__, param->key, token);
|
||||
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) {
|
||||
errorf(fc, "libceph: Failed to parse ip: %d", err);
|
||||
return err;
|
||||
}
|
||||
opt->flags |= CEPH_OPT_MYIP;
|
||||
break;
|
||||
|
||||
case Opt_fsid:
|
||||
err = parse_fsid(param->string, &opt->fsid);
|
||||
if (err) {
|
||||
errorf(fc, "libceph: Failed to parse fsid: %d", err);
|
||||
return err;
|
||||
}
|
||||
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);
|
||||
|
||||
opt->key = kzalloc(sizeof(*opt->key), GFP_KERNEL);
|
||||
if (!opt->key)
|
||||
return -ENOMEM;
|
||||
err = ceph_crypto_key_unarmor(opt->key, param->string);
|
||||
if (err) {
|
||||
errorf(fc, "libceph: Failed to parse secret: %d", err);
|
||||
return err;
|
||||
}
|
||||
break;
|
||||
case Opt_key:
|
||||
ceph_crypto_key_destroy(opt->key);
|
||||
kfree(opt->key);
|
||||
|
||||
opt->key = kzalloc(sizeof(*opt->key), GFP_KERNEL);
|
||||
if (!opt->key)
|
||||
return -ENOMEM;
|
||||
return get_secret(opt->key, param->string, fc);
|
||||
|
||||
case Opt_osdtimeout:
|
||||
warnf(fc, "libceph: Ignoring osdtimeout");
|
||||
break;
|
||||
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;
|
||||
else
|
||||
opt->flags |= CEPH_OPT_NOSHARE;
|
||||
break;
|
||||
case Opt_crc:
|
||||
if (!result.negated)
|
||||
opt->flags &= ~CEPH_OPT_NOCRC;
|
||||
else
|
||||
opt->flags |= CEPH_OPT_NOCRC;
|
||||
break;
|
||||
case Opt_cephx_require_signatures:
|
||||
if (!result.negated)
|
||||
opt->flags &= ~CEPH_OPT_NOMSGAUTH;
|
||||
else
|
||||
opt->flags |= CEPH_OPT_NOMSGAUTH;
|
||||
break;
|
||||
case Opt_cephx_sign_messages:
|
||||
if (!result.negated)
|
||||
opt->flags &= ~CEPH_OPT_NOMSGSIGN;
|
||||
else
|
||||
opt->flags |= CEPH_OPT_NOMSGSIGN;
|
||||
break;
|
||||
case Opt_tcp_nodelay:
|
||||
if (!result.negated)
|
||||
opt->flags |= CEPH_OPT_TCP_NODELAY;
|
||||
else
|
||||
opt->flags &= ~CEPH_OPT_TCP_NODELAY;
|
||||
break;
|
||||
|
||||
case Opt_abort_on_full:
|
||||
opt->flags |= CEPH_OPT_ABORT_ON_FULL;
|
||||
break;
|
||||
|
||||
default:
|
||||
BUG();
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
out_of_range:
|
||||
return invalf(fc, "libceph: %s out of range", param->key);
|
||||
}
|
||||
EXPORT_SYMBOL(ceph_parse_param);
|
||||
|
||||
int ceph_print_client_options(struct seq_file *m, struct ceph_client *client,
|
||||
bool show_all)
|
||||
|
@ -2004,10 +2004,8 @@ int ceph_parse_ips(const char *c, const char *end,
|
||||
return 0;
|
||||
|
||||
bad:
|
||||
pr_err("parse_ips bad ip '%.*s'\n", (int)(end - c), c);
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL(ceph_parse_ips);
|
||||
|
||||
static int process_banner(struct ceph_connection *con)
|
||||
{
|
||||
|
@ -1233,9 +1233,6 @@ static void dispatch(struct ceph_connection *con, struct ceph_msg *msg)
|
||||
struct ceph_mon_client *monc = con->private;
|
||||
int type = le16_to_cpu(msg->hdr.type);
|
||||
|
||||
if (!monc)
|
||||
return;
|
||||
|
||||
switch (type) {
|
||||
case CEPH_MSG_AUTH_REPLY:
|
||||
handle_auth_reply(monc, msg);
|
||||
|
Loading…
Reference in New Issue
Block a user