From: Mike Snitzer <[email-masked]>

Set 'merge_failed' flag if a snapshot fails to merge.  Update
snapshot_status() to report "Merge failed" if 'merge_failed' is set.

Signed-off-by: Mike Snitzer <[email-masked]>
Signed-off-by: Alasdair G Kergon <[email-masked]>

---
drivers/md/dm-snap.c |   14 +++++++++++++-
 drivers/md/dm-snap.c |   30 ++++++++++++++++++++++++++++--
 1 file changed, 28 insertions(+), 2 deletions(-)

Index: linux-2.6.32/drivers/md/dm-snap.c
===================================================================
--- linux-2.6.32.orig/drivers/md/dm-snap.c
+++ linux-2.6.32/drivers/md/dm-snap.c
@@ -71,7 +71,10 @@ struct dm_snapshot {
 	/* List of snapshots per Origin */
 	struct list_head list;
 
-	/* You can't use a snapshot if this is 0 (e.g. if full) */
+	/*
+	 * You can't use a snapshot if this is 0 (e.g. if full).
+	 * A snapshot-merge target never clears this.
+	 */
 	int valid;
 
 	/* Origin writes don't trigger exceptions until this is set */
@@ -107,6 +110,21 @@ struct dm_snapshot {
 	spinlock_t tracked_chunk_lock;
 	struct hlist_head tracked_chunk_hash[DM_TRACKED_CHUNK_HASH_SIZE];
 
+	/*
+	 * The merge operation failed if this flag is set.
+	 * Failure modes are handled as follows:
+	 * - I/O error reading the header
+	 *   	=> don't load the target; abort.
+	 * - Header does not have "valid" flag set
+	 *   	=> use the origin; forget about the snapshot.
+	 * - I/O error when reading exceptions
+	 *   	=> don't load the target; abort.
+	 *         (We can't use the intermediate origin state.)
+	 * - I/O error while merging
+	 *	=> stop merging; set merge_failed; process I/O normally.
+	 */
+	int merge_failed;
+
 	/* Wait for events based on state_bits */
 	unsigned long state_bits;
 
@@ -906,9 +924,13 @@ static void snapshot_merge_next_chunks(s
 	linear_chunks = s->store->type->prepare_merge(s->store, &old_chunk,
 						      &new_chunk);
 	if (linear_chunks <= 0) {
-		if (linear_chunks < 0)
+		if (linear_chunks < 0) {
 			DMERR("Read error in exception store: "
 			      "shutting down merge");
+			down_write(&s->lock);
+			s->merge_failed = 1;
+			up_write(&s->lock);
+		}
 		goto shut;
 	}
 
@@ -992,6 +1014,7 @@ static void merge_callback(int read_err,
 
 shut:
 	down_write(&s->lock);
+	s->merge_failed = 1;
 	b = __release_write_interlock(s);
 	up_write(&s->lock);
 	error_bios(b);
@@ -1094,6 +1117,7 @@ static int snapshot_ctr(struct dm_target
 	INIT_LIST_HEAD(&s->list);
 	spin_lock_init(&s->pe_lock);
 	s->state_bits = 0;
+	s->merge_failed = 0;
 	s->merge_write_interlock = 0;
 	s->merge_write_interlock_n = 0;
 	bio_list_init(&s->merge_write_list);
@@ -1839,6 +1863,8 @@ static int snapshot_status(struct dm_tar
 
 		if (!snap->valid)
 			DMEMIT("Invalid");
+		else if (snap->merge_failed)
+			DMEMIT("Merge failed");
 		else {
 			if (snap->store->type->usage) {
 				sector_t total_sectors, sectors_allocated,