[dm-devel] [PATCH] dm cache: verify metadata device is exclussive

Mike Snitzer snitzer at redhat.com
Thu Jul 25 00:50:56 UTC 2013


Do not allow a cache device to use a metadata device that is already
in use by another cache device.

Add list member to the cache structure.  Also reorder members in the
cache structure to eliminate 6 out of 7 holes (reclaiming 24 bytes).

Example:
 echo "0 8192 linear 253:1 0" | dmsetup create metadata
 echo "0 2097152 linear 253:1 8192" | dmsetup create ssd
 echo "0 33554432 linear 253:0 0" | dmsetup create origin
 echo "0 33554432 cache /dev/mapper/metadata /dev/mapper/ssd /dev/mapper/origin 512 0 default 0" | dmsetup create cache1
 echo "0 33554432 cache /dev/mapper/metadata /dev/mapper/ssd /dev/mapper/origin 512 0 default 0" | dmsetup create cache2
device-mapper: reload ioctl on cache2 failed: Device or resource busy
Command failed

Kernel log shows:
device-mapper: table: 253:8: cache: metadata device already in use by a cache
device-mapper: ioctl: error adding target to table

Signed-off-by: Mike Snitzer <snitzer at redhat.com>
---
 drivers/md/dm-cache-target.c |   95 +++++++++++++++++++++++++++++++++++-------
 1 files changed, 80 insertions(+), 15 deletions(-)

diff --git a/drivers/md/dm-cache-target.c b/drivers/md/dm-cache-target.c
index 0df3ec0..77b3629 100644
--- a/drivers/md/dm-cache-target.c
+++ b/drivers/md/dm-cache-target.c
@@ -101,6 +101,8 @@ struct cache {
 	struct dm_target *ti;
 	struct dm_target_callbacks callbacks;
 
+	struct dm_cache_metadata *cmd;
+
 	/*
 	 * Metadata is written to this device.
 	 */
@@ -117,11 +119,6 @@ struct cache {
 	struct dm_dev *cache_dev;
 
 	/*
-	 * Cache features such as write-through.
-	 */
-	struct cache_features features;
-
-	/*
 	 * Size of the origin device in _complete_ blocks and native sectors.
 	 */
 	dm_oblock_t origin_blocks;
@@ -138,8 +135,6 @@ struct cache {
 	uint32_t sectors_per_block;
 	int sectors_per_block_shift;
 
-	struct dm_cache_metadata *cmd;
-
 	spinlock_t lock;
 	struct bio_list deferred_bios;
 	struct bio_list deferred_flush_bios;
@@ -148,8 +143,8 @@ struct cache {
 	struct list_head completed_migrations;
 	struct list_head need_commit_migrations;
 	sector_t migration_threshold;
-	atomic_t nr_migrations;
 	wait_queue_head_t migration_wait;
+	atomic_t nr_migrations;
 
 	/*
 	 * cache_size entries, dirty if set
@@ -160,9 +155,16 @@ struct cache {
 	/*
 	 * origin_blocks entries, discarded if set.
 	 */
-	uint32_t discard_block_size; /* a power of 2 times sectors per block */
 	dm_dblock_t discard_nr_blocks;
 	unsigned long *discard_bitset;
+	uint32_t discard_block_size; /* a power of 2 times sectors per block */
+
+	/*
+	 * Rather than reconstructing the table line for the status we just
+	 * save it and regurgitate.
+	 */
+	unsigned nr_ctr_args;
+	const char **ctr_args;
 
 	struct dm_kcopyd_client *copier;
 	struct workqueue_struct *wq;
@@ -187,14 +189,14 @@ struct cache {
 	bool loaded_mappings:1;
 	bool loaded_discards:1;
 
-	struct cache_stats stats;
-
 	/*
-	 * Rather than reconstructing the table line for the status we just
-	 * save it and regurgitate.
+	 * Cache features such as write-through.
 	 */
-	unsigned nr_ctr_args;
-	const char **ctr_args;
+	struct cache_features features;
+
+	struct cache_stats stats;
+
+	struct list_head list;
 };
 
 struct per_bio_data {
@@ -231,6 +233,52 @@ struct dm_cache_migration {
 	struct dm_bio_prison_cell *new_ocell;
 };
 
+/*----------------------------------------------------------------*/
+
+/*
+ * A global list of caches that uses the metadata block_device as a key.
+ */
+static struct dm_cache_table {
+	struct mutex mutex;
+	struct list_head caches;
+} dm_cache_table;
+
+static void cache_table_init(void)
+{
+	mutex_init(&dm_cache_table.mutex);
+	INIT_LIST_HEAD(&dm_cache_table.caches);
+}
+
+static void __cache_table_insert(struct cache *cache)
+{
+	BUG_ON(!mutex_is_locked(&dm_cache_table.mutex));
+	list_add(&cache->list, &dm_cache_table.caches);
+}
+
+static void __cache_table_remove(struct cache *cache)
+{
+	BUG_ON(!mutex_is_locked(&dm_cache_table.mutex));
+	list_del(&cache->list);
+}
+
+static struct cache *__cache_table_lookup(struct block_device *md_dev)
+{
+	struct cache *cache = NULL, *tmp;
+
+	BUG_ON(!mutex_is_locked(&dm_cache_table.mutex));
+
+	list_for_each_entry(tmp, &dm_cache_table.caches, list) {
+		if (tmp->metadata_dev->bdev == md_dev) {
+			cache = tmp;
+			break;
+		}
+	}
+
+	return cache;
+}
+
+/*----------------------------------------------------------------*/
+
 /*
  * Processing a bio in the worker thread may require these memory
  * allocations.  We prealloc to avoid deadlocks (the same worker thread
@@ -1533,6 +1581,10 @@ static void cache_dtr(struct dm_target *ti)
 {
 	struct cache *cache = ti->private;
 
+	mutex_lock(&dm_cache_table.mutex);
+	__cache_table_remove(cache);
+	mutex_unlock(&dm_cache_table.mutex);
+
 	destroy(cache);
 }
 
@@ -2134,6 +2186,17 @@ static int cache_ctr(struct dm_target *ti, unsigned argc, char **argv)
 		goto out;
 	}
 
+	mutex_lock(&dm_cache_table.mutex);
+	if (__cache_table_lookup(cache->metadata_dev->bdev)) {
+		ti->error = "metadata device already in use by a cache";
+		r = -EBUSY;
+		mutex_unlock(&dm_cache_table.mutex);
+		destroy(cache);
+		goto out;
+	}
+	__cache_table_insert(cache);
+	mutex_unlock(&dm_cache_table.mutex);
+
 	ti->private = cache;
 
 out:
@@ -2639,6 +2702,8 @@ static int __init dm_cache_init(void)
 {
 	int r;
 
+	cache_table_init();
+
 	r = dm_register_target(&cache_target);
 	if (r) {
 		DMERR("cache target registration failed: %d", r);




More information about the dm-devel mailing list