All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 00/12] snapshot-merge target
@ 2009-10-05 19:02 Mike Snitzer
  2009-10-05 19:02 ` [PATCH 01/12] dm-exception-store-merge-methods Mike Snitzer
                   ` (11 more replies)
  0 siblings, 12 replies; 15+ messages in thread
From: Mike Snitzer @ 2009-10-05 19:02 UTC (permalink / raw)
  To: dm-devel

snapshot-merge target, quilt tree is maintained here:
http://people.redhat.com/msnitzer/patches/snapshot-merge/kernel/2.6.32/

For userspace LVM2 support please see:
http://people.redhat.com/msnitzer/patches/snapshot-merge/lvm2/LVM2-2.02.54/

Mike Snitzer (1):
  dm-snapshot-merge-use-larger-io-when-merging

Mikulas Patocka (11):
  dm-exception-store-merge-methods
  dm-exception-store-merge-accounting
  dm-snapshot-target-merge
  dm-snapshot-target-merge-no-new-alloc
  dm-snapshot-no-more-merging-snapshots
  dm-snapshot-merge-process
  dm-snapshot-merge-interlock-writes
  dm-snapshot-merge-track-writes
  dm-snapshot-merge-make-exceptions-in-other-snapshots-when-merging
  dm-snapshot-merge-make-exceptions-in-other-snapshots-if-remapped-to-origin
  dm-snapshot-merge-redirect-to-origin-if-invalidated

 drivers/md/dm-exception-store.h |   27 +++
 drivers/md/dm-snap-persistent.c |   89 +++++++++-
 drivers/md/dm-snap.c            |  379 +++++++++++++++++++++++++++++++++++++--
 3 files changed, 482 insertions(+), 13 deletions(-)

^ permalink raw reply	[flat|nested] 15+ messages in thread

* [PATCH 01/12] dm-exception-store-merge-methods
  2009-10-05 19:02 [PATCH 00/12] snapshot-merge target Mike Snitzer
@ 2009-10-05 19:02 ` Mike Snitzer
  2009-10-05 19:02 ` [PATCH 02/12] dm-exception-store-merge-accounting Mike Snitzer
                   ` (10 subsequent siblings)
  11 siblings, 0 replies; 15+ messages in thread
From: Mike Snitzer @ 2009-10-05 19:02 UTC (permalink / raw)
  To: dm-devel; +Cc: Mikulas Patocka

From: Mikulas Patocka <mpatocka@redhat.com>

This patch adds two new methods to exception store:

prepare_merge: returns the last chunk in the variables passed by reference.
	The return value is the number of consecutive chunks.
commit_merge: permanently removes 'n' chunks from the exception store.
	'n' is less or equal that the number returned by prepare_merge.

If the caller wishes, it can do the optimization of merging several consecutive
chunks at once. If it doesn't want to do this optimization, it just calls
commit_merge with n == 1.

Signed-off-by: Mikulas Patocka <mpatocka@redhat.com>
Reviewed-by: Mike Snitzer <snitzer@redhat.com>
---
 drivers/md/dm-exception-store.h |   16 +++++++++
 drivers/md/dm-snap-persistent.c |   70 +++++++++++++++++++++++++++++++++++++++
 2 files changed, 86 insertions(+), 0 deletions(-)

diff --git a/drivers/md/dm-exception-store.h b/drivers/md/dm-exception-store.h
index 5737796..b293595 100644
--- a/drivers/md/dm-exception-store.h
+++ b/drivers/md/dm-exception-store.h
@@ -79,6 +79,22 @@ struct dm_exception_store_type {
 				  void *callback_context);
 
 	/*
+	 * Returns the last chunk in the pointers. (TODO: -ENOPARSE)
+	 * > 0:  the number of consecutive chunks that can
+	 *       be copied in one shot.
+	 * == 0: the exception store is empty.
+	 * < 0:  error.
+	 */
+	int (*prepare_merge) (struct dm_exception_store *store,
+			      chunk_t *old_chunk, chunk_t *new_chunk);
+
+	/*
+	 * Clear the last n exceptions.
+	 * n must be <= the value returned by prepare_merge.
+	 */
+	int (*commit_merge) (struct dm_exception_store *store, int n);
+
+	/*
 	 * The snapshot is invalid, note this in the metadata.
 	 */
 	void (*drop_snapshot) (struct dm_exception_store *store);
diff --git a/drivers/md/dm-snap-persistent.c b/drivers/md/dm-snap-persistent.c
index fbcedc3..2523056 100644
--- a/drivers/md/dm-snap-persistent.c
+++ b/drivers/md/dm-snap-persistent.c
@@ -409,6 +409,15 @@ static void write_exception(struct pstore *ps,
 	e->new_chunk = cpu_to_le64(de->new_chunk);
 }
 
+static void clear_exception(struct pstore *ps, uint32_t index)
+{
+	struct disk_exception *e = get_exception(ps, index);
+
+	/* clear it */
+	e->old_chunk = 0;
+	e->new_chunk = 0;
+}
+
 /*
  * Registers the exceptions that are present in the current area.
  * 'full' is filled in to indicate if the area has been
@@ -670,6 +679,63 @@ static void persistent_commit_exception(struct dm_exception_store *store,
 	ps->callback_count = 0;
 }
 
+static int persistent_prepare_merge(struct dm_exception_store *store,
+				    chunk_t *old_chunk, chunk_t *new_chunk)
+{
+	int r, i;
+	struct pstore *ps = get_info(store);
+	struct disk_exception de;
+
+	if (!ps->current_committed) {
+		if (!ps->current_area)
+			return 0;
+		ps->current_area--;
+		r = area_io(ps, READ);
+		if (r < 0)
+			return r;
+		ps->current_committed = ps->exceptions_per_area;
+	}
+
+	read_exception(ps, ps->current_committed - 1, &de);
+	*old_chunk = de.old_chunk;
+	*new_chunk = de.new_chunk;
+
+	for (i = 1; i < ps->current_committed; i++) {
+		read_exception(ps, ps->current_committed - 1 - i, &de);
+		if (de.old_chunk != *old_chunk - i ||
+		    de.new_chunk != *new_chunk - i)
+			break;
+	}
+
+	return i;
+}
+
+static int persistent_commit_merge(struct dm_exception_store *store, int n)
+{
+	int r, i;
+	struct pstore *ps = get_info(store);
+
+	BUG_ON(n > ps->current_committed);
+
+	for (i = 0; i < n; i++)
+		clear_exception(ps, ps->current_committed - 1 - i);
+
+	r = area_io(ps, WRITE);
+	if (r < 0)
+		return r;
+
+	ps->current_committed -= i;
+
+	/*
+	 * ps->next_free cannot really be reliably decreased here (because of
+	 * misordered chunks), so don't do it. We don't even need it, because
+	 * there is no situation where merging snapshot would become
+	 * non-merging.
+	 */
+
+	return 0;
+}
+
 static void persistent_drop_snapshot(struct dm_exception_store *store)
 {
 	struct pstore *ps = get_info(store);
@@ -739,6 +805,8 @@ static struct dm_exception_store_type _persistent_type = {
 	.read_metadata = persistent_read_metadata,
 	.prepare_exception = persistent_prepare_exception,
 	.commit_exception = persistent_commit_exception,
+	.prepare_merge = persistent_prepare_merge,
+	.commit_merge = persistent_commit_merge,
 	.drop_snapshot = persistent_drop_snapshot,
 	.fraction_full = persistent_fraction_full,
 	.status = persistent_status,
@@ -752,6 +820,8 @@ static struct dm_exception_store_type _persistent_compat_type = {
 	.read_metadata = persistent_read_metadata,
 	.prepare_exception = persistent_prepare_exception,
 	.commit_exception = persistent_commit_exception,
+	.prepare_merge = persistent_prepare_merge,
+	.commit_merge = persistent_commit_merge,
 	.drop_snapshot = persistent_drop_snapshot,
 	.fraction_full = persistent_fraction_full,
 	.status = persistent_status,
-- 
1.6.2.5

^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [PATCH 02/12] dm-exception-store-merge-accounting
  2009-10-05 19:02 [PATCH 00/12] snapshot-merge target Mike Snitzer
  2009-10-05 19:02 ` [PATCH 01/12] dm-exception-store-merge-methods Mike Snitzer
