|
@@ -31,6 +31,7 @@
|
|
|
#include <linux/ceph/osd_client.h>
|
|
|
#include <linux/ceph/mon_client.h>
|
|
|
#include <linux/ceph/decode.h>
|
|
|
+#include <linux/parser.h>
|
|
|
|
|
|
#include <linux/kernel.h>
|
|
|
#include <linux/device.h>
|
|
@@ -54,6 +55,8 @@
|
|
|
|
|
|
#define DEV_NAME_LEN 32
|
|
|
|
|
|
+#define RBD_NOTIFY_TIMEOUT_DEFAULT 10
|
|
|
+
|
|
|
/*
|
|
|
* block device image metadata (in-memory version)
|
|
|
*/
|
|
@@ -71,6 +74,12 @@ struct rbd_image_header {
|
|
|
|
|
|
char *snap_names;
|
|
|
u64 *snap_sizes;
|
|
|
+
|
|
|
+ u64 obj_version;
|
|
|
+};
|
|
|
+
|
|
|
+struct rbd_options {
|
|
|
+ int notify_timeout;
|
|
|
};
|
|
|
|
|
|
/*
|
|
@@ -78,6 +87,7 @@ struct rbd_image_header {
|
|
|
*/
|
|
|
struct rbd_client {
|
|
|
struct ceph_client *client;
|
|
|
+ struct rbd_options *rbd_opts;
|
|
|
struct kref kref;
|
|
|
struct list_head node;
|
|
|
};
|
|
@@ -124,6 +134,9 @@ struct rbd_device {
|
|
|
char pool_name[RBD_MAX_POOL_NAME_LEN];
|
|
|
int poolid;
|
|
|
|
|
|
+ struct ceph_osd_event *watch_event;
|
|
|
+ struct ceph_osd_request *watch_request;
|
|
|
+
|
|
|
char snap_name[RBD_MAX_SNAP_NAME_LEN];
|
|
|
u32 cur_snap; /* index+1 of current snapshot within snap context
|
|
|
0 - for the head */
|
|
@@ -177,6 +190,8 @@ static void rbd_put_dev(struct rbd_device *rbd_dev)
|
|
|
put_device(&rbd_dev->dev);
|
|
|
}
|
|
|
|
|
|
+static int __rbd_update_snaps(struct rbd_device *rbd_dev);
|
|
|
+
|
|
|
static int rbd_open(struct block_device *bdev, fmode_t mode)
|
|
|
{
|
|
|
struct gendisk *disk = bdev->bd_disk;
|
|
@@ -211,7 +226,8 @@ static const struct block_device_operations rbd_bd_ops = {
|
|
|
* Initialize an rbd client instance.
|
|
|
* We own *opt.
|
|
|
*/
|
|
|
-static struct rbd_client *rbd_client_create(struct ceph_options *opt)
|
|
|
+static struct rbd_client *rbd_client_create(struct ceph_options *opt,
|
|
|
+ struct rbd_options *rbd_opts)
|
|
|
{
|
|
|
struct rbd_client *rbdc;
|
|
|
int ret = -ENOMEM;
|
|
@@ -233,6 +249,8 @@ static struct rbd_client *rbd_client_create(struct ceph_options *opt)
|
|
|
if (ret < 0)
|
|
|
goto out_err;
|
|
|
|
|
|
+ rbdc->rbd_opts = rbd_opts;
|
|
|
+
|
|
|
spin_lock(&node_lock);
|
|
|
list_add_tail(&rbdc->node, &rbd_client_list);
|
|
|
spin_unlock(&node_lock);
|
|
@@ -266,6 +284,59 @@ static struct rbd_client *__rbd_client_find(struct ceph_options *opt)
|
|
|
return NULL;
|
|
|
}
|
|
|
|
|
|
+/*
|
|
|
+ * mount options
|
|
|
+ */
|
|
|
+enum {
|
|
|
+ Opt_notify_timeout,
|
|
|
+ Opt_last_int,
|
|
|
+ /* int args above */
|
|
|
+ Opt_last_string,
|
|
|
+ /* string args above */
|
|
|
+};
|
|
|
+
|
|
|
+static match_table_t rbdopt_tokens = {
|
|
|
+ {Opt_notify_timeout, "notify_timeout=%d"},
|
|
|
+ /* int args above */
|
|
|
+ /* string args above */
|
|
|
+ {-1, NULL}
|
|
|
+};
|
|
|
+
|
|
|
+static int parse_rbd_opts_token(char *c, void *private)
|
|
|
+{
|
|
|
+ struct rbd_options *rbdopt = private;
|
|
|
+ substring_t argstr[MAX_OPT_ARGS];
|
|
|
+ int token, intval, ret;
|
|
|
+
|
|
|
+ token = match_token((char *)c, rbdopt_tokens, argstr);
|
|
|
+ if (token < 0)
|
|
|
+ return -EINVAL;
|
|
|
+
|
|
|
+ if (token < Opt_last_int) {
|
|
|
+ ret = match_int(&argstr[0], &intval);
|
|
|
+ if (ret < 0) {
|
|
|
+ pr_err("bad mount 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_notify_timeout:
|
|
|
+ rbdopt->notify_timeout = intval;
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ BUG_ON(token);
|
|
|
+ }
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
/*
|
|
|
* Get a ceph client with specific addr and configuration, if one does
|
|
|
* not exist create it.
|
|
@@ -276,11 +347,18 @@ static int rbd_get_client(struct rbd_device *rbd_dev, const char *mon_addr,
|
|
|
struct rbd_client *rbdc;
|
|
|
struct ceph_options *opt;
|
|
|
int ret;
|
|
|
+ struct rbd_options *rbd_opts;
|
|
|
+
|
|
|
+ rbd_opts = kzalloc(sizeof(*rbd_opts), GFP_KERNEL);
|
|
|
+ if (!rbd_opts)
|
|
|
+ return -ENOMEM;
|
|
|
+
|
|
|
+ rbd_opts->notify_timeout = RBD_NOTIFY_TIMEOUT_DEFAULT;
|
|
|
|
|
|
ret = ceph_parse_options(&opt, options, mon_addr,
|
|
|
- mon_addr + strlen(mon_addr), NULL, NULL);
|
|
|
+ mon_addr + strlen(mon_addr), parse_rbd_opts_token, rbd_opts);
|
|
|
if (ret < 0)
|
|
|
- return ret;
|
|
|
+ goto done_err;
|
|
|
|
|
|
spin_lock(&node_lock);
|
|
|
rbdc = __rbd_client_find(opt);
|
|
@@ -296,13 +374,18 @@ static int rbd_get_client(struct rbd_device *rbd_dev, const char *mon_addr,
|
|
|
}
|
|
|
spin_unlock(&node_lock);
|
|
|
|
|
|
- rbdc = rbd_client_create(opt);
|
|
|
- if (IS_ERR(rbdc))
|
|
|
- return PTR_ERR(rbdc);
|
|
|
+ rbdc = rbd_client_create(opt, rbd_opts);
|
|
|
+ if (IS_ERR(rbdc)) {
|
|
|
+ ret = PTR_ERR(rbdc);
|
|
|
+ goto done_err;
|
|
|
+ }
|
|
|
|
|
|
rbd_dev->rbd_client = rbdc;
|
|
|
rbd_dev->client = rbdc->client;
|
|
|
return 0;
|
|
|
+done_err:
|
|
|
+ kfree(rbd_opts);
|
|
|
+ return ret;
|
|
|
}
|
|
|
|
|
|
/*
|
|
@@ -318,6 +401,7 @@ static void rbd_client_release(struct kref *kref)
|
|
|
spin_unlock(&node_lock);
|
|
|
|
|
|
ceph_destroy_client(rbdc->client);
|
|
|
+ kfree(rbdc->rbd_opts);
|
|
|
kfree(rbdc);
|
|
|
}
|
|
|
|
|
@@ -666,7 +750,9 @@ static int rbd_do_request(struct request *rq,
|
|
|
struct ceph_osd_req_op *ops,
|
|
|
int num_reply,
|
|
|
void (*rbd_cb)(struct ceph_osd_request *req,
|
|
|
- struct ceph_msg *msg))
|
|
|
+ struct ceph_msg *msg),
|
|
|
+ struct ceph_osd_request **linger_req,
|
|
|
+ u64 *ver)
|
|
|
{
|
|
|
struct ceph_osd_request *req;
|
|
|
struct ceph_file_layout *layout;
|
|
@@ -729,12 +815,20 @@ static int rbd_do_request(struct request *rq,
|
|
|
req->r_oid, req->r_oid_len);
|
|
|
up_read(&header->snap_rwsem);
|
|
|
|
|
|
+ if (linger_req) {
|
|
|
+ ceph_osdc_set_request_linger(&dev->client->osdc, req);
|
|
|
+ *linger_req = req;
|
|
|
+ }
|
|
|
+
|
|
|
ret = ceph_osdc_start_request(&dev->client->osdc, req, false);
|
|
|
if (ret < 0)
|
|
|
goto done_err;
|
|
|
|
|
|
if (!rbd_cb) {
|
|
|
ret = ceph_osdc_wait_request(&dev->client->osdc, req);
|
|
|
+ if (ver)
|
|
|
+ *ver = le64_to_cpu(req->r_reassert_version.version);
|
|
|
+ dout("reassert_ver=%lld\n", le64_to_cpu(req->r_reassert_version.version));
|
|
|
ceph_osdc_put_request(req);
|
|
|
}
|
|
|
return ret;
|
|
@@ -789,6 +883,11 @@ static void rbd_req_cb(struct ceph_osd_request *req, struct ceph_msg *msg)
|
|
|
kfree(req_data);
|
|
|
}
|
|
|
|
|
|
+static void rbd_simple_req_cb(struct ceph_osd_request *req, struct ceph_msg *msg)
|
|
|
+{
|
|
|
+ ceph_osdc_put_request(req);
|
|
|
+}
|
|
|
+
|
|
|
/*
|
|
|
* Do a synchronous ceph osd operation
|
|
|
*/
|
|
@@ -801,7 +900,9 @@ static int rbd_req_sync_op(struct rbd_device *dev,
|
|
|
int num_reply,
|
|
|
const char *obj,
|
|
|
u64 ofs, u64 len,
|
|
|
- char *buf)
|
|
|
+ char *buf,
|
|
|
+ struct ceph_osd_request **linger_req,
|
|
|
+ u64 *ver)
|
|
|
{
|
|
|
int ret;
|
|
|
struct page **pages;
|
|
@@ -833,7 +934,8 @@ static int rbd_req_sync_op(struct rbd_device *dev,
|
|
|
flags,
|
|
|
ops,
|
|
|
2,
|
|
|
- NULL);
|
|
|
+ NULL,
|
|
|
+ linger_req, ver);
|
|
|
if (ret < 0)
|
|
|
goto done_ops;
|
|
|
|
|
@@ -893,7 +995,7 @@ static int rbd_do_op(struct request *rq,
|
|
|
flags,
|
|
|
ops,
|
|
|
num_reply,
|
|
|
- rbd_req_cb);
|
|
|
+ rbd_req_cb, 0, NULL);
|
|
|
done:
|
|
|
kfree(seg_name);
|
|
|
return ret;
|
|
@@ -940,18 +1042,174 @@ static int rbd_req_sync_read(struct rbd_device *dev,
|
|
|
u64 snapid,
|
|
|
const char *obj,
|
|
|
u64 ofs, u64 len,
|
|
|
- char *buf)
|
|
|
+ char *buf,
|
|
|
+ u64 *ver)
|
|
|
{
|
|
|
return rbd_req_sync_op(dev, NULL,
|
|
|
(snapid ? snapid : CEPH_NOSNAP),
|
|
|
CEPH_OSD_OP_READ,
|
|
|
CEPH_OSD_FLAG_READ,
|
|
|
NULL,
|
|
|
- 1, obj, ofs, len, buf);
|
|
|
+ 1, obj, ofs, len, buf, NULL, ver);
|
|
|
}
|
|
|
|
|
|
/*
|
|
|
- * Request sync osd read
|
|
|
+ * Request sync osd watch
|
|
|
+ */
|
|
|
+static int rbd_req_sync_notify_ack(struct rbd_device *dev,
|
|
|
+ u64 ver,
|
|
|
+ u64 notify_id,
|
|
|
+ const char *obj)
|
|
|
+{
|
|
|
+ struct ceph_osd_req_op *ops;
|
|
|
+ struct page **pages = NULL;
|
|
|
+ int ret = rbd_create_rw_ops(&ops, 1, CEPH_OSD_OP_NOTIFY_ACK, 0);
|
|
|
+ if (ret < 0)
|
|
|
+ return ret;
|
|
|
+
|
|
|
+ ops[0].watch.ver = cpu_to_le64(dev->header.obj_version);
|
|
|
+ ops[0].watch.cookie = notify_id;
|
|
|
+ ops[0].watch.flag = 0;
|
|
|
+
|
|
|
+ ret = rbd_do_request(NULL, dev, NULL, CEPH_NOSNAP,
|
|
|
+ obj, 0, 0, NULL,
|
|
|
+ pages, 0,
|
|
|
+ CEPH_OSD_FLAG_READ,
|
|
|
+ ops,
|
|
|
+ 1,
|
|
|
+ rbd_simple_req_cb, 0, NULL);
|
|
|
+
|
|
|
+ rbd_destroy_ops(ops);
|
|
|
+ return ret;
|
|
|
+}
|
|
|
+
|
|
|
+static void rbd_watch_cb(u64 ver, u64 notify_id, u8 opcode, void *data)
|
|
|
+{
|
|
|
+ struct rbd_device *dev = (struct rbd_device *)data;
|
|
|
+ if (!dev)
|
|
|
+ return;
|
|
|
+
|
|
|
+ dout("rbd_watch_cb %s notify_id=%lld opcode=%d\n", dev->obj_md_name,
|
|
|
+ notify_id, (int)opcode);
|
|
|
+ mutex_lock_nested(&ctl_mutex, SINGLE_DEPTH_NESTING);
|
|
|
+ __rbd_update_snaps(dev);
|
|
|
+ mutex_unlock(&ctl_mutex);
|
|
|
+
|
|
|
+ rbd_req_sync_notify_ack(dev, ver, notify_id, dev->obj_md_name);
|
|
|
+}
|
|
|
+
|
|
|
+/*
|
|
|
+ * Request sync osd watch
|
|
|
+ */
|
|
|
+static int rbd_req_sync_watch(struct rbd_device *dev,
|
|
|
+ const char *obj,
|
|
|
+ u64 ver)
|
|
|
+{
|
|
|
+ struct ceph_osd_req_op *ops;
|
|
|
+ struct ceph_osd_client *osdc = &dev->client->osdc;
|
|
|
+
|
|
|
+ int ret = rbd_create_rw_ops(&ops, 1, CEPH_OSD_OP_WATCH, 0);
|
|
|
+ if (ret < 0)
|
|
|
+ return ret;
|
|
|
+
|
|
|
+ ret = ceph_osdc_create_event(osdc, rbd_watch_cb, 0,
|
|
|
+ (void *)dev, &dev->watch_event);
|
|
|
+ if (ret < 0)
|
|
|
+ goto fail;
|
|
|
+
|
|
|
+ ops[0].watch.ver = cpu_to_le64(ver);
|
|
|
+ ops[0].watch.cookie = cpu_to_le64(dev->watch_event->cookie);
|
|
|
+ ops[0].watch.flag = 1;
|
|
|
+
|
|
|
+ ret = rbd_req_sync_op(dev, NULL,
|
|
|
+ CEPH_NOSNAP,
|
|
|
+ 0,
|
|
|
+ CEPH_OSD_FLAG_WRITE | CEPH_OSD_FLAG_ONDISK,
|
|
|
+ ops,
|
|
|
+ 1, obj, 0, 0, NULL,
|
|
|
+ &dev->watch_request, NULL);
|
|
|
+
|
|
|
+ if (ret < 0)
|
|
|
+ goto fail_event;
|
|
|
+
|
|
|
+ rbd_destroy_ops(ops);
|
|
|
+ return 0;
|
|
|
+
|
|
|
+fail_event:
|
|
|
+ ceph_osdc_cancel_event(dev->watch_event);
|
|
|
+ dev->watch_event = NULL;
|
|
|
+fail:
|
|
|
+ rbd_destroy_ops(ops);
|
|
|
+ return ret;
|
|
|
+}
|
|
|
+
|
|
|
+struct rbd_notify_info {
|
|
|
+ struct rbd_device *dev;
|
|
|
+};
|
|
|
+
|
|
|
+static void rbd_notify_cb(u64 ver, u64 notify_id, u8 opcode, void *data)
|
|
|
+{
|
|
|
+ struct rbd_device *dev = (struct rbd_device *)data;
|
|
|
+ if (!dev)
|
|
|
+ return;
|
|
|
+
|
|
|
+ dout("rbd_notify_cb %s notify_id=%lld opcode=%d\n", dev->obj_md_name,
|
|
|
+ notify_id, (int)opcode);
|
|
|
+}
|
|
|
+
|
|
|
+/*
|
|
|
+ * Request sync osd notify
|
|
|
+ */
|
|
|
+static int rbd_req_sync_notify(struct rbd_device *dev,
|
|
|
+ const char *obj)
|
|
|
+{
|
|
|
+ struct ceph_osd_req_op *ops;
|
|
|
+ struct ceph_osd_client *osdc = &dev->client->osdc;
|
|
|
+ struct ceph_osd_event *event;
|
|
|
+ struct rbd_notify_info info;
|
|
|
+ int payload_len = sizeof(u32) + sizeof(u32);
|
|
|
+ int ret;
|
|
|
+
|
|
|
+ ret = rbd_create_rw_ops(&ops, 1, CEPH_OSD_OP_NOTIFY, payload_len);
|
|
|
+ if (ret < 0)
|
|
|
+ return ret;
|
|
|
+
|
|
|
+ info.dev = dev;
|
|
|
+
|
|
|
+ ret = ceph_osdc_create_event(osdc, rbd_notify_cb, 1,
|
|
|
+ (void *)&info, &event);
|
|
|
+ if (ret < 0)
|
|
|
+ goto fail;
|
|
|
+
|
|
|
+ ops[0].watch.ver = 1;
|
|
|
+ ops[0].watch.flag = 1;
|
|
|
+ ops[0].watch.cookie = event->cookie;
|
|
|
+ ops[0].watch.prot_ver = RADOS_NOTIFY_VER;
|
|
|
+ ops[0].watch.timeout = 12;
|
|
|
+
|
|
|
+ ret = rbd_req_sync_op(dev, NULL,
|
|
|
+ CEPH_NOSNAP,
|
|
|
+ 0,
|
|
|
+ CEPH_OSD_FLAG_WRITE | CEPH_OSD_FLAG_ONDISK,
|
|
|
+ ops,
|
|
|
+ 1, obj, 0, 0, NULL, NULL, NULL);
|
|
|
+ if (ret < 0)
|
|
|
+ goto fail_event;
|
|
|
+
|
|
|
+ ret = ceph_osdc_wait_event(event, CEPH_OSD_TIMEOUT_DEFAULT);
|
|
|
+ dout("ceph_osdc_wait_event returned %d\n", ret);
|
|
|
+ rbd_destroy_ops(ops);
|
|
|
+ return 0;
|
|
|
+
|
|
|
+fail_event:
|
|
|
+ ceph_osdc_cancel_event(event);
|
|
|
+fail:
|
|
|
+ rbd_destroy_ops(ops);
|
|
|
+ return ret;
|
|
|
+}
|
|
|
+
|
|
|
+/*
|
|
|
+ * Request sync osd rollback
|
|
|
*/
|
|
|
static int rbd_req_sync_rollback_obj(struct rbd_device *dev,
|
|
|
u64 snapid,
|
|
@@ -969,13 +1227,10 @@ static int rbd_req_sync_rollback_obj(struct rbd_device *dev,
|
|
|
0,
|
|
|
CEPH_OSD_FLAG_WRITE | CEPH_OSD_FLAG_ONDISK,
|
|
|
ops,
|
|
|
- 1, obj, 0, 0, NULL);
|
|
|
+ 1, obj, 0, 0, NULL, NULL, NULL);
|
|
|
|
|
|
rbd_destroy_ops(ops);
|
|
|
|
|
|
- if (ret < 0)
|
|
|
- return ret;
|
|
|
-
|
|
|
return ret;
|
|
|
}
|
|
|
|
|
@@ -987,7 +1242,8 @@ static int rbd_req_sync_exec(struct rbd_device *dev,
|
|
|
const char *cls,
|
|
|
const char *method,
|
|
|
const char *data,
|
|
|
- int len)
|
|
|
+ int len,
|
|
|
+ u64 *ver)
|
|
|
{
|
|
|
struct ceph_osd_req_op *ops;
|
|
|
int cls_len = strlen(cls);
|
|
@@ -1010,7 +1266,7 @@ static int rbd_req_sync_exec(struct rbd_device *dev,
|
|
|
0,
|
|
|
CEPH_OSD_FLAG_WRITE | CEPH_OSD_FLAG_ONDISK,
|
|
|
ops,
|
|
|
- 1, obj, 0, 0, NULL);
|
|
|
+ 1, obj, 0, 0, NULL, NULL, ver);
|
|
|
|
|
|
rbd_destroy_ops(ops);
|
|
|
|
|
@@ -1156,6 +1412,7 @@ static int rbd_read_header(struct rbd_device *rbd_dev,
|
|
|
struct rbd_image_header_ondisk *dh;
|
|
|
int snap_count = 0;
|
|
|
u64 snap_names_len = 0;
|
|
|
+ u64 ver;
|
|
|
|
|
|
while (1) {
|
|
|
int len = sizeof(*dh) +
|
|
@@ -1171,7 +1428,7 @@ static int rbd_read_header(struct rbd_device *rbd_dev,
|
|
|
NULL, CEPH_NOSNAP,
|
|
|
rbd_dev->obj_md_name,
|
|
|
0, len,
|
|
|
- (char *)dh);
|
|
|
+ (char *)dh, &ver);
|
|
|
if (rc < 0)
|
|
|
goto out_dh;
|
|
|
|
|
@@ -1188,6 +1445,7 @@ static int rbd_read_header(struct rbd_device *rbd_dev,
|
|
|
}
|
|
|
break;
|
|
|
}
|
|
|
+ header->obj_version = ver;
|
|
|
|
|
|
out_dh:
|
|
|
kfree(dh);
|
|
@@ -1205,6 +1463,7 @@ static int rbd_header_add_snap(struct rbd_device *dev,
|
|
|
u64 new_snapid;
|
|
|
int ret;
|
|
|
void *data, *data_start, *data_end;
|
|
|
+ u64 ver;
|
|
|
|
|
|
/* we should create a snapshot only if we're pointing at the head */
|
|
|
if (dev->cur_snap)
|
|
@@ -1227,7 +1486,7 @@ static int rbd_header_add_snap(struct rbd_device *dev,
|
|
|
ceph_encode_64_safe(&data, data_end, new_snapid, bad);
|
|
|
|
|
|
ret = rbd_req_sync_exec(dev, dev->obj_md_name, "rbd", "snap_add",
|
|
|
- data_start, data - data_start);
|
|
|
+ data_start, data - data_start, &ver);
|
|
|
|
|
|
kfree(data_start);
|
|
|
|
|
@@ -1259,6 +1518,7 @@ static int __rbd_update_snaps(struct rbd_device *rbd_dev)
|
|
|
int ret;
|
|
|
struct rbd_image_header h;
|
|
|
u64 snap_seq;
|
|
|
+ int follow_seq = 0;
|
|
|
|
|
|
ret = rbd_read_header(rbd_dev, &h);
|
|
|
if (ret < 0)
|
|
@@ -1267,6 +1527,11 @@ static int __rbd_update_snaps(struct rbd_device *rbd_dev)
|
|
|
down_write(&rbd_dev->header.snap_rwsem);
|
|
|
|
|
|
snap_seq = rbd_dev->header.snapc->seq;
|
|
|
+ if (rbd_dev->header.total_snaps &&
|
|
|
+ rbd_dev->header.snapc->snaps[0] == snap_seq)
|
|
|
+ /* pointing at the head, will need to follow that
|
|
|
+ if head moves */
|
|
|
+ follow_seq = 1;
|
|
|
|
|
|
kfree(rbd_dev->header.snapc);
|
|
|
kfree(rbd_dev->header.snap_names);
|
|
@@ -1277,7 +1542,10 @@ static int __rbd_update_snaps(struct rbd_device *rbd_dev)
|
|
|
rbd_dev->header.snap_names = h.snap_names;
|
|
|
rbd_dev->header.snap_names_len = h.snap_names_len;
|
|
|
rbd_dev->header.snap_sizes = h.snap_sizes;
|
|
|
- rbd_dev->header.snapc->seq = snap_seq;
|
|
|
+ if (follow_seq)
|
|
|
+ rbd_dev->header.snapc->seq = rbd_dev->header.snapc->snaps[0];
|
|
|
+ else
|
|
|
+ rbd_dev->header.snapc->seq = snap_seq;
|
|
|
|
|
|
ret = __rbd_init_snaps_header(rbd_dev);
|
|
|
|
|
@@ -1699,7 +1967,28 @@ static void rbd_bus_del_dev(struct rbd_device *rbd_dev)
|
|
|
device_unregister(&rbd_dev->dev);
|
|
|
}
|
|
|
|
|
|
-static ssize_t rbd_add(struct bus_type *bus, const char *buf, size_t count)
|
|
|
+static int rbd_init_watch_dev(struct rbd_device *rbd_dev)
|
|
|
+{
|
|
|
+ int ret, rc;
|
|
|
+
|
|
|
+ do {
|
|
|
+ ret = rbd_req_sync_watch(rbd_dev, rbd_dev->obj_md_name,
|
|
|
+ rbd_dev->header.obj_version);
|
|
|
+ if (ret == -ERANGE) {
|
|
|
+ mutex_lock_nested(&ctl_mutex, SINGLE_DEPTH_NESTING);
|
|
|
+ rc = __rbd_update_snaps(rbd_dev);
|
|
|
+ mutex_unlock(&ctl_mutex);
|
|
|
+ if (rc < 0)
|
|
|
+ return rc;
|
|
|
+ }
|
|
|
+ } while (ret == -ERANGE);
|
|
|
+
|
|
|
+ return ret;
|
|
|
+}
|
|
|
+
|
|
|
+static ssize_t rbd_add(struct bus_type *bus,
|
|
|
+ const char *buf,
|
|
|
+ size_t count)
|
|
|
{
|
|
|
struct ceph_osd_client *osdc;
|
|
|
struct rbd_device *rbd_dev;
|
|
@@ -1797,6 +2086,10 @@ static ssize_t rbd_add(struct bus_type *bus, const char *buf, size_t count)
|
|
|
if (rc)
|
|
|
goto err_out_bus;
|
|
|
|
|
|
+ rc = rbd_init_watch_dev(rbd_dev);
|
|
|
+ if (rc)
|
|
|
+ goto err_out_bus;
|
|
|
+
|
|
|
return count;
|
|
|
|
|
|
err_out_bus:
|
|
@@ -1849,6 +2142,12 @@ static void rbd_dev_release(struct device *dev)
|
|
|
struct rbd_device *rbd_dev =
|
|
|
container_of(dev, struct rbd_device, dev);
|
|
|
|
|
|
+ if (rbd_dev->watch_request)
|
|
|
+ ceph_osdc_unregister_linger_request(&rbd_dev->client->osdc,
|
|
|
+ rbd_dev->watch_request);
|
|
|
+ if (rbd_dev->watch_event)
|
|
|
+ ceph_osdc_cancel_event(rbd_dev->watch_event);
|
|
|
+
|
|
|
rbd_put_client(rbd_dev);
|
|
|
|
|
|
/* clean up and free blkdev */
|
|
@@ -1914,14 +2213,24 @@ static ssize_t rbd_snap_add(struct device *dev,
|
|
|
ret = rbd_header_add_snap(rbd_dev,
|
|
|
name, GFP_KERNEL);
|
|
|
if (ret < 0)
|
|
|
- goto done_unlock;
|
|
|
+ goto err_unlock;
|
|
|
|
|
|
ret = __rbd_update_snaps(rbd_dev);
|
|
|
if (ret < 0)
|
|
|
- goto done_unlock;
|
|
|
+ goto err_unlock;
|
|
|
+
|
|
|
+ /* shouldn't hold ctl_mutex when notifying.. notify might
|
|
|
+ trigger a watch callback that would need to get that mutex */
|
|
|
+ mutex_unlock(&ctl_mutex);
|
|
|
+
|
|
|
+ /* make a best effort, don't error if failed */
|
|
|
+ rbd_req_sync_notify(rbd_dev, rbd_dev->obj_md_name);
|
|
|
|
|
|
ret = count;
|
|
|
-done_unlock:
|
|
|
+ kfree(name);
|
|
|
+ return ret;
|
|
|
+
|
|
|
+err_unlock:
|
|
|
mutex_unlock(&ctl_mutex);
|
|
|
kfree(name);
|
|
|
return ret;
|