rslib: Split rs control struct

The decoder library uses variable length arrays on stack. To get rid of
them it would be simple to allocate fixed length arrays on stack, but those
might become rather large. The other solution is to allocate the buffers in
the rs control structure, but this cannot be done as long as the structure
can be shared by several users. Sharing is desired because the RS polynom
tables are large and initialization is time consuming.

To solve this split the codec information out of the control structure and
have a pointer to a shared codec in it. Instantiate the control structure
for each user, create a new codec if no shareable is avaiable yet.  Adjust
all affected usage sites to the new scheme.

This allows to add per instance decoder buffers to the control structure
later on.

Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Acked-by: Boris Brezillon <boris.brezillon@bootlin.com>
Cc: Tony Luck <tony.luck@intel.com>
Cc: Kees Cook <keescook@chromium.org>
Cc: Segher Boessenkool <segher@kernel.crashing.org>
Cc: Kernel Hardening <kernel-hardening@lists.openwall.com>
Cc: Richard Weinberger <richard@nod.at>
Cc: Mike Snitzer <snitzer@redhat.com>
Cc: Anton Vorontsov <anton@enomsg.org>
Cc: Colin Cross <ccross@android.com>
Cc: Andrew Morton <akpm@linuxfoundation.org>
Cc: David Woodhouse <dwmw2@infradead.org>
Cc: Alasdair Kergon <agk@redhat.com>
Signed-off-by: Kees Cook <keescook@chromium.org>
This commit is contained in:
Thomas Gleixner 2018-04-22 18:23:53 +02:00 committed by Kees Cook
parent a85e126abf
commit 2163398192
6 changed files with 100 additions and 70 deletions

View File

