clk: rockchip: rk3288: fix up the clk register for hclk_vio
[firefly-linux-kernel-4.4.55.git] / drivers / md / dm-verity-target.c
index 7e200ba631fb4c36af65fdda913a9b96d4f62fb8..c7e97cf6e7fb1ac118eebe3df4350b100985ce23 100644 (file)
@@ -15,6 +15,7 @@
  */
 
 #include "dm-verity.h"
+#include "dm-verity-fec.h"
 
 #include <linux/module.h>
 #include <linux/reboot.h>
@@ -30,8 +31,9 @@
 
 #define DM_VERITY_OPT_LOGGING          "ignore_corruption"
 #define DM_VERITY_OPT_RESTART          "restart_on_corruption"
+#define DM_VERITY_OPT_IGN_ZEROES       "ignore_zero_blocks"
 
-#define DM_VERITY_OPTS_MAX             1
+#define DM_VERITY_OPTS_MAX             (2 + DM_VERITY_OPTS_FEC)
 
 static unsigned dm_verity_prefetch_cluster = DM_VERITY_DEFAULT_PREFETCH_SIZE;
 
@@ -282,6 +284,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)) {
@@ -304,10 +310,9 @@ release_ret_r:
  * of the hash tree if necessary.
  */
 int verity_hash_for_block(struct dm_verity *v, struct dm_verity_io *io,
-                         sector_t block, u8 *digest)
+                         sector_t block, u8 *digest, bool *is_zero)
 {
-       int i;
-       int r;
+       int r = 0, i;
 
        if (likely(v->levels)) {
                /*
@@ -319,7 +324,7 @@ int verity_hash_for_block(struct dm_verity *v, struct dm_verity_io *io,
                 */
                r = verity_verify_level(v, io, block, 0, true, digest);
                if (likely(r <= 0))
-                       return r;
+                       goto out;
        }
 
        memcpy(digest, v->root_digest, v->digest_size);
@@ -327,56 +332,109 @@ int verity_hash_for_block(struct dm_verity *v, struct dm_verity_io *io,
        for (i = v->levels - 1; i >= 0; i--) {
                r = verity_verify_level(v, io, block, i, false, digest);
                if (unlikely(r))
-                       return r;
+                       goto out;
        }
+out:
+       if (!r && v->zero_digest)
+               *is_zero = !memcmp(v->zero_digest, digest, v->digest_size);
+       else
+               *is_zero = false;
+
+       return r;
+}
+
+/*
+ * 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);
+}
+
+static int verity_bv_zero(struct dm_verity *v, struct dm_verity_io *io,
+                         u8 *data, size_t len)
+{
+       memset(data, 0, len);
+       return 0;
+}
+
 /*
  * Verify one "dm_verity_io" structure.
  */
 static int verity_verify_io(struct dm_verity_io *io)
 {
+       bool is_zero;
        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,
-                                         verity_io_want_digest(v, io));
+                                         verity_io_want_digest(v, io),
+                                         &is_zero);
                if (unlikely(r < 0))
                        return r;
 
+               if (is_zero) {
+                       /*
+                        * If we expect a zero block, don't validate, just
+                        * return zeros.
+                        */
+                       r = verity_for_bv_block(v, io, &io->iter,
+                                               verity_bv_zero);
+                       if (unlikely(r < 0))
+                               return r;
+
+                       continue;
+               }
+
                r = verity_hash_init(v, desc);
                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 +443,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 +465,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 +481,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;
        }
@@ -438,6 +501,7 @@ static void verity_prefetch_io(struct work_struct *work)
                container_of(work, struct dm_verity_prefetch_work, work);
        struct dm_verity *v = pw->v;
        int i;
+       sector_t prefetch_size;
 
        for (i = v->levels - 2; i >= 0; i--) {
                sector_t hash_block_start;
@@ -460,8 +524,14 @@ static void verity_prefetch_io(struct work_struct *work)
                                hash_block_end = v->hash_blocks - 1;
                }
 no_prefetch_cluster:
+               // for emmc, it is more efficient to send bigger read
+               prefetch_size = max((sector_t)CONFIG_DM_VERITY_HASH_PREFETCH_MIN_SIZE,
+                       hash_block_end - hash_block_start + 1);
+               if ((hash_block_start + prefetch_size) >= (v->hash_start + v->hash_blocks)) {
+                       prefetch_size = hash_block_end - hash_block_start + 1;
+               }
                dm_bufio_prefetch(v->bufio, hash_block_start,
-                                 hash_block_end - hash_block_start + 1);
+                                 prefetch_size);
        }
 
        kfree(pw);
@@ -488,7 +558,7 @@ static void verity_submit_prefetch(struct dm_verity *v, struct dm_verity_io *io)
  * Bio map function. It allocates dm_verity_io structure and bio vector and
  * fills them. Then it issues prefetches and the I/O.
  */
