crypto: tcrypt - Fix memory leaks/crashes in multibuffer hash speed test
[cascardo/linux.git] / drivers / block / rbd.c
index 94a1843..81666a5 100644 (file)
@@ -350,12 +350,12 @@ struct rbd_device {
        struct rbd_spec         *spec;
        struct rbd_options      *opts;
 
-       char                    *header_name;
+       struct ceph_object_id   header_oid;
+       struct ceph_object_locator header_oloc;
 
        struct ceph_file_layout layout;
 
-       struct ceph_osd_event   *watch_event;
-       struct rbd_obj_request  *watch_request;
+       struct ceph_osd_linger_request *watch_handle;
 
        struct rbd_spec         *parent_spec;
        u64                     parent_overlap;
@@ -538,7 +538,6 @@ 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 u64 rbd_snap_id_by_name(struct rbd_device *rbd_dev, const char *name);
 
 static int rbd_open(struct block_device *bdev, fmode_t mode)
 {
@@ -1597,12 +1596,6 @@ static int rbd_obj_request_wait(struct rbd_obj_request *obj_request)
        return __rbd_obj_request_wait(obj_request, 0);
 }
 
-static int rbd_obj_request_wait_timeout(struct rbd_obj_request *obj_request,
-                                       unsigned long timeout)
-{
-       return __rbd_obj_request_wait(obj_request, timeout);
-}
-
 static void rbd_img_request_complete(struct rbd_img_request *img_request)
 {
 
@@ -1752,12 +1745,6 @@ static void rbd_obj_request_complete(struct rbd_obj_request *obj_request)
                complete_all(&obj_request->completion);
 }
 
-static void rbd_osd_trivial_callback(struct rbd_obj_request *obj_request)
-{
-       dout("%s: obj %p\n", __func__, obj_request);
-       obj_request_done_set(obj_request);
-}
-
 static void rbd_osd_read_callback(struct rbd_obj_request *obj_request)
 {
        struct rbd_img_request *img_request = NULL;
@@ -1829,13 +1816,12 @@ static void rbd_osd_call_callback(struct rbd_obj_request *obj_request)
                obj_request_done_set(obj_request);
 }
 
-static void rbd_osd_req_callback(struct ceph_osd_request *osd_req,
-                               struct ceph_msg *msg)
+static void rbd_osd_req_callback(struct ceph_osd_request *osd_req)
 {
        struct rbd_obj_request *obj_request = osd_req->r_priv;
        u16 opcode;
 
-       dout("%s: osd_req %p msg %p\n", __func__, osd_req, msg);
+       dout("%s: osd_req %p\n", __func__, osd_req);
        rbd_assert(osd_req == obj_request->osd_req);
        if (obj_request_img_data_test(obj_request)) {
                rbd_assert(obj_request->img_request);
@@ -1879,10 +1865,6 @@ static void rbd_osd_req_callback(struct ceph_osd_request *osd_req,
        case CEPH_OSD_OP_CALL:
                rbd_osd_call_callback(obj_request);
                break;
-       case CEPH_OSD_OP_NOTIFY_ACK:
-       case CEPH_OSD_OP_WATCH:
-               rbd_osd_trivial_callback(obj_request);
-               break;
        default:
                rbd_warn(NULL, "%s: unsupported op %hu",
                        obj_request->object_name, (unsigned short) opcode);
@@ -1897,27 +1879,17 @@ static void rbd_osd_req_format_read(struct rbd_obj_request *obj_request)
 {
        struct rbd_img_request *img_request = obj_request->img_request;
        struct ceph_osd_request *osd_req = obj_request->osd_req;
-       u64 snap_id;
-
-       rbd_assert(osd_req != NULL);
 
-       snap_id = img_request ? img_request->snap_id : CEPH_NOSNAP;
-       ceph_osdc_build_request(osd_req, obj_request->offset,
-                       NULL, snap_id, NULL);
+       if (img_request)
+               osd_req->r_snapid = img_request->snap_id;
 }
 
 static void rbd_osd_req_format_write(struct rbd_obj_request *obj_request)
 {
-       struct rbd_img_request *img_request = obj_request->img_request;
        struct ceph_osd_request *osd_req = obj_request->osd_req;
-       struct ceph_snap_context *snapc;
-       struct timespec mtime = CURRENT_TIME;
 
-       rbd_assert(osd_req != NULL);
-
-       snapc = img_request ? img_request->snapc : NULL;
-       ceph_osdc_build_request(osd_req, obj_request->offset,
-                       snapc, CEPH_NOSNAP, &mtime);
+       osd_req->r_mtime = CURRENT_TIME;
+       osd_req->r_data_offset = obj_request->offset;
 }
 
 /*
@@ -1955,7 +1927,7 @@ static struct ceph_osd_request *rbd_osd_req_create(
        osd_req = ceph_osdc_alloc_request(osdc, snapc, num_ops, false,
                                          GFP_NOIO);
        if (!osd_req)
-               return NULL;    /* ENOMEM */
+               goto fail;
 
        if (op_type == OBJ_OP_WRITE || op_type == OBJ_OP_DISCARD)
                osd_req->r_flags = CEPH_OSD_FLAG_WRITE | CEPH_OSD_FLAG_ONDISK;
@@ -1966,9 +1938,18 @@ static struct ceph_osd_request *rbd_osd_req_create(
        osd_req->r_priv = obj_request;
 
        osd_req->r_base_oloc.pool = ceph_file_layout_pg_pool(rbd_dev->layout);
-       ceph_oid_set_name(&osd_req->r_base_oid, obj_request->object_name);
+       if (ceph_oid_aprintf(&osd_req->r_base_oid, GFP_NOIO, "%s",
+                            obj_request->object_name))
+               goto fail;
+
+       if (ceph_osdc_alloc_messages(osd_req, GFP_NOIO))
+               goto fail;
 
        return osd_req;
+
+fail:
+       ceph_osdc_put_request(osd_req);
+       return NULL;
 }
 
 /*
@@ -2004,16 +1985,25 @@ rbd_osd_req_create_copyup(struct rbd_obj_request *obj_request)
        osd_req = ceph_osdc_alloc_request(osdc, snapc, num_osd_ops,
                                                false, GFP_NOIO);
        if (!osd_req)
-               return NULL;    /* ENOMEM */
+               goto fail;
 
        osd_req->r_flags = CEPH_OSD_FLAG_WRITE | CEPH_OSD_FLAG_ONDISK;
        osd_req->r_callback = rbd_osd_req_callback;
        osd_req->r_priv = obj_request;
 
        osd_req->r_base_oloc.pool = ceph_file_layout_pg_pool(rbd_dev->layout);
-       ceph_oid_set_name(&osd_req->r_base_oid, obj_request->object_name);
+       if (ceph_oid_aprintf(&osd_req->r_base_oid, GFP_NOIO, "%s",
+                            obj_request->object_name))
+               goto fail;
+
+       if (ceph_osdc_alloc_messages(osd_req, GFP_NOIO))
+               goto fail;
 
        return osd_req;
+
+fail:
+       ceph_osdc_put_request(osd_req);
+       return NULL;
 }
 
 
@@ -2974,17 +2964,20 @@ static int rbd_img_request_submit(struct rbd_img_request *img_request)
 {
        struct rbd_obj_request *obj_request;
        struct rbd_obj_request *next_obj_request;
+       int ret = 0;
 
        dout("%s: img %p\n", __func__, img_request);
-       for_each_obj_request_safe(img_request, obj_request, next_obj_request) {
-               int ret;
 
+       rbd_img_request_get(img_request);
+       for_each_obj_request_safe(img_request, obj_request, next_obj_request) {
                ret = rbd_img_obj_request_submit(obj_request);
                if (ret)
-                       return ret;
+                       goto out_put_ireq;
        }
 
-       return 0;
+out_put_ireq:
+       rbd_img_request_put(img_request);
+       return ret;
 }
 
 static void rbd_img_parent_read_callback(struct rbd_img_request *img_request)
@@ -3091,48 +3084,18 @@ out_err:
        obj_request_done_set(obj_request);
 }
 
-static int rbd_obj_notify_ack_sync(struct rbd_device *rbd_dev, u64 notify_id)
-{
-       struct rbd_obj_request *obj_request;
-       struct ceph_osd_client *osdc = &rbd_dev->rbd_client->client->osdc;
-       int ret;
-
-       obj_request = rbd_obj_request_create(rbd_dev->header_name, 0, 0,
-                                                       OBJ_REQUEST_NODATA);
-       if (!obj_request)
-               return -ENOMEM;
-
-       ret = -ENOMEM;
-       obj_request->osd_req = rbd_osd_req_create(rbd_dev, OBJ_OP_READ, 1,
-                                                 obj_request);
-       if (!obj_request->osd_req)
-               goto out;
-
-       osd_req_op_watch_init(obj_request->osd_req, 0, CEPH_OSD_OP_NOTIFY_ACK,
-                                       notify_id, 0, 0);
-       rbd_osd_req_format_read(obj_request);
+static int rbd_dev_header_watch_sync(struct rbd_device *rbd_dev);
+static void __rbd_dev_header_unwatch_sync(struct rbd_device *rbd_dev);
 
-       ret = rbd_obj_request_submit(osdc, obj_request);
-       if (ret)
-               goto out;
-       ret = rbd_obj_request_wait(obj_request);
-out:
-       rbd_obj_request_put(obj_request);
-
-       return ret;
-}
-
-static void rbd_watch_cb(u64 ver, u64 notify_id, u8 opcode, void *data)
+static void rbd_watch_cb(void *arg, u64 notify_id, u64 cookie,
+                        u64 notifier_id, void *data, size_t data_len)
 {
-       struct rbd_device *rbd_dev = (struct rbd_device *)data;
+       struct rbd_device *rbd_dev = arg;
+       struct ceph_osd_client *osdc = &rbd_dev->rbd_client->client->osdc;
        int ret;
 
-       if (!rbd_dev)
-               return;
-
-       dout("%s: \"%s\" notify_id %llu opcode %u\n", __func__,
-               rbd_dev->header_name, (unsigned long long)notify_id,
-               (unsigned int)opcode);
+       dout("%s rbd_dev %p cookie %llu notify_id %llu\n", __func__, rbd_dev,
+            cookie, notify_id);
 
        /*
         * Until adequate refresh error handling is in place, there is
@@ -3144,63 +3107,31 @@ static void rbd_watch_cb(u64 ver, u64 notify_id, u8 opcode, void *data)
        if (ret)
                rbd_warn(rbd_dev, "refresh failed: %d", ret);
 
-       ret = rbd_obj_notify_ack_sync(rbd_dev, notify_id);
+       ret = ceph_osdc_notify_ack(osdc, &rbd_dev->header_oid,
+                                  &rbd_dev->header_oloc, notify_id, cookie,
+                                  NULL, 0);
        if (ret)
                rbd_warn(rbd_dev, "notify_ack ret %d", ret);
 }
 
-/*
- * Send a (un)watch request and wait for the ack.  Return a request
- * with a ref held on success or error.
- */
-static struct rbd_obj_request *rbd_obj_watch_request_helper(
-                                               struct rbd_device *rbd_dev,
-                                               bool watch)
+static void rbd_watch_errcb(void *arg, u64 cookie, int err)
 {
-       struct ceph_osd_client *osdc = &rbd_dev->rbd_client->client->osdc;
-       struct ceph_options *opts = osdc->client->options;
-       struct rbd_obj_request *obj_request;
+       struct rbd_device *rbd_dev = arg;
        int ret;
 
-       obj_request = rbd_obj_request_create(rbd_dev->header_name, 0, 0,
-                                            OBJ_REQUEST_NODATA);
-       if (!obj_request)
-               return ERR_PTR(-ENOMEM);
-
-       obj_request->osd_req = rbd_osd_req_create(rbd_dev, OBJ_OP_WRITE, 1,
-                                                 obj_request);
-       if (!obj_request->osd_req) {
-               ret = -ENOMEM;
-               goto out;
-       }
-
-       osd_req_op_watch_init(obj_request->osd_req, 0, CEPH_OSD_OP_WATCH,
-                             rbd_dev->watch_event->cookie, 0, watch);
-       rbd_osd_req_format_write(obj_request);
+       rbd_warn(rbd_dev, "encountered watch error: %d", err);
 
-       if (watch)
-               ceph_osdc_set_request_linger(osdc, obj_request->osd_req);
-
-       ret = rbd_obj_request_submit(osdc, obj_request);
-       if (ret)
-               goto out;
-
-       ret = rbd_obj_request_wait_timeout(obj_request, opts->mount_timeout);
-       if (ret)
-               goto out;
+       __rbd_dev_header_unwatch_sync(rbd_dev);
 
-       ret = obj_request->result;
+       ret = rbd_dev_header_watch_sync(rbd_dev);
        if (ret) {
-               if (watch)
-                       rbd_obj_request_end(obj_request);
-               goto out;
+               rbd_warn(rbd_dev, "failed to reregister watch: %d", ret);
+               return;
        }
 
-       return obj_request;
-
-out:
-       rbd_obj_request_put(obj_request);
-       return ERR_PTR(ret);
+       ret = rbd_dev_refresh(rbd_dev);
+       if (ret)
+               rbd_warn(rbd_dev, "reregisteration refresh failed: %d", ret);
 }
 
 /*
@@ -3209,35 +3140,33 @@ out:
 static int rbd_dev_header_watch_sync(struct rbd_device *rbd_dev)
 {
        struct ceph_osd_client *osdc = &rbd_dev->rbd_client->client->osdc;
-       struct rbd_obj_request *obj_request;
-       int ret;
+       struct ceph_osd_linger_request *handle;
 
-       rbd_assert(!rbd_dev->watch_event);
-       rbd_assert(!rbd_dev->watch_request);
+       rbd_assert(!rbd_dev->watch_handle);
 
-       ret = ceph_osdc_create_event(osdc, rbd_watch_cb, rbd_dev,
-                                    &rbd_dev->watch_event);
-       if (ret < 0)
-               return ret;
+       handle = ceph_osdc_watch(osdc, &rbd_dev->header_oid,
+                                &rbd_dev->header_oloc, rbd_watch_cb,
+                                rbd_watch_errcb, rbd_dev);
+       if (IS_ERR(handle))
+               return PTR_ERR(handle);
 
-       obj_request = rbd_obj_watch_request_helper(rbd_dev, true);
-       if (IS_ERR(obj_request)) {
-               ceph_osdc_cancel_event(rbd_dev->watch_event);
-               rbd_dev->watch_event = NULL;
-               return PTR_ERR(obj_request);
-       }
+       rbd_dev->watch_handle = handle;
+       return 0;
+}
 
-       /*
-        * A watch request is set to linger, so the underlying osd
-        * request won't go away until we unregister it.  We retain
-        * a pointer to the object request during that time (in
-        * rbd_dev->watch_request), so we'll keep a reference to it.
-        * We'll drop that reference after we've unregistered it in
-        * rbd_dev_header_unwatch_sync().
-        */
-       rbd_dev->watch_request = obj_request;
+static void __rbd_dev_header_unwatch_sync(struct rbd_device *rbd_dev)
+{
+       struct ceph_osd_client *osdc = &rbd_dev->rbd_client->client->osdc;
+       int ret;
 
-       return 0;
+       if (!rbd_dev->watch_handle)
+               return;
+
+       ret = ceph_osdc_unwatch(osdc, rbd_dev->watch_handle);
+       if (ret)
+               rbd_warn(rbd_dev, "failed to unwatch: %d", ret);
+
+       rbd_dev->watch_handle = NULL;
 }
 
 /*
@@ -3245,24 +3174,10 @@ static int rbd_dev_header_watch_sync(struct rbd_device *rbd_dev)
  */
 static void rbd_dev_header_unwatch_sync(struct rbd_device *rbd_dev)
 {
-       struct rbd_obj_request *obj_request;
-
-       rbd_assert(rbd_dev->watch_event);
-       rbd_assert(rbd_dev->watch_request);
+       __rbd_dev_header_unwatch_sync(rbd_dev);
 
-       rbd_obj_request_end(rbd_dev->watch_request);
-       rbd_obj_request_put(rbd_dev->watch_request);
-       rbd_dev->watch_request = NULL;
-
-       obj_request = rbd_obj_watch_request_helper(rbd_dev, false);
-       if (!IS_ERR(obj_request))
-               rbd_obj_request_put(obj_request);
-       else
-               rbd_warn(rbd_dev, "unable to tear down watch request (%ld)",
-                        PTR_ERR(obj_request));
-
-       ceph_osdc_cancel_event(rbd_dev->watch_event);
-       rbd_dev->watch_event = NULL;
+       dout("%s flushing notifies\n", __func__);
+       ceph_osdc_flush_notifies(&rbd_dev->rbd_client->client->osdc);
 }
 
 /*
@@ -3592,7 +3507,7 @@ static int rbd_dev_v1_header_info(struct rbd_device *rbd_dev)
                if (!ondisk)
                        return -ENOMEM;
 
-               ret = rbd_obj_read_sync(rbd_dev, rbd_dev->header_name,
+               ret = rbd_obj_read_sync(rbd_dev, rbd_dev->header_oid.name,
                                       0, size, ondisk);
                if (ret < 0)
                        goto out;
@@ -3642,21 +3557,14 @@ static void rbd_exists_validate(struct rbd_device *rbd_dev)
 static void rbd_dev_update_size(struct rbd_device *rbd_dev)
 {
        sector_t size;
-       bool removing;
 
        /*
-        * Don't hold the lock while doing disk operations,
-        * or lock ordering will conflict with the bdev mutex via:
-        * rbd_add() -> blkdev_get() -> rbd_open()
-        */
-       spin_lock_irq(&rbd_dev->lock);
-       removing = test_bit(RBD_DEV_FLAG_REMOVING, &rbd_dev->flags);
-       spin_unlock_irq(&rbd_dev->lock);
-       /*
-        * If the device is being removed, rbd_dev->disk has
-        * been destroyed, so don't try to update its size
+        * If EXISTS is not set, rbd_dev->disk may be NULL, so don't
+        * try to update its size.  If REMOVING is set, updating size
+        * is just useless work since the device can't be opened.
         */
-       if (!removing) {
+       if (test_bit(RBD_DEV_FLAG_EXISTS, &rbd_dev->flags) &&
+           !test_bit(RBD_DEV_FLAG_REMOVING, &rbd_dev->flags)) {
                size = (sector_t)rbd_dev->mapping.size / SECTOR_SIZE;
                dout("setting size to %llu sectors", (unsigned long long)size);
                set_capacity(rbd_dev->disk, size);
@@ -4041,6 +3949,8 @@ static void rbd_dev_release(struct device *dev)
        struct rbd_device *rbd_dev = dev_to_rbd_dev(dev);
        bool need_put = !!rbd_dev->opts;
 
+       ceph_oid_destroy(&rbd_dev->header_oid);
+
        rbd_put_client(rbd_dev->rbd_client);
        rbd_spec_put(rbd_dev->spec);
        kfree(rbd_dev->opts);
@@ -4071,6 +3981,9 @@ static struct rbd_device *rbd_dev_create(struct rbd_client *rbdc,
        INIT_LIST_HEAD(&rbd_dev->node);
        init_rwsem(&rbd_dev->header_rwsem);
 
+       ceph_oid_init(&rbd_dev->header_oid);
+       ceph_oloc_init(&rbd_dev->header_oloc);
+
        rbd_dev->dev.bus = &rbd_bus_type;
        rbd_dev->dev.type = &rbd_device_type;
        rbd_dev->dev.parent = &rbd_root_dev;
@@ -4119,7 +4032,7 @@ static int _rbd_dev_v2_snap_size(struct rbd_device *rbd_dev, u64 snap_id,
                __le64 size;
        } __attribute__ ((packed)) size_buf = { 0 };
 
-       ret = rbd_obj_method_sync(rbd_dev, rbd_dev->header_name,
+       ret = rbd_obj_method_sync(rbd_dev, rbd_dev->header_oid.name,
                                "rbd", "get_size",
                                &snapid, sizeof (snapid),
                                &size_buf, sizeof (size_buf));
@@ -4159,7 +4072,7 @@ static int rbd_dev_v2_object_prefix(struct rbd_device *rbd_dev)
        if (!reply_buf)
                return -ENOMEM;
 
-       ret = rbd_obj_method_sync(rbd_dev, rbd_dev->header_name,
+       ret = rbd_obj_method_sync(rbd_dev, rbd_dev->header_oid.name,
                                "rbd", "get_object_prefix", NULL, 0,
                                reply_buf, RBD_OBJ_PREFIX_LEN_MAX);
        dout("%s: rbd_obj_method_sync returned %d\n", __func__, ret);
@@ -4191,10 +4104,10 @@ static int _rbd_dev_v2_snap_features(struct rbd_device *rbd_dev, u64 snap_id,
                __le64 features;
                __le64 incompat;
        } __attribute__ ((packed)) features_buf = { 0 };
-       u64 incompat;
+       u64 unsup;
        int ret;
 
-       ret = rbd_obj_method_sync(rbd_dev, rbd_dev->header_name,
+       ret = rbd_obj_method_sync(rbd_dev, rbd_dev->header_oid.name,
                                "rbd", "get_features",
                                &snapid, sizeof (snapid),
                                &features_buf, sizeof (features_buf));
@@ -4204,9 +4117,12 @@ static int _rbd_dev_v2_snap_features(struct rbd_device *rbd_dev, u64 snap_id,
        if (ret < sizeof (features_buf))
                return -ERANGE;
 
-       incompat = le64_to_cpu(features_buf.incompat);
-       if (incompat & ~RBD_FEATURES_SUPPORTED)
+       unsup = le64_to_cpu(features_buf.incompat) & ~RBD_FEATURES_SUPPORTED;
+       if (unsup) {
+               rbd_warn(rbd_dev, "image uses unsupported features: 0x%llx",
+                        unsup);
                return -ENXIO;
+       }
 
        *snap_features = le64_to_cpu(features_buf.features);
 
@@ -4253,7 +4169,7 @@ static int rbd_dev_v2_parent_info(struct rbd_device *rbd_dev)
        }
 
        snapid = cpu_to_le64(rbd_dev->spec->snap_id);
-       ret = rbd_obj_method_sync(rbd_dev, rbd_dev->header_name,
+       ret = rbd_obj_method_sync(rbd_dev, rbd_dev->header_oid.name,
                                "rbd", "get_parent",
                                &snapid, sizeof (snapid),
                                reply_buf, size);
@@ -4356,7 +4272,7 @@ static int rbd_dev_v2_striping_info(struct rbd_device *rbd_dev)
        u64 stripe_count;
        int ret;
 
-       ret = rbd_obj_method_sync(rbd_dev, rbd_dev->header_name,
+       ret = rbd_obj_method_sync(rbd_dev, rbd_dev->header_oid.name,
                                "rbd", "get_stripe_unit_count", NULL, 0,
                                (char *)&striping_info_buf, size);
        dout("%s: rbd_obj_method_sync returned %d\n", __func__, ret);
@@ -4604,7 +4520,7 @@ static int rbd_dev_v2_snap_context(struct rbd_device *rbd_dev)
        if (!reply_buf)
                return -ENOMEM;
 
-       ret = rbd_obj_method_sync(rbd_dev, rbd_dev->header_name,
+       ret = rbd_obj_method_sync(rbd_dev, rbd_dev->header_oid.name,
                                "rbd", "get_snapcontext", NULL, 0,
                                reply_buf, size);
        dout("%s: rbd_obj_method_sync returned %d\n", __func__, ret);
@@ -4669,7 +4585,7 @@ static const char *rbd_dev_v2_snap_name(struct rbd_device *rbd_dev,
                return ERR_PTR(-ENOMEM);
 
        snapid = cpu_to_le64(snap_id);
-       ret = rbd_obj_method_sync(rbd_dev, rbd_dev->header_name,
+       ret = rbd_obj_method_sync(rbd_dev, rbd_dev->header_oid.name,
                                "rbd", "get_snapshot_name",
                                &snapid, sizeof (snapid),
                                reply_buf, size);
@@ -4980,13 +4896,13 @@ static int rbd_add_get_pool_id(struct rbd_client *rbdc, const char *pool_name)
 again:
        ret = ceph_pg_poolid_by_name(rbdc->client->osdc.osdmap, pool_name);
        if (ret == -ENOENT && tries++ < 1) {
-               ret = ceph_monc_do_get_version(&rbdc->client->monc, "osdmap",
-                                              &newest_epoch);
+               ret = ceph_monc_get_version(&rbdc->client->monc, "osdmap",
+                                           &newest_epoch);
                if (ret < 0)
                        return ret;
 
                if (rbdc->client->osdc.osdmap->epoch < newest_epoch) {
-                       ceph_monc_request_next_osdmap(&rbdc->client->monc);
+                       ceph_osdc_maybe_request_map(&rbdc->client->osdc);
                        (void) ceph_monc_wait_osdmap(&rbdc->client->monc,
                                                     newest_epoch,
                                                     opts->mount_timeout);
@@ -5187,6 +5103,10 @@ out_err:
        return ret;
 }
 
+/*
+ * rbd_dev->header_rwsem must be locked for write and will be unlocked
+ * upon return.
+ */
 static int rbd_dev_device_setup(struct rbd_device *rbd_dev)
 {
        int ret;
@@ -5195,7 +5115,7 @@ static int rbd_dev_device_setup(struct rbd_device *rbd_dev)
 
        ret = rbd_dev_id_get(rbd_dev);
        if (ret)
-               return ret;
+               goto err_out_unlock;
 
        BUILD_BUG_ON(DEV_NAME_LEN
                        < sizeof (RBD_DRV_NAME) + MAX_INT_FORMAT_WIDTH);
@@ -5236,8 +5156,9 @@ static int rbd_dev_device_setup(struct rbd_device *rbd_dev)
        /* Everything's ready.  Announce the disk to the world. */
 
        set_bit(RBD_DEV_FLAG_EXISTS, &rbd_dev->flags);
-       add_disk(rbd_dev->disk);
+       up_write(&rbd_dev->header_rwsem);
 
+       add_disk(rbd_dev->disk);
        pr_info("%s: added with size 0x%llx\n", rbd_dev->disk->disk_name,
                (unsigned long long) rbd_dev->mapping.size);
 
@@ -5252,41 +5173,34 @@ err_out_blkdev:
                unregister_blkdev(rbd_dev->major, rbd_dev->name);
 err_out_id:
        rbd_dev_id_put(rbd_dev);
+err_out_unlock:
+       up_write(&rbd_dev->header_rwsem);
        return ret;
 }
 
 static int rbd_dev_header_name(struct rbd_device *rbd_dev)
 {
        struct rbd_spec *spec = rbd_dev->spec;
-       size_t size;
+       int ret;
 
        /* Record the header object name for this rbd image. */
 
        rbd_assert(rbd_image_format_valid(rbd_dev->image_format));
 
+       rbd_dev->header_oloc.pool = ceph_file_layout_pg_pool(rbd_dev->layout);
        if (rbd_dev->image_format == 1)
-               size = strlen(spec->image_name) + sizeof (RBD_SUFFIX);
+               ret = ceph_oid_aprintf(&rbd_dev->header_oid, GFP_KERNEL, "%s%s",
+                                      spec->image_name, RBD_SUFFIX);
        else
-               size = sizeof (RBD_HEADER_PREFIX) + strlen(spec->image_id);
-
-       rbd_dev->header_name = kmalloc(size, GFP_KERNEL);
-       if (!rbd_dev->header_name)
-               return -ENOMEM;
+               ret = ceph_oid_aprintf(&rbd_dev->header_oid, GFP_KERNEL, "%s%s",
+                                      RBD_HEADER_PREFIX, spec->image_id);
 
-       if (rbd_dev->image_format == 1)
-               sprintf(rbd_dev->header_name, "%s%s",
-                       spec->image_name, RBD_SUFFIX);
-       else
-               sprintf(rbd_dev->header_name, "%s%s",
-                       RBD_HEADER_PREFIX, spec->image_id);
-       return 0;
+       return ret;
 }
 
 static void rbd_dev_image_release(struct rbd_device *rbd_dev)
 {
        rbd_dev_unprobe(rbd_dev);
-       kfree(rbd_dev->header_name);
-       rbd_dev->header_name = NULL;
        rbd_dev->image_format = 0;
        kfree(rbd_dev->spec->image_id);
        rbd_dev->spec->image_id = NULL;
@@ -5325,7 +5239,7 @@ static int rbd_dev_image_probe(struct rbd_device *rbd_dev, int depth)
                                pr_info("image %s/%s does not exist\n",
                                        rbd_dev->spec->pool_name,
                                        rbd_dev->spec->image_name);
-                       goto out_header_name;
+                       goto err_out_format;
                }
        }
 
@@ -5371,7 +5285,7 @@ static int rbd_dev_image_probe(struct rbd_device *rbd_dev, int depth)
                goto err_out_probe;
 
        dout("discovered format %u image, header name is %s\n",
-               rbd_dev->image_format, rbd_dev->header_name);
+               rbd_dev->image_format, rbd_dev->header_oid.name);
        return 0;
 
 err_out_probe:
@@ -5379,9 +5293,6 @@ err_out_probe:
 err_out_watch:
        if (!depth)
                rbd_dev_header_unwatch_sync(rbd_dev);
-out_header_name:
-       kfree(rbd_dev->header_name);
-       rbd_dev->header_name = NULL;
 err_out_format:
        rbd_dev->image_format = 0;
        kfree(rbd_dev->spec->image_id);
@@ -5442,6 +5353,7 @@ 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 */
 
+       down_write(&rbd_dev->header_rwsem);
        rc = rbd_dev_image_probe(rbd_dev, 0);
        if (rc < 0)
                goto err_out_rbd_dev;
@@ -5471,6 +5383,7 @@ out:
        return rc;
 
 err_out_rbd_dev:
+       up_write(&rbd_dev->header_rwsem);
        rbd_dev_destroy(rbd_dev);
 err_out_client:
        rbd_put_client(rbdc);
@@ -5577,12 +5490,6 @@ static ssize_t do_rbd_remove(struct bus_type *bus,
                return ret;
 
        rbd_dev_header_unwatch_sync(rbd_dev);
-       /*
-        * flush remaining watch callbacks - these must be complete
-        * before the osd_client is shutdown
-        */
-       dout("%s: flushing notifies", __func__);
-       ceph_osdc_flush_notifies(&rbd_dev->rbd_client->client->osdc);
 
        /*
         * Don't free anything from rbd_dev->disk until after all