@ -394,12 +394,13 @@ static int cafe_nand_read_page(struct mtd_info *mtd, struct nand_chip *chip,
for (i=0; i<8; i+=2) { for (i=0; i<8; i+=2) {
uint32_t tmp = cafe_readl(cafe, NAND_ECC_SYN01 + (i*2)); uint32_t tmp = cafe_readl(cafe, NAND_ECC_SYN01 + (i*2));
syn[i] = cafe->rs->index_of[tmp & 0xfff];
syn[i+1] = cafe->rs->index_of[(tmp >> 16) & 0xfff]; syn[i] = cafe->rs->codec->index_of[tmp & 0xfff];
syn[i+1] = cafe->rs->codec->index_of[(tmp >> 16) & 0xfff];
} }
n = decode_rs16(cafe->rs, NULL, NULL, 1367, syn, 0, pos, 0, n = decode_rs16(cafe->rs, NULL, NULL, 1367, syn, 0, pos, 0,
pat); pat);
for (i = 0; i < n; i++) { for (i = 0; i < n; i++) {
int p = pos[i]; int p = pos[i];

View File

@ -140,6 +140,7 @@ static int doc_ecc_decode(struct rs_control *rs, uint8_t *data, uint8_t *ecc)
int i, j, nerr, errpos[8]; int i, j, nerr, errpos[8];
uint8_t parity; uint8_t parity;
uint16_t ds[4], s[5], tmp, errval[8], syn[4]; uint16_t ds[4], s[5], tmp, errval[8], syn[4];
struct rs_codec *cd = rs->codec;
memset(syn, 0, sizeof(syn)); memset(syn, 0, sizeof(syn));
/* Convert the ecc bytes into words */ /* Convert the ecc bytes into words */
@ -160,15 +161,15 @@ static int doc_ecc_decode(struct rs_control *rs, uint8_t *data, uint8_t *ecc)
for (j = 1; j < NROOTS; j++) { for (j = 1; j < NROOTS; j++) {
if (ds[j] == 0) if (ds[j] == 0)
continue; continue;
tmp = rs->index_of[ds[j]]; tmp = cd->index_of[ds[j]];
for (i = 0; i < NROOTS; i++) for (i = 0; i < NROOTS; i++)
s[i] ^= rs->alpha_to[rs_modnn(rs, tmp + (FCR + i) * j)]; s[i] ^= cd->alpha_to[rs_modnn(cd, tmp + (FCR + i) * j)];
} }
/* Calc syn[i] = s[i] / alpha^(v + i) */ /* Calc syn[i] = s[i] / alpha^(v + i) */
for (i = 0; i < NROOTS; i++) { for (i = 0; i < NROOTS; i++) {
if (s[i]) if (s[i])
syn[i] = rs_modnn(rs, rs->index_of[s[i]] + (NN - FCR - i)); syn[i] = rs_modnn(cd, cd->index_of[s[i]] + (NN - FCR - i));
} }
/* Call the decoder library */ /* Call the decoder library */
nerr = decode_rs16(rs, NULL, NULL, 1019, syn, 0, errpos, 0, errval); nerr = decode_rs16(rs, NULL, NULL, 1019, syn, 0, errpos, 0, errval);

View File

@ -15,7 +15,7 @@
#include <linux/gfp.h> /* for GFP_KERNEL */ #include <linux/gfp.h> /* for GFP_KERNEL */
/** /**
* struct rs_control - rs control structure * struct rs_codec - rs codec data
* *
* @mm: Bits per symbol * @mm: Bits per symbol
* @nn: Symbols per block (= (1<<mm)-1) * @nn: Symbols per block (= (1<<mm)-1)
@ -29,9 +29,9 @@
* @gfpoly: The primitive generator polynominal * @gfpoly: The primitive generator polynominal
* @gffunc: Function to generate the field, if non-canonical representation * @gffunc: Function to generate the field, if non-canonical representation
* @users: Users of this structure * @users: Users of this structure
* @list: List entry for the rs control list * @list: List entry for the rs codec list
*/ */
struct rs_control { struct rs_codec {
int mm; int mm;
int nn; int nn;
uint16_t *alpha_to; uint16_t *alpha_to;
@ -47,6 +47,14 @@ struct rs_control {
struct list_head list; struct list_head list;
}; };
/**
* struct rs_control - rs control structure per instance
* @codec: The codec used for this instance
*/
struct rs_control {
struct rs_codec *codec;
};
/* General purpose RS codec, 8-bit data width, symbol width 1-15 bit */ /* General purpose RS codec, 8-bit data width, symbol width 1-15 bit */
#ifdef CONFIG_REED_SOLOMON_ENC8 #ifdef CONFIG_REED_SOLOMON_ENC8
int encode_rs8(struct rs_control *rs, uint8_t *data, int len, uint16_t *par, int encode_rs8(struct rs_control *rs, uint8_t *data, int len, uint16_t *par,
@ -69,7 +77,6 @@ int decode_rs16(struct rs_control *rs, uint16_t *data, uint16_t *par, int len,
uint16_t *corr); uint16_t *corr);
#endif #endif
/* Create or get a matching rs control structure */
struct rs_control *init_rs_gfp(int symsize, int gfpoly, int fcr, int prim, struct rs_control *init_rs_gfp(int symsize, int gfpoly, int fcr, int prim,
int nroots, gfp_t gfp); int nroots, gfp_t gfp);
@ -100,7 +107,7 @@ void free_rs(struct rs_control *rs);
/** modulo replacement for galois field arithmetics /** modulo replacement for galois field arithmetics
* *
* @rs: the rs control structure * @rs: Pointer to the RS codec
* @x: the value to reduce * @x: the value to reduce
* *
* where * where
@ -110,7 +117,7 @@ void free_rs(struct rs_control *rs);
* Simple arithmetic modulo would return a wrong result for values * Simple arithmetic modulo would return a wrong result for values
* >= 3 * rs->nn * >= 3 * rs->nn
*/ */
static inline int rs_modnn(struct rs_control *rs, int x) static inline int rs_modnn(struct rs_codec *rs, int x)
{ {
while (x >= rs->nn) { while (x >= rs->nn) {
x -= rs->nn; x -= rs->nn;

View File

@ -10,6 +10,7 @@
* Generic data width independent code which is included by the wrappers. * Generic data width independent code which is included by the wrappers.
*/ */
{ {
struct rs_codec *rs = rsc->codec;
int deg_lambda, el, deg_omega; int deg_lambda, el, deg_omega;
int i, j, r, k, pad; int i, j, r, k, pad;
int nn = rs->nn; int nn = rs->nn;

View File

@ -10,6 +10,7 @@
* Generic data width independent code which is included by the wrappers. * Generic data width independent code which is included by the wrappers.
*/ */
{ {
struct rs_codec *rs = rsc->codec;
int i, j, pad; int i, j, pad;
int nn = rs->nn; int nn = rs->nn;
int nroots = rs->nroots; int nroots = rs->nroots;

View File

@ -11,22 +11,23 @@
* *
* The generic Reed Solomon library provides runtime configurable * The generic Reed Solomon library provides runtime configurable
* encoding / decoding of RS codes. * encoding / decoding of RS codes.
* Each user must call init_rs to get a pointer to a rs_control *
* structure for the given rs parameters. This structure is either * Each user must call init_rs to get a pointer to a rs_control structure
* generated or a already available matching control structure is used. * for the given rs parameters. The control struct is unique per instance.
* If a structure is generated then the polynomial arrays for * It points to a codec which can be shared by multiple control structures.
* fast encoding / decoding are built. This can take some time so * If a codec is newly allocated then the polynomial arrays for fast
* make sure not to call this function from a time critical path. * encoding / decoding are built. This can take some time so make sure not
* Usually a module / driver should initialize the necessary * to call this function from a time critical path. Usually a module /
* rs_control structure on module / driver init and release it * driver should initialize the necessary rs_control structure on module /
* on exit. * driver init and release it on exit.
* The encoding puts the calculated syndrome into a given syndrome *
* buffer. * The encoding puts the calculated syndrome into a given syndrome buffer.
* The decoding is a two step process. The first step calculates *
* the syndrome over the received (data + syndrome) and calls the * The decoding is a two step process. The first step calculates the
* second stage, which does the decoding / error correction itself. * syndrome over the received (data + syndrome) and calls the second stage,
* Many hw encoders provide a syndrome calculation over the received * which does the decoding / error correction itself. Many hw encoders
* data + syndrome and can call the second stage directly. * provide a syndrome calculation over the received data + syndrome and can
* call the second stage directly.
*/ */
#include <linux/errno.h> #include <linux/errno.h>
#include <linux/kernel.h> #include <linux/kernel.h>
@ -36,13 +37,13 @@
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/mutex.h> #include <linux/mutex.h>
/* This list holds all currently allocated rs control structures */ /* This list holds all currently allocated rs codec structures */
static LIST_HEAD (rslist); static LIST_HEAD(codec_list);
/* Protection for the list */ /* Protection for the list */
static DEFINE_MUTEX(rslistlock); static DEFINE_MUTEX(rslistlock);
/** /**
* rs_init - Initialize a Reed-Solomon codec * codec_init - Initialize a Reed-Solomon codec
* @symsize: symbol size, bits (1-8) * @symsize: symbol size, bits (1-8)
* @gfpoly: Field generator polynomial coefficients * @gfpoly: Field generator polynomial coefficients
* @gffunc: Field generator function * @gffunc: Field generator function
@ -51,14 +52,14 @@ static DEFINE_MUTEX(rslistlock);
* @nroots: RS code generator polynomial degree (number of roots) * @nroots: RS code generator polynomial degree (number of roots)
* @gfp: GFP_ flags for allocations * @gfp: GFP_ flags for allocations
* *
* Allocate a control structure and the polynom arrays for faster * Allocate a codec structure and the polynom arrays for faster
* en/decoding. Fill the arrays according to the given parameters. * en/decoding. Fill the arrays according to the given parameters.
*/ */
static struct rs_control *rs_init(int symsize, int gfpoly, int (*gffunc)(int), static struct rs_codec *codec_init(int symsize, int gfpoly, int (*gffunc)(int),
int fcr, int prim, int nroots, gfp_t gfp) int fcr, int prim, int nroots, gfp_t gfp)
{ {
struct rs_control *rs;
int i, j, sr, root, iprim; int i, j, sr, root, iprim;
struct rs_codec *rs;
rs = kzalloc(sizeof(*rs), gfp); rs = kzalloc(sizeof(*rs), gfp);
if (!rs) if (!rs)
@ -138,6 +139,9 @@ static struct rs_control *rs_init(int symsize, int gfpoly, int (*gffunc)(int),
/* convert rs->genpoly[] to index form for quicker encoding */ /* convert rs->genpoly[] to index form for quicker encoding */
for (i = 0; i <= nroots; i++) for (i = 0; i <= nroots; i++)
rs->genpoly[i] = rs->index_of[rs->genpoly[i]]; rs->genpoly[i] = rs->index_of[rs->genpoly[i]];
rs->users = 1;
list_add(&rs->list, &codec_list);
return rs; return rs;
err: err:
@ -150,27 +154,37 @@ err:
/** /**
* free_rs - Free the rs control structure, if it is no longer used * free_rs - Free the rs control structure
* @rs: the control structure which is not longer used by the * @rs: The control structure which is not longer used by the
* caller * caller
*
* Free the control structure. If @rs is the last user of the associated
* codec, free the codec as well.
*/ */
void free_rs(struct rs_control *rs) void free_rs(struct rs_control *rs)
{ {
struct rs_codec *cd;
if (!rs)
return;
cd = rs->codec;
mutex_lock(&rslistlock); mutex_lock(&rslistlock);
rs->users--; cd->users--;
if(!rs->users) { if(!cd->users) {
list_del(&rs->list); list_del(&cd->list);
kfree(rs->alpha_to); kfree(cd->alpha_to);
kfree(rs->index_of); kfree(cd->index_of);
kfree(rs->genpoly); kfree(cd->genpoly);
kfree(rs); kfree(cd);
} }
mutex_unlock(&rslistlock); mutex_unlock(&rslistlock);
kfree(rs);
} }
EXPORT_SYMBOL_GPL(free_rs); EXPORT_SYMBOL_GPL(free_rs);
/** /**
* init_rs_internal - Find a matching or allocate a new rs control structure * init_rs_internal - Allocate rs control, find a matching codec or allocate a new one
* @symsize: the symbol size (number of bits) * @symsize: the symbol size (number of bits)
* @gfpoly: the extended Galois field generator polynomial coefficients, * @gfpoly: the extended Galois field generator polynomial coefficients,
* with the 0th coefficient in the low order bit. The polynomial * with the 0th coefficient in the low order bit. The polynomial
@ -201,33 +215,39 @@ static struct rs_control *init_rs_internal(int symsize, int gfpoly,
if (nroots < 0 || nroots >= (1<<symsize)) if (nroots < 0 || nroots >= (1<<symsize))
return NULL; return NULL;
rs = kzalloc(sizeof(*rs), GFP_KERNEL);
if (!rs)
return NULL;
mutex_lock(&rslistlock); mutex_lock(&rslistlock);
/* Walk through the list and look for a matching entry */ /* Walk through the list and look for a matching entry */
list_for_each(tmp, &rslist) { list_for_each(tmp, &codec_list) {
rs = list_entry(tmp, struct rs_control, list); struct rs_codec *cd = list_entry(tmp, struct rs_codec, list);
if (symsize != rs->mm)
if (symsize != cd->mm)
continue; continue;
if (gfpoly != rs->gfpoly) if (gfpoly != cd->gfpoly)
continue; continue;
if (gffunc != rs->gffunc) if (gffunc != cd->gffunc)
continue; continue;
if (fcr != rs->fcr) if (fcr != cd->fcr)
continue; continue;
if (prim != rs->prim) if (prim != cd->prim)
continue; continue;
if (nroots != rs->nroots) if (nroots != cd->nroots)
continue; continue;
/* We have a matching one already */ /* We have a matching one already */
rs->users++; cd->users++;
rs->codec = cd;
goto out; goto out;
} }
/* Create a new one */ /* Create a new one */
rs = rs_init(symsize, gfpoly, gffunc, fcr, prim, nroots, gfp); rs->codec = codec_init(symsize, gfpoly, gffunc, fcr, prim, nroots, gfp);
if (rs) { if (!rs->codec) {
rs->users = 1; kfree(rs);
list_add(&rs->list, &rslist); rs = NULL;
} }
out: out:
mutex_unlock(&rslistlock); mutex_unlock(&rslistlock);
@ -235,7 +255,7 @@ out:
} }
/** /**
* init_rs_gfp - Find a matching or allocate a new rs control structure * init_rs_gfp - Create a RS control struct and initialize it
* @symsize: the symbol size (number of bits) * @symsize: the symbol size (number of bits)
* @gfpoly: the extended Galois field generator polynomial coefficients, * @gfpoly: the extended Galois field generator polynomial coefficients,
* with the 0th coefficient in the low order bit. The polynomial * with the 0th coefficient in the low order bit. The polynomial
@ -254,9 +274,8 @@ struct rs_control *init_rs_gfp(int symsize, int gfpoly, int fcr, int prim,
EXPORT_SYMBOL_GPL(init_rs_gfp); EXPORT_SYMBOL_GPL(init_rs_gfp);
/** /**
* init_rs_non_canonical - Find a matching or allocate a new rs control * init_rs_non_canonical - Allocate rs control struct for fields with
* structure, for fields with non-canonical * non-canonical representation
* representation
* @symsize: the symbol size (number of bits) * @symsize: the symbol size (number of bits)
* @gffunc: pointer to function to generate the next field element, * @gffunc: pointer to function to generate the next field element,
* or the multiplicative identity element if given 0. Used * or the multiplicative identity element if given 0. Used
@ -277,7 +296,7 @@ EXPORT_SYMBOL_GPL(init_rs_non_canonical);
#ifdef CONFIG_REED_SOLOMON_ENC8 #ifdef CONFIG_REED_SOLOMON_ENC8
/** /**
* encode_rs8 - Calculate the parity for data values (8bit data width) * encode_rs8 - Calculate the parity for data values (8bit data width)
* @rs: the rs control structure * @rsc: the rs control structure
* @data: data field of a given type * @data: data field of a given type
* @len: data length * @len: data length
* @par: parity data, must be initialized by caller (usually all 0) * @par: parity data, must be initialized by caller (usually all 0)
@ -287,7 +306,7 @@ EXPORT_SYMBOL_GPL(init_rs_non_canonical);
* symbol size > 8. The calling code must take care of encoding of the * symbol size > 8. The calling code must take care of encoding of the
* syndrome result for storage itself. * syndrome result for storage itself.
*/ */
int encode_rs8(struct rs_control *rs, uint8_t *data, int len, uint16_t *par, int encode_rs8(struct rs_control *rsc, uint8_t *data, int len, uint16_t *par,
uint16_t invmsk) uint16_t invmsk)
{ {
#include "encode_rs.c" #include "encode_rs.c"
@ -298,7 +317,7 @@ EXPORT_SYMBOL_GPL(encode_rs8);
#ifdef CONFIG_REED_SOLOMON_DEC8 #ifdef CONFIG_REED_SOLOMON_DEC8
/** /**
* decode_rs8 - Decode codeword (8bit data width) * decode_rs8 - Decode codeword (8bit data width)
* @rs: the rs control structure * @rsc: the rs control structure
* @data: data field of a given type * @data: data field of a given type
* @par: received parity data field * @par: received parity data field
* @len: data length * @len: data length
@ -313,7 +332,7 @@ EXPORT_SYMBOL_GPL(encode_rs8);
* syndrome result and the received parity before calling this code. * syndrome result and the received parity before calling this code.
* Returns the number of corrected bits or -EBADMSG for uncorrectable errors. * Returns the number of corrected bits or -EBADMSG for uncorrectable errors.
*/ */
int decode_rs8(struct rs_control *rs, uint8_t *data, uint16_t *par, int len, int decode_rs8(struct rs_control *rsc, uint8_t *data, uint16_t *par, int len,
uint16_t *s, int no_eras, int *eras_pos, uint16_t invmsk, uint16_t *s, int no_eras, int *eras_pos, uint16_t invmsk,
uint16_t *corr) uint16_t *corr)
{ {
@ -325,7 +344,7 @@ EXPORT_SYMBOL_GPL(decode_rs8);
#ifdef CONFIG_REED_SOLOMON_ENC16 #ifdef CONFIG_REED_SOLOMON_ENC16
/** /**
* encode_rs16 - Calculate the parity for data values (16bit data width) * encode_rs16 - Calculate the parity for data values (16bit data width)
* @rs: the rs control structure * @rsc: the rs control structure
* @data: data field of a given type * @data: data field of a given type
* @len: data length * @len: data length
* @par: parity data, must be initialized by caller (usually all 0) * @par: parity data, must be initialized by caller (usually all 0)
@ -333,7 +352,7 @@ EXPORT_SYMBOL_GPL(decode_rs8);
* *
* Each field in the data array contains up to symbol size bits of valid data. * Each field in the data array contains up to symbol size bits of valid data.
*/ */
int encode_rs16(struct rs_control *rs, uint16_t *data, int len, uint16_t *par, int encode_rs16(struct rs_control *rsc, uint16_t *data, int len, uint16_t *par,
uint16_t invmsk) uint16_t invmsk)
{ {
#include "encode_rs.c" #include "encode_rs.c"
@ -344,7 +363,7 @@ EXPORT_SYMBOL_GPL(encode_rs16);
#ifdef CONFIG_REED_SOLOMON_DEC16 #ifdef CONFIG_REED_SOLOMON_DEC16
/** /**
* decode_rs16 - Decode codeword (16bit data width) * decode_rs16 - Decode codeword (16bit data width)
* @rs: the rs control structure * @rsc: the rs control structure
* @data: data field of a given type * @data: data field of a given type
* @par: received parity data field * @par: received parity data field
* @len: data length * @len: data length
@ -357,7 +376,7 @@ EXPORT_SYMBOL_GPL(encode_rs16);
* Each field in the data array contains up to symbol size bits of valid data. * Each field in the data array contains up to symbol size bits of valid data.
* Returns the number of corrected bits or -EBADMSG for uncorrectable errors. * Returns the number of corrected bits or -EBADMSG for uncorrectable errors.
*/ */
int decode_rs16(struct rs_control *rs, uint16_t *data, uint16_t *par, int len, int decode_rs16(struct rs_control *rsc, uint16_t *data, uint16_t *par, int len,
uint16_t *s, int no_eras, int *eras_pos, uint16_t invmsk, uint16_t *s, int no_eras, int *eras_pos, uint16_t invmsk,
uint16_t *corr) uint16_t *corr)
{ {