Commit c2bcb2b7 authored by Mikulas Patocka's avatar Mikulas Patocka Committed by Mike Snitzer

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: default avatarMikulas Patocka <mpatocka@redhat.com>
Signed-off-by: default avatarMike Snitzer <snitzer@redhat.com>
parent 1aa0efd4
...@@ -59,6 +59,11 @@ Target arguments: ...@@ -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
......
...@@ -1216,6 +1216,9 @@ static void integrity_metadata(struct work_struct *w) ...@@ -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 @@ static void integrity_metadata(struct work_struct *w) ...@@ -1288,6 +1291,7 @@ static void integrity_metadata(struct work_struct *w)
} }
} }
} }
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) ...@@ -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) ...@@ -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) ...@@ -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,9 +2874,9 @@ static int dm_integrity_ctr(struct dm_target *ti, unsigned argc, char **argv) ...@@ -2864,9 +2874,9 @@ 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; should_write_sb = false;
} else { if (memcmp(ic->sb->magic, SB_MAGIC, 8)) {
if (ic->mode != 'R') {
for (i = 0; i < 512; i += 8) { for (i = 0; i < 512; i += 8) {
if (*(__u64 *)((__u8 *)ic->sb + i)) { if (*(__u64 *)((__u8 *)ic->sb + i)) {
r = -EINVAL; r = -EINVAL;
...@@ -2874,12 +2884,14 @@ static int dm_integrity_ctr(struct dm_target *ti, unsigned argc, char **argv) ...@@ -2874,12 +2884,14 @@ static int dm_integrity_ctr(struct dm_target *ti, unsigned argc, char **argv)
goto bad; goto bad;
} }
} }
}
r = initialize_superblock(ic, journal_sectors, interleave_sectors); r = initialize_superblock(ic, journal_sectors, interleave_sectors);
if (r) { if (r) {
ti->error = "Could not initialize superblock"; ti->error = "Could not initialize superblock";
goto bad; goto bad;
} }
if (ic->mode != 'R')
should_write_sb = true; should_write_sb = true;
} }
...@@ -2954,9 +2966,11 @@ static int dm_integrity_ctr(struct dm_target *ti, unsigned argc, char **argv) ...@@ -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);
if (ic->mode != 'R') {
r = create_journal(ic, &ti->error); r = create_journal(ic, &ti->error);
if (r) if (r)
goto bad; goto bad;
}
if (should_write_sb) { if (should_write_sb) {
int r; int r;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment