From: FUJITA Tomonori <[email-masked]>

[checkpatch errors]

Signed-off-by: FUJITA Tomonori <[email-masked]>
---
 drivers/md/dm-snap.c |  175 ++++++++++++++++++++++++++++++++++++++++++++++++---
 1 files changed, 165 insertions(+), 10 deletions(-)

Index: linux-2.6.28-rc3/drivers/md/dm-snap.c
===================================================================
--- linux-2.6.28-rc3.orig/drivers/md/dm-snap.c	2008-11-07 13:15:48.000000000 +0000
+++ linux-2.6.28-rc3/drivers/md/dm-snap.c	2008-11-07 13:15:53.000000000 +0000
@@ -582,8 +582,8 @@ static int snapshot_ctr(struct dm_target
 	cow_path = argv[1];
 	persistent = toupper(*argv[2]);
 
-	if (persistent != 'P' && persistent != 'N') {
-		ti->error = "Persistent flag is not P or N";
+	if (persistent != 'P' && persistent != 'N' && persistent != 'S') {
+		ti->error = "Persistent flag is not P, N, or S";
 		r = -EINVAL;
 		goto bad1;
 	}
@@ -632,7 +632,9 @@ static int snapshot_ctr(struct dm_target
 
 	s->store.snap = s;
 
-	if (persistent == 'P')
+	if (persistent == 'S')
+		r = dm_create_shared(&s->store);
+	else if (persistent == 'P')
 		r = dm_create_persistent(&s->store);
 	else
 		r = dm_create_transient(&s->store);
@@ -1404,6 +1406,136 @@ static int origin_status(struct dm_targe
 	return 0;
 }
 
+/*
+ * shared exception origin code
+ */
+
+struct dm_shared_origin {
+	struct dm_dev *origin;
+
+	struct dm_snapshot *origin_snap;
+};
+
+/*
+ * Construct a shared origin mapping: <origin_dev> <COW-dev> <p/n> <chunk-size>
+ */
+static int shared_origin_ctr(struct dm_target *ti, unsigned int argc,
+			     char **argv)
+{
+	int r;
+	struct dm_shared_origin *dso;
+	struct dm_snapshot *s;
+
+	if (argc != 4) {
+		ti->error = "origin: incorrect number of arguments";
+		return -EINVAL;
+	}
+
+	dso = kzalloc(sizeof(*dso), GFP_KERNEL);
+	if (!dso)
+		return -ENOMEM;
+
+	r = snapshot_ctr(ti, argc, argv);
+	if (r)
+		goto out;
+
+	s = ti->private;
+
+	if (s->chunk_size != 16) {
+		r = -EINVAL;
+		ti->error = "invalid chunk size";
+		snapshot_dtr(ti);
+		goto out;
+	}
+
+	s->active = 1;
+
+	dso->origin = s->origin;
+	dso->origin_snap = s;
+	ti->private = dso;
+
+	return 0;
+out:
+	kfree(dso);
+	return r;
+}
+
+static void shared_origin_dtr(struct dm_target *ti)
+{
+	struct dm_shared_origin *dso = ti->private;
+
+	ti->private = dso->origin_snap;
+	snapshot_dtr(ti);
+	kfree(dso);
+}
+
+static int shared_origin_map(struct dm_target *ti, struct bio *bio,
+			     union map_info *map_context)
+{
+	struct dm_shared_origin *dso = ti->private;
+	struct dm_dev *dev = dso->origin;
+
+	bio->bi_bdev = dev->bdev;
+
+	/* Only tell snapshots if this is a write */
+	return (bio_rw(bio) == WRITE) ? do_origin(dev, bio) : DM_MAPIO_REMAPPED;
+}
+
+static int shared_origin_status(struct dm_target *ti, status_type_t type,
+				char *result, unsigned int maxlen)
+{
+	struct dm_shared_origin *dso = ti->private;
+	struct dm_snapshot *snap = dso->origin_snap;
+	struct exception_store *store = &snap->store;
+	unsigned long map[DIV_ROUND_UP(MAX_SNAPSHOTS, BITS_PER_LONG)];
+	int r, nr, i;
+
+	switch (type) {
+	case STATUSTYPE_INFO:
+
+		memset(map, 0, sizeof(map));
+
+		down_write(&store->snap->lock);
+		nr = store->get_snapshot_info(store, map, sizeof(map));
+		up_write(&store->snap->lock);
+
+		if (nr) {
+			r = snprintf(result, maxlen, "%d ", nr);
+			result += r;
+			maxlen -= min_t(int, r, maxlen);
+
+			for (i = 0; maxlen && i < MAX_SNAPSHOTS; i++) {
+				if (test_bit(i, map)) {
+					r = snprintf(result, maxlen, "%d ", i);
+					result += r;
+					maxlen -= min_t(int, r, maxlen);
+				}
+			}
+		} else
+			snprintf(result, maxlen, ": no snapshot");
+
+		break;
+
+	case STATUSTYPE_TABLE:
+		r = snprintf(result, maxlen, "%s", snap->origin->name);
+		if (r > 0 && r < maxlen)
+			snprintf(result + r, maxlen - r, "%s %s %llu",
+				 snap->origin->name, snap->cow->name,
+				 (unsigned long long)snap->chunk_size);
+		break;
+	}
+
+	return 0;
+}
+
+static int shared_origin_message(struct dm_target *ti, unsigned argc,
+				 char **argv)
+{
+	struct dm_shared_origin *dso = ti->private;
+	struct dm_snapshot *snap = dso->origin_snap;
+	return snap->store.message(&snap->store, argc, argv);
+}
+
 static struct target_type origin_target = {
 	.name    = "snapshot-origin",
 	.version = {1, 6, 0},
@@ -1427,6 +1559,17 @@ static struct target_type snapshot_targe
 	.status  = snapshot_status,
 };
 
+static struct target_type shared_origin_target = {
+	.name    = "s-snap-origin",
+	.version = {1, 0, 0},
+	.module  = THIS_MODULE,
+	.ctr     = shared_origin_ctr,
+	.dtr     = shared_origin_dtr,
+	.map     = shared_origin_map,
+	.status  = shared_origin_status,
+	.message = shared_origin_message,
+};
+
 static int __init dm_snapshot_init(void)
 {
 	int r;
@@ -1443,31 +1586,37 @@ static int __init dm_snapshot_init(void)
 		goto bad1;
 	}
 
+	r = dm_register_target(&shared_origin_target);
+	if (r) {
+		DMERR("shared origin target register failed %d", r);
+		goto bad2;
+	}
+
 	r = init_origin_hash();
 	if (r) {
 		DMERR("init_origin_hash failed.");
-		goto bad2;
+		goto bad3;
 	}
 
 	exception_cache = KMEM_CACHE(dm_snap_exception, 0);
 	if (!exception_cache) {
 		DMERR("Couldn't create exception cache.");
 		r = -ENOMEM;
-		goto bad3;
+		goto bad4;
 	}
 
 	pending_cache = KMEM_CACHE(dm_snap_pending_exception, 0);
 	if (!pending_cache) {
 		DMERR("Couldn't create pending cache.");
 		r = -ENOMEM;
-		goto bad4;
+		goto bad5;
 	}
 
 	tracked_chunk_cache = KMEM_CACHE(dm_snap_tracked_chunk, 0);
 	if (!tracked_chunk_cache) {
 		DMERR("Couldn't create cache to track chunks in use.");
 		r = -ENOMEM;
-		goto bad5;
+		goto bad6;
 	}
 
 	ksnapd = create_singlethread_workqueue("ksnapd");
@@ -1481,12 +1630,14 @@ static int __init dm_snapshot_init(void)
 
       bad_pending_pool:
 	kmem_cache_destroy(tracked_chunk_cache);
-      bad5:
+      bad6:
 	kmem_cache_destroy(pending_cache);
-      bad4:
+      bad5:
 	kmem_cache_destroy(exception_cache);
-      bad3:
+      bad4:
 	exit_origin_hash();
+      bad3:
+	dm_unregister_target(&shared_origin_target);
       bad2:
 	dm_unregister_target(&origin_target);
       bad1:
@@ -1501,6 +1652,10 @@ static void __exit dm_snapshot_exit(void
 	dm_unregister_target(&snapshot_target);
 	dm_unregister_target(&origin_target);
 
+	r = dm_unregister_target(&shared_origin_target);
+	if (r)
+		DMERR("shared origin unregister failed %d", r);
+
 	exit_origin_hash();
 	kmem_cache_destroy(pending_cache);
 	kmem_cache_destroy(exception_cache);