diff mbox

Re: dm snapshot: allow live exception store handover between tables

Message ID 20091106184642.GC13427@redhat.com (mailing list archive)
State Superseded, archived
Headers show

Commit Message

Mike Snitzer Nov. 6, 2009, 6:46 p.m. UTC
None
diff mbox

Patch

Index: linux-2.6/drivers/md/dm-snap.c
===================================================================
--- linux-2.6.orig/drivers/md/dm-snap.c
+++ linux-2.6/drivers/md/dm-snap.c
@@ -644,7 +644,7 @@  static int init_hash_tables(struct dm_sn
 }
 
 /*
- * Reserve snap_source for handover to snap_dest.
+ * Reserve snap_src for handover to snap_dest.
  */
 static int link_snapshots_for_handover(struct dm_snapshot *snap_src,
 				       struct dm_snapshot *snap_dest)
@@ -670,6 +670,33 @@  out:
 }
 
 /*
+ * Unreserve snap_src for handover to snap_dest.
+ */
+static int unlink_snapshots_for_handover(struct dm_snapshot *snap_src,
+					 struct dm_snapshot *snap_dest)
+{
+	int r = -EINVAL;
+
+	down_write(&snap_src->lock);
+
+	/* make sure these snapshots are already linked */
+	if ((snap_src->handover_snap != snap_dest) ||
+	    (snap_dest->handover_snap != snap_src))
+		goto out;
+
+	snap_src->handover_snap = NULL;
+
+	snap_dest->handover_snap = NULL;
+	snap_dest->is_handover_destination = 0;
+
+	r = 0;
+
+out:
+	up_write(&snap_src->lock);
+	return r;
+}
+
+/*
  * Construct a snapshot mapping: <origin_dev> <COW-dev> <p/n> <chunk-size>
  */
 static int snapshot_ctr(struct dm_target *ti, unsigned int argc, char **argv)
@@ -778,6 +805,16 @@  static int snapshot_ctr(struct dm_target
 		}
 	}
 
+	bio_list_init(&s->queued_bios);
+	INIT_WORK(&s->queued_bios_work, flush_queued_bios);
+
+	ti->private = s;
+	ti->num_flush_requests = 1;
+
+	if (handover_snap)
+		/* register_snapshot is deferred until after handover_exceptions */
+		return 0;
+
 	/* Metadata must only be loaded into one table at once */
 	r = s->store->type->read_metadata(s->store, dm_add_exception,
 					  (void *)s);
@@ -789,13 +826,11 @@  static int snapshot_ctr(struct dm_target
 		DMWARN("Snapshot is marked invalid.");
 	}
 
-	bio_list_init(&s->queued_bios);
-	INIT_WORK(&s->queued_bios_work, flush_queued_bios);
-
 	if (!s->store->chunk_size) {
 		ti->error = "Chunk size not set";
 		goto bad_load_and_register;
 	}
+	ti->split_io = s->store->chunk_size;
 
 	/* Add snapshot to the list of snapshots for this origin */
 	/* Exceptions aren't triggered till snapshot_resume() is called */
@@ -805,10 +840,6 @@  static int snapshot_ctr(struct dm_target
 		goto bad_load_and_register;
 	}
 
-	ti->private = s;
-	ti->split_io = s->store->chunk_size;
-	ti->num_flush_requests = 1;
-
 	return 0;
 
 bad_load_and_register:
@@ -863,6 +894,15 @@  static void handover_exceptions(struct d
 	       (new->is_handover_destination != 1));
 	BUG_ON(!old->suspended);
 
+	/* make sure old snapshot is still valid */
+	if (!old->valid) {
+		new->valid = 0;
+		DMERR("Unable to handover exceptions "
+		      "from an invalid snapshot.");
+		return;
+	}
+
+	/* swap exceptions tables and stores */
 	u.table_swap = new->complete;
 	new->complete = old->complete;
 	old->complete = u.table_swap;
@@ -873,6 +913,10 @@  static void handover_exceptions(struct d
 	new->store->snap = new;
 	old->store->snap = old;
 
+	/* reset split_io to store's chunk_size */
+	if (new->ti->split_io != new->store->chunk_size)
+		new->ti->split_io = new->store->chunk_size;
+
 	/* Mark old snapshot invalid and inactive */
 	old->valid = 0;
 	old->active = 0;
@@ -890,23 +934,36 @@  static void snapshot_dtr(struct dm_targe
 	int i;
 #endif
 	struct dm_snapshot *s = ti->private;
-	struct dm_snapshot *new_snap;
+	struct dm_snapshot *new_snap = NULL;
 
 	flush_workqueue(ksnapd);
 
 	/* This snapshot may need to handover its exception store */
 	down_write(&s->lock);
 	if (s->handover_snap) {
-		new_snap = s->handover_snap;
+		if (!s->is_handover_destination) {
+			/* Handover exceptions to another snapshot */
+			new_snap = s->handover_snap;
 
-		down_write_nested(&new_snap->lock, SINGLE_DEPTH_NESTING);
-		handover_exceptions(s, new_snap);
-		up_write(&new_snap->lock);
+			down_write_nested(&new_snap->lock,
+					  SINGLE_DEPTH_NESTING);
+			handover_exceptions(s, new_snap);
+			up_write(&new_snap->lock);
+		} else {
+			/* allow table_clear to cancel handover */
+			unlink_snapshots_for_handover(s->handover_snap, s);
+		}
 	}
 	/* An incomplete exception handover is not allowed */
 	BUG_ON(s->handover_snap);
 	up_write(&s->lock);
 
+	if (new_snap && register_snapshot(new_snap)) {
+		DMERR("Unable to register snapshot "
+		      "after exception handover.");
+		new_snap->valid = 0;
+	}
+
 	/* Prevent further origin writes from using this snapshot. */
 	/* After this returns there can be no new kcopyd jobs. */
 	unregister_snapshot(s);
@@ -1322,33 +1379,37 @@  static void snapshot_presuspend(struct d
 static void snapshot_resume(struct dm_target *ti)
 {
 	struct dm_snapshot *s = ti->private;
-	struct dm_snapshot *old_snap, *new_snap, *lock_snap;
+	struct dm_snapshot *old_snap, *new_snap = NULL;
 
 	down_write(&s->lock);
 	if (s->handover_snap) {
-		/*
-		 * Initially assumes this snapshot will get
-		 * exception store from another snapshot
-		 */
+		if (!s->is_handover_destination) {
+			DMERR("Unable to handover exceptions to "
+			      "another snapshot on resume.");
+			goto out;
+		}
+		/* Get exception store from another snapshot */
 		old_snap = s->handover_snap;
 		new_snap = s;
-		lock_snap = old_snap;
 
-		if (!s->is_handover_destination) {
-			/* Handover exceptions to another snapshot */
-			old_snap = s;
-			new_snap = s->handover_snap;
-			lock_snap = new_snap;
-		}
-		down_write_nested(&lock_snap->lock,
+		down_write_nested(&old_snap->lock,
 				  SINGLE_DEPTH_NESTING);
 		handover_exceptions(old_snap, new_snap);
-		up_write(&lock_snap->lock);
+		up_write(&old_snap->lock);
 	}
 	/* An incomplete exception handover is not allowed */
 	BUG_ON(s->handover_snap);
+
+	if (new_snap && register_snapshot(new_snap)) {
+		DMERR("Unable to register snapshot "
+		      "after exception handover.");
+		new_snap->valid = 0;
+		goto out;
+	}
+
 	s->active = 1;
 	s->suspended = 0;
+out:
 	up_write(&s->lock);
 }