dm-verity: introduce the options restart_on_error and panic_on_error
authorMikulas Patocka <mpatocka@redhat.com>
Wed, 2 Oct 2024 14:03:41 +0000 (16:03 +0200)
committerMikulas Patocka <mpatocka@redhat.com>
Wed, 2 Oct 2024 14:21:08 +0000 (16:21 +0200)
This patch introduces the options restart_on_error and panic_on_error on
dm-verity.

Previously, restarting on error was handled by the patch
e6a3531dd542cb127c8de32ab1e54a48ae19962b, but Google engineers wanted to
have a special option for it.

Signed-off-by: Mikulas Patocka <mpatocka@redhat.com>
Suggested-by: Sami Tolvanen <samitolvanen@google.com>
Suggested-by: Will Drewry <wad@chromium.org>
drivers/md/dm-verity-target.c
drivers/md/dm-verity.h

index 24ba9a10444cff49eacfe65395432afd7796cc1b..7d4d90b4395aee9d48d4a58af9efe94e5de30480 100644 (file)
 #define DM_VERITY_OPT_LOGGING          "ignore_corruption"
 #define DM_VERITY_OPT_RESTART          "restart_on_corruption"
 #define DM_VERITY_OPT_PANIC            "panic_on_corruption"
+#define DM_VERITY_OPT_ERROR_RESTART    "restart_on_error"
+#define DM_VERITY_OPT_ERROR_PANIC      "panic_on_error"
 #define DM_VERITY_OPT_IGN_ZEROES       "ignore_zero_blocks"
 #define DM_VERITY_OPT_AT_MOST_ONCE     "check_at_most_once"
 #define DM_VERITY_OPT_TASKLET_VERIFY   "try_verify_in_tasklet"
 
-#define DM_VERITY_OPTS_MAX             (4 + DM_VERITY_OPTS_FEC + \
+#define DM_VERITY_OPTS_MAX             (5 + DM_VERITY_OPTS_FEC + \
                                         DM_VERITY_ROOT_HASH_VERIFICATION_OPTS)
 
 static unsigned int dm_verity_prefetch_cluster = DM_VERITY_DEFAULT_PREFETCH_SIZE;
@@ -583,6 +585,11 @@ static inline bool verity_is_system_shutting_down(void)
                || system_state == SYSTEM_RESTART;
 }
 
+static void restart_io_error(struct work_struct *w)
+{
+       kernel_restart("dm-verity device has I/O error");
+}
+
 /*
  * End one "io" structure with a given error.
  */
@@ -597,6 +604,23 @@ static void verity_finish_io(struct dm_verity_io *io, blk_status_t status)
        if (!static_branch_unlikely(&use_bh_wq_enabled) || !io->in_bh)
                verity_fec_finish_io(io);
 
+       if (unlikely(status != BLK_STS_OK) &&
+           unlikely(!(bio->bi_opf & REQ_RAHEAD)) &&
+           !verity_is_system_shutting_down()) {
+               if (v->error_mode == DM_VERITY_MODE_PANIC) {
+                       panic("dm-verity device has I/O error");
+               }
+               if (v->error_mode == DM_VERITY_MODE_RESTART) {
+                       static DECLARE_WORK(restart_work, restart_io_error);
+                       queue_work(v->verify_wq, &restart_work);
+                       /*
+                        * We deliberately don't call bio_endio here, because
+                        * the machine will be restarted anyway.
+                        */
+                       return;
+               }
+       }
+
        bio_endio(bio);
 }
 
@@ -805,6 +829,8 @@ static void verity_status(struct dm_target *ti, status_type_t type,
                                DMEMIT("%02x", v->salt[x]);
                if (v->mode != DM_VERITY_MODE_EIO)
                        args++;
+               if (v->error_mode != DM_VERITY_MODE_EIO)
+                       args++;
                if (verity_fec_is_enabled(v))
                        args += DM_VERITY_OPTS_FEC;
                if (v->zero_digest)
@@ -834,6 +860,19 @@ static void verity_status(struct dm_target *ti, status_type_t type,
                                BUG();
                        }
                }