@ 2009-10-05 19:02 ` Mike Snitzer
  2009-10-05 19:53   ` Mike Snitzer
  2009-10-05 19:02 ` [PATCH 03/12] dm-snapshot-target-merge Mike Snitzer
                   ` (9 subsequent siblings)
  11 siblings, 1 reply; 15+ messages in thread
From: Mike Snitzer @ 2009-10-05 19:02 UTC (permalink / raw)
  To: dm-devel; +Cc: Mikulas Patocka

From: Mikulas Patocka <mpatocka@redhat.com>

Conditionally adjust snapshot usage accounting if snapshot-merge is in
progress.  Care is taken to preserve the established kernel<->userspace
interface.

Signed-off-by: Mike Snitzer <snitzer@redhat.com>
Cc: Mikulas Patocka <mpatocka@redhat.com>
---
 drivers/md/dm-snap-persistent.c |   19 ++++++++++++++++++-
 1 files changed, 18 insertions(+), 1 deletions(-)

diff --git a/drivers/md/dm-snap-persistent.c b/drivers/md/dm-snap-persistent.c
index 2523056..005c60c 100644
--- a/drivers/md/dm-snap-persistent.c
+++ b/drivers/md/dm-snap-persistent.c
@@ -91,6 +91,7 @@ struct pstore {
 	struct dm_exception_store *store;
 	int version;
 	int valid;
+	int merging;	/* 1 if there is merging going on */
 	uint32_t exceptions_per_area;
 
 	/*
@@ -502,7 +503,18 @@ static struct pstore *get_info(struct dm_exception_store *store)
 static void persistent_fraction_full(struct dm_exception_store *store,
 				     sector_t *numerator, sector_t *denominator)
 {
-	*numerator = get_info(store)->next_free * store->chunk_size;
+	struct pstore *ps = get_info(store);
+	/*
+	 * Must maintain the fact that DM reports the first two chunks as used
+	 * (externally) even though they should've been hidden (used internally).
+	 * - this preserves the established (flawed) kernel<->userspace interface
+	 * - snapshot-merge must add these two chunks into its usage reporting
+	 */
+	if (!ps->merging)
+		*numerator = ps->next_free * store->chunk_size;
+	else
+		*numerator = (area_location(ps, ps->current_area) - 1 +
+			      ps->current_committed + 2) * store->chunk_size;
 	*denominator = get_dev_size(dm_snap_get_cow(store->snap)->bdev);
 }
 
