diff options
Diffstat (limited to 'drivers/md/dm-verity-fec.c')
| -rw-r--r-- | drivers/md/dm-verity-fec.c | 134 |
1 files changed, 53 insertions, 81 deletions
diff --git a/drivers/md/dm-verity-fec.c b/drivers/md/dm-verity-fec.c index c79de517afee..7583607a8aa6 100644 --- a/drivers/md/dm-verity-fec.c +++ b/drivers/md/dm-verity-fec.c @@ -11,21 +11,15 @@ #define DM_MSG_PREFIX "verity-fec" /* - * If error correction has been configured, returns true. + * When correcting a data block, the FEC code performs optimally when it can + * collect all the associated RS blocks at the same time. As each byte is part + * of a different RS block, there are '1 << data_dev_block_bits' RS blocks. + * There are '1 << DM_VERITY_FEC_BUF_RS_BITS' RS blocks per buffer, so that + * gives '1 << (data_dev_block_bits - DM_VERITY_FEC_BUF_RS_BITS)' buffers. */ -bool verity_fec_is_enabled(struct dm_verity *v) +static inline unsigned int fec_max_nbufs(struct dm_verity *v) { - return v->fec && v->fec->dev; -} - -/* - * Return a pointer to dm_verity_fec_io after dm_verity_io and its variable - * length fields. - */ -static inline struct dm_verity_fec_io *fec_io(struct dm_verity_io *io) -{ - return (struct dm_verity_fec_io *) - ((char *)io + io->v->ti->per_io_data_size - sizeof(struct dm_verity_fec_io)); + return 1 << (v->data_dev_block_bits - DM_VERITY_FEC_BUF_RS_BITS); } /* @@ -69,14 +63,6 @@ static u8 *fec_read_parity(struct dm_verity *v, u64 rsb, int index, return res; } -/* Loop over each preallocated buffer slot. */ -#define fec_for_each_prealloc_buffer(__i) \ - for (__i = 0; __i < DM_VERITY_FEC_BUF_PREALLOC; __i++) - -/* Loop over each extra buffer slot. */ -#define fec_for_each_extra_buffer(io, __i) \ - for (__i = DM_VERITY_FEC_BUF_PREALLOC; __i < DM_VERITY_FEC_BUF_MAX; __i++) - /* Loop over each allocated buffer. */ #define fec_for_each_buffer(io, __i) \ for (__i = 0; __i < (io)->nbufs; __i++) @@ -211,7 +197,7 @@ static int fec_read_bufs(struct dm_verity *v, struct dm_verity_io *io, int i, j, target_index = -1; struct dm_buffer *buf; struct dm_bufio_client *bufio; - struct dm_verity_fec_io *fio = fec_io(io); + struct dm_verity_fec_io *fio = io->fec_io; u64 block, ileaved; u8 *bbuf, *rs_block; u8 want_digest[HASH_MAX_DIGESTSIZE]; @@ -307,39 +293,38 @@ done: } /* - * Allocate RS control structure and FEC buffers from preallocated mempools, - * and attempt to allocate as many extra buffers as available. + * Allocate and initialize a struct dm_verity_fec_io to use for FEC for a bio. + * This runs the first time a block needs to be corrected for a bio. In the + * common case where no block needs to be corrected, this code never runs. + * + * This always succeeds, as all required allocations are done from mempools. + * Additional buffers are also allocated opportunistically to improve error + * correction performance, but these aren't required to succeed. */ -static int fec_alloc_bufs(struct dm_verity *v, struct dm_verity_fec_io *fio) +static struct dm_verity_fec_io *fec_alloc_and_init_io(struct dm_verity *v) { + const unsigned int max_nbufs = fec_max_nbufs(v); + struct dm_verity_fec *f = v->fec; + struct dm_verity_fec_io *fio; unsigned int n; - if (!fio->rs) - fio->rs = mempool_alloc(&v->fec->rs_pool, GFP_NOIO); + fio = mempool_alloc(&f->fio_pool, GFP_NOIO); + fio->rs = mempool_alloc(&f->rs_pool, GFP_NOIO); - fec_for_each_prealloc_buffer(n) { - if (fio->bufs[n]) - continue; - - fio->bufs[n] = mempool_alloc(&v->fec->prealloc_pool, GFP_NOIO); - } + fio->bufs[0] = mempool_alloc(&f->prealloc_pool, GFP_NOIO); /* try to allocate the maximum number of buffers */ - fec_for_each_extra_buffer(fio, n) { - if (fio->bufs[n]) - continue; - - fio->bufs[n] = kmem_cache_alloc(v->fec->cache, GFP_NOWAIT); + for (n = 1; n < max_nbufs; n++) { + fio->bufs[n] = kmem_cache_alloc(f->cache, GFP_NOWAIT); /* we can manage with even one buffer if necessary */ if (unlikely(!fio->bufs[n])) break; } fio->nbufs = n; - if (!fio->output) - fio->output = mempool_alloc(&v->fec->output_pool, GFP_NOIO); - - return 0; + fio->output = mempool_alloc(&f->output_pool, GFP_NOIO); + fio->level = 0; + return fio; } /* @@ -368,10 +353,6 @@ static int fec_decode_rsb(struct dm_verity *v, struct dm_verity_io *io, int r, neras = 0; unsigned int pos; - r = fec_alloc_bufs(v, fio); - if (unlikely(r < 0)) - return r; - for (pos = 0; pos < 1 << v->data_dev_block_bits; ) { fec_init_bufs(v, fio); @@ -408,12 +389,16 @@ int verity_fec_decode(struct dm_verity *v, struct dm_verity_io *io, sector_t block, u8 *dest) { int r; - struct dm_verity_fec_io *fio = fec_io(io); + struct dm_verity_fec_io *fio; u64 offset, res, rsb; if (!verity_fec_is_enabled(v)) return -EOPNOTSUPP; + fio = io->fec_io; + if (!fio) + fio = io->fec_io = fec_alloc_and_init_io(v); + if (fio->level) return -EIO; @@ -463,42 +448,23 @@ done: /* * Clean up per-bio data. */ -void verity_fec_finish_io(struct dm_verity_io *io) +void __verity_fec_finish_io(struct dm_verity_io *io) { unsigned int n; struct dm_verity_fec *f = io->v->fec; - struct dm_verity_fec_io *fio = fec_io(io); - - if (!verity_fec_is_enabled(io->v)) - return; + struct dm_verity_fec_io *fio = io->fec_io; mempool_free(fio->rs, &f->rs_pool); - fec_for_each_prealloc_buffer(n) - mempool_free(fio->bufs[n], &f->prealloc_pool); + mempool_free(fio->bufs[0], &f->prealloc_pool); - fec_for_each_extra_buffer(fio, n) - if (fio->bufs[n]) - kmem_cache_free(f->cache, fio->bufs[n]); + for (n = 1; n < fio->nbufs; n++) + kmem_cache_free(f->cache, fio->bufs[n]); mempool_free(fio->output, &f->output_pool); -} -/* - * Initialize per-bio data. - */ -void verity_fec_init_io(struct dm_verity_io *io) -{ - struct dm_verity_fec_io *fio = fec_io(io); - - if (!verity_fec_is_enabled(io->v)) - return; - - fio->rs = NULL; - memset(fio->bufs, 0, sizeof(fio->bufs)); - fio->nbufs = 0; - fio->output = NULL; - fio->level = 0; + mempool_free(fio, &f->fio_pool); + io->fec_io = NULL; } /* @@ -529,14 +495,15 @@ void verity_fec_dtr(struct dm_verity *v) if (!verity_fec_is_enabled(v)) goto out; + mempool_exit(&f->fio_pool); mempool_exit(&f->rs_pool); mempool_exit(&f->prealloc_pool); mempool_exit(&f->output_pool); kmem_cache_destroy(f->cache); - if (f->data_bufio) + if (!IS_ERR_OR_NULL(f->data_bufio)) dm_bufio_client_destroy(f->data_bufio); - if (f->bufio) + if (!IS_ERR_OR_NULL(f->bufio)) dm_bufio_client_destroy(f->bufio); if (f->dev) @@ -758,6 +725,15 @@ int verity_fec_ctr(struct dm_verity *v) return -E2BIG; } + /* Preallocate some dm_verity_fec_io structures */ + ret = mempool_init_kmalloc_pool(&f->fio_pool, num_online_cpus(), + struct_size((struct dm_verity_fec_io *)0, + bufs, fec_max_nbufs(v))); + if (ret) { + ti->error = "Cannot allocate FEC IO pool"; + return ret; + } + /* Preallocate an rs_control structure for each worker thread */ ret = mempool_init(&f->rs_pool, num_online_cpus(), fec_rs_alloc, fec_rs_free, (void *) v); @@ -774,9 +750,8 @@ int verity_fec_ctr(struct dm_verity *v) return -ENOMEM; } - /* Preallocate DM_VERITY_FEC_BUF_PREALLOC buffers for each thread */ - ret = mempool_init_slab_pool(&f->prealloc_pool, num_online_cpus() * - DM_VERITY_FEC_BUF_PREALLOC, + /* Preallocate one buffer for each thread */ + ret = mempool_init_slab_pool(&f->prealloc_pool, num_online_cpus(), f->cache); if (ret) { ti->error = "Cannot allocate FEC buffer prealloc pool"; @@ -791,8 +766,5 @@ int verity_fec_ctr(struct dm_verity *v) return ret; } - /* Reserve space for our per-bio data */ - ti->per_io_data_size += sizeof(struct dm_verity_fec_io); - return 0; } |
