summary refs log tree commit diff
path: root/drivers/md/dm-integrity.c
diff options
context:
space:
mode:
authorMikulas Patocka <mpatocka@redhat.com>2017-03-17 12:40:51 -0400
committerMike Snitzer <snitzer@redhat.com>2017-03-24 15:54:23 -0400
commitc2bcb2b702e4684e566d295d687228498184e0c4 (patch)
treed68dcd1dff76ca211a50caac235c36d8f333c8d2 /drivers/md/dm-integrity.c
parent1aa0efd4210df1c57764b77040a6615bc9b3ac0f (diff)
downloadlinux-c2bcb2b702e4684e566d295d687228498184e0c4.tar.gz
dm integrity: add recovery mode
In recovery mode, we don't:
- replay the journal
- check checksums
- allow writes to the device

This mode can be used as a last resort for data recovery.  The
motivation for recovery mode is that when there is a single error in the
journal, the user should not lose access to the whole device.

Signed-off-by: Mikulas Patocka <mpatocka@redhat.com>
Signed-off-by: Mike Snitzer <snitzer@redhat.com>
Diffstat (limited to 'drivers/md/dm-integrity.c')
-rw-r--r--drivers/md/dm-integrity.c40
1 files changed, 27 insertions, 13 deletions
diff --git a/drivers/md/dm-integrity.c b/drivers/md/dm-integrity.c
index ecb0b592f5a0..e26a079b41ea 100644
--- a/drivers/md/dm-integrity.c
+++ b/drivers/md/dm-integrity.c
@@ -1216,6 +1216,9 @@ static void integrity_metadata(struct work_struct *w)
 		unsigned sectors_to_process = dio->range.n_sectors;
 		sector_t sector = dio->range.logical_sector;
 
+		if (unlikely(ic->mode == 'R'))
+			goto skip_io;
+
 		checksums = kmalloc((PAGE_SIZE >> SECTOR_SHIFT) * ic->tag_size + extra_space,
 				    GFP_NOIO | __GFP_NORETRY | __GFP_NOWARN);
 		if (!checksums)
@@ -1288,6 +1291,7 @@ again:
 			}
 		}
 	}
+skip_io:
 	dec_in_flight(dio);
 	return;
 error:
@@ -1327,6 +1331,9 @@ static int dm_integrity_map(struct dm_target *ti, struct bio *bio)
 		return -EIO;
 	}
 
+	if (unlikely(ic->mode == 'R') && unlikely(dio->write))
+		return -EIO;
+
 	get_area_and_offset(ic, dio->range.logical_sector, &area, &offset);
 	dio->metadata_block = get_metadata_sector_and_offset(ic, area, offset, &dio->metadata_offset);
 	bio->bi_iter.bi_sector = get_data_sector(ic, area, offset);
@@ -1926,6 +1933,9 @@ static void replay_journal(struct dm_integrity_c *ic)
 	bool journal_empty;
 	unsigned char unused, last_used, want_commit_seq;
 
+	if (ic->mode == 'R')
+		return;
+
 	if (ic->journal_uptodate)
 		return;
 
@@ -2705,7 +2715,7 @@ static int dm_integrity_ctr(struct dm_target *ti, unsigned argc, char **argv)
 		}
 	}
 
-	if (!strcmp(argv[3], "J") || !strcmp(argv[3], "D"))
+	if (!strcmp(argv[3], "J") || !strcmp(argv[3], "D") || !strcmp(argv[3], "R"))
 		ic->mode = argv[3][0];
 	else {
 		ti->error = "Invalid mode (expecting J or D)";
@@ -2864,14 +2874,15 @@ static int dm_integrity_ctr(struct dm_target *ti, unsigned argc, char **argv)
 		ti->error = "Error reading superblock";
 		goto bad;
 	}
-	if (!memcmp(ic->sb->magic, SB_MAGIC, 8)) {
-		should_write_sb = false;
-	} else {
-		for (i = 0; i < 512; i += 8) {
-			if (*(__u64 *)((__u8 *)ic->sb + i)) {
-				r = -EINVAL;
-				ti->error = "The device is not initialized";
-				goto bad;
+	should_write_sb = false;
+	if (memcmp(ic->sb->magic, SB_MAGIC, 8)) {
+		if (ic->mode != 'R') {
+			for (i = 0; i < 512; i += 8) {
+				if (*(__u64 *)((__u8 *)ic->sb + i)) {
+					r = -EINVAL;
+					ti->error = "The device is not initialized";
+					goto bad;
+				}
 			}
 		}
 
@@ -2880,7 +2891,8 @@ static int dm_integrity_ctr(struct dm_target *ti, unsigned argc, char **argv)
 			ti->error = "Could not initialize superblock";
 			goto bad;
 		}
-		should_write_sb = true;
+		if (ic->mode != 'R')
+			should_write_sb = true;
 	}
 
 	if (ic->sb->version != SB_VERSION) {
@@ -2954,9 +2966,11 @@ static int dm_integrity_ctr(struct dm_target *ti, unsigned argc, char **argv)
 	}
 	dm_bufio_set_sector_offset(ic->bufio, ic->start + ic->initial_sectors);
 
-	r = create_journal(ic, &ti->error);
-	if (r)
-		goto bad;
+	if (ic->mode != 'R') {
+		r = create_journal(ic, &ti->error);
+		if (r)
+			goto bad;
+	}
 
 	if (should_write_sb) {
 		int r;