snapraid/raid/module.c

920 lines
21 KiB
C

// SPDX-License-Identifier: GPL-2.0-or-later
// Copyright (C) 2013 Andrea Mazzoleni
#include "internal.h"
#include "memory.h"
#include "cpu.h"
/**
* Forwarders for parity generation.
*
* Set by the raid_mode() call.
*
* Index 0 is for one parity, 1 for the two parities, and so on.
*/
static raid_gen_fn *raid_gen_ptr[RAID_PARITY_MAX];
struct raid_gen_algo {
raid_gen_fn *gen;
const char *tag; /**< Hardware descriptive tag. */
};
struct raid_rec_algo {
raid_rec_fn *rec;
const char *tag; /**< Hardware descriptive tag. */
};
/**
* Registered algorithms for parity generation and recovery.
*
* Set by the raid_gen_register() / raid_rec_register() calls.
*/
static struct raid_gen_algo raid_gen_algo_raid[RAID_ALGO_MAX];
static struct raid_rec_algo raid_rec_algo_raid[RAID_PARITY_MAX];
static struct raid_gen_algo raid_gen_algo_aes[RAID_ALGO_MAX];
static struct raid_rec_algo raid_rec_algo_aes[RAID_PARITY_MAX];
static struct raid_gen_algo *raid_gen_algo;
static struct raid_rec_algo *raid_rec_algo;
int raid_mode_active = RAID_MODE_GET;
const uint8_t(*raid_gfmul)[256];
const uint8_t *raid_gfexp;
const uint8_t *raid_gfinv;
const uint8_t(*raid_gfvandermonde)[256];
const uint8_t(*raid_gfcauchy)[256];
#if defined(CONFIG_X86) || defined(CONFIG_NEON) || defined(CONFIG_NEON32)
const uint8_t(*raid_gfcauchypshufb)[5][2][16];
const uint8_t(*raid_gfmulpshufb)[2][16];
#endif
const uint8_t(*raid_gfgen)[256];
uint8_t raid_poly_byte;
uint8_t raid_inv2_byte;
uint32_t raid_poly_32;
uint64_t raid_poly_64;
uint32_t raid_inv2_32;
uint64_t raid_inv2_64;
#if defined(CONFIG_X86) || defined(CONFIG_NEON) || defined(CONFIG_NEON32)
struct gfconst16 __aligned(16) gfconst16 =
{
{ 0 },
{
0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f,
0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f
},
{ 0 },
{
0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f,
0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f
}
};
#endif
void raid_gen(int nd, int np, size_t size, void **v, int streaming)
{
/* enforce limit on size */
BUG_ON(size % 64 != 0);
/* enforce limit on number of data blocks */
BUG_ON(nd < 1 || nd > RAID_DATA_MAX);
/* enforce limit on number of parity blocks */
BUG_ON(np < 1);
BUG_ON(np > RAID_PARITY_MAX || (raid_mode_active == RAID_MODE_VANDERMONDE_RAID && np > 3));
/* enforce valid vector */
BUG_ON(v == 0);
/* enforce valid streaming flag */
BUG_ON(streaming != 0 && streaming != 1);
BUG_ON(raid_gen_ptr[np - 1] == 0);
raid_gen_ptr[np - 1](nd, size, v, streaming);
}
/**
* Forwarders for data recovery.
*
* Set by the raid_mode() call.
*
* Index 0 is for one parity, 1 for the two parities, and so on.
*/
static raid_rec_fn *raid_rec_ptr[RAID_PARITY_MAX];
void raid_rec(int nr, int *ir, int nd, int np, size_t size, void **v)
{
int nrd; /* number of data blocks to recover */
int nrp; /* number of parity blocks to recover */
/* enforce limit on size */
BUG_ON(size % 64 != 0);
/* enforce limit on number of data blocks */
BUG_ON(nd < 1 || nd > RAID_DATA_MAX);
/* enforce limit on number of parity blocks */
BUG_ON(np < 1);
BUG_ON(np > RAID_PARITY_MAX || (raid_mode_active == RAID_MODE_VANDERMONDE_RAID && np > 3));
/* enforce limit on number of failures */
BUG_ON(nr < 0 || nr > np);
/* enforce valid vectors */
BUG_ON(v == 0);
BUG_ON(nr > 0 && ir == 0);
/* enforce order in index vector */
BUG_ON(nr >= 2 && ir[0] >= ir[1]);
BUG_ON(nr >= 3 && ir[1] >= ir[2]);
BUG_ON(nr >= 4 && ir[2] >= ir[3]);
BUG_ON(nr >= 5 && ir[3] >= ir[4]);
BUG_ON(nr >= 6 && ir[4] >= ir[5]);
/* enforce limit on index vector */
BUG_ON(nr > 0 && ir[0] < 0);
BUG_ON(nr > 0 && ir[nr - 1] >= nd + np);
/* count the number of data blocks to recover */
nrd = 0;
while (nrd < nr && ir[nrd] < nd)
++nrd;
/* all the remaining are parity */
nrp = nr - nrd;
/* enforce limit on number of failures */
BUG_ON(nrd > nd);
BUG_ON(nrp > np);
/* if failed data is present */
if (nrd != 0) {
int ip[RAID_PARITY_MAX];
int i, j, k;
/* setup the vector of parities to use */
for (i = 0, j = 0, k = 0; i < np; ++i) {
if (j < nrp && ir[nrd + j] == nd + i) {
/* this parity has to be recovered */
++j;
} else {
/* this parity is used for recovering */
ip[k] = i;
++k;
}
}
/*
* Recover the nrd data blocks specified in ir[],
* using the first nrd parity in ip[] for recovering
*/
BUG_ON(raid_rec_ptr[nrd - 1] == 0);
raid_rec_ptr[nrd - 1](nrd, ir, ip, nd, size, v);
}
/* recompute all the parities up to the last bad one */
if (nrp != 0) {
void *v_gen[RAID_DATA_MAX + RAID_PARITY_MAX];
int max_p = ir[nr - 1] - nd + 1;
int i, j;
/* copy data block pointers */
for (i = 0; i < nd; ++i)
v_gen[i] = v[i];
/*
* Setup parity pointers.
* To avoid writing into healthy parity blocks (which may be read-only),
* redirect each healthy parity to the buffer of the next bad parity.
* Because raid_gen() generates and writes parities in increasing order,
* the subsequent bad parity will overwrite any intermediate data.
*/
for (i = 0, j = 0; i < max_p; ++i) {
if (j < nrp && ir[nrd + j] == nd + i) {
/* this parity has to be recovered */
v_gen[nd + i] = v[nd + i];
++j;
} else {
/* this parity is healthy, redirect to the next bad parity */
v_gen[nd + i] = v[ir[nrd + j]];
}
}
raid_gen(nd, max_p, size, v_gen, 0);
}
}
void raid_data(int nr, int *id, int *ip, int nd, size_t size, void **v)
{
/* enforce limit on size */
BUG_ON(size % 64 != 0);
/* enforce limit on number of data blocks */
BUG_ON(nd < 1 || nd > RAID_DATA_MAX);
/* enforce limit on number of failures */
BUG_ON(nr < 0 || nr > nd);
BUG_ON(nr > RAID_PARITY_MAX);
/* enforce valid vectors */
BUG_ON(v == 0);
BUG_ON(nr > 0 && id == 0);
BUG_ON(nr > 0 && ip == 0);
/* enforce order in index vector for data */
BUG_ON(nr >= 2 && id[0] >= id[1]);
BUG_ON(nr >= 3 && id[1] >= id[2]);
BUG_ON(nr >= 4 && id[2] >= id[3]);
BUG_ON(nr >= 5 && id[3] >= id[4]);
BUG_ON(nr >= 6 && id[4] >= id[5]);
/* enforce limit on index vector for data */
BUG_ON(nr > 0 && id[0] < 0);
BUG_ON(nr > 0 && id[nr - 1] >= nd);
/* enforce order in index vector for parity */
BUG_ON(nr >= 2 && ip[0] >= ip[1]);
BUG_ON(nr >= 3 && ip[1] >= ip[2]);
BUG_ON(nr >= 4 && ip[2] >= ip[3]);
BUG_ON(nr >= 5 && ip[3] >= ip[4]);
BUG_ON(nr >= 6 && ip[4] >= ip[5]);
/* enforce limit on index vector for parity */
BUG_ON(nr > 0 && ip[0] < 0);
BUG_ON(nr > 0 && (ip[nr - 1] >= RAID_PARITY_MAX || (raid_mode_active == RAID_MODE_VANDERMONDE_RAID && ip[nr - 1] >= 3)));
/* if failed data is present */
if (nr != 0) {
BUG_ON(raid_rec_ptr[nr - 1] == 0);
raid_rec_ptr[nr - 1](nr, id, ip, nd, size, v);
}
}
const char * raid_gen_tag(int na)
{
BUG_ON(na < 0 || na >= RAID_ALGO_MAX);
return raid_gen_algo[na].tag;
}
const char * raid_rec_tag(int na)
{
/* there is no custom recover for vandermonde */
if (na == RAID_ALGO_VANDERMONDE_PAR3)
na = RAID_ALGO_CAUCHY_PAR3;
BUG_ON(na < 0 || na >= RAID_PARITY_MAX);
return raid_rec_algo[na].tag;
}
int raid_mode(int mode)
{
int prev = raid_mode_active;
if (mode == RAID_MODE_GET)
return prev;
BUG_ON(mode != RAID_MODE_CAUCHY_RAID && mode != RAID_MODE_VANDERMONDE_RAID && mode != RAID_MODE_CAUCHY_AES);
raid_mode_active = mode;
if (mode == RAID_MODE_CAUCHY_AES) {
raid_gfmul = raid_gfmul_aes;
raid_gfexp = raid_gfexp_aes;
raid_gfinv = raid_gfinv_aes;
raid_gfvandermonde = 0;
raid_gfcauchy = raid_gfcauchy_aes;
#if defined(CONFIG_X86) || defined(CONFIG_NEON) || defined(CONFIG_NEON32)
raid_gfcauchypshufb = raid_gfcauchypshufb_aes;
raid_gfmulpshufb = raid_gfmulpshufb_aes;
#endif
raid_poly_byte = RAID_POLY_AES;
raid_inv2_byte = RAID_INV2_AES;
raid_poly_32 = RAID_POLY_32_AES;
raid_poly_64 = RAID_POLY_64_AES;
raid_inv2_32 = RAID_INV2_32_AES;
raid_inv2_64 = RAID_INV2_64_AES;
raid_gen_algo = raid_gen_algo_aes;
raid_rec_algo = raid_rec_algo_aes;
} else {
raid_gfmul = raid_gfmul_raid;
raid_gfexp = raid_gfexp_raid;
raid_gfinv = raid_gfinv_raid;
raid_gfvandermonde = raid_gfvandermonde_raid;
raid_gfcauchy = raid_gfcauchy_raid;
#if defined(CONFIG_X86) || defined(CONFIG_NEON) || defined(CONFIG_NEON32)
raid_gfcauchypshufb = raid_gfcauchypshufb_raid;
raid_gfmulpshufb = raid_gfmulpshufb_raid;
#endif
raid_poly_byte = RAID_POLY_RAID;
raid_inv2_byte = RAID_INV2_RAID;
raid_poly_32 = RAID_POLY_32_RAID;
raid_poly_64 = RAID_POLY_64_RAID;
raid_inv2_32 = RAID_INV2_32_RAID;
raid_inv2_64 = RAID_INV2_64_RAID;
raid_gen_algo = raid_gen_algo_raid;
raid_rec_algo = raid_rec_algo_raid;
}
#if defined(CONFIG_X86) || defined(CONFIG_NEON) || defined(CONFIG_NEON32)
memset(gfconst16.poly, raid_poly_byte, 16);
memset(gfconst16.half, raid_inv2_byte, 16);
#endif
if (mode == RAID_MODE_VANDERMONDE_RAID) {
raid_gen_ptr[0] = raid_gen_algo[RAID_ALGO_CAUCHY_PAR1].gen;
raid_gen_ptr[1] = raid_gen_algo[RAID_ALGO_CAUCHY_PAR2].gen;
raid_gen_ptr[2] = raid_gen_algo[RAID_ALGO_VANDERMONDE_PAR3].gen;
raid_gen_ptr[3] = 0;
raid_gen_ptr[4] = 0;
raid_gen_ptr[5] = 0;
raid_rec_ptr[0] = raid_rec_algo[RAID_ALGO_CAUCHY_PAR1].rec;
raid_rec_ptr[1] = raid_rec_algo[RAID_ALGO_CAUCHY_PAR2].rec;
raid_rec_ptr[2] = raid_rec_algo[RAID_ALGO_CAUCHY_PAR3].rec;
raid_rec_ptr[3] = 0;
raid_rec_ptr[4] = 0;
raid_rec_ptr[5] = 0;
raid_gfgen = raid_gfvandermonde;
} else {
raid_gen_ptr[0] = raid_gen_algo[RAID_ALGO_CAUCHY_PAR1].gen;
raid_gen_ptr[1] = raid_gen_algo[RAID_ALGO_CAUCHY_PAR2].gen;
raid_gen_ptr[2] = raid_gen_algo[RAID_ALGO_CAUCHY_PAR3].gen;
raid_gen_ptr[3] = raid_gen_algo[RAID_ALGO_CAUCHY_PAR4].gen;
raid_gen_ptr[4] = raid_gen_algo[RAID_ALGO_CAUCHY_PAR5].gen;
raid_gen_ptr[5] = raid_gen_algo[RAID_ALGO_CAUCHY_PAR6].gen;
raid_rec_ptr[0] = raid_rec_algo[RAID_ALGO_CAUCHY_PAR1].rec;
raid_rec_ptr[1] = raid_rec_algo[RAID_ALGO_CAUCHY_PAR2].rec;
raid_rec_ptr[2] = raid_rec_algo[RAID_ALGO_CAUCHY_PAR3].rec;
raid_rec_ptr[3] = raid_rec_algo[RAID_ALGO_CAUCHY_PAR4].rec;
raid_rec_ptr[4] = raid_rec_algo[RAID_ALGO_CAUCHY_PAR5].rec;
raid_rec_ptr[5] = raid_rec_algo[RAID_ALGO_CAUCHY_PAR6].rec;
raid_gfgen = raid_gfcauchy;
}
return prev;
}
void raid_gen_force(int np, raid_gen_fn *fn)
{
BUG_ON(np < 1 || np > RAID_PARITY_MAX);
raid_gen_ptr[np - 1] = fn;
}
void raid_gen_register(int na, const char *tag, raid_gen_fn *gen, uint8_t poly)
{
BUG_ON(na < 0 || na >= RAID_ALGO_MAX);
if (poly == RAID_POLY_ANY || poly == RAID_POLY_RAID) {
raid_gen_algo_raid[na].tag = tag;
raid_gen_algo_raid[na].gen = gen;
}
if (poly == RAID_POLY_ANY || poly == RAID_POLY_AES) {
raid_gen_algo_aes[na].tag = tag;
raid_gen_algo_aes[na].gen = gen;
}
}
void raid_rec_register(int na, const char *tag, raid_rec_fn *rec, uint8_t poly)
{
BUG_ON(na < 0 || na >= RAID_PARITY_MAX);
if (poly == RAID_POLY_ANY || poly == RAID_POLY_RAID) {
raid_rec_algo_raid[na].tag = tag;
raid_rec_algo_raid[na].rec = rec;
}
if (poly == RAID_POLY_ANY || poly == RAID_POLY_AES) {
raid_rec_algo_aes[na].tag = tag;
raid_rec_algo_aes[na].rec = rec;
}
}
/*
* Initializes and selects the best algorithm.
*/
void raid_init(void)
{
raid_register_int();
#ifdef CONFIG_X86
raid_register_sse2();
raid_register_ssse3();
raid_register_avx2();
#endif
#ifdef CONFIG_X86_64
raid_register_avx512();
raid_register_avx2gfni();
raid_register_avx512gfni();
#endif
#ifdef CONFIG_NEON
raid_register_neon();
#endif
#ifdef CONFIG_NEON32
raid_register_neon32();
#endif
/* set the default mode */
raid_mode(RAID_MODE_CAUCHY_RAID);
}
/*
* Reference parity computation.
*/
void raid_gen_ref(int nd, int np, size_t size, void **vv)
{
uint8_t **v = (uint8_t **)vv;
size_t i;
for (i = 0; i < size; ++i) {
uint8_t p[RAID_PARITY_MAX];
int j, d;
for (j = 0; j < np; ++j)
p[j] = 0;
for (d = 0; d < nd; ++d) {
uint8_t b = v[d][i];
for (j = 0; j < np; ++j)
p[j] ^= raid_gfmul[b][raid_gfgen[j][d]];
}
for (j = 0; j < np; ++j)
v[nd + j][i] = p[j];
}
}
/*
* Size of the blocks to test.
*
* Slicing the 64 KiB GF multiplication table into 2048-byte blocks tests 32 data disks.
*/
#define TEST_SIZE 2048
/*
* Number of data blocks to test (32 data disks).
*/
#define TEST_COUNT (65536 / TEST_SIZE)
/*
* Parity generation test.
*/
static int selftest_par(int nd, int np, size_t size, void **v, void **ref)
{
int i, streaming;
void *t[TEST_COUNT + RAID_PARITY_MAX];
/* setup data */
for (i = 0; i < nd; ++i)
t[i] = ref[i];
/* test both cached and streaming generation */
for (streaming = 0; streaming <= 1; ++streaming) {
/* setup parity */
for (i = 0; i < np; ++i) {
memset(v[nd + i], 0x55, size);
t[nd + i] = v[nd + i];
}
raid_gen(nd, np, size, t, streaming);
/* compare parity */
for (i = 0; i < np; ++i) {
if (memcmp(t[nd + i], ref[nd + i], size) != 0) {
/* LCOV_EXCL_START */
return -1;
/* LCOV_EXCL_STOP */
}
}
}
return 0;
}
/*
* Recovering test.
*/
static int selftest_rec(int nr, int *ir, int nd, int np, size_t size, void **v, void **ref)
{
int i, j;
void *t[TEST_COUNT + RAID_PARITY_MAX];
/* setup data and parity vector */
for (i = 0, j = 0; i < nd + np; ++i) {
if (j < nr && ir[j] == i) {
/* this block has to be recovered */
memset(v[i], 0x55, size);
t[i] = v[i];
++j;
} else {
/* this block is used for recovering */
t[i] = ref[i];
}
}
raid_rec(nr, ir, nd, np, size, t);
/* compare all data and parity */
for (i = 0; i < nd + np; ++i) {
if (t[i] != ref[i]
&& memcmp(t[i], ref[i], size) != 0) {
/* LCOV_EXCL_START */
return -1;
/* LCOV_EXCL_STOP */
}
}
return 0;
}
/*
* Recovering test for data.
*/
static int selftest_data(int nr, int *id, int *ip, int nd, int np, size_t size, void **v, void **ref)
{
int i, j;
void *t[TEST_COUNT + RAID_PARITY_MAX];
/* setup data vector */
for (i = 0, j = 0; i < nd; ++i) {
if (j < nr && id[j] == i) {
/* this block has to be recovered */
memset(v[i], 0x55, size);
t[i] = v[i];
++j;
} else {
/* this block is left unchanged */
t[i] = ref[i];
}
}
/* setup parity vector */
for (i = 0, j = 0; i < np; ++i) {
if (j < nr && ip[j] == i) {
/* this block is used for recovering */
t[nd + i] = ref[nd + i];
++j;
} else {
/* this block should not be read or written */
t[nd + i] = 0;
}
}
raid_data(nr, id, ip, nd, size, t);
/* compare all data and parity */
for (i = 0; i < nd; ++i) {
if (t[i] != ref[i]
&& t[i] != 0
&& memcmp(t[i], ref[i], size) != 0) {
/* LCOV_EXCL_START */
return -1;
/* LCOV_EXCL_STOP */
}
}
return 0;
}
/*
* Scan test.
*/
static int selftest_scan(int nr, int *ir, int nd, int np, size_t size, void **v, void **ref)
{
int i, j, ret;
void *t[TEST_COUNT + RAID_PARITY_MAX];
int is[RAID_PARITY_MAX];
/* setup data and parity vector */
for (i = 0, j = 0; i < nd + np; ++i) {
if (j < nr && ir[j] == i) {
/* this block is bad */
t[i] = v[i];
++j;
} else {
/* this block is used for recovering */
t[i] = ref[i];
}
}
ret = raid_scan(is, nd, np, size, t);
/* compare identified bad blocks */
if (ret != nr)
return -1;
for (i = 0; i < nr; ++i) {
if (ir[i] != is[i]) {
/* LCOV_EXCL_START */
return -1;
/* LCOV_EXCL_STOP */
}
}
return 0;
}
int raid_selftest(void)
{
const int nd = TEST_COUNT;
const size_t size = TEST_SIZE;
const int nv = nd + RAID_PARITY_MAX * 2;
void *v_alloc;
void **v;
void *ref[TEST_COUNT + RAID_PARITY_MAX];
int ir[RAID_PARITY_MAX];
int ip[RAID_PARITY_MAX];
int i, np;
int np_max;
int ret = 0;
/* ensure to have enough space for data */
BUG_ON(nd * size > 65536);
/* vandermonde mode only supports up to 3 parity rows */
if (raid_mode_active == RAID_MODE_VANDERMONDE_RAID)
np_max = 3;
else
np_max = RAID_PARITY_MAX;
v = raid_malloc_vector(nv, size, &v_alloc);
if (!v) {
/* LCOV_EXCL_START */
return -1;
/* LCOV_EXCL_STOP */
}
/* use the multiplication table as data */
for (i = 0; i < nd; ++i)
ref[i] = ((uint8_t *)raid_gfmul) + size * i;
/* setup reference parity */
for (i = 0; i < np_max; ++i)
ref[nd + i] = v[nd + RAID_PARITY_MAX + i];
/* compute reference parity */
raid_gen_ref(nd, np_max, size, ref);
/* test for each parity level */
for (np = 1; np <= np_max; ++np) {
/* test parity generation */
ret = selftest_par(nd, np, size, v, ref);
if (ret != 0) {
/* LCOV_EXCL_START */
goto bail;
/* LCOV_EXCL_STOP */
}
/* test recovering with broken ending data disks */
for (i = 0; i < np; ++i) {
/* bad data */
ir[i] = nd - np + i;
/* good parity */
ip[i] = i;
}
ret = selftest_rec(np, ir, nd, np, size, v, ref);
if (ret != 0) {
/* LCOV_EXCL_START */
goto bail;
/* LCOV_EXCL_STOP */
}
ret = selftest_data(np, ir, ip, nd, np, size, v, ref);
if (ret != 0) {
/* LCOV_EXCL_START */
goto bail;
/* LCOV_EXCL_STOP */
}
/* test recovering with no failures */
ret = selftest_rec(0, 0, nd, np, size, v, ref);
if (ret != 0) {
/* LCOV_EXCL_START */
goto bail;
/* LCOV_EXCL_STOP */
}
/* test recovering with each single broken parity */
for (i = 0; i < np; ++i) {
ir[0] = nd + i;
ret = selftest_rec(1, ir, nd, np, size, v, ref);
if (ret != 0) {
/* LCOV_EXCL_START */
goto bail;
/* LCOV_EXCL_STOP */
}
}
/* test recovering with all broken parities */
for (i = 0; i < np; ++i)
ir[i] = nd + i;
ret = selftest_rec(np, ir, nd, np, size, v, ref);
if (ret != 0) {
/* LCOV_EXCL_START */
goto bail;
/* LCOV_EXCL_STOP */
}
/* test recovering with one broken data and one broken parity */
if (np >= 2) {
for (i = 0; i < np; ++i) {
/* bad data */
ir[0] = 0;
/* bad parity */
ir[1] = nd + i;
ret = selftest_rec(2, ir, nd, np, size, v, ref);
if (ret != 0) {
/* LCOV_EXCL_START */
goto bail;
/* LCOV_EXCL_STOP */
}
}
}
/* test recovering with broken alternating parities */
if (np >= 3) {
/* bad data */
ir[0] = 0;
/* bad parity P (index 0) and R (index 2) */
ir[1] = nd + 0;
ir[2] = nd + 2;
ret = selftest_rec(3, ir, nd, np, size, v, ref);
if (ret != 0) {
/* LCOV_EXCL_START */
goto bail;
/* LCOV_EXCL_STOP */
}
}
/* test recovering with np - 1 broken data and 1 broken parity */
if (np >= 2) {
for (i = 0; i < np - 1; ++i)
ir[i] = i;
/* bad parity P */
ir[np - 1] = nd + 0;
ret = selftest_rec(np, ir, nd, np, size, v, ref);
if (ret != 0) {
/* LCOV_EXCL_START */
goto bail;
/* LCOV_EXCL_STOP */
}
}
/* test recovering with broken leading data and broken leading parity */
for (i = 0; i < np / 2; ++i) {
/* bad data */
ir[i] = i;
/* good parity */
ip[i] = (np + 1) / 2 + i;
}
/* bad parity */
for (i = 0; i < (np + 1) / 2; ++i)
ir[np / 2 + i] = nd + i;
ret = selftest_rec(np, ir, nd, np, size, v, ref);
if (ret != 0) {
/* LCOV_EXCL_START */
goto bail;
/* LCOV_EXCL_STOP */
}
ret = selftest_data(np / 2, ir, ip, nd, np, size, v, ref);
if (ret != 0) {
/* LCOV_EXCL_START */
goto bail;
/* LCOV_EXCL_STOP */
}
/* test recovering with broken leading data and broken ending parity */
for (i = 0; i < np / 2; ++i) {
/* bad data */
ir[i] = i;
/* good parity */
ip[i] = i;
}
/* bad parity */
for (i = 0; i < (np + 1) / 2; ++i)
ir[np / 2 + i] = nd + np - (np + 1) / 2 + i;
ret = selftest_rec(np, ir, nd, np, size, v, ref);
if (ret != 0) {
/* LCOV_EXCL_START */
goto bail;
/* LCOV_EXCL_STOP */
}
ret = selftest_data(np / 2, ir, ip, nd, np, size, v, ref);
if (ret != 0) {
/* LCOV_EXCL_START */
goto bail;
/* LCOV_EXCL_STOP */
}
/* scan test with broken data and parity */
for (i = 0; i < np / 2; ++i) {
/* bad data */
ir[i] = i;
}
for (i = 0; i < (np - 1) / 2; ++i) {
/* bad parity */
ir[np / 2 + i] = nd + i;
}
for (i = 0; i < np - 1; ++i) {
/*
* Make blocks bad
* we cannot fill them with 0, because the original
* data may be already filled with 0
*/
memset(v[ir[i]], 0x55, size);
}
ret = selftest_scan(np - 1, ir, nd, np, size, v, ref);
if (ret != 0) {
/* LCOV_EXCL_START */
goto bail;
/* LCOV_EXCL_STOP */
}
}
/* scan test with no parity */
ret = selftest_scan(0, 0, nd, 0, size, v, ref);
if (ret != -1) {
/* LCOV_EXCL_START */
goto bail;
/* LCOV_EXCL_STOP */
}
ret = 0;
bail:
free(v);
free(v_alloc);
return ret;
}
#ifdef __KERNEL__ /* to build the user mode test */
static int speedtest = 1;
static int __init raid_cauchy_init(void)
{
int ret;
raid_init();
pr_info("raid: Using xor_blocks\n");
#ifdef RAID_USE_RAID6_PQ
pr_info("raid: Using raid6\n");
#endif
ret = raid_selftest();
if (ret != 0)
return ret;
pr_info("raid: Self test passed\n");
if (speedtest) {
pr_info("raid: Speed test\n");
raid_speedtest(0);
pr_info("raid: Speed test with optimized memory layout\n");
raid_speedtest(64); /* 64 is the typical cache line size */
}
return 0;
}
static void raid_cauchy_exit(void)
{
}
subsys_initcall(raid_cauchy_init);
module_exit(raid_cauchy_exit);
module_param(speedtest, int, 0);
MODULE_PARM_DESC(speedtest, "Runs a startup speed test");
MODULE_AUTHOR("Andrea Mazzoleni <amadvance@gmail.com>");
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("RAID Cauchy functions");
#endif