+               if (v->error_mode != DM_VERITY_MODE_EIO) {
+                       DMEMIT(" ");
+                       switch (v->error_mode) {
+                       case DM_VERITY_MODE_RESTART:
+                               DMEMIT(DM_VERITY_OPT_ERROR_RESTART);
+                               break;
+                       case DM_VERITY_MODE_PANIC:
+                               DMEMIT(DM_VERITY_OPT_ERROR_PANIC);
+                               break;
+                       default:
+                               BUG();
+                       }
+               }
                if (v->zero_digest)
                        DMEMIT(" " DM_VERITY_OPT_IGN_ZEROES);
                if (v->validated_blocks)
@@ -886,6 +925,19 @@ static void verity_status(struct dm_target *ti, status_type_t type,
                                DMEMIT("invalid");
                        }
                }
+               if (v->error_mode != DM_VERITY_MODE_EIO) {
+                       DMEMIT(",verity_error_mode=");
+                       switch (v->error_mode) {
+                       case DM_VERITY_MODE_RESTART:
+                               DMEMIT(DM_VERITY_OPT_ERROR_RESTART);
+                               break;
+                       case DM_VERITY_MODE_PANIC:
+                               DMEMIT(DM_VERITY_OPT_ERROR_PANIC);
+                               break;
+                       default:
+                               DMEMIT("invalid");
+                       }
+               }
                DMEMIT(";");
                break;
        }
@@ -1088,6 +1140,25 @@ static int verity_parse_verity_mode(struct dm_verity *v, const char *arg_name)
        return 0;
 }
 
+static inline bool verity_is_verity_error_mode(const char *arg_name)
+{
+       return (!strcasecmp(arg_name, DM_VERITY_OPT_ERROR_RESTART) ||
+               !strcasecmp(arg_name, DM_VERITY_OPT_ERROR_PANIC));
+}
+
+static int verity_parse_verity_error_mode(struct dm_verity *v, const char *arg_name)
+{
+       if (v->error_mode)
+               return -EINVAL;
+
+       if (!strcasecmp(arg_name, DM_VERITY_OPT_ERROR_RESTART))
+               v->error_mode = DM_VERITY_MODE_RESTART;
+       else if (!strcasecmp(arg_name, DM_VERITY_OPT_ERROR_PANIC))
+               v->error_mode = DM_VERITY_MODE_PANIC;
+
+       return 0;
+}
+
 static int verity_parse_opt_args(struct dm_arg_set *as, struct dm_verity *v,
                                 struct dm_verity_sig_opts *verify_args,
                                 bool only_modifier_opts)
@@ -1122,6 +1193,16 @@ static int verity_parse_opt_args(struct dm_arg_set *as, struct dm_verity *v,
                        }
                        continue;
 
+               } else if (verity_is_verity_error_mode(arg_name)) {
+                       if (only_modifier_opts)
+                               continue;
+                       r = verity_parse_verity_error_mode(v, arg_name);
+                       if (r) {
+                               ti->error = "Conflicting error handling parameters";
+                               return r;
+                       }
+                       continue;
+
                } else if (!strcasecmp(arg_name, DM_VERITY_OPT_IGN_ZEROES)) {
                        if (only_modifier_opts)
                                continue;
index 754e70bb5fe09074a6ed75621046bde4c5dacc33..6b75159bf835acebb8e2c7c116a3dda0c0b430b7 100644 (file)
@@ -64,6 +64,7 @@ struct dm_verity {
        unsigned int digest_size;       /* digest size for the current hash algorithm */
        unsigned int hash_reqsize; /* the size of temporary space for crypto */
        enum verity_mode mode;  /* mode for handling verification errors */
+       enum verity_mode error_mode;/* mode for handling I/O errors */
        unsigned int corrupted_errs;/* Number of errors for corrupted blocks */
 
        struct workqueue_struct *verify_wq;