dm integrity: add recovery mode
authorMikulas Patocka <mpatocka@redhat.com>
Fri, 17 Mar 2017 16:40:51 +0000 (12:40 -0400)
committerMike Snitzer <snitzer@redhat.com>
Fri, 24 Mar 2017 19:54:23 +0000 (15:54 -0400)
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>
Documentation/device-mapper/dm-integrity.txt
drivers/md/dm-integrity.c

index 2406f56501dca3b362c4fa67700b6ebbbd54f078..9d9089f7420687b60f68ea7cb69c4f59cde77dcf 100644 (file)
@@ -59,6 +59,11 @@ Target arguments:
                either both data and tag or none of them are written. The
                journaled mode degrades write throughput twice because the
                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
 
index ecb0b592f5a03470050ba7664532bda0dbdae25e..e26a079b41ead49db24d277616594efef5861810 100644 (file)
@@ -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;