diff --git a/drivers/md/dm-snap.c b/drivers/md/dm-snap.c
index 41735a25d50a..48788431e7bf 100644
--- a/drivers/md/dm-snap.c
+++ b/drivers/md/dm-snap.c
@@ -39,6 +39,28 @@ static const char dm_snapshot_merge_target_name[] = "snapshot-merge";
 #define DM_TRACKED_CHUNK_HASH(x)	((unsigned long)(x) & \
 					 (DM_TRACKED_CHUNK_HASH_SIZE - 1))
 
+#define INVALID_CAUSE_BIT		16
+#define DM_SNAPSHOT_VALID(s)		(s->valid & \
+					((1 << INVALID_CAUSE_BIT) - 1))
+#define DM_SNAPSHOT_INVALID_CAUSE(s)	(s->valid >> INVALID_CAUSE_BIT)
+#define SET_INVALID_CAUSE(s, cause)	(s->valid = \
+					(DM_SNAPSHOT_VALID(s) | \
+					(cause << INVALID_CAUSE_BIT)))
+
+enum invalid_cause {
+	INVALID_BY_METADATA = 1,
+	INVALID_BY_HANDOVER = 2,
+	INVALID_BY_CACELLING_HANDOVER = 3,
+	INVALID_BY_EIO = 4,
+	INVALID_BY_ENOMEM = 5
+};
+
+char *display_invalid_cause[] = {"by METADATA",
+				 "by HANDOVER",
+				 "by CANCELLING HANDOVER",
+				 "by EIO",
+				 "by ENOMEM"};
+
 struct dm_exception_table {
 	uint32_t hash_mask;
 	unsigned hash_shift;
@@ -1054,7 +1076,7 @@ static void snapshot_merge_next_chunks(struct dm_snapshot *s)
 	/*
 	 * valid flag never changes during merge, so no lock required.
 	 */
-	if (!s->valid) {
+	if (!DM_SNAPSHOT_VALID(s)) {
 		DMERR("Snapshot is invalid: can't merge");
 		goto shut;
 	}
@@ -1403,6 +1425,7 @@ static int snapshot_ctr(struct dm_target *ti, unsigned int argc, char **argv)
 		goto bad_read_metadata;
 	} else if (r > 0) {
 		s->valid = 0;
+		SET_INVALID_CAUSE(s, INVALID_BY_METADATA);
 		DMWARN("Snapshot is marked invalid.");
 	}
 
@@ -1480,6 +1503,7 @@ static void __handover_exceptions(struct dm_snapshot *snap_src,
 	 * Set source invalid to ensure it receives no further I/O.
 	 */
 	snap_src->valid = 0;
+	SET_INVALID_CAUSE(snap_src, INVALID_BY_HANDOVER);
 }
 
 static void snapshot_dtr(struct dm_target *ti)
@@ -1496,6 +1520,7 @@ static void snapshot_dtr(struct dm_target *ti)
 	if (snap_src && snap_dest && (s == snap_src)) {
 		down_write(&snap_dest->lock);
 		snap_dest->valid = 0;
+		SET_INVALID_CAUSE(snap_dest, INVALID_BY_HANDOVER);
 		up_write(&snap_dest->lock);
 		DMERR("Cancelling snapshot handover.");
 	}
@@ -1635,18 +1660,21 @@ static void error_bios(struct bio *bio)
 
 static void __invalidate_snapshot(struct dm_snapshot *s, int err)
 {
-	if (!s->valid)
+	if (!DM_SNAPSHOT_VALID(s))
 		return;
 
-	if (err == -EIO)
-		DMERR("Invalidating snapshot: Error reading/writing.");
-	else if (err == -ENOMEM)
-		DMERR("Invalidating snapshot: Unable to allocate exception.");
-
 	if (s->store->type->drop_snapshot)
 		s->store->type->drop_snapshot(s->store);
 
 	s->valid = 0;
+	if (err == -EIO) {
+		SET_INVALID_CAUSE(s, INVALID_BY_EIO);
+		DMERR("Invalidating snapshot: Error reading/writing.");
+	} else if (err == -ENOMEM) {
+		SET_INVALID_CAUSE(s, INVALID_BY_ENOMEM);
+		DMERR("Invalidating snapshot: Unable to allocate exception.");
+	}
+
 
 	dm_table_event(s->ti->table);
 }
@@ -1692,7 +1720,7 @@ static void pending_complete(void *context, int success)
 
 	down_read(&s->lock);
 	dm_exception_table_lock(&lock);
-	if (!s->valid) {
+	if (!DM_SNAPSHOT_VALID(s)) {
 		up_read(&s->lock);
 		free_completed_exception(e);
 		error = 1;
@@ -1984,7 +2012,7 @@ static int snapshot_map(struct dm_target *ti, struct bio *bio)
 
 	/* Full snapshots are not usable */
 	/* To get here the table must be live so s->active is always set. */
-	if (!s->valid)
+	if (!DM_SNAPSHOT_VALID(s))
 		return DM_MAPIO_KILL;
 
 	if (bio_data_dir(bio) == WRITE) {
@@ -1995,7 +2023,7 @@ static int snapshot_map(struct dm_target *ti, struct bio *bio)
 	down_read(&s->lock);
 	dm_exception_table_lock(&lock);
 
-	if (!s->valid || (unlikely(s->snapshot_overflowed) &&
+	if (!DM_SNAPSHOT_VALID(s) || (unlikely(s->snapshot_overflowed) &&
 	    bio_data_dir(bio) == WRITE)) {
 		r = DM_MAPIO_KILL;
 		goto out_unlock;
@@ -2068,7 +2096,7 @@ static int snapshot_map(struct dm_target *ti, struct bio *bio)
 				down_write(&s->lock);
 
 				if (s->store->userspace_supports_overflow) {
-					if (s->valid && !s->snapshot_overflowed) {
+					if (DM_SNAPSHOT_VALID(s) && !s->snapshot_overflowed) {
 						s->snapshot_overflowed = 1;
 						DMERR("Snapshot overflowed: Unable to allocate exception.");
 					}
@@ -2159,7 +2187,7 @@ static int snapshot_merge_map(struct dm_target *ti, struct bio *bio)
 	down_write(&s->lock);
 
 	/* Full merging snapshots are redirected to the origin */
-	if (!s->valid)
+	if (!DM_SNAPSHOT_VALID(s))
 		goto redirect_to_origin;
 
 	/* If the block is already remapped - use that */
@@ -2344,8 +2372,10 @@ static void snapshot_status(struct dm_target *ti, status_type_t type,
 
 		down_write(&snap->lock);
 
-		if (!snap->valid)
-			DMEMIT("Invalid");
+		if (!DM_SNAPSHOT_VALID(snap))
+			DMEMIT("Invalid %s", DM_SNAPSHOT_INVALID_CAUSE(snap) ?
+					display_invalid_cause[DM_SNAPSHOT_INVALID_CAUSE(snap) - 1] :
+					"");
 		else if (snap->merge_failed)
 			DMEMIT("Merge failed");
 		else if (snap->snapshot_overflowed)
@@ -2477,7 +2507,7 @@ static int __origin_write(struct list_head *snapshots, sector_t sector,
 		dm_exception_table_lock(&lock);
 
 		/* Only deal with valid and active snapshots */
-		if (!snap->valid || !snap->active)
+		if (!DM_SNAPSHOT_VALID(snap) || !snap->active)
 			goto next_snapshot;
 
 		pe = __lookup_pending_exception(snap, chunk);
