From f855424082f8d697a0b44368711e66f0d328a922 Mon Sep 17 00:00:00 2001 From: Andrea Mazzoleni Date: Sun, 24 Apr 2011 10:24:05 +0200 Subject: [PATCH] Adds recovering at RAID6 support. Adds selftesting of RAID5 and RAID5. --- Makefile.am | 149 +++++++++++++++--------- TODO | 12 -- check.c | 147 ++++++++++++++++-------- raid.c | 214 +++++++++++++++++++++++++---------- raid.h | 35 ++++-- selftest.c | 159 ++++++++++++++++++++++++-- snapraid.c | 4 +- speed.c | 12 +- sync.c | 6 +- test.conf => test-raid5.conf | 0 test-raid6.conf | 8 ++ 11 files changed, 546 insertions(+), 200 deletions(-) rename test.conf => test-raid5.conf (100%) create mode 100644 test-raid6.conf diff --git a/Makefile.am b/Makefile.am index 7cf5f3cd..94acb2f8 100644 --- a/Makefile.am +++ b/Makefile.am @@ -41,7 +41,8 @@ noinst_HEADERS = \ tommylist.c \ fnmatch.h \ cpu.h \ - raid.h + raid.h \ + tables.h mktest_SOURCES = \ mktest.c @@ -51,7 +52,8 @@ EXTRA_DIST = \ noautomake.sh \ README AUTHORS HISTORY INSTALL COPYING TODO \ snapraid.d snapraid.1 snapraid.txt \ - test.conf \ + test-raid5.conf \ + test-raid6.conf \ snapraid.conf.example \ acinclude.m4 @@ -73,83 +75,126 @@ check-local: mkdir test/disk2 mkdir test/disk3 echo --- As empty, sync and check - $(TESTENV) ./snapraid$(EXEEXT) -v -c $(srcdir)/test.conf sync - $(TESTENV) ./snapraid$(EXEEXT) -c $(srcdir)/test.conf check - echo --- Create some files, sync and check + $(TESTENV) ./snapraid$(EXEEXT) -v -c $(srcdir)/test-raid6.conf sync + $(TESTENV) ./snapraid$(EXEEXT) -c $(srcdir)/test-raid6.conf check + echo --- Create some files, sync raid6 and check $(TESTENV) ./mktest$(EXEEXT) generate 1 3 1000 4000 - $(TESTENV) ./snapraid$(EXEEXT) -v -c $(srcdir)/test.conf sync - $(TESTENV) ./snapraid$(EXEEXT) -c $(srcdir)/test.conf check - echo --- Create some more files, sync and check + $(TESTENV) ./snapraid$(EXEEXT) -v -c $(srcdir)/test-raid6.conf sync + $(TESTENV) ./snapraid$(EXEEXT) -c $(srcdir)/test-raid5.conf check + $(TESTENV) ./snapraid$(EXEEXT) -c $(srcdir)/test-raid6.conf check + echo --- Create some more files, sync raid5 and check $(TESTENV) ./mktest$(EXEEXT) generate 2 3 1000 4000 - $(TESTENV) ./snapraid$(EXEEXT) -v -c $(srcdir)/test.conf sync - $(TESTENV) ./snapraid$(EXEEXT) -c $(srcdir)/test.conf check + $(TESTENV) ./snapraid$(EXEEXT) -v -c $(srcdir)/test-raid5.conf sync + $(TESTENV) ./snapraid$(EXEEXT) -c $(srcdir)/test-raid5.conf check + echo --- Extend raid5 to raid6 with fix and check + $(TESTENV) ./snapraid$(EXEEXT) -R -c $(srcdir)/test-raid6.conf check 2>/dev/null + $(TESTENV) ./snapraid$(EXEEXT) -c $(srcdir)/test-raid6.conf fix 2>/dev/null + $(TESTENV) ./snapraid$(EXEEXT) -c $(srcdir)/test-raid5.conf check + $(TESTENV) ./snapraid$(EXEEXT) -c $(srcdir)/test-raid6.conf check echo --- Dry - $(TESTENV) ./snapraid$(EXEEXT) -c $(srcdir)/test.conf dry - echo --- Delete some files and create some new, sync and check + $(TESTENV) ./snapraid$(EXEEXT) -c $(srcdir)/test-raid5.conf dry + $(TESTENV) ./snapraid$(EXEEXT) -c $(srcdir)/test-raid6.conf dry + echo --- Delete some files, create some new, sync and check rm test/disk1/1* rm test/disk2/2* rm test/disk3/3* $(TESTENV) ./mktest$(EXEEXT) generate 3 3 100 4000 - $(TESTENV) ./snapraid$(EXEEXT) -v -c $(srcdir)/test.conf sync - $(TESTENV) ./snapraid$(EXEEXT) -c $(srcdir)/test.conf check + $(TESTENV) ./snapraid$(EXEEXT) -v -c $(srcdir)/test-raid6.conf sync + $(TESTENV) ./snapraid$(EXEEXT) -c $(srcdir)/test-raid6.conf check echo --- Move some files, sync and check mv test/disk1/7* test/disk1/_a/ mv test/disk2/7* test/disk2/_a/ mv test/disk3/7* test/disk3/_a/ - $(TESTENV) ./snapraid$(EXEEXT) -v -c $(srcdir)/test.conf sync - $(TESTENV) ./snapraid$(EXEEXT) -c $(srcdir)/test.conf check + $(TESTENV) ./snapraid$(EXEEXT) -v -c $(srcdir)/test-raid6.conf sync + $(TESTENV) ./snapraid$(EXEEXT) -c $(srcdir)/test-raid6.conf check + echo --- Delete one disk, fix and check with raid5 + rm -r test/disk3 + mkdir test/disk3 + $(TESTENV) ./snapraid$(EXEEXT) -R -c $(srcdir)/test-raid5.conf check 2>/dev/null + $(TESTENV) ./snapraid$(EXEEXT) -c $(srcdir)/test-raid5.conf fix 2>/dev/null + $(TESTENV) ./snapraid$(EXEEXT) -c $(srcdir)/test-raid6.conf check + echo --- Delete two disks, fix and check with raid6 + rm -r test/disk1 + mkdir test/disk1 + rm -r test/disk2 + mkdir test/disk2 + $(TESTENV) ./snapraid$(EXEEXT) -U -c $(srcdir)/test-raid5.conf check 2>/dev/null + $(TESTENV) ./snapraid$(EXEEXT) -R -c $(srcdir)/test-raid6.conf check 2>/dev/null + $(TESTENV) ./snapraid$(EXEEXT) -c $(srcdir)/test-raid6.conf fix 2>/dev/null + $(TESTENV) ./snapraid$(EXEEXT) -c $(srcdir)/test-raid6.conf check + echo --- Sync after all the fixes with an empty drive to test -E + $(TESTENV) ./snapraid$(EXEEXT) -v -E -c $(srcdir)/test-raid6.conf sync echo --- Delete some files and create some new, sync and check in multiple steps rm test/disk1/4* rm test/disk2/5* rm test/disk3/6* $(TESTENV) ./mktest$(EXEEXT) generate 4 3 100 4000 - $(TESTENV) ./snapraid$(EXEEXT) -v -c $(srcdir)/test.conf -t 7 sync - $(TESTENV) ./snapraid$(EXEEXT) -R -c $(srcdir)/test.conf check 2>/dev/null - $(TESTENV) ./snapraid$(EXEEXT) -v -c $(srcdir)/test.conf -t 11 sync - $(TESTENV) ./snapraid$(EXEEXT) -R -c $(srcdir)/test.conf check 2>/dev/null - $(TESTENV) ./snapraid$(EXEEXT) -v -c $(srcdir)/test.conf sync - $(TESTENV) ./snapraid$(EXEEXT) -c $(srcdir)/test.conf check - echo --- Delete some files, fix and check + $(TESTENV) ./snapraid$(EXEEXT) -v -c $(srcdir)/test-raid6.conf -t 7 sync + $(TESTENV) ./snapraid$(EXEEXT) -R -c $(srcdir)/test-raid6.conf check 2>/dev/null + $(TESTENV) ./snapraid$(EXEEXT) -v -c $(srcdir)/test-raid6.conf -t 11 sync + $(TESTENV) ./snapraid$(EXEEXT) -R -c $(srcdir)/test-raid6.conf check 2>/dev/null + $(TESTENV) ./snapraid$(EXEEXT) -v -c $(srcdir)/test-raid6.conf sync + $(TESTENV) ./snapraid$(EXEEXT) -c $(srcdir)/test-raid6.conf check + echo --- Delete some files, fix and check with raid5 rm test/disk1/1* - $(TESTENV) ./snapraid$(EXEEXT) -R -c $(srcdir)/test.conf check 2>/dev/null - $(TESTENV) ./snapraid$(EXEEXT) -c $(srcdir)/test.conf fix 2>/dev/null - $(TESTENV) ./snapraid$(EXEEXT) -c $(srcdir)/test.conf check - echo --- Delete some dirs, fix and check + $(TESTENV) ./snapraid$(EXEEXT) -R -c $(srcdir)/test-raid5.conf check 2>/dev/null + $(TESTENV) ./snapraid$(EXEEXT) -c $(srcdir)/test-raid5.conf fix 2>/dev/null + $(TESTENV) ./snapraid$(EXEEXT) -c $(srcdir)/test-raid6.conf check + echo --- Delete some dirs, fix and check with raid5 rm -r test/disk2/_a rm -r test/disk2/_b - $(TESTENV) ./snapraid$(EXEEXT) -R -c $(srcdir)/test.conf check 2>/dev/null - $(TESTENV) ./snapraid$(EXEEXT) -c $(srcdir)/test.conf fix 2>/dev/null - $(TESTENV) ./snapraid$(EXEEXT) -c $(srcdir)/test.conf check - echo --- Delete a whole disk, fix and check - rm -r test/disk3 - mkdir test/disk3 - $(TESTENV) ./snapraid$(EXEEXT) -R -c $(srcdir)/test.conf check 2>/dev/null - $(TESTENV) ./snapraid$(EXEEXT) -c $(srcdir)/test.conf fix 2>/dev/null - $(TESTENV) ./snapraid$(EXEEXT) -c $(srcdir)/test.conf check - echo --- Sync after all the fixes with an empty drive - $(TESTENV) ./snapraid$(EXEEXT) -v -E -c $(srcdir)/test.conf sync - echo --- Delete the parity, fix and check + $(TESTENV) ./snapraid$(EXEEXT) -R -c $(srcdir)/test-raid5.conf check 2>/dev/null + $(TESTENV) ./snapraid$(EXEEXT) -c $(srcdir)/test-raid5.conf fix 2>/dev/null + $(TESTENV) ./snapraid$(EXEEXT) -c $(srcdir)/test-raid6.conf check + echo --- Delete some dirs, fix and check with raid6 + rm -r test/disk2/_a + rm -r test/disk2/_b + rm -r test/disk3/_a + rm -r test/disk3/_b + $(TESTENV) ./snapraid$(EXEEXT) -U -c $(srcdir)/test-raid5.conf check 2>/dev/null + $(TESTENV) ./snapraid$(EXEEXT) -R -c $(srcdir)/test-raid6.conf check 2>/dev/null + $(TESTENV) ./snapraid$(EXEEXT) -c $(srcdir)/test-raid6.conf fix 2>/dev/null + $(TESTENV) ./snapraid$(EXEEXT) -c $(srcdir)/test-raid6.conf check + echo --- Delete the parity, fix and check with raid5 rm test/parity - $(TESTENV) ./snapraid$(EXEEXT) -c $(srcdir)/test.conf check 2>/dev/null - $(TESTENV) ./snapraid$(EXEEXT) -c $(srcdir)/test.conf fix 2>/dev/null - $(TESTENV) ./snapraid$(EXEEXT) -c $(srcdir)/test.conf check - echo --- Corrupt the parity, fix and check + $(TESTENV) ./snapraid$(EXEEXT) -c $(srcdir)/test-raid5.conf check 2>/dev/null + $(TESTENV) ./snapraid$(EXEEXT) -c $(srcdir)/test-raid5.conf fix 2>/dev/null + $(TESTENV) ./snapraid$(EXEEXT) -c $(srcdir)/test-raid6.conf check + echo --- Delete the parity and the q-parity, fix and check with raid6 + rm test/parity + rm test/q-parity + $(TESTENV) ./snapraid$(EXEEXT) -c $(srcdir)/test-raid6.conf fix 2>/dev/null + $(TESTENV) ./snapraid$(EXEEXT) -c $(srcdir)/test-raid6.conf check + echo --- Delete the parity and a disk, fix and check with raid6 + rm test/parity + rm -r test/disk2/_a + rm -r test/disk2/_b + $(TESTENV) ./snapraid$(EXEEXT) -c $(srcdir)/test-raid6.conf fix 2>/dev/null + $(TESTENV) ./snapraid$(EXEEXT) -c $(srcdir)/test-raid6.conf check + echo --- Delete the q-parity and a disk, fix and check with raid6 + rm test/q-parity + rm -r test/disk3/_a + rm -r test/disk3/_b + $(TESTENV) ./snapraid$(EXEEXT) -c $(srcdir)/test-raid6.conf fix 2>/dev/null + $(TESTENV) ./snapraid$(EXEEXT) -c $(srcdir)/test-raid6.conf check + echo --- Corrupt the parity, fix and check with raid5 $(TESTENV) ./mktest$(EXEEXT) damage 1 10 10000 test/parity - $(TESTENV) ./snapraid$(EXEEXT) -R -c $(srcdir)/test.conf check 2>/dev/null - $(TESTENV) ./snapraid$(EXEEXT) -c $(srcdir)/test.conf fix 2>/dev/null - $(TESTENV) ./snapraid$(EXEEXT) -c $(srcdir)/test.conf check - echo --- Corrupt some files, fix and check + $(TESTENV) ./snapraid$(EXEEXT) -R -c $(srcdir)/test-raid5.conf check 2>/dev/null + $(TESTENV) ./snapraid$(EXEEXT) -c $(srcdir)/test-raid5.conf fix 2>/dev/null + $(TESTENV) ./snapraid$(EXEEXT) -c $(srcdir)/test-raid6.conf check + echo --- Corrupt some files, fix and check with raid5 $(TESTENV) ./mktest$(EXEEXT) damage 1 1 500 test/disk2/b* - $(TESTENV) ./snapraid$(EXEEXT) -R -c $(srcdir)/test.conf check 2>/dev/null - $(TESTENV) ./snapraid$(EXEEXT) -c $(srcdir)/test.conf fix 2>/dev/null - $(TESTENV) ./snapraid$(EXEEXT) -c $(srcdir)/test.conf check + $(TESTENV) ./snapraid$(EXEEXT) -R -c $(srcdir)/test-raid5.conf check 2>/dev/null + $(TESTENV) ./snapraid$(EXEEXT) -c $(srcdir)/test-raid5.conf fix 2>/dev/null + $(TESTENV) ./snapraid$(EXEEXT) -c $(srcdir)/test-raid6.conf check echo --- Sync after all the fixes - $(TESTENV) ./snapraid$(EXEEXT) -v -c $(srcdir)/test.conf sync + $(TESTENV) ./snapraid$(EXEEXT) -v -c $(srcdir)/test-raid6.conf sync + $(TESTENV) ./snapraid$(EXEEXT) -c $(srcdir)/test-raid6.conf check echo Success! # Manual testing -CHECK = ./snapraid -v -c test.conf sync +CHECK = ./snapraid -v -c test-raid5.conf sync valgrind: valgrind \ diff --git a/TODO b/TODO index 3afc369f..f3731570 100644 --- a/TODO +++ b/TODO @@ -14,21 +14,9 @@ completely restored. * Provide an "include" logic in filtering. -* Add RAID6 redundancy support. There is the very good raid6 library from -H. Peter Anvin (hpa) in the Linux Kernel ready to use. See: -http://kernel.org/pub/linux/kernel/people/hpa/raid6.pdf -http://lxr.linux.no/#linux+v2.6.38/lib/raid6 - * Recognize that a file is moved from one disk to another, and if the parity data doesn't overlap, do not recompute it. -* Go multithread to speed up the MD5 computation. We can do it when syncing and -checking. - -* Evaluate the new MurmorHash3 to use instead of MD5. It should be faster, -but unsure of it's reliability. In fact, in MurmorHash2 was found a serious -issue that suggests to wait some time before using it. - * Use the O_NOATIME flag if run as 'root'. Anyway a better solution is to mount the disk with the "noatime" and "nodiratime" options. diff --git a/check.c b/check.c index 07b32d73..30ad463a 100644 --- a/check.c +++ b/check.c @@ -38,66 +38,115 @@ struct failed_struct { * Returns <0 if failure for missing stratey, >0 if data is wrong and we cannot rebuild correctly, 0 on success. * If success, the parity and qarity are computed in the buffer variable. */ -static int repair(struct snapraid_state* state, unsigned i, unsigned diskmax, struct failed_struct* failed, unsigned failed_count, unsigned char** buffer, unsigned char* buffer_parity, unsigned char* buffer_qarity) +static int repair(struct snapraid_state* state, unsigned i, unsigned diskmax, struct failed_struct* failed, unsigned failed_count, unsigned char** buffer, unsigned char* buffer_parity, unsigned char* buffer_qarity, unsigned char* buffer_zero) { + unsigned char hash[HASH_SIZE]; + unsigned size; + unsigned j; + int error = 0; + /* no fix required */ if (failed_count == 0) { /* compute parity and qarity to check it */ - if (state->level == 1) { - raid5_gen(buffer, diskmax + state->level, state->block_size); - } else { - raid6_gen(buffer, diskmax + state->level, state->block_size); - } - + raid_gen(state->level, buffer, diskmax, state->block_size); return 0; } - /* fix with RAID5 */ if (failed_count == 1 && buffer_parity != 0) { - unsigned char hash[HASH_SIZE]; - unsigned size; - unsigned j; + /* copy the redundancy to use */ + memcpy(buffer[diskmax], buffer_parity, state->block_size); - /* recompute the block */ - memcpy(buffer[failed[0].index], buffer_parity, state->block_size); - for(j=0;jblock_size); + /* recover */ + raid5_recov_data(buffer, diskmax, state->block_size, failed[0].index); + + for(j=0;j<1;++j) { + size = block_file_size(failed[j].block, state->block_size); + + /* now compute the hash */ + memhash(state->hash, hash, buffer[failed[j].index], size); + + /* compare the hash */ + if (memcmp(hash, failed[j].block->hash, HASH_SIZE) != 0) { + break; } } - size = block_file_size(failed[0].block, state->block_size); - - /* now compute the hash */ - memhash(state->hash, hash, buffer[failed[0].index], size); - - /* compare the hash */ - if (memcmp(hash, failed[0].block->hash, HASH_SIZE) != 0) { - fprintf(stderr, "%u: Parity data error\n", i); - return 1; + if (j==1) { + /* compute parity and qarity to check it */ + /* we recompute everything because we may have used only a small part of the redundancy */ + raid_gen(state->level, buffer, diskmax, state->block_size); + return 0; } - /* compute parity and qarity to check it */ - /* we recompute also the just used parity, because we may have used only a small part of it */ - if (state->level == 1) { - raid5_gen(buffer, diskmax + state->level, state->block_size); - } else { - raid6_gen(buffer, diskmax + state->level, state->block_size); - } - - return 0; + fprintf(stderr, "%u: Parity data error\n", i); + ++error; } if (failed_count == 1 && buffer_qarity != 0) { - /* TODO RAID6 */ + /* copy the redundancy to use */ + memcpy(buffer[diskmax+1], buffer_qarity, state->block_size); + + raid6_recov_datap(buffer, diskmax, state->block_size, failed[0].index, buffer_zero); + + for(j=0;j<1;++j) { + size = block_file_size(failed[j].block, state->block_size); + + /* now compute the hash */ + memhash(state->hash, hash, buffer[failed[j].index], size); + + /* compare the hash */ + if (memcmp(hash, failed[j].block->hash, HASH_SIZE) != 0) { + break; + } + } + + if (j==1) { + /* compute parity and qarity to check it */ + /* we recompute everything because we may have used only a small part of the redundancy */ + raid_gen(state->level, buffer, diskmax, state->block_size); + return 0; + } + + fprintf(stderr, "%u: Q-Parity data error\n", i); + ++error; } if (failed_count == 2 && buffer_parity != 0 && buffer_qarity != 0) { - /* TODO RAID6 */ + /* copy the redundancy to use */ + memcpy(buffer[diskmax], buffer_parity, state->block_size); + memcpy(buffer[diskmax+1], buffer_qarity, state->block_size); + + /* recover */ + raid6_recov_2data(buffer, diskmax, state->block_size, failed[0].index, failed[1].index, buffer_zero); + + for(j=0;j<2;++j) { + size = block_file_size(failed[j].block, state->block_size); + + /* now compute the hash */ + memhash(state->hash, hash, buffer[failed[j].index], size); + + /* compare the hash */ + if (memcmp(hash, failed[j].block->hash, HASH_SIZE) != 0) { + break; + } + } + + if (j==2) { + /* compute parity and qarity to check it */ + /* we recompute everything because we may have used only a small part of the redundancy */ + raid_gen(state->level, buffer, diskmax, state->block_size); + return 0; + } + + fprintf(stderr, "%u: Parity/Q-Parity data error\n", i); + ++error; } /* no more stragety to fix */ - return -1; + if (error) + return error; + else + return -1; } static int state_check_process(struct snapraid_state* state, int fix, int parity_f, int qarity_f, block_off_t blockstart, block_off_t blockmax) @@ -120,14 +169,15 @@ static int state_check_process(struct snapraid_state* state, int fix, int parity unsigned unrecoverable_error; unsigned recovered_error; - /* we need disk + 2 for each parity level buffers */ - buffermax = diskmax + state->level * 2; + /* we need disk + 2 for each parity level buffers + 1 zero buffer */ + buffermax = diskmax + state->level * 2 + 1; buffer_aligned = malloc_nofail_align(buffermax * state->block_size, &buffer_alloc); buffer = malloc_nofail(buffermax * sizeof(void*)); for(i=0;iblock_size; } + memset(buffer[buffermax-1], 0, state->block_size); handle = malloc_nofail(diskmax * sizeof(struct snapraid_handle)); for(i=0;ilevel == 1) { @@ -303,7 +354,8 @@ static int state_check_process(struct snapraid_state* state, int fix, int parity buffer_parity = buffer[diskmax + 2]; buffer_qarity = buffer[diskmax + 3]; } - + buffer_zero = buffer[buffermax-1]; + /* read the parity */ if (parity_f != -1) { ret = parity_read(state->parity, parity_f, i, buffer_parity, state->block_size); @@ -332,7 +384,7 @@ static int state_check_process(struct snapraid_state* state, int fix, int parity } } - ret = repair(state, i, diskmax, failed, failed_count, buffer, buffer_parity, buffer_qarity); + ret = repair(state, i, diskmax, failed, failed_count, buffer, buffer_parity, buffer_qarity, buffer_zero); if (ret != 0) { /* increment the number of errors */ if (ret > 0) @@ -402,10 +454,10 @@ static int state_check_process(struct snapraid_state* state, int fix, int parity ++unrecoverable_error; goto bail; } - } - fprintf(stderr, "%u: Fixed Q-Parity error\n", i); - ++recovered_error; + fprintf(stderr, "%u: Fixed Q-Parity error\n", i); + ++recovered_error; + } } } } @@ -471,13 +523,10 @@ bail: return -1; } } else { - if (state->expect_unrecoverable && state->expect_recoverable) { - if (error == 0 || unrecoverable_error == 0) - return -1; - } else if (state->expect_unrecoverable) { + if (state->expect_unrecoverable) { if (unrecoverable_error == 0) return -1; - } if (state->expect_recoverable) { + } else if (state->expect_recoverable) { if (error == 0) return -1; } else { diff --git a/raid.c b/raid.c index 9ee398d6..eb02daf3 100644 --- a/raid.c +++ b/raid.c @@ -31,11 +31,16 @@ #include "raid.h" #include "cpu.h" +#include "tables.h" + +/* internal forwarder */ +static void (*raid5_gen)(unsigned char** buffer, unsigned diskmax, unsigned size); +static void (*raid6_gen)(unsigned char** buffer, unsigned diskmax, unsigned size); /* * 2-way unrolled portable integer math RAID-5 instruction set */ -void raid5_int32r2(unsigned char** dptr, unsigned diskmax, unsigned block_size) +void raid5_int32r2(unsigned char** buffer, unsigned diskmax, unsigned size) { unsigned char* p; int z, z0; @@ -44,15 +49,15 @@ void raid5_int32r2(unsigned char** dptr, unsigned diskmax, unsigned block_size) uint32_t wp0; uint32_t wp1; - z0 = diskmax - 2; /* Highest data disk */ - p = dptr[z0+1]; /* XOR parity */ + z0 = diskmax - 1; /* Highest data disk */ + p = buffer[z0+1]; /* XOR parity */ - for(d=0;d=0;--z) { - wp0 ^= *(uint32_t *)&dptr[z][d+0*4]; - wp1 ^= *(uint32_t *)&dptr[z][d+1*4]; + wp0 ^= *(uint32_t *)&buffer[z][d+0*4]; + wp1 ^= *(uint32_t *)&buffer[z][d+1*4]; } *(uint32_t *)&p[d+4*0] = wp0; *(uint32_t *)&p[d+4*1] = wp1; @@ -62,21 +67,21 @@ void raid5_int32r2(unsigned char** dptr, unsigned diskmax, unsigned block_size) /* * Unrolled-by-2 MMX implementation */ -void raid5_mmxr2(unsigned char** dptr, unsigned diskmax, unsigned block_size) +void raid5_mmxr2(unsigned char** buffer, unsigned diskmax, unsigned size) { unsigned char* p; int z, z0; unsigned d; - z0 = diskmax - 2; /* Highest data disk */ - p = dptr[z0+1]; /* XOR parity */ + z0 = diskmax - 1; /* Highest data disk */ + p = buffer[z0+1]; /* XOR parity */ - for(d=0;d=0;--z) { - asm volatile("movq %0,%%mm5" : : "m" (dptr[z][d])); - asm volatile("movq %0,%%mm7" : : "m" (dptr[z][d+8])); + asm volatile("movq %0,%%mm5" : : "m" (buffer[z][d])); + asm volatile("movq %0,%%mm7" : : "m" (buffer[z][d+8])); asm volatile("pxor %mm5,%mm2"); asm volatile("pxor %mm7,%mm3"); } @@ -87,21 +92,21 @@ void raid5_mmxr2(unsigned char** dptr, unsigned diskmax, unsigned block_size) asm volatile("emms" : : : "memory"); } -void raid5_sse2r2(unsigned char** dptr, unsigned diskmax, unsigned block_size) +void raid5_sse2r2(unsigned char** buffer, unsigned diskmax, unsigned size) { unsigned char* p; int z, z0; unsigned d; - z0 = diskmax - 2; /* Highest data disk */ - p = dptr[z0+1]; /* XOR parity */ + z0 = diskmax - 1; /* Highest data disk */ + p = buffer[z0+1]; /* XOR parity */ - for(d=0;d=0;--z) { - asm volatile("movdqa %0,%%xmm5" : : "m" (dptr[z][d])); - asm volatile("movdqa %0,%%xmm7" : : "m" (dptr[z][d+16])); + asm volatile("movdqa %0,%%xmm5" : : "m" (buffer[z][d])); + asm volatile("movdqa %0,%%xmm7" : : "m" (buffer[z][d+16])); asm volatile("pxor %xmm5,%xmm2"); asm volatile("pxor %xmm7,%xmm3"); } @@ -150,7 +155,7 @@ static inline uint32_t MASK(uint32_t v) /* * 2-way unrolled portable integer math RAID-6 instruction set */ -void raid6_int32r2(unsigned char** dptr, unsigned diskmax, unsigned block_size) +void raid6_int32r2(unsigned char** buffer, unsigned diskmax, unsigned size) { unsigned char* p; unsigned char* q; @@ -160,16 +165,16 @@ void raid6_int32r2(unsigned char** dptr, unsigned diskmax, unsigned block_size) uint32_t wd0, wq0, wp0, w10, w20; uint32_t wd1, wq1, wp1, w11, w21; - z0 = diskmax - 3; /* Highest data disk */ - p = dptr[z0+1]; /* XOR parity */ - q = dptr[z0+2]; /* RS qarity */ + z0 = diskmax - 1; /* Highest data disk */ + p = buffer[z0+1]; /* XOR parity */ + q = buffer[z0+2]; /* RS qarity */ - for(d=0;d= 0;--z) { - wd0 = *(uint32_t *)&dptr[z][d+0*4]; - wd1 = *(uint32_t *)&dptr[z][d+1*4]; + wd0 = *(uint32_t *)&buffer[z][d+0*4]; + wd1 = *(uint32_t *)&buffer[z][d+1*4]; wp0 ^= wd0; wp1 ^= wd1; w20 = MASK(wq0); @@ -201,24 +206,24 @@ static const struct raid6_mmx_constants { /* * Unrolled-by-2 MMX implementation */ -void raid6_mmxr2(unsigned char** dptr, unsigned diskmax, unsigned block_size) +void raid6_mmxr2(unsigned char** buffer, unsigned diskmax, unsigned size) { unsigned char* p; unsigned char* q; int z, z0; unsigned d; - z0 = diskmax - 3; /* Highest data disk */ - p = dptr[z0+1]; /* XOR parity */ - q = dptr[z0+2]; /* RS qarity */ + z0 = diskmax - 1; /* Highest data disk */ + p = buffer[z0+1]; /* XOR parity */ + q = buffer[z0+2]; /* RS qarity */ asm volatile("movq %0,%%mm0" : : "m" (raid6_mmx_constants.x1d)); asm volatile("pxor %mm5,%mm5"); /* Zero temp */ asm volatile("pxor %mm7,%mm7"); /* Zero temp */ - for(d=0;d=0;--z) { @@ -230,8 +235,8 @@ void raid6_mmxr2(unsigned char** dptr, unsigned diskmax, unsigned block_size) asm volatile("pand %mm0,%mm7"); asm volatile("pxor %mm5,%mm4"); asm volatile("pxor %mm7,%mm6"); - asm volatile("movq %0,%%mm5" : : "m" (dptr[z][d])); - asm volatile("movq %0,%%mm7" : : "m" (dptr[z][d+8])); + asm volatile("movq %0,%%mm5" : : "m" (buffer[z][d])); + asm volatile("movq %0,%%mm7" : : "m" (buffer[z][d+8])); asm volatile("pxor %mm5,%mm2"); asm volatile("pxor %mm7,%mm3"); asm volatile("pxor %mm5,%mm4"); @@ -257,30 +262,30 @@ static const struct raid6_sse_constants { /* * Unrolled-by-2 SSE2 implementation */ -void raid6_sse2r2(unsigned char** dptr, unsigned diskmax, unsigned block_size) +void raid6_sse2r2(unsigned char** buffer, unsigned diskmax, unsigned size) { unsigned char* p; unsigned char* q; int z, z0; unsigned d; - z0 = diskmax - 3; /* Highest data disk */ - p = dptr[z0+1]; /* XOR parity */ - q = dptr[z0+2]; /* RS qarity */ + z0 = diskmax - 1; /* Highest data disk */ + p = buffer[z0+1]; /* XOR parity */ + q = buffer[z0+2]; /* RS qarity */ asm volatile("movdqa %0,%%xmm0" : : "m" (raid6_sse_constants.x1d[0])); asm volatile("pxor %xmm5,%xmm5"); /* Zero temp */ asm volatile("pxor %xmm7,%xmm7"); /* Zero temp */ /* We uniformly assume a single prefetch covers at least 32 bytes */ - for(d=0;d=0;--z) { - asm volatile("prefetchnta %0" : : "m" (dptr[z][d])); + asm volatile("prefetchnta %0" : : "m" (buffer[z][d])); asm volatile("pcmpgtb %xmm4,%xmm5"); asm volatile("pcmpgtb %xmm6,%xmm7"); asm volatile("paddb %xmm4,%xmm4"); @@ -289,8 +294,8 @@ void raid6_sse2r2(unsigned char** dptr, unsigned diskmax, unsigned block_size) asm volatile("pand %xmm0,%xmm7"); asm volatile("pxor %xmm5,%xmm4"); asm volatile("pxor %xmm7,%xmm6"); - asm volatile("movdqa %0,%%xmm5" : : "m" (dptr[z][d])); - asm volatile("movdqa %0,%%xmm7" : : "m" (dptr[z][d+16])); + asm volatile("movdqa %0,%%xmm5" : : "m" (buffer[z][d])); + asm volatile("movdqa %0,%%xmm7" : : "m" (buffer[z][d+16])); asm volatile("pxor %xmm5,%xmm2"); asm volatile("pxor %xmm7,%xmm3"); asm volatile("pxor %xmm5,%xmm4"); @@ -308,15 +313,111 @@ void raid6_sse2r2(unsigned char** dptr, unsigned diskmax, unsigned block_size) } #endif -void (*raid5_gen)(unsigned char** dptr, unsigned diskmax, unsigned block_size); -void (*raid6_gen)(unsigned char** dptr, unsigned diskmax, unsigned block_size); +void raid5_recov_data(unsigned char** dptrs, unsigned diskmax, unsigned size, int faila) +{ + unsigned char *p, *d; + + d = dptrs[faila]; + p = dptrs[diskmax]; + + /* Compute syndrome using parity for the missing data page. */ + dptrs[faila] = p; + dptrs[diskmax] = d; + + raid5_gen(dptrs, diskmax, size); + + /* Restore pointer table */ + dptrs[faila] = d; + dptrs[diskmax] = p; +} + +void raid6_recov_2data(unsigned char** dptrs, unsigned diskmax, unsigned size, int faila, int failb, unsigned char* zero) +{ + unsigned char *p, *q, *dp, *dq; + unsigned char px, qx, db; + const unsigned char *pbmul; /* P multiplier table for B data */ + const unsigned char *qmul; /* Q multiplier table (for both) */ + + p = dptrs[diskmax]; + q = dptrs[diskmax+1]; + + /* Compute syndrome with zero for the missing data pages. */ + /* Use the dead data pages as temporary storage for delta p and delta q. */ + dp = dptrs[faila]; + dptrs[faila] = zero; + dptrs[diskmax] = dp; + dq = dptrs[failb]; + dptrs[failb] = zero; + dptrs[diskmax+1] = dq; + + raid6_gen(dptrs, diskmax, size); + + /* Restore pointer table */ + dptrs[faila] = dp; + dptrs[failb] = dq; + dptrs[diskmax] = p; + dptrs[diskmax+1] = q; + + /* Now, pick the proper data tables */ + pbmul = raid6_gfmul[raid6_gfexi[failb-faila]]; + qmul = raid6_gfmul[raid6_gfinv[raid6_gfexp[faila]^raid6_gfexp[failb]]]; + + /* Now do it... */ + while (size--) { + px = *p ^ *dp; + qx = qmul[*q ^ *dq]; + *dq++ = db = pbmul[px] ^ qx; /* Reconstructed B */ + *dp++ = db ^ px; /* Reconstructed A */ + p++; + q++; + } +} + +void raid6_recov_datap(unsigned char** dptrs, unsigned diskmax, unsigned size, int faila, unsigned char* zero) +{ + unsigned char *p, *q, *dq; + const unsigned char* qmul; /* Q multiplier table */ + + p = dptrs[diskmax]; + q = dptrs[diskmax+1]; + + /* Compute syndrome with zero for the missing data page. */ + /* Use the dead data page as temporary storage for delta q. */ + dq = dptrs[faila]; + dptrs[faila] = zero; + dptrs[diskmax+1] = dq; + + raid6_gen(dptrs, diskmax, size); + + /* Restore pointer table */ + dptrs[faila] = dq; + dptrs[diskmax+1] = q; + + /* Now, pick the proper data tables */ + qmul = raid6_gfmul[raid6_gfinv[raid6_gfexp[faila]]]; + + /* Now do it... */ + while (size--) { + *p++ ^= *dq = qmul[*q ^ *dq]; + q++; + dq++; + } +} + +void raid_gen(unsigned level, unsigned char** buffer, unsigned diskmax, unsigned size) +{ + if (level == 1) { + raid5_gen(buffer, diskmax, size); + } else { + raid6_gen(buffer, diskmax, size); + } +} void raid_init(void) { raid5_gen = raid5_int32r2; raid6_gen = raid6_int32r2; #if defined(__i386__) || defined(__x86_64__) -/* if (cpu_has_mmx()) { raid5_gen = raid5_mmxr2; raid6_gen = raid6_mmxr2; @@ -325,7 +426,6 @@ void raid_init(void) raid5_gen = raid5_sse2r2; raid6_gen = raid6_sse2r2; } -*/ #endif } diff --git a/raid.h b/raid.h index 610d6c7d..6db271e9 100644 --- a/raid.h +++ b/raid.h @@ -21,21 +21,40 @@ /****************************************************************************/ /* raid */ -void raid5_int32r2(unsigned char** dptr, unsigned diskmax, unsigned block_size); -void raid5_mmxr2(unsigned char** dptr, unsigned diskmax, unsigned block_size); -void raid5_sse2r2(unsigned char** dptr, unsigned diskmax, unsigned block_size); +/** + * Syndrome computation. + */ +void raid_gen(unsigned level, unsigned char** buffer, unsigned diskmax, unsigned size); -void raid6_int32r2(unsigned char** dptr, unsigned diskmax, unsigned block_size); -void raid6_mmxr2(unsigned char** dptr, unsigned diskmax, unsigned block_size); -void raid6_sse2r2(unsigned char** dptr, unsigned diskmax, unsigned block_size); +/* + * Recover failure of one data block. + */ +void raid5_recov_data(unsigned char** dptrs, unsigned diskmax, unsigned size, int faila); -extern void (*raid5_gen)(uint8_t** ptrs, unsigned disks, unsigned block_size); -extern void (*raid6_gen)(uint8_t** ptrs, unsigned disks, unsigned block_size); +/* + * Recover two failed data blocks. + */ +void raid6_recov_2data(unsigned char** dptrs, unsigned diskmax, unsigned size, int faila, int failb, unsigned char* zero); + +/* + * Recover failure of one data block plus the P block. + */ +void raid6_recov_datap(unsigned char** dptrs, unsigned diskmax, unsigned size, int faila, unsigned char* zero); /** * Initializes the RAID system. */ void raid_init(void); +/** + * Internal specialized syndrome computation. + */ +void raid5_int32r2(unsigned char** dptr, unsigned diskmax, unsigned size); +void raid5_mmxr2(unsigned char** dptr, unsigned diskmax, unsigned size); +void raid5_sse2r2(unsigned char** dptr, unsigned diskmax, unsigned size); +void raid6_int32r2(unsigned char** dptr, unsigned diskmax, unsigned size); +void raid6_mmxr2(unsigned char** dptr, unsigned diskmax, unsigned size); +void raid6_sse2r2(unsigned char** dptr, unsigned diskmax, unsigned size); + #endif diff --git a/selftest.c b/selftest.c index 1460a0e9..e3b00945 100644 --- a/selftest.c +++ b/selftest.c @@ -78,18 +78,139 @@ struct test_vector TEST_MURMUR3[] = { { 0, { 0 } } }; -void selftest(void) +static void raid5test(unsigned diskmax, unsigned block_size) +{ + void* buffer_alloc; + unsigned char* buffer_aligned; + unsigned char** buffer; + unsigned char* buffer_save; + unsigned buffermax; + unsigned i, j; + + buffermax = diskmax + 1 + 1; + buffer_aligned = malloc_nofail_align(buffermax * block_size, &buffer_alloc); + buffer = malloc_nofail(buffermax * sizeof(void*)); + for(i=0;ilevel == 1) { - raid5_gen(buffer, diskmax + state->level, state->block_size); - } else { - raid6_gen(buffer, diskmax + state->level, state->block_size); - } + raid_gen(state->level, buffer, diskmax, state->block_size); /* write the parity */ ret = parity_write(state->parity, parity_f, i, buffer[diskmax], state->block_size); diff --git a/test.conf b/test-raid5.conf similarity index 100% rename from test.conf rename to test-raid5.conf diff --git a/test-raid6.conf b/test-raid6.conf new file mode 100644 index 00000000..fe9aced6 --- /dev/null +++ b/test-raid6.conf @@ -0,0 +1,8 @@ +block_size 1 +parity test/parity +q-parity test/q-parity +content test/content +disk d1 test/disk1/ +disk d2 test/disk2/ +disk d3 test/disk3/ +