aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSami Tolvanen <samitolvanen@google.com>2015-03-18 11:52:14 -0400
committerMike Snitzer <snitzer@redhat.com>2015-04-15 12:10:22 -0400
commit65ff5b7ddf0541f2b6e5cc59c47bfbf6cbcd91b8 (patch)
tree2a5391011a315f42e409a33d8238544451484c14
parent0e0e32c16cfd2eeaf4fd4f16aa6cccd1333ce1e0 (diff)
dm verity: add error handling modes for corrupted blocks
Add device specific modes to dm-verity to specify how corrupted blocks should be handled. The following modes are defined: - DM_VERITY_MODE_EIO is the default behavior, where reading a corrupted block results in -EIO. - DM_VERITY_MODE_LOGGING only logs corrupted blocks, but does not block the read. - DM_VERITY_MODE_RESTART calls kernel_restart when a corrupted block is discovered. In addition, each mode sends a uevent to notify userspace of corruption and to allow further recovery actions. The driver defaults to previous behavior (DM_VERITY_MODE_EIO) and other modes can be enabled with an additional parameter to the verity table. Signed-off-by: Sami Tolvanen <samitolvanen@google.com> Signed-off-by: Mike Snitzer <snitzer@redhat.com>
-rw-r--r--Documentation/device-mapper/verity.txt17
-rw-r--r--drivers/md/dm-verity.c147
-rw-r--r--drivers/md/dm.c1
3 files changed, 153 insertions, 12 deletions
diff --git a/Documentation/device-mapper/verity.txt b/Documentation/device-mapper/verity.txt
index 9884681535ee..64ccc5a079a5 100644
--- a/Documentation/device-mapper/verity.txt
+++ b/Documentation/device-mapper/verity.txt
@@ -11,6 +11,7 @@ Construction Parameters
11 <data_block_size> <hash_block_size> 11 <data_block_size> <hash_block_size>
12 <num_data_blocks> <hash_start_block> 12 <num_data_blocks> <hash_start_block>
13 <algorithm> <digest> <salt> 13 <algorithm> <digest> <salt>
14 [<#opt_params> <opt_params>]
14 15
15<version> 16<version>
16 This is the type of the on-disk hash format. 17 This is the type of the on-disk hash format.
@@ -62,6 +63,22 @@ Construction Parameters
62<salt> 63<salt>
63 The hexadecimal encoding of the salt value. 64 The hexadecimal encoding of the salt value.
64 65
66<#opt_params>
67 Number of optional parameters. If there are no optional parameters,
68 the optional paramaters section can be skipped or #opt_params can be zero.
69 Otherwise #opt_params is the number of following arguments.
70
71 Example of optional parameters section:
72 1 ignore_corruption
73
74ignore_corruption
75 Log corrupted blocks, but allow read operations to proceed normally.
76
77restart_on_corruption
78 Restart the system when a corrupted block is discovered. This option is
79 not compatible with ignore_corruption and requires user space support to
80 avoid restart loops.
81
65Theory of operation 82Theory of operation
66=================== 83===================
67 84
diff --git a/drivers/md/dm-verity.c b/drivers/md/dm-verity.c
index 7a7bab8947ae..66616db33e6f 100644
--- a/drivers/md/dm-verity.c
+++ b/drivers/md/dm-verity.c
@@ -18,20 +18,39 @@
18 18
19#include <linux/module.h> 19#include <linux/module.h>
20#include <linux/device-mapper.h> 20#include <linux/device-mapper.h>
21#include <linux/reboot.h>
21#include <crypto/hash.h> 22#include <crypto/hash.h>
22 23
23#define DM_MSG_PREFIX "verity" 24#define DM_MSG_PREFIX "verity"
24 25
26#define DM_VERITY_ENV_LENGTH 42
27#define DM_VERITY_ENV_VAR_NAME "DM_VERITY_ERR_BLOCK_NR"
28
25#define DM_VERITY_IO_VEC_INLINE 16 29#define DM_VERITY_IO_VEC_INLINE 16
26#define DM_VERITY_MEMPOOL_SIZE 4 30#define DM_VERITY_MEMPOOL_SIZE 4
27#define DM_VERITY_DEFAULT_PREFETCH_SIZE 262144 31#define DM_VERITY_DEFAULT_PREFETCH_SIZE 262144
28 32
29#define DM_VERITY_MAX_LEVELS 63 33#define DM_VERITY_MAX_LEVELS 63
34#define DM_VERITY_MAX_CORRUPTED_ERRS 100
35
36#define DM_VERITY_OPT_LOGGING "ignore_corruption"
37#define DM_VERITY_OPT_RESTART "restart_on_corruption"
30 38
31static unsigned dm_verity_prefetch_cluster = DM_VERITY_DEFAULT_PREFETCH_SIZE; 39static unsigned dm_verity_prefetch_cluster = DM_VERITY_DEFAULT_PREFETCH_SIZE;
32 40
33module_param_named(prefetch_cluster, dm_verity_prefetch_cluster, uint, S_IRUGO | S_IWUSR); 41module_param_named(prefetch_cluster, dm_verity_prefetch_cluster, uint, S_IRUGO | S_IWUSR);
34 42
43enum verity_mode {
44 DM_VERITY_MODE_EIO,
45 DM_VERITY_MODE_LOGGING,
46 DM_VERITY_MODE_RESTART
47};
48
49enum verity_block_type {
50 DM_VERITY_BLOCK_TYPE_DATA,
51 DM_VERITY_BLOCK_TYPE_METADATA
52};
53
35struct dm_verity { 54struct dm_verity {
36 struct dm_dev *data_dev; 55 struct dm_dev *data_dev;
37 struct dm_dev *hash_dev; 56 struct dm_dev *hash_dev;
@@ -54,6 +73,8 @@ struct dm_verity {
54 unsigned digest_size; /* digest size for the current hash algorithm */ 73 unsigned digest_size; /* digest size for the current hash algorithm */
55 unsigned shash_descsize;/* the size of temporary space for crypto */ 74 unsigned shash_descsize;/* the size of temporary space for crypto */
56 int hash_failed; /* set to 1 if hash of any block failed */ 75 int hash_failed; /* set to 1 if hash of any block failed */
76 enum verity_mode mode; /* mode for handling verification errors */
77 unsigned corrupted_errs;/* Number of errors for corrupted blocks */
57 78
58 mempool_t *vec_mempool; /* mempool of bio vector */ 79 mempool_t *vec_mempool; /* mempool of bio vector */
59 80
@@ -175,6 +196,57 @@ static void verity_hash_at_level(struct dm_verity *v, sector_t block, int level,
175} 196}
176 197
177/* 198/*
199 * Handle verification errors.
200 */
201static int verity_handle_err(struct dm_verity *v, enum verity_block_type type,
202 unsigned long long block)
203{
204 char verity_env[DM_VERITY_ENV_LENGTH];
205 char *envp[] = { verity_env, NULL };
206 const char *type_str = "";
207 struct mapped_device *md = dm_table_get_md(v->ti->table);
208
209 /* Corruption should be visible in device status in all modes */
210 v->hash_failed = 1;
211
212 if (v->corrupted_errs >= DM_VERITY_MAX_CORRUPTED_ERRS)
213 goto out;
214
215 v->corrupted_errs++;
216
217 switch (type) {
218 case DM_VERITY_BLOCK_TYPE_DATA:
219 type_str = "data";
220 break;
221 case DM_VERITY_BLOCK_TYPE_METADATA:
222 type_str = "metadata";
223 break;
224 default:
225 BUG();
226 }
227
228 DMERR("%s: %s block %llu is corrupted", v->data_dev->name, type_str,
229 block);
230
231 if (v->corrupted_errs == DM_VERITY_MAX_CORRUPTED_ERRS)
232 DMERR("%s: reached maximum errors", v->data_dev->name);
233
234 snprintf(verity_env, DM_VERITY_ENV_LENGTH, "%s=%d,%llu",
235 DM_VERITY_ENV_VAR_NAME, type, block);
236
237 kobject_uevent_env(&disk_to_dev(dm_disk(md))->kobj, KOBJ_CHANGE, envp);
238
239out:
240 if (v->mode == DM_VERITY_MODE_LOGGING)
241 return 0;
242
243 if (v->mode == DM_VERITY_MODE_RESTART)
244 kernel_restart("dm-verity device corrupted");
245
246 return 1;
247}
248
249/*
178 * Verify hash of a metadata block pertaining to the specified data block 250 * Verify hash of a metadata block pertaining to the specified data block
179 * ("block" argument) at a specified level ("level" argument). 251 * ("block" argument) at a specified level ("level" argument).
180 * 252 *
@@ -251,11 +323,11 @@ static int verity_verify_level(struct dm_verity_io *io, sector_t block,
251 goto release_ret_r; 323 goto release_ret_r;
252 } 324 }
253 if (unlikely(memcmp(result, io_want_digest(v, io), v->digest_size))) { 325 if (unlikely(memcmp(result, io_want_digest(v, io), v->digest_size))) {
254 DMERR_LIMIT("metadata block %llu is corrupted", 326 if (verity_handle_err(v, DM_VERITY_BLOCK_TYPE_METADATA,
255 (unsigned long long)hash_block); 327 hash_block)) {
256 v->hash_failed = 1; 328 r = -EIO;
257 r = -EIO; 329 goto release_ret_r;
258 goto release_ret_r; 330 }
259 } else 331 } else
260 aux->hash_verified = 1; 332 aux->hash_verified = 1;
261 } 333 }
@@ -367,10 +439,9 @@ test_block_hash:
367 return r; 439 return r;
368 } 440 }
369 if (unlikely(memcmp(result, io_want_digest(v, io), v->digest_size))) { 441 if (unlikely(memcmp(result, io_want_digest(v, io), v->digest_size))) {
370 DMERR_LIMIT("data block %llu is corrupted", 442 if (verity_handle_err(v, DM_VERITY_BLOCK_TYPE_DATA,
371 (unsigned long long)(io->block + b)); 443 io->block + b))
372 v->hash_failed = 1; 444 return -EIO;
373 return -EIO;
374 } 445 }
375 } 446 }
376 447
@@ -546,6 +617,19 @@ static void verity_status(struct dm_target *ti, status_type_t type,
546 else 617 else
547 for (x = 0; x < v->salt_size; x++) 618 for (x = 0; x < v->salt_size; x++)
548 DMEMIT("%02x", v->salt[x]); 619 DMEMIT("%02x", v->salt[x]);
620 if (v->mode != DM_VERITY_MODE_EIO) {
621 DMEMIT(" 1 ");
622 switch (v->mode) {
623 case DM_VERITY_MODE_LOGGING:
624 DMEMIT(DM_VERITY_OPT_LOGGING);
625 break;
626 case DM_VERITY_MODE_RESTART:
627 DMEMIT(DM_VERITY_OPT_RESTART);
628 break;
629 default:
630 BUG();
631 }
632 }
549 break; 633 break;
550 } 634 }
551} 635}
@@ -647,13 +731,19 @@ static void verity_dtr(struct dm_target *ti)
647static int verity_ctr(struct dm_target *ti, unsigned argc, char **argv) 731static int verity_ctr(struct dm_target *ti, unsigned argc, char **argv)
648{ 732{
649 struct dm_verity *v; 733 struct dm_verity *v;
650 unsigned num; 734 struct dm_arg_set as;
735 const char *opt_string;
736 unsigned int num, opt_params;
651 unsigned long long num_ll; 737 unsigned long long num_ll;
652 int r; 738 int r;
653 int i; 739 int i;
654 sector_t hash_position; 740 sector_t hash_position;
655 char dummy; 741 char dummy;
656 742
743 static struct dm_arg _args[] = {
744 {0, 1, "Invalid number of feature args"},
745 };
746
657 v = kzalloc(sizeof(struct dm_verity), GFP_KERNEL); 747 v = kzalloc(sizeof(struct dm_verity), GFP_KERNEL);
658 if (!v) { 748 if (!v) {
659 ti->error = "Cannot allocate verity structure"; 749 ti->error = "Cannot allocate verity structure";
@@ -668,8 +758,8 @@ static int verity_ctr(struct dm_target *ti, unsigned argc, char **argv)
668 goto bad; 758 goto bad;
669 } 759 }
670 760
671 if (argc != 10) { 761 if (argc < 10) {
672 ti->error = "Invalid argument count: exactly 10 arguments required"; 762 ti->error = "Not enough arguments";
673 r = -EINVAL; 763 r = -EINVAL;
674 goto bad; 764 goto bad;
675 } 765 }
@@ -790,6 +880,39 @@ static int verity_ctr(struct dm_target *ti, unsigned argc, char **argv)
790 } 880 }
791 } 881 }
792 882
883 argv += 10;
884 argc -= 10;
885
886 /* Optional parameters */
887 if (argc) {
888 as.argc = argc;
889 as.argv = argv;
890
891 r = dm_read_arg_group(_args, &as, &opt_params, &ti->error);
892 if (r)
893 goto bad;
894
895 while (opt_params) {
896 opt_params--;
897 opt_string = dm_shift_arg(&as);
898 if (!opt_string) {
899 ti->error = "Not enough feature arguments";
900 r = -EINVAL;
901 goto bad;
902 }
903
904 if (!strcasecmp(opt_string, DM_VERITY_OPT_LOGGING))
905 v->mode = DM_VERITY_MODE_LOGGING;
906 else if (!strcasecmp(opt_string, DM_VERITY_OPT_RESTART))
907 v->mode = DM_VERITY_MODE_RESTART;
908 else {
909 ti->error = "Invalid feature arguments";
910 r = -EINVAL;
911 goto bad;
912 }
913 }
914 }
915
793 v->hash_per_block_bits = 916 v->hash_per_block_bits =
794 __fls((1 << v->hash_dev_block_bits) / v->digest_size); 917 __fls((1 << v->hash_dev_block_bits) / v->digest_size);
795 918
diff --git a/drivers/md/dm.c b/drivers/md/dm.c
index 944cdb322708..f8c7ca3e8947 100644
--- a/drivers/md/dm.c
+++ b/drivers/md/dm.c
@@ -3483,6 +3483,7 @@ struct gendisk *dm_disk(struct mapped_device *md)
3483{ 3483{
3484 return md->disk; 3484 return md->disk;
3485} 3485}
3486EXPORT_SYMBOL_GPL(dm_disk);
3486 3487
3487struct kobject *dm_kobject(struct mapped_device *md) 3488struct kobject *dm_kobject(struct mapped_device *md)
3488{ 3489{