rbd: define image specification structure

Group the fields that uniquely specify an rbd image into a new
reference-counted rbd_spec structure.  This structure will be used
to describe the desired image when mapping an image, and when
probing parent images in layered rbd devices.  Replace the set of
fields in the rbd device structure with a pointer to a dynamically
allocated rbd_spec.

Signed-off-by: Alex Elder <elder@inktank.com>
Reviewed-by: Josh Durgin <josh.durgin@inktank.com>
diff --git a/drivers/block/rbd.c b/drivers/block/rbd.c
index ab6e6d8..2049810 100644
--- a/drivers/block/rbd.c
+++ b/drivers/block/rbd.c
@@ -112,6 +112,27 @@
 	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;
 };
@@ -189,16 +210,9 @@
 
 	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;
@@ -654,7 +668,7 @@
 
 	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;
 
@@ -669,14 +683,14 @@
 {
 	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;
@@ -1096,7 +1110,7 @@
 	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);
@@ -1261,7 +1275,7 @@
 		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;
 	}
 
@@ -1545,7 +1559,7 @@
 		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);
@@ -1726,13 +1740,13 @@
 			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;
 		}
 
@@ -1783,7 +1797,7 @@
 {
 	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;
@@ -1957,7 +1971,7 @@
 {
 	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,
@@ -1965,7 +1979,8 @@
 {
 	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,
@@ -1973,7 +1988,7 @@
 {
 	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,
@@ -1981,7 +1996,7 @@
 {
 	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);
 }
 
 /*
@@ -1994,7 +2009,7 @@
 {
 	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,
@@ -2547,11 +2562,12 @@
 
 			/* 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 */
@@ -2869,16 +2885,17 @@
 	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 */
 
 	/*
@@ -2893,11 +2910,11 @@
 		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 */
 
@@ -2921,11 +2938,11 @@
 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;
@@ -2957,11 +2974,11 @@
 	 * 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 */
@@ -2984,15 +3001,15 @@
 	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);
@@ -3008,20 +3025,21 @@
 
 	/* 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 */
 
@@ -3038,8 +3056,8 @@
 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;
 }
@@ -3054,12 +3072,12 @@
 	 * 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 */
 
@@ -3147,6 +3165,9 @@
 	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);
@@ -3167,10 +3188,10 @@
 
 	/* 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)
@@ -3257,15 +3278,16 @@
 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);
@@ -3314,11 +3336,11 @@
 	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);