]> git.proxmox.com Git - mirror_ubuntu-focal-kernel.git/blobdiff - drivers/md/dm-verity-target.c
dm verity: add support for forward error correction
[mirror_ubuntu-focal-kernel.git] / drivers / md / dm-verity-target.c
index 7e200ba631fb4c36af65fdda913a9b96d4f62fb8..4f90ec2c6b7a6d4878891b8d1e70d793929ebb02 100644 (file)
@@ -15,6 +15,7 @@
  */
 
 #include "dm-verity.h"
+#include "dm-verity-fec.h"
 
 #include <linux/module.h>
 #include <linux/reboot.h>
@@ -31,7 +32,7 @@
 #define DM_VERITY_OPT_LOGGING          "ignore_corruption"
 #define DM_VERITY_OPT_RESTART          "restart_on_corruption"
 
-#define DM_VERITY_OPTS_MAX             1
+#define DM_VERITY_OPTS_MAX             (1 + DM_VERITY_OPTS_FEC)
 
 static unsigned dm_verity_prefetch_cluster = DM_VERITY_DEFAULT_PREFETCH_SIZE;
 
@@ -282,6 +283,10 @@ static int verity_verify_level(struct dm_verity *v, struct dm_verity_io *io,
                if (likely(memcmp(verity_io_real_digest(v, io), want_digest,
                                  v->digest_size) == 0))
                        aux->hash_verified = 1;
+               else if (verity_fec_decode(v, io,
+                                          DM_VERITY_BLOCK_TYPE_METADATA,
+                                          hash_block, data, NULL) == 0)
+                       aux->hash_verified = 1;
                else if (verity_handle_err(v,
                                           DM_VERITY_BLOCK_TYPE_METADATA,
                                           hash_block)) {
@@ -333,19 +338,61 @@ int verity_hash_for_block(struct dm_verity *v, struct dm_verity_io *io,
        return 0;
 }
 
+/*
+ * Calls function process for 1 << v->data_dev_block_bits bytes in the bio_vec
+ * starting from iter.
+ */
+int verity_for_bv_block(struct dm_verity *v, struct dm_verity_io *io,
+                       struct bvec_iter *iter,
+                       int (*process)(struct dm_verity *v,
+                                      struct dm_verity_io *io, u8 *data,
+                                      size_t len))
+{
+       unsigned todo = 1 << v->data_dev_block_bits;
+       struct bio *bio = dm_bio_from_per_bio_data(io, v->ti->per_bio_data_size);
+
+       do {
+               int r;
+               u8 *page;
+               unsigned len;
+               struct bio_vec bv = bio_iter_iovec(bio, *iter);
+
+               page = kmap_atomic(bv.bv_page);
+               len = bv.bv_len;
+
+               if (likely(len >= todo))
+                       len = todo;
+
+               r = process(v, io, page + bv.bv_offset, len);
+               kunmap_atomic(page);
+
+               if (r < 0)
+                       return r;
+
+               bio_advance_iter(bio, iter, len);
+               todo -= len;
+       } while (todo);
+
+       return 0;
+}
+
+static int verity_bv_hash_update(struct dm_verity *v, struct dm_verity_io *io,
+                                u8 *data, size_t len)
+{
+       return verity_hash_update(v, verity_io_hash_desc(v, io), data, len);
+}
+
 /*
  * Verify one "dm_verity_io" structure.
  */
 static int verity_verify_io(struct dm_verity_io *io)
 {
        struct dm_verity *v = io->v;
-       struct bio *bio = dm_bio_from_per_bio_data(io,
-                                                  v->ti->per_bio_data_size);
+       struct bvec_iter start;
        unsigned b;
 
        for (b = 0; b < io->n_blocks; b++) {
                int r;
-               unsigned todo;
                struct shash_desc *desc = verity_io_hash_desc(v, io);
 
                r = verity_hash_for_block(v, io, io->block + b,
@@ -357,26 +404,10 @@ static int verity_verify_io(struct dm_verity_io *io)
                if (unlikely(r < 0))
                        return r;
 
-               todo = 1 << v->data_dev_block_bits;
-               do {
-                       u8 *page;
-                       unsigned len;
-                       struct bio_vec bv = bio_iter_iovec(bio, io->iter);
-
-                       page = kmap_atomic(bv.bv_page);
-                       len = bv.bv_len;
-                       if (likely(len >= todo))
-                               len = todo;
-                       r = verity_hash_update(v, desc,  page + bv.bv_offset,
-                                              len);
-                       kunmap_atomic(page);
-
-                       if (unlikely(r < 0))
-                               return r;
-
-                       bio_advance_iter(bio, &io->iter, len);
-                       todo -= len;
-               } while (todo);
+               start = io->iter;
+               r = verity_for_bv_block(v, io, &io->iter, verity_bv_hash_update);
+               if (unlikely(r < 0))
+                       return r;
 
                r = verity_hash_final(v, desc, verity_io_real_digest(v, io));
                if (unlikely(r < 0))
@@ -385,8 +416,11 @@ static int verity_verify_io(struct dm_verity_io *io)
                if (likely(memcmp(verity_io_real_digest(v, io),
                                  verity_io_want_digest(v, io), v->digest_size) == 0))
                        continue;
+               else if (verity_fec_decode(v, io, DM_VERITY_BLOCK_TYPE_DATA,
+                                          io->block + b, NULL, &start) == 0)
+                       continue;
                else if (verity_handle_err(v, DM_VERITY_BLOCK_TYPE_DATA,
-                               io->block + b))
+                                          io->block + b))
                        return -EIO;
        }
 
@@ -404,6 +438,8 @@ static void verity_finish_io(struct dm_verity_io *io, int error)
        bio->bi_end_io = io->orig_bi_end_io;
        bio->bi_error = error;
 
+       verity_fec_finish_io(io);
+
        bio_endio(bio);
 }
 
@@ -418,7 +454,7 @@ static void verity_end_io(struct bio *bio)
 {
        struct dm_verity_io *io = bio->bi_private;
 
-       if (bio->bi_error) {
+       if (bio->bi_error && !verity_fec_is_enabled(io->v)) {
                verity_finish_io(io, bio->bi_error);
                return;
        }
@@ -521,6 +557,8 @@ static int verity_map(struct dm_target *ti, struct bio *bio)
        bio->bi_private = io;
        io->iter = bio->bi_iter;
 
+       verity_fec_init_io(io);
+
        verity_submit_prefetch(v, io);
 
        generic_make_request(bio);
@@ -535,6 +573,7 @@ static void verity_status(struct dm_target *ti, status_type_t type,
                          unsigned status_flags, char *result, unsigned maxlen)
 {
        struct dm_verity *v = ti->private;
+       unsigned args = 0;
        unsigned sz = 0;
        unsigned x;
 
@@ -561,8 +600,15 @@ static void verity_status(struct dm_target *ti, status_type_t type,
                else
                        for (x = 0; x < v->salt_size; x++)
                                DMEMIT("%02x", v->salt[x]);
+               if (v->mode != DM_VERITY_MODE_EIO)
+                       args++;
+               if (verity_fec_is_enabled(v))
+                       args += DM_VERITY_OPTS_FEC;
+               if (!args)
+                       return;
+               DMEMIT(" %u", args);
                if (v->mode != DM_VERITY_MODE_EIO) {
-                       DMEMIT(" ");
+                       DMEMIT(" ");
                        switch (v->mode) {
                        case DM_VERITY_MODE_LOGGING:
                                DMEMIT(DM_VERITY_OPT_LOGGING);
@@ -574,6 +620,7 @@ static void verity_status(struct dm_target *ti, status_type_t type,
                                BUG();
                        }
                }
+               sz = verity_fec_status_table(v, sz, result, maxlen);
                break;
        }
 }
@@ -636,6 +683,8 @@ static void verity_dtr(struct dm_target *ti)
        if (v->data_dev)
                dm_put_device(ti, v->data_dev);
 
+       verity_fec_dtr(v);
+
        kfree(v);
 }
 
@@ -668,6 +717,12 @@ static int verity_parse_opt_args(struct dm_arg_set *as, struct dm_verity *v)
                } else if (!strcasecmp(arg_name, DM_VERITY_OPT_RESTART)) {
                        v->mode = DM_VERITY_MODE_RESTART;
                        continue;
+
+               } else if (verity_is_fec_opt_arg(arg_name)) {
+                       r = verity_fec_parse_opt_args(as, v, &argc, arg_name);
+                       if (r)
+                               return r;
+                       continue;
                }
 
                ti->error = "Unrecognized verity feature request";
@@ -710,6 +765,10 @@ static int verity_ctr(struct dm_target *ti, unsigned argc, char **argv)
        ti->private = v;
        v->ti = ti;
 
+       r = verity_fec_ctr_alloc(v);
+       if (r)
+               goto bad;
+
        if ((dm_table_get_mode(ti->table) & ~FMODE_READ)) {
                ti->error = "Device must be readonly";
                r = -EINVAL;
@@ -898,8 +957,6 @@ static int verity_ctr(struct dm_target *ti, unsigned argc, char **argv)
                goto bad;
        }
 
-       ti->per_bio_data_size = roundup(sizeof(struct dm_verity_io) + v->shash_descsize + v->digest_size * 2, __alignof__(struct dm_verity_io));
-
        /* WQ_UNBOUND greatly improves performance when running on ramdisk */
        v->verify_wq = alloc_workqueue("kverityd", WQ_CPU_INTENSIVE | WQ_MEM_RECLAIM | WQ_UNBOUND, num_online_cpus());
        if (!v->verify_wq) {
@@ -908,6 +965,16 @@ static int verity_ctr(struct dm_target *ti, unsigned argc, char **argv)
                goto bad;
        }
 
+       ti->per_bio_data_size = sizeof(struct dm_verity_io) +
+                               v->shash_descsize + v->digest_size * 2;
+
+       r = verity_fec_ctr(v);
+       if (r)
+               goto bad;
+
+       ti->per_bio_data_size = roundup(ti->per_bio_data_size,
+                                       __alignof__(struct dm_verity_io));
+
        return 0;
 
 bad:
@@ -918,7 +985,7 @@ bad:
 
 static struct target_type verity_target = {
        .name           = "verity",
-       .version        = {1, 2, 0},
+       .version        = {1, 3, 0},
        .module         = THIS_MODULE,
        .ctr            = verity_ctr,
        .dtr            = verity_dtr,