u64 obj_version;
 };
 
+/*
+ * An rbd image specification.
+ *
+ * The tuple (pool_id, image_id, snap_id) is sufficient to uniquely
+ * identify an image.
+ */
+struct rbd_spec {
+       u64             pool_id;
+       char            *pool_name;
+
+       char            *image_id;
+       size_t          image_id_len;
+       char            *image_name;
+       size_t          image_name_len;
+
+       u64             snap_id;
+       char            *snap_name;
+
+       struct kref     kref;
+};
+
 struct rbd_options {
        bool    read_only;
 };
 
        struct rbd_image_header header;
        bool                    exists;
-       char                    *image_id;
-       size_t                  image_id_len;
-       char                    *image_name;
-       size_t                  image_name_len;
-       char                    *header_name;
-       char                    *pool_name;
-       u64                     pool_id;
+       struct rbd_spec         *spec;
 
-       char                    *snap_name;
-       u64                     snap_id;
+       char                    *header_name;
 
        struct ceph_osd_event   *watch_event;
        struct ceph_osd_request *watch_request;
 
        list_for_each_entry(snap, &rbd_dev->snaps, node) {
                if (!strcmp(snap_name, snap->name)) {
-                       rbd_dev->snap_id = snap->id;
+                       rbd_dev->spec->snap_id = snap->id;
                        rbd_dev->mapping.size = snap->size;
                        rbd_dev->mapping.features = snap->features;
 
 {
        int ret;
 
-       if (!memcmp(rbd_dev->snap_name, RBD_SNAP_HEAD_NAME,
+       if (!memcmp(rbd_dev->spec->snap_name, RBD_SNAP_HEAD_NAME,
                    sizeof (RBD_SNAP_HEAD_NAME))) {
-               rbd_dev->snap_id = CEPH_NOSNAP;
+               rbd_dev->spec->snap_id = CEPH_NOSNAP;
                rbd_dev->mapping.size = rbd_dev->header.image_size;
                rbd_dev->mapping.features = rbd_dev->header.features;
                ret = 0;
        } else {
-               ret = snap_by_name(rbd_dev, rbd_dev->snap_name);
+               ret = snap_by_name(rbd_dev, rbd_dev->spec->snap_name);
                if (ret < 0)
                        goto done;
                rbd_dev->mapping.read_only = true;
        layout->fl_stripe_unit = cpu_to_le32(1 << RBD_MAX_OBJ_ORDER);
        layout->fl_stripe_count = cpu_to_le32(1);
        layout->fl_object_size = cpu_to_le32(1 << RBD_MAX_OBJ_ORDER);
-       layout->fl_pg_pool = cpu_to_le32((int) rbd_dev->pool_id);
+       layout->fl_pg_pool = cpu_to_le32((int) rbd_dev->spec->pool_id);
        ret = ceph_calc_raw_layout(osdc, layout, snapid, ofs, &len, &bno,
                                   req, ops);
        rbd_assert(ret == 0);
                opcode = CEPH_OSD_OP_READ;
                flags = CEPH_OSD_FLAG_READ;
                snapc = NULL;
-               snapid = rbd_dev->snap_id;
+               snapid = rbd_dev->spec->snap_id;
                payload_len = 0;
        }
 
                down_read(&rbd_dev->header_rwsem);
 
                if (!rbd_dev->exists) {
-                       rbd_assert(rbd_dev->snap_id != CEPH_NOSNAP);
+                       rbd_assert(rbd_dev->spec->snap_id != CEPH_NOSNAP);
                        up_read(&rbd_dev->header_rwsem);
                        dout("request for non-existent snapshot");
                        spin_lock_irq(q->queue_lock);
                        ret = -ENXIO;
                        pr_warning("short header read for image %s"
                                        " (want %zd got %d)\n",
-                               rbd_dev->image_name, size, ret);
+                               rbd_dev->spec->image_name, size, ret);
                        goto out_err;
                }
                if (!rbd_dev_ondisk_valid(ondisk)) {
                        ret = -ENXIO;
                        pr_warning("invalid header for image %s\n",
-                               rbd_dev->image_name);
+                               rbd_dev->spec->image_name);
                        goto out_err;
                }
 
 {
        sector_t size;
 
-       if (rbd_dev->snap_id != CEPH_NOSNAP)
+       if (rbd_dev->spec->snap_id != CEPH_NOSNAP)
                return;
 
        size = (sector_t) rbd_dev->header.image_size / SECTOR_SIZE;
 {
        struct rbd_device *rbd_dev = dev_to_rbd_dev(dev);
 
-       return sprintf(buf, "%s\n", rbd_dev->pool_name);
+       return sprintf(buf, "%s\n", rbd_dev->spec->pool_name);
 }
 
 static ssize_t rbd_pool_id_show(struct device *dev,
 {
        struct rbd_device *rbd_dev = dev_to_rbd_dev(dev);
 
-       return sprintf(buf, "%llu\n", (unsigned long long) rbd_dev->pool_id);
+       return sprintf(buf, "%llu\n",
+               (unsigned long long) rbd_dev->spec->pool_id);
 }
 
 static ssize_t rbd_name_show(struct device *dev,
 {
        struct rbd_device *rbd_dev = dev_to_rbd_dev(dev);
 
-       return sprintf(buf, "%s\n", rbd_dev->image_name);
+       return sprintf(buf, "%s\n", rbd_dev->spec->image_name);
 }
 
 static ssize_t rbd_image_id_show(struct device *dev,
 {
        struct rbd_device *rbd_dev = dev_to_rbd_dev(dev);
 
-       return sprintf(buf, "%s\n", rbd_dev->image_id);
+       return sprintf(buf, "%s\n", rbd_dev->spec->image_id);
 }
 
 /*
 {
        struct rbd_device *rbd_dev = dev_to_rbd_dev(dev);
 
-       return sprintf(buf, "%s\n", rbd_dev->snap_name);
+       return sprintf(buf, "%s\n", rbd_dev->spec->snap_name);
 }
 
 static ssize_t rbd_image_refresh(struct device *dev,
 
                        /* Existing snapshot not in the new snap context */
 
-                       if (rbd_dev->snap_id == snap->id)
+                       if (rbd_dev->spec->snap_id == snap->id)
                                rbd_dev->exists = false;
                        rbd_remove_snap_dev(snap);
                        dout("%ssnap id %llu has been removed\n",
-                               rbd_dev->snap_id == snap->id ?  "mapped " : "",
+                               rbd_dev->spec->snap_id == snap->id ?
+                                                       "mapped " : "",
                                (unsigned long long) snap->id);
 
                        /* Done with this list entry; advance */
        if (!*options)
                goto out_err;   /* Missing options */
 
-       rbd_dev->pool_name = dup_token(&buf, NULL);
-       if (!rbd_dev->pool_name)
+       rbd_dev->spec->pool_name = dup_token(&buf, NULL);
+       if (!rbd_dev->spec->pool_name)
                goto out_mem;
-       if (!*rbd_dev->pool_name)
+       if (!*rbd_dev->spec->pool_name)
                goto out_err;   /* Missing pool name */
 
-       rbd_dev->image_name = dup_token(&buf, &rbd_dev->image_name_len);
-       if (!rbd_dev->image_name)
+       rbd_dev->spec->image_name =
+               dup_token(&buf, &rbd_dev->spec->image_name_len);
+       if (!rbd_dev->spec->image_name)
                goto out_mem;
-       if (!*rbd_dev->image_name)
+       if (!*rbd_dev->spec->image_name)
                goto out_err;   /* Missing image name */
 
        /*
                ret = -ENAMETOOLONG;
                goto out_err;
        }
-       rbd_dev->snap_name = kmalloc(len + 1, GFP_KERNEL);
-       if (!rbd_dev->snap_name)
+       rbd_dev->spec->snap_name = kmalloc(len + 1, GFP_KERNEL);
+       if (!rbd_dev->spec->snap_name)
                goto out_mem;
-       memcpy(rbd_dev->snap_name, buf, len);
-       *(rbd_dev->snap_name + len) = '\0';
+       memcpy(rbd_dev->spec->snap_name, buf, len);
+       *(rbd_dev->spec->snap_name + len) = '\0';
 
        /* Initialize all rbd options to the defaults */
 
 out_mem:
        ret = -ENOMEM;
 out_err:
-       kfree(rbd_dev->image_name);
-       rbd_dev->image_name = NULL;
-       rbd_dev->image_name_len = 0;
-       kfree(rbd_dev->pool_name);
-       rbd_dev->pool_name = NULL;
+       kfree(rbd_dev->spec->image_name);
+       rbd_dev->spec->image_name = NULL;
+       rbd_dev->spec->image_name_len = 0;
+       kfree(rbd_dev->spec->pool_name);
+       rbd_dev->spec->pool_name = NULL;
        kfree(options);
 
        return ret;
         * First, see if the format 2 image id file exists, and if
         * so, get the image's persistent id from it.
         */
-       size = sizeof (RBD_ID_PREFIX) + rbd_dev->image_name_len;
+       size = sizeof (RBD_ID_PREFIX) + rbd_dev->spec->image_name_len;
        object_name = kmalloc(size, GFP_NOIO);
        if (!object_name)
                return -ENOMEM;
-       sprintf(object_name, "%s%s", RBD_ID_PREFIX, rbd_dev->image_name);
+       sprintf(object_name, "%s%s", RBD_ID_PREFIX, rbd_dev->spec->image_name);
        dout("rbd id object name is %s\n", object_name);
 
        /* Response will be an encoded string, which includes a length */
        ret = 0;    /* rbd_req_sync_exec() can return positive */
 
        p = response;
-       rbd_dev->image_id = ceph_extract_encoded_string(&p,
+       rbd_dev->spec->image_id = ceph_extract_encoded_string(&p,
                                                p + RBD_IMAGE_ID_LEN_MAX,
-                                               &rbd_dev->image_id_len,
+                                               &rbd_dev->spec->image_id_len,
                                                GFP_NOIO);
-       if (IS_ERR(rbd_dev->image_id)) {
-               ret = PTR_ERR(rbd_dev->image_id);
-               rbd_dev->image_id = NULL;
+       if (IS_ERR(rbd_dev->spec->image_id)) {
+               ret = PTR_ERR(rbd_dev->spec->image_id);
+               rbd_dev->spec->image_id = NULL;
        } else {
-               dout("image_id is %s\n", rbd_dev->image_id);
+               dout("image_id is %s\n", rbd_dev->spec->image_id);
        }
 out:
        kfree(response);
 
        /* Version 1 images have no id; empty string is used */
 
-       rbd_dev->image_id = kstrdup("", GFP_KERNEL);
-       if (!rbd_dev->image_id)
+       rbd_dev->spec->image_id = kstrdup("", GFP_KERNEL);
+       if (!rbd_dev->spec->image_id)
                return -ENOMEM;
-       rbd_dev->image_id_len = 0;
+       rbd_dev->spec->image_id_len = 0;
 
        /* Record the header object name for this rbd image. */
 
-       size = rbd_dev->image_name_len + sizeof (RBD_SUFFIX);
+       size = rbd_dev->spec->image_name_len + sizeof (RBD_SUFFIX);
        rbd_dev->header_name = kmalloc(size, GFP_KERNEL);
        if (!rbd_dev->header_name) {
                ret = -ENOMEM;
                goto out_err;
        }
-       sprintf(rbd_dev->header_name, "%s%s", rbd_dev->image_name, RBD_SUFFIX);
+       sprintf(rbd_dev->header_name, "%s%s",
+               rbd_dev->spec->image_name, RBD_SUFFIX);
 
        /* Populate rbd image metadata */
 
 out_err:
        kfree(rbd_dev->header_name);
        rbd_dev->header_name = NULL;
-       kfree(rbd_dev->image_id);
-       rbd_dev->image_id = NULL;
+       kfree(rbd_dev->spec->image_id);
+       rbd_dev->spec->image_id = NULL;
 
        return ret;
 }
         * Image id was filled in by the caller.  Record the header
         * object name for this rbd image.
         */
-       size = sizeof (RBD_HEADER_PREFIX) + rbd_dev->image_id_len;
+       size = sizeof (RBD_HEADER_PREFIX) + rbd_dev->spec->image_id_len;
        rbd_dev->header_name = kmalloc(size, GFP_KERNEL);
        if (!rbd_dev->header_name)
                return -ENOMEM;
        sprintf(rbd_dev->header_name, "%s%s",
-                       RBD_HEADER_PREFIX, rbd_dev->image_id);
+                       RBD_HEADER_PREFIX, rbd_dev->spec->image_id);
 
        /* Get the size and object order for the image */
 
        rbd_dev = kzalloc(sizeof(*rbd_dev), GFP_KERNEL);
        if (!rbd_dev)
                return -ENOMEM;
+       rbd_dev->spec = kzalloc(sizeof (*rbd_dev->spec), GFP_KERNEL);
+       if (!rbd_dev->spec)
+               goto err_out_mem;
 
        /* static rbd_device initialization */
        spin_lock_init(&rbd_dev->lock);
 
        /* pick the pool */
        osdc = &rbd_dev->rbd_client->client->osdc;
-       rc = ceph_pg_poolid_by_name(osdc->osdmap, rbd_dev->pool_name);
+       rc = ceph_pg_poolid_by_name(osdc->osdmap, rbd_dev->spec->pool_name);
        if (rc < 0)
                goto err_out_client;
-       rbd_dev->pool_id = (u64) rc;
+       rbd_dev->spec->pool_id = (u64) rc;
 
        rc = rbd_dev_probe(rbd_dev);
        if (rc < 0)
 err_out_client:
        kfree(rbd_dev->header_name);
        rbd_put_client(rbd_dev);
-       kfree(rbd_dev->image_id);
+       kfree(rbd_dev->spec->image_id);
 err_out_args:
        if (ceph_opts)
                ceph_destroy_options(ceph_opts);
-       kfree(rbd_dev->snap_name);
-       kfree(rbd_dev->image_name);
-       kfree(rbd_dev->pool_name);
+       kfree(rbd_dev->spec->snap_name);
+       kfree(rbd_dev->spec->image_name);
+       kfree(rbd_dev->spec->pool_name);
        kfree(rbd_opts);
 err_out_mem:
+       kfree(rbd_dev->spec);
        kfree(rbd_dev);
 
        dout("Error adding device %s\n", buf);
        rbd_header_free(&rbd_dev->header);
 
        /* done with the id, and with the rbd_dev */
-       kfree(rbd_dev->snap_name);
-       kfree(rbd_dev->image_id);
+       kfree(rbd_dev->spec->snap_name);
+       kfree(rbd_dev->spec->image_id);
        kfree(rbd_dev->header_name);
-       kfree(rbd_dev->pool_name);
-       kfree(rbd_dev->image_name);
+       kfree(rbd_dev->spec->pool_name);
+       kfree(rbd_dev->spec->image_name);
        rbd_dev_id_put(rbd_dev);
        kfree(rbd_dev);