Эх сурвалжийг харах

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>
Mikulas Patocka 8 жил өмнө
parent
commit
c2bcb2b702

+ 5 - 0
Documentation/device-mapper/dm-integrity.txt

@@ -59,6 +59,11 @@ Target arguments:
 		either both data and tag or none of them are written. The
 		either both data and tag or none of them are written. The
 		journaled mode degrades write throughput twice because the
 		journaled mode degrades write throughput twice because the
 		data have to be written twice.
 		data have to be written twice.
+	R - recovery mode - in this mode, journal is not replayed,
+		checksums are not checked and writes to the device are not
+		allowed. This mode is useful for data recovery if the
+		device cannot be activated in any of the other standard
+		modes.
 
 
 5. the number of additional arguments
 5. the number of additional arguments
 
 

+ 27 - 13
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;
 		unsigned sectors_to_process = dio->range.n_sectors;
 		sector_t sector = dio->range.logical_sector;
 		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,
 		checksums = kmalloc((PAGE_SIZE >> SECTOR_SHIFT) * ic->tag_size + extra_space,
 				    GFP_NOIO | __GFP_NORETRY | __GFP_NOWARN);
 				    GFP_NOIO | __GFP_NORETRY | __GFP_NOWARN);
 		if (!checksums)
 		if (!checksums)
@@ -1288,6 +1291,7 @@ again:
 			}
 			}
 		}
 		}
 	}
 	}
+skip_io:
 	dec_in_flight(dio);
 	dec_in_flight(dio);
 	return;
 	return;
 error:
 error:
@@ -1327,6 +1331,9 @@ static int dm_integrity_map(struct dm_target *ti, struct bio *bio)
 		return -EIO;
 		return -EIO;
 	}
 	}
 
 
+	if (unlikely(ic->mode == 'R') && unlikely(dio->write))
+		return -EIO;
+
 	get_area_and_offset(ic, dio->range.logical_sector, &area, &offset);
 	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);
 	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);
 	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;
 	bool journal_empty;
 	unsigned char unused, last_used, want_commit_seq;
 	unsigned char unused, last_used, want_commit_seq;
 
 
+	if (ic->mode == 'R')
+		return;
+
 	if (ic->journal_uptodate)
 	if (ic->journal_uptodate)
 		return;
 		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];
 		ic->mode = argv[3][0];
 	else {
 	else {
 		ti->error = "Invalid mode (expecting J or D)";
 		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";
 		ti->error = "Error reading superblock";
 		goto bad;
 		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";
 			ti->error = "Could not initialize superblock";
 			goto bad;
 			goto bad;
 		}
 		}
-		should_write_sb = true;
+		if (ic->mode != 'R')
+			should_write_sb = true;
 	}
 	}
 
 
 	if (ic->sb->version != SB_VERSION) {
 	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);
 	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) {
 	if (should_write_sb) {
 		int r;
 		int r;