-static int verity_map(struct dm_target *ti, struct bio *bio)
+int verity_map(struct dm_target *ti, struct bio *bio)
 {
        struct dm_verity *v = ti->private;
        struct dm_verity_io *io;
@@ -521,20 +591,24 @@ 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);
 
        return DM_MAPIO_SUBMITTED;
 }
+EXPORT_SYMBOL_GPL(verity_map);
 
 /*
  * Status: V (valid) or C (corruption found)
  */
-static void verity_status(struct dm_target *ti, status_type_t type,
+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 +635,17 @@ 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 (v->zero_digest)
+                       args++;
+               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,11 +657,15 @@ static void verity_status(struct dm_target *ti, status_type_t type,
                                BUG();
                        }
                }
+               if (v->zero_digest)
+                       DMEMIT(" " DM_VERITY_OPT_IGN_ZEROES);
+               sz = verity_fec_status_table(v, sz, result, maxlen);
                break;
        }
 }
+EXPORT_SYMBOL_GPL(verity_status);
 
-static int verity_prepare_ioctl(struct dm_target *ti,
+int verity_prepare_ioctl(struct dm_target *ti,
                struct block_device **bdev, fmode_t *mode)
 {
        struct dm_verity *v = ti->private;
@@ -590,16 +677,18 @@ static int verity_prepare_ioctl(struct dm_target *ti,
                return 1;
        return 0;
 }
+EXPORT_SYMBOL_GPL(verity_prepare_ioctl);
 
-static int verity_iterate_devices(struct dm_target *ti,
+int verity_iterate_devices(struct dm_target *ti,
                                  iterate_devices_callout_fn fn, void *data)
 {
        struct dm_verity *v = ti->private;
 
        return fn(ti, v->data_dev, v->data_start, ti->len, data);
 }
+EXPORT_SYMBOL_GPL(verity_iterate_devices);
 
-static void verity_io_hints(struct dm_target *ti, struct queue_limits *limits)
+void verity_io_hints(struct dm_target *ti, struct queue_limits *limits)
 {
        struct dm_verity *v = ti->private;
 
@@ -611,8 +700,9 @@ static void verity_io_hints(struct dm_target *ti, struct queue_limits *limits)
 
        blk_limits_io_min(limits, limits->logical_block_size);
 }
+EXPORT_SYMBOL_GPL(verity_io_hints);
 
-static void verity_dtr(struct dm_target *ti)
+void verity_dtr(struct dm_target *ti)
 {
        struct dm_verity *v = ti->private;
 
@@ -624,6 +714,7 @@ static void verity_dtr(struct dm_target *ti)
 
        kfree(v->salt);
        kfree(v->root_digest);
+       kfree(v->zero_digest);
 
        if (v->tfm)
                crypto_free_shash(v->tfm);
@@ -636,8 +727,42 @@ 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);
 }
+EXPORT_SYMBOL_GPL(verity_dtr);
+
+static int verity_alloc_zero_digest(struct dm_verity *v)
+{
+       int r = -ENOMEM;
+       struct shash_desc *desc;
+       u8 *zero_data;
+
+       v->zero_digest = kmalloc(v->digest_size, GFP_KERNEL);
+
+       if (!v->zero_digest)
+               return r;
+
+       desc = kmalloc(v->shash_descsize, GFP_KERNEL);
+
+       if (!desc)
+               return r; /* verity_dtr will free zero_digest */
+
+       zero_data = kzalloc(1 << v->data_dev_block_bits, GFP_KERNEL);
+
+       if (!zero_data)
+               goto out;
+
+       r = verity_hash(v, desc, zero_data, 1 << v->data_dev_block_bits,
+                       v->zero_digest);
+
+out:
+       kfree(desc);
+       kfree(zero_data);
+
+       return r;
+}
 
 static int verity_parse_opt_args(struct dm_arg_set *as, struct dm_verity *v)
 {
@@ -668,6 +793,20 @@ 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 (!strcasecmp(arg_name, DM_VERITY_OPT_IGN_ZEROES)) {
+                       r = verity_alloc_zero_digest(v);
+                       if (r) {
+                               ti->error = "Cannot allocate zero digest";
+                               return r;
+                       }
+                       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";
@@ -691,7 +830,7 @@ static int verity_parse_opt_args(struct dm_arg_set *as, struct dm_verity *v)
  *     <digest>
  *     <salt>          Hex string or "-" if no salt.
  */
-static int verity_ctr(struct dm_target *ti, unsigned argc, char **argv)
+int verity_ctr(struct dm_target *ti, unsigned argc, char **argv)
 {
        struct dm_verity *v;
        struct dm_arg_set as;
@@ -710,6 +849,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 +1041,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 +1049,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:
@@ -915,10 +1066,11 @@ bad:
 
        return r;
 }
+EXPORT_SYMBOL_GPL(verity_ctr);
 
 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,