From: Jonathan Brassow <[email-masked]>

This patch completes the clean break we wanted between
snapshots and exception stores.  Now it is possible to
create exception store modules and be able to use them
without having to alter the snapshot code.  There are
currently two types of exception stores, but more have
been proposed - including a 'shared' exception store.

Additionally, the constructor table has been enhanced
to allow more flexibility in the arguments passed to
the exception stores, as well as providing an easy
path to further expand options in the future.  This
was done while maintaining backwards compatibility.

[FIXME This needs splitting up!!!]

Signed-off-by: Jonathan Brassow <[email-masked]>
---
 drivers/md/dm-exception-store.c |   36 ++++++-----
 drivers/md/dm-exception-store.h |    7 +-
 drivers/md/dm-snap-persistent.c |   14 ++++
 drivers/md/dm-snap-transient.c  |   23 +++++++
 drivers/md/dm-snap.c            |  124 ++++++++++++++++++++++++++++++----------
 5 files changed, 154 insertions(+), 50 deletions(-)

Index: linux/drivers/md/dm-exception-store.c
===================================================================
--- linux.orig/drivers/md/dm-exception-store.c	2009-01-19 13:10:41.000000000 +0000
+++ linux/drivers/md/dm-exception-store.c	2009-01-19 13:10:47.000000000 +0000
@@ -195,16 +195,16 @@ static int set_chunk_size(struct dm_exce
 	return 0;
 }
 
