diff --git a/Makefile.am b/Makefile.am index a468a924..17aecd5e 100644 --- a/Makefile.am +++ b/Makefile.am @@ -3,6 +3,7 @@ check_PROGRAMS = mktest mkstream snapraid_SOURCES = \ raid/raid.c \ + raid/check.c \ raid/module.c \ raid/tables.c \ raid/int.c \ diff --git a/raid/module.c b/raid/module.c index 1f751e2a..e03c783f 100644 --- a/raid/module.c +++ b/raid/module.c @@ -259,6 +259,49 @@ static int raid_test_rec_dataonly(int nr, int *id, int *ip, int nd, int np, size return 0; } +/* + * Scan test. + */ +static int raid_test_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; + + /* make it really bad */ + memset(t[i], i, size); + } 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) { + /* LCOV_EXCL_START */ + return -1; + /* LCOV_EXCL_STOP */ + } + for (i = 0; i < nr; ++i) { + if (ir[i] != is[i]) { + /* LCOV_EXCL_START */ + return -1; + /* LCOV_EXCL_STOP */ + } + } + + return 0; +} + /* * Basic functionality self test. */ @@ -385,6 +428,23 @@ int raid_selftest(void) 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; + } + + ret = raid_test_scan(np - 1, ir, nd, np, size, v, ref); + if (ret != 0) { + /* LCOV_EXCL_START */ + goto bail; + /* LCOV_EXCL_STOP */ + } } bail: diff --git a/raid/raid.h b/raid/raid.h index 1fd1783f..dede4567 100644 --- a/raid/raid.h +++ b/raid/raid.h @@ -124,7 +124,7 @@ void raid_gen(int nd, int np, size_t size, void **v); * and some form of integrity verification would be possible. * * @nr Number of failed data and parity blocks to recover. - * @ir[] Vector of @nr indexes of the data and parity blocks to recover. + * @ir[] Vector of @nr indexes of the failed data and parity blocks. * The indexes start from 0. They must be in order. * The first parity is represented with value @nd, the second with value * @nd + 1, just like positions in the @v vector. @@ -158,5 +158,72 @@ void raid_rec(int nr, int *ir, int nd, int np, size_t size, void **v); */ void raid_rec_dataonly(int nr, int *id, int *ip, int nd, size_t size, void **v); +/** + * Check the provided failed blocks combination. + * + * This function checks if the specified failed blocks combination satisfies + * the redundancy information. A combination is assumed matching, if the + * remaining valid parity is matching the expected value after recovering. + * + * The number of failed blocks @nr must be strictly less than the number of + * parities @np, because you need one more parity to validate the recovering. + * + * No data or parity blocks are modified. + * + * @nr Number of failed data and parity blocks. + * @ir[] Vector of @nr indexes of the failed data and parity blocks. + * The indexes start from 0. They must be in order. + * The first parity is represented with value @nd, the second with value + * @nd + 1, just like positions in the @v vector. + * @nd Number of data blocks. + * @np Number of parity blocks. + * @size Size of the blocks pointed by @v. It must be a multiplier of 64. + * @v Vector of pointers to the blocks of data and parity. + * It has (@nd + @np) elements. The starting elements are the blocks + * for data, following with the parity blocks. + * Each block has @size bytes. + * @return 0 if the check is satisfied. -1 otherwise. + */ +int raid_check(int nr, int *ir, int nd, int np, size_t size, void **v); + +/** + * Scan for failed blocks. + * + * This function identifies the failed data and parity blocks using the + * available redundancy. + * + * It uses a brute force method, and then the call can be expansive. + * The expected execution time is proportional at the binomial coefficient + * @np + @nd choose @np - 1, usually written as: + * + * ( @np + @nd ) + * ( ) + * ( @np - 1 ) + * + * No data or parity blocks are modified. + * + * The failed block indexes are returned in the @ir vector. + * It must have space for at least @np - 1 values. + * + * The returned @ir vector can then be used in a raid_rec() call to recover + * the failed data and parity blocks. + * + * @ir[] Vector filled with the indexes of the failed data and parity blocks. + * The indexes start from 0 and they are in order. + * The first parity is represented with value @nd, the second with value + * @nd + 1, just like positions in the @v vector. + * @nd Number of data blocks. + * @np Number of parity blocks. + * @size Size of the blocks pointed by @v. It must be a multiplier of 64. + * @v Vector of pointers to the blocks of data and parity. + * It has (@nd + @np) elements. The starting elements are the blocks + * for data, following with the parity blocks. + * Each block has @size bytes. + * @return Number of block indexes returned in the @ir vector. + * 0 if no error is detected. + * -1 if it's not possible to identify the failed disks. + */ +int raid_scan(int *ir, int nd, int np, size_t size, void **v); + #endif diff --git a/raid/test/Makefile b/raid/test/Makefile index 1d7ede06..229d5ad8 100644 --- a/raid/test/Makefile +++ b/raid/test/Makefile @@ -24,7 +24,7 @@ CFLAGS += -O2 else CFLAGS += -O0 --coverage -DCOVERAGE=1 -DNDEBUG=1 endif -OBJS = raid.o int.o intz.o x86.o x86z.o tables.o memory.o test.o helper.o module.o tag.o +OBJS = raid.o check.o int.o intz.o x86.o x86z.o tables.o memory.o test.o helper.o module.o tag.o %.o: ../%.c $(CC) $(CFLAGS) -c -o $@ $<