Merge branch 'linux-linaro-lsk-v4.4' into linux-linaro-lsk-v4.4-android
[firefly-linux-kernel-4.4.55.git] / drivers / md / dm-verity-fec.c
index 1cc10c4de70101ace370a7716c2a0d5f42887ce2..1dd667b975307ee5928c094a3b045e7a044b887b 100644 (file)
@@ -11,6 +11,7 @@
 
 #include "dm-verity-fec.h"
 #include <linux/math64.h>
+#include <linux/sysfs.h>
 
 #define DM_MSG_PREFIX  "verity-fec"
 
@@ -175,9 +176,11 @@ error:
        if (r < 0 && neras)
                DMERR_LIMIT("%s: FEC %llu: failed to correct: %d",
                            v->data_dev->name, (unsigned long long)rsb, r);
-       else if (r > 0)
+       else if (r > 0) {
                DMWARN_LIMIT("%s: FEC %llu: corrected %d errors",
                             v->data_dev->name, (unsigned long long)rsb, r);
+               atomic_add_unless(&v->fec->corrected, 1, INT_MAX);
+       }
 
        return r;
 }
@@ -439,6 +442,13 @@ int verity_fec_decode(struct dm_verity *v, struct dm_verity_io *io,
        if (!verity_fec_is_enabled(v))
                return -EOPNOTSUPP;
 
+       if (fio->level >= DM_VERITY_FEC_MAX_RECURSION) {
+               DMWARN_LIMIT("%s: FEC: recursion too deep", v->data_dev->name);
+               return -EIO;
+       }
+
+       fio->level++;
+
        if (type == DM_VERITY_BLOCK_TYPE_METADATA)
                block += v->data_blocks;
 
@@ -453,9 +463,7 @@ int verity_fec_decode(struct dm_verity *v, struct dm_verity_io *io,
         */
 
        offset = block << v->data_dev_block_bits;
-
-       res = offset;
-       div64_u64(res, v->fec->rounds << v->data_dev_block_bits);
+       res = div64_u64(offset, v->fec->rounds << v->data_dev_block_bits);
 
        /*
         * The base RS block we can feed to the interleaver to find out all
@@ -472,7 +480,7 @@ int verity_fec_decode(struct dm_verity *v, struct dm_verity_io *io,
        if (r < 0) {
                r = fec_decode_rsb(v, io, fio, rsb, offset, true);
                if (r < 0)
-                       return r;
+                       goto done;
        }
 
        if (dest)
@@ -482,6 +490,8 @@ int verity_fec_decode(struct dm_verity *v, struct dm_verity_io *io,
                r = verity_for_bv_block(v, io, iter, fec_bv_copy);
        }
 
+done:
+       fio->level--;
        return r;
 }
 
@@ -522,6 +532,7 @@ void verity_fec_init_io(struct dm_verity_io *io)
        memset(fio->bufs, 0, sizeof(fio->bufs));
        fio->nbufs = 0;
        fio->output = NULL;
+       fio->level = 0;
 }
 
 /*
@@ -548,6 +559,7 @@ unsigned verity_fec_status_table(struct dm_verity *v, unsigned sz,
 void verity_fec_dtr(struct dm_verity *v)
 {
        struct dm_verity_fec *f = v->fec;
+       struct kobject *kobj = &f->kobj_holder.kobj;
 
        if (!verity_fec_is_enabled(v))
                goto out;
@@ -564,6 +576,12 @@ void verity_fec_dtr(struct dm_verity *v)
 
        if (f->dev)
                dm_put_device(v->ti, f->dev);
+
+       if (kobj->state_initialized) {
+               kobject_put(kobj);
+               wait_for_completion(dm_get_completion_from_kobject(kobj));
+       }
+
 out:
        kfree(f);
        v->fec = NULL;
@@ -652,6 +670,28 @@ int verity_fec_parse_opt_args(struct dm_arg_set *as, struct dm_verity *v,
        return 0;
 }
 
+static ssize_t corrected_show(struct kobject *kobj, struct kobj_attribute *attr,
+                             char *buf)
+{
+       struct dm_verity_fec *f = container_of(kobj, struct dm_verity_fec,
+                                              kobj_holder.kobj);
+
+       return sprintf(buf, "%d\n", atomic_read(&f->corrected));
+}
+
+static struct kobj_attribute attr_corrected = __ATTR_RO(corrected);
+
+static struct attribute *fec_attrs[] = {
+       &attr_corrected.attr,
+       NULL
+};
+
+static struct kobj_type fec_ktype = {
+       .sysfs_ops = &kobj_sysfs_ops,
+       .default_attrs = fec_attrs,
+       .release = dm_kobject_release
+};
+
 /*
  * Allocate dm_verity_fec for v->fec. Must be called before verity_fec_ctr.
  */
@@ -675,8 +715,10 @@ int verity_fec_ctr_alloc(struct dm_verity *v)
  */
 int verity_fec_ctr(struct dm_verity *v)
 {
+       int r;
        struct dm_verity_fec *f = v->fec;
        struct dm_target *ti = v->ti;
+       struct mapped_device *md = dm_table_get_md(ti->table);
        u64 hash_blocks;
 
        if (!verity_fec_is_enabled(v)) {
@@ -684,6 +726,16 @@ int verity_fec_ctr(struct dm_verity *v)
                return 0;
        }
 
+       /* Create a kobject and sysfs attributes */
+       init_completion(&f->kobj_holder.completion);
+
+       r = kobject_init_and_add(&f->kobj_holder.kobj, &fec_ktype,
+                                &disk_to_dev(dm_disk(md))->kobj, "%s", "fec");
+       if (r) {
+               ti->error = "Cannot create kobject";
+               return r;
+       }
+
        /*
         * FEC is computed over data blocks, possible metadata, and
         * hash blocks. In other words, FEC covers total of fec_blocks