@@ -598,6 +610,8 @@ static int persistent_prepare_exception(struct dm_exception_store *store,
 	chunk_t next_free;
 	sector_t size = get_dev_size(dm_snap_get_cow(store->snap)->bdev);
 
+	ps->merging = 0;
+
 	/* Is there enough room ? */
 	if (size < ((ps->next_free + 1) * store->chunk_size))
 		return -ENOSPC;
@@ -686,6 +700,8 @@ static int persistent_prepare_merge(struct dm_exception_store *store,
 	struct pstore *ps = get_info(store);
 	struct disk_exception de;
 
+	ps->merging = 1;
+
 	if (!ps->current_committed) {
 		if (!ps->current_area)
 			return 0;
@@ -757,6 +773,7 @@ static int persistent_ctr(struct dm_exception_store *store,
 
 	ps->store = store;
 	ps->valid = 1;
+	ps->merging = 0;
 	ps->version = SNAPSHOT_DISK_VERSION;
 	ps->area = NULL;
 	ps->zero_area = NULL;
-- 
1.6.2.5

^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [PATCH 03/12] dm-snapshot-target-merge
  2009-10-05 19:02 [PATCH 00/12] snapshot-merge target Mike Snitzer
  2009-10-05 19:02 ` [PATCH 01/12] dm-exception-store-merge-methods Mike Snitzer
  2009-10-05 19:02 ` [PATCH 02/12] dm-exception-store-merge-accounting Mike Snitzer
@ 2009-10-05 19:02 ` Mike Snitzer
  2009-10-05 19:02 ` [PATCH 04/12] dm-snapshot-target-merge-no-new-alloc Mike Snitzer
                   ` (8 subsequent siblings)
  11 siblings, 0 replies; 15+ messages in thread
From: Mike Snitzer @ 2009-10-05 19:02 UTC (permalink / raw)
  To: dm-devel; +Cc: Mikulas Patocka

From: Mikulas Patocka <mpatocka@redhat.com>

Add a new target snapshot-merge.
For now, the target is identical to snapshot.

Signed-off-by: Mikulas Patocka <mpatocka@redhat.com>
Signed-off-by: Mike Snitzer <snitzer@redhat.com>
---
 drivers/md/dm-snap.c |   46 ++++++++++++++++++++++++++++++++++------------
 1 files changed, 34 insertions(+), 12 deletions(-)

diff --git a/drivers/md/dm-snap.c b/drivers/md/dm-snap.c
index 275ae33..27b2f22 100644
--- a/drivers/md/dm-snap.c
+++ b/drivers/md/dm-snap.c
@@ -1525,6 +1525,19 @@ static struct target_type snapshot_target = {
 	.iterate_devices = snapshot_iterate_devices,
 };
 
+static struct target_type snapshot_merge_target = {
+	.name    = "snapshot-merge",
+	.version = {1, 7, 0},
+	.module  = THIS_MODULE,
+	.ctr     = snapshot_ctr,
+	.dtr     = snapshot_dtr,
+	.map     = snapshot_map,
+	.end_io  = snapshot_end_io,
+	.resume  = snapshot_resume,
+	.status  = snapshot_status,
+	.iterate_devices = snapshot_iterate_devices,
+};
+
 static int __init dm_snapshot_init(void)
 {
 	int r;
@@ -1536,7 +1549,7 @@ static int __init dm_snapshot_init(void)
 	}
 
 	r = dm_register_target(&snapshot_target);
-	if (r) {
+	if (r < 0) {
 		DMERR("snapshot target register failed %d", r);
 		goto bad_register_snapshot_target;
 	}
@@ -1544,34 +1557,40 @@ static int __init dm_snapshot_init(void)
 	r = dm_register_target(&origin_target);
 	if (r < 0) {
 		DMERR("Origin target register failed %d", r);
-		goto bad1;
+		goto bad_register_origin_target;
+	}
+
+	r = dm_register_target(&snapshot_merge_target);
+	if (r < 0) {
+		DMERR("snapshot-merge target register failed %d", r);
+		goto bad_register_snapshot_merge_target;
 	}
 
 	r = init_origin_hash();
 	if (r) {
 		DMERR("init_origin_hash failed.");
-		goto bad2;
+		goto bad_origin_hash;
 	}
 
 	exception_cache = KMEM_CACHE(dm_snap_exception, 0);
 	if (!exception_cache) {
 		DMERR("Couldn't create exception cache.");
 		r = -ENOMEM;
-		goto bad3;
+		goto bad_exception_cache;
 	}
 
 	pending_cache = KMEM_CACHE(dm_snap_pending_exception, 0);
 	if (!pending_cache) {
 		DMERR("Couldn't create pending cache.");
 		r = -ENOMEM;
-		goto bad4;
+		goto bad_pending_cache;
 	}
 
 	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 bad_tracked_chunk_cache;
 	}
 
 	ksnapd = create_singlethread_workqueue("ksnapd");
@@ -1585,19 +1604,21 @@ static int __init dm_snapshot_init(void)
 
 bad_pending_pool:
 	kmem_cache_destroy(tracked_chunk_cache);
-bad5:
+bad_tracked_chunk_cache:
 	kmem_cache_destroy(pending_cache);
-bad4:
+bad_pending_cache:
 	kmem_cache_destroy(exception_cache);
-bad3:
+bad_exception_cache:
 	exit_origin_hash();
-bad2:
+bad_origin_hash:
+	dm_unregister_target(&snapshot_merge_target);
+bad_register_snapshot_merge_target:
 	dm_unregister_target(&origin_target);
-bad1:
+bad_register_origin_target:
 	dm_unregister_target(&snapshot_target);
-
 bad_register_snapshot_target:
 	dm_exception_store_exit();
+
 	return r;
 }
 
@@ -1607,6 +1628,7 @@ static void __exit dm_snapshot_exit(void)
 
 	dm_unregister_target(&snapshot_target);
 	dm_unregister_target(&origin_target);
+	dm_unregister_target(&snapshot_merge_target);
 
 	exit_origin_hash();
 	kmem_cache_destroy(pending_cache);
-- 
1.6.2.5

^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [PATCH 04/12] dm-snapshot-target-merge-no-new-alloc
  2009-10-05 19:02 [PATCH 00/12] snapshot-merge target Mike Snitzer
                   ` (2 preceding siblings ...)
  2009-10-05 19:02 ` [PATCH 03/12] dm-snapshot-target-merge Mike Snitzer
@ 2009-10-05 19:02 ` Mike Snitzer
  2009-10-05 19:02 ` [PATCH 05/12] dm-snapshot-no-more-merging-snapshots Mike Snitzer
                   ` (7 subsequent siblings)
  11 siblings, 0 replies; 15+ messages in thread
From: Mike Snitzer @ 2009-10-05 19:02 UTC (permalink / raw)
  To: dm-devel; +Cc: Mikulas Patocka

From: Mikulas Patocka <mpatocka@redhat.com>

Merge target shouldn't allocate new exceptions:
- make new method snapshot_merge_map that don't allocate exceptions.
- modify __origin_write so that it doesn't allocate exceptions in merging
  snapshots.
- disallow creating merging snapshots with exception store that doesn't
  provide .prepare_merge (e.g. transient)

Signed-off-by: Mikulas Patocka <mpatocka@redhat.com>
Signed-off-by: Mike Snitzer <snitzer@redhat.com>
---
 drivers/md/dm-snap.c |   53 +++++++++++++++++++++++++++++++++++++++++++++++++-
 1 files changed, 52 insertions(+), 1 deletions(-)

diff --git a/drivers/md/dm-snap.c b/drivers/md/dm-snap.c
index 27b2f22..31040f9 100644
--- a/drivers/md/dm-snap.c
+++ b/drivers/md/dm-snap.c
@@ -127,6 +127,11 @@ static int bdev_equal(struct block_device *lhs, struct block_device *rhs)
 	return lhs == rhs;
 }
 
+static int snapshot_merge_map(struct dm_target *ti, struct bio *bio,
+			       union map_info *map_context);
+
+#define is_merge(ti)	((ti)->type->map == snapshot_merge_map)
+
 struct dm_snap_pending_exception {
 	struct dm_snap_exception e;
 
@@ -655,6 +660,12 @@ static int snapshot_ctr(struct dm_target *ti, unsigned int argc, char **argv)
 	argv += args_used;
 	argc -= args_used;
 
+	if (is_merge(ti) && !s->store->type->prepare_merge) {
+		ti->error = "Merging snapshot must support snapshot-merge";
+		r = -EINVAL;
+		goto bad_merge_store;
+	}
+
 	r = dm_get_device(ti, origin_path, 0, ti->len, FMODE_READ, &s->origin);
 	if (r) {
 		ti->error = "Cannot get origin device";
@@ -756,6 +767,7 @@ bad_hash_tables:
 	dm_put_device(ti, s->origin);
 
 bad_origin:
+bad_merge_store:
 	dm_exception_store_destroy(s->store);
 
 bad_store:
@@ -1198,6 +1210,40 @@ static int snapshot_map(struct dm_target *ti, struct bio *bio,
 	return r;
 }
 
+static int snapshot_merge_map(struct dm_target *ti, struct bio *bio,
+			       union map_info *map_context)
+{
+	struct dm_snap_exception *e;
+	struct dm_snapshot *s = ti->private;
+	int r = DM_MAPIO_REMAPPED;
+	chunk_t chunk;
+
+	chunk = sector_to_chunk(s->store, bio->bi_sector);
+
+	down_read(&s->lock);
+
+	/* Full snapshots are not usable */
+	if (!s->valid) {
+		r = -EIO;
+		goto out_unlock;
+	}
+
+	/* If the block is already remapped - use that */
+	e = lookup_exception(&s->complete, chunk);
+	if (e) {
+		remap_exception(s, e, bio, chunk);
+		goto out_unlock;
+	}
+
+	bio->bi_bdev = s->origin->bdev;
+
+ out_unlock:
+	up_read(&s->lock);
+
+	return r;
+}
+
+
 static int snapshot_end_io(struct dm_target *ti, struct bio *bio,
 			   int error, union map_info *map_context)
 {
@@ -1305,6 +1351,11 @@ static int __origin_write(struct list_head *snapshots,
 	/* Do all the snapshots on this origin */
 	list_for_each_entry (snap, snapshots, list) {
 
+		/* If the snapshot is merging, don't make new exceptions in it
+		   - but in this case no one should be writing to the origin */
+		if (is_merge(snap->ti))
+			continue;
+
 		down_write(&snap->lock);
 
 		/* Only deal with valid and active snapshots */
@@ -1531,7 +1582,7 @@ static struct target_type snapshot_merge_target = {
 	.module  = THIS_MODULE,
 	.ctr     = snapshot_ctr,
 	.dtr     = snapshot_dtr,
-	.map     = snapshot_map,
+	.map     = snapshot_merge_map,
 	.end_io  = snapshot_end_io,
 	.resume  = snapshot_resume,
 	.status  = snapshot_status,
-- 
1.6.2.5

^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [PATCH 05/12] dm-snapshot-no-more-merging-snapshots
  2009-10-05 19:02 [PATCH 00/12] snapshot-merge target Mike Snitzer
                   ` (3 preceding siblings ...)
  2009-10-05 19:02 ` [PATCH 04/12] dm-snapshot-target-merge-no-new-alloc Mike Snitzer
@ 2009-10-05 19:02 ` Mike Snitzer
  2009-10-05 19:02 ` [PATCH 06/12] dm-snapshot-merge-process Mike Snitzer
                   ` (6 subsequent siblings)
  11 siblings, 0 replies; 15+ messages in thread
From: Mike Snitzer @ 2009-10-05 19:02 UTC (permalink / raw)
  To: dm-devel; +Cc: Mikulas Patocka

From: Mikulas Patocka <mpatocka@redhat.com>

Do not allow more than one merging snapshot.

It is pointless to merge two snapshots simultaneously.
Allowing multiple merging snapshots would complicate things later.
Adds function to find merging snapshot for a given origin device.

Signed-off-by: Mikulas Patocka <mpatocka@redhat.com>
Reviewed-by: Mike Snitzer <snitzer@redhat.com>
---
 drivers/md/dm-snap.c |   23 +++++++++++++++++++++++
 1 files changed, 23 insertions(+), 0 deletions(-)

diff --git a/drivers/md/dm-snap.c b/drivers/md/dm-snap.c
index 31040f9..2cb0636 100644
--- a/drivers/md/dm-snap.c
+++ b/drivers/md/dm-snap.c
@@ -285,6 +285,22 @@ static void __insert_origin(struct origin *o)
 	list_add_tail(&o->hash_list, sl);
 }
 
+static struct dm_snapshot *__find_merging_snapshot(struct block_device *origin)
+{
+	struct origin *o;
+	struct dm_snapshot *snap;
+
+	o = __lookup_origin(origin);
+	if (!o)
+		return NULL;
+
+	list_for_each_entry(snap, &o->snapshots, list)
+		if (is_merge(snap->ti))
+			return snap;
+
+	return NULL;
+}
+
 /*
  * Make a note of the snapshot and its origin so we can look it
  * up when the origin has a write on it.
@@ -299,6 +315,13 @@ static int register_snapshot(struct dm_snapshot *snap)
 		return -ENOMEM;
 
 	down_write(&_origins_lock);
+
+	/* Do not allow more than one merging snapshot */
+	if (is_merge(snap->ti) && __find_merging_snapshot(bdev)) {
+		up_write(&_origins_lock);
+		return -EINVAL;
+	}
+
 	o = __lookup_origin(bdev);
 
 	if (o)
-- 
1.6.2.5

^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [PATCH 06/12] dm-snapshot-merge-process
  2009-10-05 19:02 [PATCH 00/12] snapshot-merge target Mike Snitzer
                   ` (4 preceding siblings ...)
  2009-10-05 19:02 ` [PATCH 05/12] dm-snapshot-no-more-merging-snapshots Mike Snitzer
@ 2009-10-05 19:02 ` Mike Snitzer
  2009-10-05 19:02 ` [PATCH 07/12] dm-snapshot-merge-interlock-writes Mike Snitzer
                   ` (5 subsequent siblings)
  11 siblings, 0 replies; 15+ messages in thread
From: Mike Snitzer @ 2009-10-05 19:02 UTC (permalink / raw)
  To: dm-devel; +Cc: Mikulas Patocka

From: Mikulas Patocka <mpatocka@redhat.com>

The snapshot merge operation.

We don't need a separate thread, kcopyd does the job just fine (provided
that we have private kcopyd).

Merging is started when origin is resumed and it is stopped when origin
is suspended or when the merging snapshot is destoyed.

Merging is not interlocked with writes, so there is a race condition
with concurrent access. It will be fixed in further patches.

Adds a supporting function to decrement consecutive chunk counter.  Care
is taken to increment the exception's old_chunk and new_chunk, prior to
the dm_consecutive_chunk_count_dec() call, if the chunk is at the start
of an exception's consecutive chunk range.  This allows for
snapshot-merge to support chunks that are added to the 'complete'
exception hash table before existing chunks.

Signed-off-by: Mikulas Patocka <mpatocka@redhat.com>
Signed-off-by: Mike Snitzer <snitzer@redhat.com>
---
 drivers/md/dm-exception-store.h |   11 +++
 drivers/md/dm-snap.c            |  153 ++++++++++++++++++++++++++++++++++++++-
 2 files changed, 163 insertions(+), 1 deletions(-)

diff --git a/drivers/md/dm-exception-store.h b/drivers/md/dm-exception-store.h
index b293595..ac568c4 100644
--- a/drivers/md/dm-exception-store.h
+++ b/drivers/md/dm-exception-store.h
@@ -153,6 +153,13 @@ static inline void dm_consecutive_chunk_count_inc(struct dm_snap_exception *e)
 	BUG_ON(!dm_consecutive_chunk_count(e));
 }
 
+static inline void dm_consecutive_chunk_count_dec(struct dm_snap_exception *e)
+{
+	BUG_ON(!dm_consecutive_chunk_count(e));
+
+	e->new_chunk -= (1ULL << DM_CHUNK_NUMBER_BITS);
+}
+
 #  else
 #    define DM_CHUNK_CONSECUTIVE_BITS 0
 
@@ -170,6 +177,10 @@ static inline void dm_consecutive_chunk_count_inc(struct dm_snap_exception *e)
 {
 }
 
+static inline void dm_consecutive_chunk_count_dec(struct dm_snap_exception *e)
+{
+}
+
 #  endif
 
 /*
diff --git a/drivers/md/dm-snap.c b/drivers/md/dm-snap.c
index 2cb0636..b3a2543 100644
--- a/drivers/md/dm-snap.c
+++ b/drivers/md/dm-snap.c
@@ -101,6 +101,13 @@ struct dm_snapshot {
 	mempool_t *tracked_chunk_pool;
 	spinlock_t tracked_chunk_lock;
 	struct hlist_head tracked_chunk_hash[DM_TRACKED_CHUNK_HASH_SIZE];
+
+	/* Merge operation is in progress */
+	int merge_running;
+
+	/* It is requested to shut down merging */
+	/* Cleared back to 0 when the merging is stopped */
+	int merge_shutdown;
 };
 
 struct dm_dev *dm_snap_get_cow(struct dm_snapshot *s)
@@ -633,6 +640,129 @@ static int init_hash_tables(struct dm_snapshot *s)
 	return 0;
 }
 
+static void merge_callback(int read_err, unsigned long write_err,
+			   void *context);
+
+static void snapshot_merge_process(struct dm_snapshot *s)
+{
+	int r;
+	chunk_t old_chunk, new_chunk;
+	struct dm_snap_exception *e;
+	struct dm_io_region src, dest;
+
+	BUG_ON(!s->merge_running);
+	if (s->merge_shutdown)
+		goto shut;
+
+	if (!s->valid) {
+		DMERR("snapshot is invalid, can't merge");
+		goto shut;
+	}
+
+	if (!s->store->type->prepare_merge ||
+	    !s->store->type->commit_merge) {
+		DMERR("target store does not support merging");
+		goto shut;
+	}
+	r = s->store->type->prepare_merge(s->store, &old_chunk, &new_chunk);
+	if (r <= 0) {
+		if (r < 0)
+			DMERR("Read error in exception store, "
+			      "shutting down merge");
+		goto shut;
+	}
+
+	/* TODO: use larger I/O size once we verify that kcopyd handles it */
+
+	/* !!! FIXME: intelock writes to this chunk */
+	down_write(&s->lock);
+	e = lookup_exception(&s->complete, old_chunk);
+	if (!e) {
+		DMERR("exception for block %llu is on disk but not in memory",
+		      (unsigned long long)old_chunk);
+		up_write(&s->lock);
+		goto shut;
+	}
+	if (dm_consecutive_chunk_count(e)) {
+		if (old_chunk == e->old_chunk) {
+			e->old_chunk++;
+			e->new_chunk++;
+		} else if (old_chunk != e->old_chunk +
+			   dm_consecutive_chunk_count(e)) {
+			DMERR("merge from the middle of a chunk range");
+			up_write(&s->lock);
+			goto shut;
+		}
+		dm_consecutive_chunk_count_dec(e);
+	} else {
+		remove_exception(e);
+		free_exception(e);
+	}
+	up_write(&s->lock);
+
+	dest.bdev = s->origin->bdev;
+	dest.sector = chunk_to_sector(s->store, old_chunk);
+	dest.count = min((sector_t)s->store->chunk_size,
+			 get_dev_size(dest.bdev) - dest.sector);
+
+	src.bdev = s->cow->bdev;
+	src.sector = chunk_to_sector(s->store, new_chunk);
+	src.count = dest.count;
+
+	dm_kcopyd_copy(s->kcopyd_client, &src, 1, &dest, 0, merge_callback, s);
+	return;
+
+shut:
+	s->merge_running = 0;
+}
+
+static void merge_callback(int read_err, unsigned long write_err, void *context)
+{
+	int r;
+	struct dm_snapshot *s = context;
+
+	if (read_err || write_err) {
+		if (read_err)
+			DMERR("Read error in data, shutting down merge");
+		else
+			DMERR("Write error in data, shutting down merge");
+		goto shut;
+	}
+
+	r = s->store->type->commit_merge(s->store, 1);
+	if (r < 0) {
+		DMERR("Write error in exception store, shutting down merge");
+		goto shut;
+	}
+
+	snapshot_merge_process(s);
+	return;
+
+shut:
+	s->merge_running = 0;
+}
+
+static void start_merge(struct dm_snapshot *merging)
+{
+	if (!merging->merge_running && !merging->merge_shutdown) {
+		merging->merge_running = 1;
+		snapshot_merge_process(merging);
+	}
+}
+
+/*
+ * Stop the merging process and wait until it finishes.
+ */
+
+static void stop_merge(struct dm_snapshot *merging)
+{
+	while (merging->merge_running) {
+		merging->merge_shutdown = 1;
+		msleep(1);
+	}
+	merging->merge_shutdown = 0;
+}
+
 /*
  * Construct a snapshot mapping: <origin_dev> <COW-dev> <p/n> <chunk-size>
  */
@@ -700,6 +830,8 @@ static int snapshot_ctr(struct dm_target *ti, unsigned int argc, char **argv)
 	s->active = 0;
 	atomic_set(&s->pending_exceptions_count, 0);
 	s->handover = 0;
+	s->merge_running = 0;
+	s->merge_shutdown = 0;
 	init_rwsem(&s->lock);
 	spin_lock_init(&s->pe_lock);
 
@@ -855,6 +987,9 @@ static void snapshot_dtr(struct dm_target *ti)
 	up_write(&s->lock);
 	up_write(&_origins_lock);
 
+	if (is_merge(ti))
+		stop_merge(s);
+
 	/* Prevent further origin writes from using this snapshot. */
 	/* After this returns there can be no new kcopyd jobs. */
 	unregister_snapshot(s);
@@ -1303,6 +1438,21 @@ static void snapshot_resume(struct dm_target *ti)
 	up_write(&_origins_lock);
 }
 
+static void snapshot_merge_resume(struct dm_target *ti)
+{
+	struct dm_snapshot *s = ti->private;
+
+	snapshot_resume(ti);
+	start_merge(s);
+}
+
+static void snapshot_merge_presuspend(struct dm_target *ti)
+{
+	struct dm_snapshot *s = ti->private;
+
+	stop_merge(s);
+}
+
 static int snapshot_status(struct dm_target *ti, status_type_t type,
 			   char *result, unsigned int maxlen)
 {
@@ -1607,7 +1757,8 @@ static struct target_type snapshot_merge_target = {
 	.dtr     = snapshot_dtr,
 	.map     = snapshot_merge_map,
 	.end_io  = snapshot_end_io,
-	.resume  = snapshot_resume,
+	.presuspend = snapshot_merge_presuspend,
+	.resume  = snapshot_merge_resume,
 	.status  = snapshot_status,
 	.iterate_devices = snapshot_iterate_devices,
 };
-- 
1.6.2.5

^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [PATCH 07/12] dm-snapshot-merge-interlock-writes
  2009-10-05 19:02 [PATCH 00/12] snapshot-merge target Mike Snitzer
                   ` (5 preceding siblings ...)
  2009-10-05 19:02 ` [PATCH 06/12] dm-snapshot-merge-process Mike Snitzer
@ 2009-10-05 19:02 ` Mike Snitzer
  2009-10-05 19:02 ` [PATCH 08/12] dm-snapshot-merge-track-writes Mike Snitzer
                   ` (4 subsequent siblings)
  11 siblings, 0 replies; 15+ messages in thread
From: Mike Snitzer @ 2009-10-05 19:02 UTC (permalink / raw)
  To: dm-devel; +Cc: Mikulas Patocka

From: Mikulas Patocka <mpatocka@redhat.com>

New variables merge_write_interlock and merge_write_interlock_n determine
the chunk number (on the origin device) and number of chunks that are being
merged. Writes to this area are held on the queue merge_write_list.

Signed-off-by: Mikulas Patocka <mpatocka@redhat.com>
Signed-off-by: Mike Snitzer <snitzer@redhat.com>
---
 drivers/md/dm-snap.c |  119 +++++++++++++++++++++++++++++++++++++-------------
 1 files changed, 88 insertions(+), 31 deletions(-)

diff --git a/drivers/md/dm-snap.c b/drivers/md/dm-snap.c
index b3a2543..91c80b7 100644
--- a/drivers/md/dm-snap.c
+++ b/drivers/md/dm-snap.c
@@ -108,6 +108,16 @@ struct dm_snapshot {
 	/* It is requested to shut down merging */
 	/* Cleared back to 0 when the merging is stopped */
 	int merge_shutdown;
+
+	/* Merging this area --- block any writes */
+	chunk_t merge_write_interlock;
+	int merge_write_interlock_n;
+
+	/*
+	 * A list of requests that were delayed because
+	 * of racing with merge
+	 */
+	struct bio_list merge_write_list;
 };
 
 struct dm_dev *dm_snap_get_cow(struct dm_snapshot *s)
@@ -640,6 +650,9 @@ static int init_hash_tables(struct dm_snapshot *s)
 	return 0;
 }
 
+static void flush_bios(struct bio *bio);
+static void error_bios(struct bio *bio);
+
 static void merge_callback(int read_err, unsigned long write_err,
 			   void *context);
 
@@ -647,7 +660,6 @@ static void snapshot_merge_process(struct dm_snapshot *s)
 {
 	int r;
 	chunk_t old_chunk, new_chunk;
-	struct dm_snap_exception *e;
 	struct dm_io_region src, dest;
 
 	BUG_ON(!s->merge_running);
@@ -674,32 +686,6 @@ static void snapshot_merge_process(struct dm_snapshot *s)
 
 	/* TODO: use larger I/O size once we verify that kcopyd handles it */
 
-	/* !!! FIXME: intelock writes to this chunk */
-	down_write(&s->lock);
-	e = lookup_exception(&s->complete, old_chunk);
-	if (!e) {
-		DMERR("exception for block %llu is on disk but not in memory",
-		      (unsigned long long)old_chunk);
-		up_write(&s->lock);
-		goto shut;
-	}
-	if (dm_consecutive_chunk_count(e)) {
-		if (old_chunk == e->old_chunk) {
-			e->old_chunk++;
-			e->new_chunk++;
-		} else if (old_chunk != e->old_chunk +
-			   dm_consecutive_chunk_count(e)) {
-			DMERR("merge from the middle of a chunk range");
-			up_write(&s->lock);
-			goto shut;
-		}
-		dm_consecutive_chunk_count_dec(e);
-	} else {
-		remove_exception(e);
-		free_exception(e);
-	}
-	up_write(&s->lock);
-
 	dest.bdev = s->origin->bdev;
 	dest.sector = chunk_to_sector(s->store, old_chunk);
 	dest.count = min((sector_t)s->store->chunk_size,
@@ -709,6 +695,13 @@ static void snapshot_merge_process(struct dm_snapshot *s)
 	src.sector = chunk_to_sector(s->store, new_chunk);
 	src.count = dest.count;
 
+	down_write(&s->lock);
+	s->merge_write_interlock = old_chunk;
+	s->merge_write_interlock_n = 1;
+	up_write(&s->lock);
+
+	/* !!! FIXME: wait until writes to this chunk drain */
+
 	dm_kcopyd_copy(s->kcopyd_client, &src, 1, &dest, 0, merge_callback, s);
 	return;
 
@@ -716,10 +709,25 @@ shut:
 	s->merge_running = 0;
 }
 
+/* This function drops s->lock */
+static inline void release_write_interlock(struct dm_snapshot *s, int err)
+{
+	struct bio *b;
+	s->merge_write_interlock = 0;
+	s->merge_write_interlock_n = 0;
+	b = bio_list_get(&s->merge_write_list);
+	up_write(&s->lock);
+	if (!err)
+		flush_bios(b);
+	else
+		error_bios(b);
+}
+
 static void merge_callback(int read_err, unsigned long write_err, void *context)
 {
-	int r;
+	int r, i;
 	struct dm_snapshot *s = context;
+	struct dm_snap_exception *e;
 
 	if (read_err || write_err) {
 		if (read_err)
@@ -729,16 +737,51 @@ static void merge_callback(int read_err, unsigned long write_err, void *context)
 		goto shut;
 	}
 
-	r = s->store->type->commit_merge(s->store, 1);
+	r = s->store->type->commit_merge(s->store, s->merge_write_interlock_n);
 	if (r < 0) {
 		DMERR("Write error in exception store, shutting down merge");
 		goto shut;
 	}
 
+	down_write(&s->lock);
+	/*
+	 * Must process chunks (and associated exceptions) in reverse
+	 * so that dm_consecutive_chunk_count_dec() accounting works
+	 */
+	for (i = s->merge_write_interlock_n - 1; i >= 0; i--) {
+		chunk_t old_chunk = s->merge_write_interlock + i;
+		e = lookup_exception(&s->complete, old_chunk);
+		if (!e) {
+			DMERR("exception for block %llu is on "
+			      "disk but not in memory",
+			      (unsigned long long)old_chunk);
+			up_write(&s->lock);
+			goto shut;
+		}
+		if (dm_consecutive_chunk_count(e)) {
+			if (old_chunk == e->old_chunk) {
+				e->old_chunk++;
+				e->new_chunk++;
+			} else if (old_chunk != e->old_chunk +
+				   dm_consecutive_chunk_count(e)) {
+				DMERR("merge from the middle of a chunk range");
+				up_write(&s->lock);
+				goto shut;
+			}
+			dm_consecutive_chunk_count_dec(e);
+		} else {
+			remove_exception(e);
+			free_exception(e);
+		}
+	}
+	release_write_interlock(s, 0);
+
 	snapshot_merge_process(s);
 	return;
 
 shut:
+	down_write(&s->lock);
+	release_write_interlock(s, 1);
 	s->merge_running = 0;
 }
 
@@ -834,6 +877,9 @@ static int snapshot_ctr(struct dm_target *ti, unsigned int argc, char **argv)
 	s->merge_shutdown = 0;
 	init_rwsem(&s->lock);
 	spin_lock_init(&s->pe_lock);
+	s->merge_write_interlock = 0;
+	s->merge_write_interlock_n = 0;
+	bio_list_init(&s->merge_write_list);
 
 	/* Allocate hash table for COW data */
 	if (init_hash_tables(s)) {
@@ -1378,7 +1424,7 @@ static int snapshot_merge_map(struct dm_target *ti, struct bio *bio,
 
 	chunk = sector_to_chunk(s->store, bio->bi_sector);
 
-	down_read(&s->lock);
+	down_write(&s->lock);
 
 	/* Full snapshots are not usable */
 	if (!s->valid) {
@@ -1389,6 +1435,17 @@ static int snapshot_merge_map(struct dm_target *ti, struct bio *bio,
 	/* If the block is already remapped - use that */
 	e = lookup_exception(&s->complete, chunk);
 	if (e) {
+		/* We are copying this area --- so don't write to it */
+		if (bio_rw(bio) == WRITE &&
+		    chunk >= s->merge_write_interlock &&
+		    chunk < (s->merge_write_interlock +
+			     s->merge_write_interlock_n)) {
+			bio->bi_bdev = s->origin->bdev;
+			bio_list_add(&s->merge_write_list, bio);
+
+			r = DM_MAPIO_SUBMITTED;
+			goto out_unlock;
+		}
 		remap_exception(s, e, bio, chunk);
 		goto out_unlock;
 	}
@@ -1396,7 +1453,7 @@ static int snapshot_merge_map(struct dm_target *ti, struct bio *bio,
 	bio->bi_bdev = s->origin->bdev;
 
  out_unlock:
-	up_read(&s->lock);
+	up_write(&s->lock);
 
 	return r;
 }
-- 
1.6.2.5

^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [PATCH 08/12] dm-snapshot-merge-track-writes
  2009-10-05 19:02 [PATCH 00/12] snapshot-merge target Mike Snitzer
                   ` (6 preceding siblings ...)
  2009-10-05 19:02 ` [PATCH 07/12] dm-snapshot-merge-interlock-writes Mike Snitzer
@ 2009-10-05 19:02 ` Mike Snitzer
  2009-10-05 19:02 ` [PATCH 09/12] dm-snapshot-merge-make-exceptions-in-other-snapshots-when-merging Mike Snitzer
                   ` (3 subsequent siblings)
  11 siblings, 0 replies; 15+ messages in thread
From: Mike Snitzer @ 2009-10-05 19:02 UTC (permalink / raw)
  To: dm-devel; +Cc: Mikulas Patocka

From: Mikulas Patocka <mpatocka@redhat.com>

Track in-progress writes on the merging snapshot device and delay merging
the chunk until all writes to that chunk finish.

Signed-off-by: Mikulas Patocka <mpatocka@redhat.com>
Reviewed-by: Mike Snitzer <snitzer@redhat.com>
---
 drivers/md/dm-snap.c |    7 ++++++-
 1 files changed, 6 insertions(+), 1 deletions(-)

diff --git a/drivers/md/dm-snap.c b/drivers/md/dm-snap.c
index 91c80b7..6a55d75 100644
--- a/drivers/md/dm-snap.c
+++ b/drivers/md/dm-snap.c
@@ -700,7 +700,8 @@ static void snapshot_merge_process(struct dm_snapshot *s)
 	s->merge_write_interlock_n = 1;
 	up_write(&s->lock);
 
-	/* !!! FIXME: wait until writes to this chunk drain */
+	while (__chunk_is_tracked(s, old_chunk))
+		msleep(1);
 
 	dm_kcopyd_copy(s->kcopyd_client, &src, 1, &dest, 0, merge_callback, s);
 	return;
@@ -1446,7 +1447,11 @@ static int snapshot_merge_map(struct dm_target *ti, struct bio *bio,
 			r = DM_MAPIO_SUBMITTED;
 			goto out_unlock;
 		}
+
 		remap_exception(s, e, bio, chunk);
+
+		if (bio_rw(bio) == WRITE)
+			map_context->ptr = track_chunk(s, chunk);
 		goto out_unlock;
 	}
 
-- 
1.6.2.5

^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [PATCH 09/12] dm-snapshot-merge-make-exceptions-in-other-snapshots-when-merging
  2009-10-05 19:02 [PATCH 00/12] snapshot-merge target Mike Snitzer
                   ` (7 preceding siblings ...)
  2009-10-05 19:02 ` [PATCH 08/12] dm-snapshot-merge-track-writes Mike Snitzer
@ 2009-10-05 19:02 ` Mike Snitzer
  2009-10-05 19:02 ` [PATCH 10/12] dm-snapshot-merge-make-exceptions-in-other-snapshots-if-remapped-to-origin Mike Snitzer
                   ` (2 subsequent siblings)
  11 siblings, 0 replies; 15+ messages in thread
From: Mike Snitzer @ 2009-10-05 19:02 UTC (permalink / raw)
  To: dm-devel; +Cc: Mikulas Patocka

From: Mikulas Patocka <mpatocka@redhat.com>

When there is one merging snapshot and other non-merging snapshots, make
exceptions in non-merging snapshots while merging.

This is not yet complete (the exception is not made when remapping directly to
the origin), another patch fixes that.

Signed-off-by: Mikulas Patocka <mpatocka@redhat.com>
Reviewed-by: Mike Snitzer <snitzer@redhat.com>
---
 drivers/md/dm-snap.c |   31 +++++++++++++++++++++++++++++++
 1 files changed, 31 insertions(+), 0 deletions(-)

diff --git a/drivers/md/dm-snap.c b/drivers/md/dm-snap.c
index 6a55d75..3be57ba 100644
--- a/drivers/md/dm-snap.c
+++ b/drivers/md/dm-snap.c
@@ -255,6 +255,8 @@ struct origin {
 static struct list_head *_origins;
 static struct rw_semaphore _origins_lock;
 
+static DECLARE_WAIT_QUEUE_HEAD(_pending_exception_done);
+
 static int init_origin_hash(void)
 {
 	int i;
@@ -653,6 +655,9 @@ static int init_hash_tables(struct dm_snapshot *s)
 static void flush_bios(struct bio *bio);
 static void error_bios(struct bio *bio);
 
+static int __origin_write(struct list_head *snapshots,
+			  sector_t sector, struct bio *bio);
+
 static void merge_callback(int read_err, unsigned long write_err,
 			   void *context);
 
@@ -660,6 +665,9 @@ static void snapshot_merge_process(struct dm_snapshot *s)
 {
 	int r;
 	chunk_t old_chunk, new_chunk;
+	struct origin *o;
+	chunk_t min_chunksize;
+	int must_wait;
 	struct dm_io_region src, dest;
 
 	BUG_ON(!s->merge_running);
@@ -695,6 +703,27 @@ static void snapshot_merge_process(struct dm_snapshot *s)
 	src.sector = chunk_to_sector(s->store, new_chunk);
 	src.count = dest.count;
 
+test_again:
+	/* Reallocate other snapshots */
+	down_read(&_origins_lock);
+	o = __lookup_origin(s->origin->bdev);
+	must_wait = 0;
+	min_chunksize = __minimum_chunk_size(o);
+	if (min_chunksize) {
+		chunk_t n;
+		for (n = 0; n < s->store->chunk_size; n += min_chunksize) {
+			r = __origin_write(&o->snapshots, dest.sector + n,
+					   NULL);
+			if (r == DM_MAPIO_SUBMITTED)
+				must_wait = 1;
+		}
+	}
+	up_read(&_origins_lock);
+	if (must_wait) {
+		sleep_on_timeout(&_pending_exception_done, HZ / 100 + 1);
+		goto test_again;
+	}
+
 	down_write(&s->lock);
 	s->merge_write_interlock = old_chunk;
 	s->merge_write_interlock_n = 1;
@@ -1204,6 +1233,8 @@ static void pending_complete(struct dm_snap_pending_exception *pe, int success)
 	origin_bios = bio_list_get(&pe->origin_bios);
 	free_pending_exception(pe);
 
+	wake_up_all(&_pending_exception_done);
+
 	up_write(&s->lock);
 
 	/* Submit any pending write bios */
-- 
1.6.2.5

^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [PATCH 10/12] dm-snapshot-merge-make-exceptions-in-other-snapshots-if-remapped-to-origin
  2009-10-05 19:02 [PATCH 00/12] snapshot-merge target Mike Snitzer
                   ` (8 preceding siblings ...)
  2009-10-05 19:02 ` [PATCH 09/12] dm-snapshot-merge-make-exceptions-in-other-snapshots-when-merging Mike Snitzer
@ 2009-10-05 19:02 ` Mike Snitzer
  2009-10-05 19:02 ` [PATCH 11/12] dm-snapshot-merge-redirect-to-origin-if-invalidated Mike Snitzer
  2009-10-05 19:02 ` [PATCH 12/12] dm-snapshot-merge-use-larger-io-when-merging Mike Snitzer
  11 siblings, 0 replies; 15+ messages in thread
From: Mike Snitzer @ 2009-10-05 19:02 UTC (permalink / raw)
  To: dm-devel; +Cc: Mikulas Patocka

From: Mikulas Patocka <mpatocka@redhat.com>

If dispatching write request to merging snapshot device directly to the origin
(because the chunk is not remapped or was already merged), we must make
exceptions in other snapshots.

Signed-off-by: Mikulas Patocka <mpatocka@redhat.com>
Reviewed-by: Mike Snitzer <snitzer@redhat.com>
---
 drivers/md/dm-snap.c |    5 +++++
 1 files changed, 5 insertions(+), 0 deletions(-)

diff --git a/drivers/md/dm-snap.c b/drivers/md/dm-snap.c
index 3be57ba..8060fe3 100644
--- a/drivers/md/dm-snap.c
+++ b/drivers/md/dm-snap.c
@@ -1488,6 +1488,11 @@ static int snapshot_merge_map(struct dm_target *ti, struct bio *bio,
 
 	bio->bi_bdev = s->origin->bdev;
 
+	if (bio_rw(bio) == WRITE) {
+		up_write(&s->lock);
+		return do_origin(s->origin, bio);
+	}
+
  out_unlock:
 	up_write(&s->lock);
 
-- 
1.6.2.5

^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [PATCH 11/12] dm-snapshot-merge-redirect-to-origin-if-invalidated
  2009-10-05 19:02 [PATCH 00/12] snapshot-merge target Mike Snitzer
                   ` (9 preceding siblings ...)
  2009-10-05 19:02 ` [PATCH 10/12] dm-snapshot-merge-make-exceptions-in-other-snapshots-if-remapped-to-origin Mike Snitzer
@ 2009-10-05 19:02 ` Mike Snitzer
  2009-10-05 19:02 ` [PATCH 12/12] dm-snapshot-merge-use-larger-io-when-merging Mike Snitzer
  11 siblings, 0 replies; 15+ messages in thread
From: Mike Snitzer @ 2009-10-05 19:02 UTC (permalink / raw)
  To: dm-devel; +Cc: Mikulas Patocka

From: Mikulas Patocka <mpatocka@redhat.com>

If we are merging invalidated snapshot, redirect all accesses to the origin
device.

Userspace code will remove the invalidated snapshot anyway after a few seconds,
but during this period, the origin device must be active (it may for example
contain root filesystem and having unbootable computer is not desirable).

Signed-off-by: Mikulas Patocka <mpatocka@redhat.com>
Reviewed-by: Mike Snitzer <snitzer@redhat.com>
---
 drivers/md/dm-snap.c |    9 ++++-----
 1 files changed, 4 insertions(+), 5 deletions(-)

diff --git a/drivers/md/dm-snap.c b/drivers/md/dm-snap.c
index 8060fe3..e0d0c30 100644
--- a/drivers/md/dm-snap.c
+++ b/drivers/md/dm-snap.c
@@ -1458,11 +1458,9 @@ static int snapshot_merge_map(struct dm_target *ti, struct bio *bio,
 
 	down_write(&s->lock);
 
-	/* Full snapshots are not usable */
-	if (!s->valid) {
-		r = -EIO;
-		goto out_unlock;
-	}
+	/* Full merging snapshots are redirected to the origin */
+	if (!s->valid)
+		goto redirect_to_origin;
 
 	/* If the block is already remapped - use that */
 	e = lookup_exception(&s->complete, chunk);
@@ -1486,6 +1484,7 @@ static int snapshot_merge_map(struct dm_target *ti, struct bio *bio,
 		goto out_unlock;
 	}
 
+ redirect_to_origin:
 	bio->bi_bdev = s->origin->bdev;
 
 	if (bio_rw(bio) == WRITE) {
-- 
1.6.2.5

^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [PATCH 12/12] dm-snapshot-merge-use-larger-io-when-merging
  2009-10-05 19:02 [PATCH 00/12] snapshot-merge target Mike Snitzer
                   ` (10 preceding siblings ...)
  2009-10-05 19:02 ` [PATCH 11/12] dm-snapshot-merge-redirect-to-origin-if-invalidated Mike Snitzer
@ 2009-10-05 19:02 ` Mike Snitzer
  2009-10-05 19:12   ` Mike Snitzer
  11 siblings, 1 reply; 15+ messages in thread
From: Mike Snitzer @ 2009-10-05 19:02 UTC (permalink / raw)
  To: dm-devel; +Cc: Mikulas Patocka

Merge a linear region of chunks using one large IO

s->store->type->prepare_merge returns the number of chunks that can be
linearly copied starting from the returned chunk number backward. (but
the caller is allowed to copy less, and the caller puts the number of
copied chunks to s->store->type->commit_merge)

I.e. if returned chunk numbers are old_chunk == 10 and new_chunk == 20
and returned value is 3, then chunk 20 can be copied to 10, chunk 19 to
9 and 18 to 8.

There is a variable, s->merge_write_interlock_n, that is now always one,
but can hold larger number --- the number of chunks that are being
copied.

Also, snapshot_merge_process() should delay the merging of _all_ chunks
that have in-progress writes; not just the first chunk in the region
that is to be merged


Here are performance results from some mkfs-based testing:

# lvcreate -n testlv -L 32G test
# lvcreate -n testlv_snap -s -L 7G test/testlv

# time mkfs.ext3 /dev/test/testlv
...
real    1m7.827s
user    0m0.116s
sys     0m11.017s

# lvs
 LV          VG   Attr   LSize  Origin Snap%  Move Log Copy%  Convert
 testlv      test owi-a- 32.00G
 testlv_snap test swi-a-  7.00G testlv   9.05

before:
-------
# time lvconvert -M test/testlv_snap
 Merging of volume testlv_snap started.
 ...
 Merge into logical volume testlv finished.
 Logical volume "snapshot1" successfully removed

real    22m33.100s
user    0m0.045s
sys     0m0.711s


after:
------
# time lvconvert -M test/testlv_snap
 Merging of volume testlv_snap started.
 testlv: Merged: 6.4%
 testlv: Merged: 3.5%
 testlv: Merged: 0.9%
 testlv: Merged: 0.0%
 Merge into logical volume testlv finished.
 Logical volume "snapshot1" successfully removed

real    1m0.881s
user    0m0.015s
sys     0m0.560s


So we're now seeing _very_ respectible snapshot-merge performance.

Signed-off-by: Mike Snitzer <snitzer@redhat.com>
Cc: Mikulas Patocka <mpatocka@redhat.com>
---
 drivers/md/dm-snap.c |   35 +++++++++++++++++++++++------------
 1 files changed, 23 insertions(+), 12 deletions(-)

diff --git a/drivers/md/dm-snap.c b/drivers/md/dm-snap.c
index e0d0c30..b271f56 100644
--- a/drivers/md/dm-snap.c
+++ b/drivers/md/dm-snap.c
@@ -663,12 +663,13 @@ static void merge_callback(int read_err, unsigned long write_err,
 
 static void snapshot_merge_process(struct dm_snapshot *s)
 {
-	int r;
+	int r, i, linear_chunks;
 	chunk_t old_chunk, new_chunk;
 	struct origin *o;
 	chunk_t min_chunksize;
 	int must_wait;
 	struct dm_io_region src, dest;
+	sector_t io_size;
 
 	BUG_ON(!s->merge_running);
 	if (s->merge_shutdown)
@@ -684,34 +685,41 @@ static void snapshot_merge_process(struct dm_snapshot *s)
 		DMERR("target store does not support merging");
 		goto shut;
 	}
-	r = s->store->type->prepare_merge(s->store, &old_chunk, &new_chunk);
-	if (r <= 0) {
-		if (r < 0)
+	linear_chunks = s->store->type->prepare_merge(s->store,
+						      &old_chunk, &new_chunk);
+	if (linear_chunks <= 0) {
+		if (linear_chunks < 0)
 			DMERR("Read error in exception store, "
 			      "shutting down merge");
 		goto shut;
 	}
+	/* Adjust old_chunk and new_chunk to reflect start of linear region */
+	old_chunk = old_chunk + 1 - linear_chunks;
+	new_chunk = new_chunk + 1 - linear_chunks;
 
-	/* TODO: use larger I/O size once we verify that kcopyd handles it */
+	/*
+	 * Use one (potentially large) I/O to copy all 'linear_chunks'
+	 * from the exception store to the origin
+	 */
+	io_size = linear_chunks * s->store->chunk_size;
 
 	dest.bdev = s->origin->bdev;
 	dest.sector = chunk_to_sector(s->store, old_chunk);
-	dest.count = min((sector_t)s->store->chunk_size,
-			 get_dev_size(dest.bdev) - dest.sector);
+	dest.count = min(io_size, get_dev_size(dest.bdev) - dest.sector);
 
 	src.bdev = s->cow->bdev;
 	src.sector = chunk_to_sector(s->store, new_chunk);
 	src.count = dest.count;
 
 test_again:
-	/* Reallocate other snapshots */
+	/* Reallocate other snapshots; must account for all 'linear_chunks' */
 	down_read(&_origins_lock);
 	o = __lookup_origin(s->origin->bdev);
 	must_wait = 0;
 	min_chunksize = __minimum_chunk_size(o);
 	if (min_chunksize) {
 		chunk_t n;
-		for (n = 0; n < s->store->chunk_size; n += min_chunksize) {
+		for (n = 0; n < io_size; n += min_chunksize) {
 			r = __origin_write(&o->snapshots, dest.sector + n,
 					   NULL);
 			if (r == DM_MAPIO_SUBMITTED)
@@ -726,11 +734,14 @@ test_again:
 
 	down_write(&s->lock);
 	s->merge_write_interlock = old_chunk;
-	s->merge_write_interlock_n = 1;
+	s->merge_write_interlock_n = linear_chunks;
 	up_write(&s->lock);
 
-	while (__chunk_is_tracked(s, old_chunk))
-		msleep(1);
+	/* Wait until writes to all 'linear_chunks' drain */
+	for (i = 0; i < linear_chunks; i++) {
+		while (__chunk_is_tracked(s, old_chunk + i))
+			msleep(1);
+	}
 
 	dm_kcopyd_copy(s->kcopyd_client, &src, 1, &dest, 0, merge_callback, s);
 	return;
-- 
1.6.2.5

^ permalink raw reply related	[flat|nested] 15+ messages in thread

* Re: [PATCH 12/12] dm-snapshot-merge-use-larger-io-when-merging
  2009-10-05 19:02 ` [PATCH 12/12] dm-snapshot-merge-use-larger-io-when-merging Mike Snitzer
@ 2009-10-05 19:12   ` Mike Snitzer
  0 siblings, 0 replies; 15+ messages in thread
From: Mike Snitzer @ 2009-10-05 19:12 UTC (permalink / raw)
  To: dm-devel

On Mon, Oct 05 2009 at  3:02pm -0400,
Mike Snitzer <snitzer@redhat.com> wrote:

> Here are performance results from some mkfs-based testing:
...
> before:
> -------
> # time lvconvert -M test/testlv_snap
...

> after:
> ------
> # time lvconvert -M test/testlv_snap
...

FYI, the above lvconvert command-line references are stale.  The only
way to start a snapshot-merge is with: lvconvert --merge $SNAP_LV

Mike

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [PATCH 02/12] dm-exception-store-merge-accounting
  2009-10-05 19:02 ` [PATCH 02/12] dm-exception-store-merge-accounting Mike Snitzer
@ 2009-10-05 19:53   ` Mike Snitzer
  0 siblings, 0 replies; 15+ messages in thread
From: Mike Snitzer @ 2009-10-05 19:53 UTC (permalink / raw)
  To: dm-devel; +Cc: Mikulas Patocka

On Mon, Oct 05 2009 at  3:02pm -0400,
Mike Snitzer <snitzer@redhat.com> wrote:

> From: Mikulas Patocka <mpatocka@redhat.com>
> 
> Conditionally adjust snapshot usage accounting if snapshot-merge is in
> progress.  Care is taken to preserve the established kernel<->userspace
> interface.
> 
> Signed-off-by: Mike Snitzer <snitzer@redhat.com>
> Cc: Mikulas Patocka <mpatocka@redhat.com>

I messed up on this patch header (the "From:" above).  Mikulas didn't
write this version (which maintains kernel<->userspace compatibility);
but the overall structure is directly from Mikulas' earlier version of
the patch.

Mike

^ permalink raw reply	[flat|nested] 15+ messages in thread

end of thread, other threads:[~2009-10-05 19:53 UTC | newest]

Thread overview: 15+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2009-10-05 19:02 [PATCH 00/12] snapshot-merge target Mike Snitzer
2009-10-05 19:02 ` [PATCH 01/12] dm-exception-store-merge-methods Mike Snitzer
2009-10-05 19:02 ` [PATCH 02/12] dm-exception-store-merge-accounting Mike Snitzer
2009-10-05 19:53   ` Mike Snitzer
2009-10-05 19:02 ` [PATCH 03/12] dm-snapshot-target-merge Mike Snitzer
2009-10-05 19:02 ` [PATCH 04/12] dm-snapshot-target-merge-no-new-alloc Mike Snitzer
2009-10-05 19:02 ` [PATCH 05/12] dm-snapshot-no-more-merging-snapshots Mike Snitzer
2009-10-05 19:02 ` [PATCH 06/12] dm-snapshot-merge-process Mike Snitzer
2009-10-05 19:02 ` [PATCH 07/12] dm-snapshot-merge-interlock-writes Mike Snitzer
2009-10-05 19:02 ` [PATCH 08/12] dm-snapshot-merge-track-writes Mike Snitzer
2009-10-05 19:02 ` [PATCH 09/12] dm-snapshot-merge-make-exceptions-in-other-snapshots-when-merging Mike Snitzer
2009-10-05 19:02 ` [PATCH 10/12] dm-snapshot-merge-make-exceptions-in-other-snapshots-if-remapped-to-origin Mike Snitzer
2009-10-05 19:02 ` [PATCH 11/12] dm-snapshot-merge-redirect-to-origin-if-invalidated Mike Snitzer
2009-10-05 19:02 ` [PATCH 12/12] dm-snapshot-merge-use-larger-io-when-merging Mike Snitzer
2009-10-05 19:12   ` Mike Snitzer

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.