-int dm_exception_store_create(struct dm_target *ti, int argc, char **argv,
-			      unsigned *args_used,
+int dm_exception_store_create(const char *type_name, struct dm_target *ti,
+			      unsigned compatible_table,
+			      unsigned int argc, char **argv,
 			      struct dm_exception_store **store)
 {
 	int r = 0;
 	struct dm_exception_store_type *type;
 	struct dm_exception_store *tmp_store;
-	char persistent;
 
-	if (argc < 3) {
+	if (argc < 2) {
 		ti->error = "Insufficient exception store arguments";
 		return -EINVAL;
 	}
@@ -215,13 +215,7 @@ int dm_exception_store_create(struct dm_
 		return -ENOMEM;
 	}
 
-	persistent = toupper(*argv[1]);
-	if (persistent != 'P' && persistent != 'N') {
-		ti->error = "Persistent flag is not P or N";
-		return -EINVAL;
-	}
-
-	type = get_type(argv[1]);
+	type = get_type(type_name);
 	if (!type) {
 		ti->error = "Exception store type not recognised";
 		r = -EINVAL;
@@ -230,6 +224,7 @@ int dm_exception_store_create(struct dm_
 
 	tmp_store->type = type;
 	tmp_store->ti = ti;
+	tmp_store->compatible_table = compatible_table;
 
 	r = dm_get_device(ti, argv[0], 0, 0,
 			  FMODE_READ | FMODE_WRITE, &tmp_store->cow);
@@ -238,17 +233,26 @@ int dm_exception_store_create(struct dm_
 		goto bad_cow;
 	}
 
-	r = set_chunk_size(tmp_store, argv[2], &ti->error);
-	if (r)
-		goto bad_cow;
+	r = set_chunk_size(tmp_store, argv[1], &ti->error);
+	if (r) {
+		goto bad_ctr;
+	}
+
+	/*
+	 * COW-dev and chunk_size are common to all types of
+	 * exception stores and are stored directly in the
+	 * dm_exception_store and not passed on to the
+	 * constructor for the dm_exception_store_type
+	 */
+	argc -= 2;
+	argv += 2;
 
-	r = type->ctr(tmp_store, 0, NULL);
+	r = type->ctr(tmp_store, argc, argv);
 	if (r) {
 		ti->error = "Exception store type constructor failed";
 		goto bad_ctr;
 	}
 
-	*args_used = 3;
 	*store = tmp_store;
 	return 0;
 
Index: linux/drivers/md/dm-exception-store.h
===================================================================
--- linux.orig/drivers/md/dm-exception-store.h	2009-01-19 13:10:41.000000000 +0000
+++ linux/drivers/md/dm-exception-store.h	2009-01-19 13:10:47.000000000 +0000
@@ -97,6 +97,8 @@ struct dm_exception_store {
 	struct dm_exception_store_type *type;
 	struct dm_target *ti;
 
+	unsigned compatible_table;
+
 	struct dm_dev *cow;
 
 	/* Size of data blocks saved - must be a power of 2 */
@@ -167,8 +169,9 @@ static inline chunk_t sector_to_chunk(st
 int dm_exception_store_type_register(struct dm_exception_store_type *type);
 int dm_exception_store_type_unregister(struct dm_exception_store_type *type);
 
-int dm_exception_store_create(struct dm_target *ti, int argc, char **argv,
-			      unsigned *args_used,
+int dm_exception_store_create(const char *type_name, struct dm_target *ti,
+			      unsigned compatible_table,
+			      unsigned argc, char **argv,
 			      struct dm_exception_store **store);
 void dm_exception_store_destroy(struct dm_exception_store *store);
 
Index: linux/drivers/md/dm-snap-persistent.c
===================================================================
--- linux.orig/drivers/md/dm-snap-persistent.c	2009-01-19 13:10:28.000000000 +0000
+++ linux/drivers/md/dm-snap-persistent.c	2009-01-19 13:10:47.000000000 +0000
@@ -694,6 +694,20 @@ static int persistent_status(struct dm_e
 {
 	int sz = 0;
 
+	switch (status) {
+	case STATUSTYPE_INFO:
+		break;
+	case STATUSTYPE_TABLE:
+		if (store->compatible_table)
+			DMEMIT("%s %s %llu", store->cow->name,
+			       store->type->name,
+			       (unsigned long long)store->chunk_size);
+		else
+			DMEMIT("%s 2 %s %llu", store->type->name,
+			       store->cow->name,
+			       (unsigned long long)store->chunk_size);
+	}
+
 	return sz;
 }
 
Index: linux/drivers/md/dm-snap-transient.c
===================================================================
--- linux.orig/drivers/md/dm-snap-transient.c	2009-01-19 13:10:28.000000000 +0000
+++ linux/drivers/md/dm-snap-transient.c	2009-01-19 13:10:47.000000000 +0000
@@ -11,7 +11,6 @@
 #include <linux/pagemap.h>
 #include <linux/vmalloc.h>
 #include <linux/slab.h>
-#include <linux/dm-io.h>
 
 #define DM_MSG_PREFIX "transient snapshot"
 
@@ -66,6 +65,14 @@ static void transient_fraction_full(stru
 	*denominator = get_dev_size(store->cow->bdev);
 }
 
+/*
+ * transient_ctr
+ * @store
+ * @argc
+ * @argv:  Always <COW-dev> <chunk-size>
+ *
+ * Returns: 0 on success, -Exxx on error
+ */
 static int transient_ctr(struct dm_exception_store *store,
 			 unsigned argc, char **argv)
 {
@@ -87,6 +94,20 @@ static int transient_status(struct dm_ex
 {
 	int sz = 0;
 
+	switch (status) {
+	case STATUSTYPE_INFO:
+		break;
+	case STATUSTYPE_TABLE:
+		if (store->compatible_table)
+			DMEMIT("%s %s %llu", store->cow->name,
+			       store->type->name,
+			       (unsigned long long)store->chunk_size);
+		else
+			DMEMIT("%s 2 %s %llu", store->type->name,
+			       store->cow->name,
+			       (unsigned long long)store->chunk_size);
+	}
+
 	return sz;
 }
 
Index: linux/drivers/md/dm-snap.c
===================================================================
--- linux.orig/drivers/md/dm-snap.c	2009-01-19 13:10:41.000000000 +0000
+++ linux/drivers/md/dm-snap.c	2009-01-19 13:10:47.000000000 +0000
@@ -7,6 +7,7 @@
  */
 
 #include <linux/blkdev.h>
+#include <linux/ctype.h>
 #include <linux/device-mapper.h>
 #include <linux/delay.h>
 #include <linux/fs.h>
@@ -70,9 +71,6 @@ struct dm_snapshot {
 	/* Origin writes don't trigger exceptions until this is set */
 	int active;
 
-	/* Used for display of table */
-	char type;
-
 	mempool_t *pending_pool;
 
 	atomic_t pending_exceptions_count;
@@ -104,8 +102,7 @@ struct dm_snapshot {
 static struct workqueue_struct *ksnapd;
 static void flush_queued_bios(struct work_struct *work);
 
-static sector_t chunk_to_sector(struct dm_exception_store *store,
-				chunk_t chunk)
+static sector_t chunk_to_sector(struct dm_exception_store *store, chunk_t chunk)
 {
 	return chunk << store->chunk_shift;
 }
@@ -500,7 +497,7 @@ out:
 }
 
 /*
- * Callback used by the exception stores to load exceptions when
+ * Used by the exception stores to load exceptions when
  * initialising.
  */
 static int dm_add_exception(void *context, chunk_t old, chunk_t new)
@@ -574,33 +571,96 @@ static int init_hash_tables(struct dm_sn
 }
 
 /*
- * Construct a snapshot mapping: <origin_dev> <COW-dev> <p/n> <chunk-size>
+ * create_exception_store
+ * @ti
+ * @argc
+ * @argv
+ * @args_used
+ * @store: contains newly allocated dm_exception_store
+ *
+ * Possible formats for argv::
+ * Backwards compatibility mode:
+ *	<COW-dev> p/n <chunk-size>
+ * Current format:
+ *	<store type> <arg count> <COW> <chunk-size> [other args]
+ *
+ * Returns: 0 on success, -Exxx on error
+ */
+static int create_exception_store(struct dm_target *ti, unsigned argc,
+				  char **argv, unsigned *args_used,
+				  struct dm_exception_store **store)
+{
+	unsigned param_count;
+	char *tmp_argv[2];
+
+	*store = NULL;
+
+	/*
+	 * Detect for old-style table line with type as second arg.
+	 */
+	if (isdigit(*argv[1])) {
+		if (argc < 3) {
+			ti->error = "Insufficient exception store arguments";
+			return -EINVAL;
+		}
+
+		tmp_argv[0] = argv[0];  /* COW dev */
+		tmp_argv[1] = argv[2];  /* chunk size */
+
+		*args_used = 3;
+
+		return dm_exception_store_create(argv[1], ti, 1, 2, tmp_argv,
+						 store);
+	}
+
+	if (sscanf(argv[1], "%u", &param_count) != 1) {
+		ti->error = "Invalid exception store argument count";
+		return -EINVAL;
+	}
+
+	*args_used = 2 + param_count;
+
+	if (argc < *args_used) {
+		ti->error = "Insufficient exception store arguments";
+		return -EINVAL;
+	}
+
+	return dm_exception_store_create(argv[0], ti, 0, param_count, argv + 2,
+					 store);
+}
+
+/*
+ * snapshot_ctr
+ * @ti
+ * @argc
+ * @argv
+ *
+ * Construct a snapshot mapping.  Possible mapping tables include:
+ *	<origin_dev> <exception store args> <feature args>
+ * See 'create_exception_store' for format of <exception store args>.
+ *
+ * Returns: 0 on success, -XXX on error
  */
 static int snapshot_ctr(struct dm_target *ti, unsigned int argc, char **argv)
 {
 	struct dm_snapshot *s;
-	int i;
+	int i = 0;
 	int r = -EINVAL;
 	char *origin_path;
-	struct dm_exception_store *store;
 	unsigned args_used;
+	struct dm_exception_store *store;
 
-	if (argc != 4) {
-		ti->error = "requires exactly 4 arguments";
-		r = -EINVAL;
-		goto bad_args;
+	if (argc < 4) {
+		ti->error = "too few arguments";
+		return -EINVAL;
 	}
 
 	origin_path = argv[0];
 	argv++;
 	argc--;
-
-	r = dm_exception_store_create(ti, argc, argv, &args_used, &store);
-	if (r) {
-		ti->error = "Couldn't create exception store";
-		r = -EINVAL;
-		goto bad_args;
-	}
+	r = create_exception_store(ti, argc, argv, &args_used, &store);
+	if (r)
+		return r;
 
 	argv += args_used;
 	argc -= args_used;
@@ -608,18 +668,19 @@ static int snapshot_ctr(struct dm_target
 	s = kmalloc(sizeof(*s), GFP_KERNEL);
 	if (!s) {
 		ti->error = "Cannot allocate snapshot context private "
-		    "structure";
+			    " structure";
 		r = -ENOMEM;
 		goto bad_snap;
 	}
 
+	s->store = store;
+
 	r = dm_get_device(ti, origin_path, 0, ti->len, FMODE_READ, &s->origin);
 	if (r) {
 		ti->error = "Cannot get origin device";
 		goto bad_origin;
 	}
 
-	s->store = store;
 	s->valid = 1;
 	s->active = 0;
 	atomic_set(&s->pending_exceptions_count, 0);
@@ -659,7 +720,8 @@ static int snapshot_ctr(struct dm_target
 	spin_lock_init(&s->tracked_chunk_lock);
 
 	/* Metadata must only be loaded into one table at once */
-	r = s->store->type->read_metadata(s->store, dm_add_exception, (void *)s);
+	r = s->store->type->read_metadata(s->store, dm_add_exception,
+					  (void *)s);
 	if (r < 0) {
 		ti->error = "Failed to read snapshot metadata";
 		goto bad_load_and_register;
@@ -704,9 +766,7 @@ bad_origin:
 	kfree(s);
 
 bad_snap:
-	dm_exception_store_destroy(store);
-
-bad_args:
+	dm_exception_store_destroy(s->store);
 	return r;
 }
 
@@ -1158,9 +1218,8 @@ static int snapshot_status(struct dm_tar
 		 * make sense.
 		 */
 		DMEMIT("%s", snap->origin->name);
-		DMEMIT(" %s %s %llu", snap->store->cow->name,
-		       snap->store->type->name,
-		       (unsigned long long)snap->store->chunk_size);
+		snap->store->type->status(snap->store, type, result, maxlen);
+
 		break;
 	}
 
@@ -1353,7 +1412,8 @@ static void origin_resume(struct dm_targ
 	o = __lookup_origin(dev->bdev);
 	if (o)
 		list_for_each_entry (snap, &o->snapshots, list)
-			chunk_size = min_not_zero(chunk_size, snap->store->chunk_size);
+			chunk_size = min_not_zero(chunk_size,
+						  snap->store->chunk_size);
 	up_read(&_origins_lock);
 
 	ti->split_io = chunk_size;
@@ -1413,7 +1473,7 @@ static int __init dm_snapshot_init(void)
 	r = dm_register_target(&snapshot_target);
 	if (r) {
 		DMERR("snapshot target register failed %d", r);
-		return r;
+		goto bad0;
 	}
 
 	r = dm_register_target(&origin_target);
@@ -1470,6 +1530,8 @@ bad2:
 	dm_unregister_target(&origin_target);
 bad1:
 	dm_unregister_target(&snapshot_target);
+bad0:
+	dm_exception_store_exit();
 	return r;
 }