diff options
author | Sami Tolvanen <samitolvanen@google.com> | 2015-03-18 11:52:14 -0400 |
---|---|---|
committer | Mike Snitzer <snitzer@redhat.com> | 2015-04-15 12:10:22 -0400 |
commit | 65ff5b7ddf0541f2b6e5cc59c47bfbf6cbcd91b8 (patch) | |
tree | 2a5391011a315f42e409a33d8238544451484c14 | |
parent | 0e0e32c16cfd2eeaf4fd4f16aa6cccd1333ce1e0 (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.txt | 17 | ||||
-rw-r--r-- | drivers/md/dm-verity.c | 147 | ||||
-rw-r--r-- | drivers/md/dm.c | 1 |
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 | |||
74 | ignore_corruption | ||
75 | Log corrupted blocks, but allow read operations to proceed normally. | ||
76 | |||
77 | restart_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 | |||
65 | Theory of operation | 82 | Theory 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 | ||
31 | static unsigned dm_verity_prefetch_cluster = DM_VERITY_DEFAULT_PREFETCH_SIZE; | 39 | static unsigned dm_verity_prefetch_cluster = DM_VERITY_DEFAULT_PREFETCH_SIZE; |
32 | 40 | ||
33 | module_param_named(prefetch_cluster, dm_verity_prefetch_cluster, uint, S_IRUGO | S_IWUSR); | 41 | module_param_named(prefetch_cluster, dm_verity_prefetch_cluster, uint, S_IRUGO | S_IWUSR); |
34 | 42 | ||
43 | enum verity_mode { | ||
44 | DM_VERITY_MODE_EIO, | ||
45 | DM_VERITY_MODE_LOGGING, | ||
46 | DM_VERITY_MODE_RESTART | ||
47 | }; | ||
48 | |||
49 | enum verity_block_type { | ||
50 | DM_VERITY_BLOCK_TYPE_DATA, | ||
51 | DM_VERITY_BLOCK_TYPE_METADATA | ||
52 | }; | ||
53 | |||
35 | struct dm_verity { | 54 | struct 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 | */ | ||
201 | static 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 | |||
239 | out: | ||
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) | |||
647 | static int verity_ctr(struct dm_target *ti, unsigned argc, char **argv) | 731 | static 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 | } |
3486 | EXPORT_SYMBOL_GPL(dm_disk); | ||
3486 | 3487 | ||
3487 | struct kobject *dm_kobject(struct mapped_device *md) | 3488 | struct kobject *dm_kobject(struct mapped_device *md) |
3488 | { | 3489 | { |