Adds recovering at RAID6 support.

Adds selftesting of RAID5 and RAID5.
This commit is contained in:
Andrea Mazzoleni 2011-04-24 10:24:05 +02:00
parent 08831c6641
commit f855424082
11 changed files with 546 additions and 200 deletions

View file

@ -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 \

12
TODO
View file

@ -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.

147
check.c
View file

@ -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;j<diskmax;++j) {
if (j != failed[0].index) {
memxor(buffer[failed[0].index], buffer[j], state->block_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;i<buffermax;++i) {
buffer[i] = buffer_aligned + i * state->block_size;
}
memset(buffer[buffermax-1], 0, state->block_size);
handle = malloc_nofail(diskmax * sizeof(struct snapraid_handle));
for(i=0;i<diskmax;++i) {
@ -294,6 +344,7 @@ static int state_check_process(struct snapraid_state* state, int fix, int parity
{
unsigned char* buffer_parity;
unsigned char* buffer_qarity;
unsigned char* buffer_zero;
/* buffers for parity read and not computed */
if (state->level == 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 {

214
raid.c
View file

@ -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<block_size;d+=8) {
wp0 = *(uint32_t *)&dptr[z0][d+0*4];
wp1 = *(uint32_t *)&dptr[z0][d+1*4];
for(d=0;d<size;d+=8) {
wp0 = *(uint32_t *)&buffer[z0][d+0*4];
wp1 = *(uint32_t *)&buffer[z0][d+1*4];
for(z=z0-1;z>=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<block_size;d+=16) {
asm volatile("movq %0,%%mm2" : : "m" (dptr[z0][d]));
asm volatile("movq %0,%%mm3" : : "m" (dptr[z0][d+8]));
for(d=0;d<size;d+=16) {
asm volatile("movq %0,%%mm2" : : "m" (buffer[z0][d]));
asm volatile("movq %0,%%mm3" : : "m" (buffer[z0][d+8]));
for(z=z0-1;z>=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<block_size;d+=32) {
asm volatile("movdqa %0,%%xmm2" : : "m" (dptr[z0][d]));
asm volatile("movdqa %0,%%xmm3" : : "m" (dptr[z0][d+16]));
for(d=0;d<size;d+=32) {
asm volatile("movdqa %0,%%xmm2" : : "m" (buffer[z0][d]));
asm volatile("movdqa %0,%%xmm3" : : "m" (buffer[z0][d+16]));
for(z=z0-1;z>=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<block_size;d+=8) {
wq0 = wp0 = *(uint32_t *)&dptr[z0][d+0*4];
wq1 = wp1 = *(uint32_t *)&dptr[z0][d+1*4];
for(d=0;d<size;d+=8) {
wq0 = wp0 = *(uint32_t *)&buffer[z0][d+0*4];
wq1 = wp1 = *(uint32_t *)&buffer[z0][d+1*4];
for (z=z0-1;z>= 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<block_size;d+=16) {
asm volatile("movq %0,%%mm2" : : "m" (dptr[z0][d]));
asm volatile("movq %0,%%mm3" : : "m" (dptr[z0][d+8]));
for(d=0;d<size;d+=16) {
asm volatile("movq %0,%%mm2" : : "m" (buffer[z0][d]));
asm volatile("movq %0,%%mm3" : : "m" (buffer[z0][d+8]));
asm volatile("movq %mm2,%mm4");
asm volatile("movq %mm3,%mm6");
for(z=z0-1;z>=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<block_size;d+=32) {
asm volatile("prefetchnta %0" : : "m" (dptr[z0][d]));
asm volatile("movdqa %0,%%xmm2" : : "m" (dptr[z0][d])); /* P[0] */
asm volatile("movdqa %0,%%xmm3" : : "m" (dptr[z0][d+16])); /* P[1] */
for(d=0;d<size;d+=32) {
asm volatile("prefetchnta %0" : : "m" (buffer[z0][d]));
asm volatile("movdqa %0,%%xmm2" : : "m" (buffer[z0][d])); /* P[0] */
asm volatile("movdqa %0,%%xmm3" : : "m" (buffer[z0][d+16])); /* P[1] */
asm volatile("movdqa %xmm2,%xmm4"); /* Q[0] */
asm volatile("movdqa %xmm3,%xmm6"); /* Q[1] */
for(z=z0-1;z>=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
}

35
raid.h
View file

@ -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

View file

@ -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;i<buffermax;++i) {
buffer[i] = buffer_aligned + i * block_size;
}
buffer_save = buffer[buffermax-1];
/* fill with random */
for(i=0;i<diskmax;++i) {
for(j=0;j<block_size;++j)
buffer[i][j] = rand();
}
raid_gen(1, buffer, diskmax, block_size);
for(i=0;i<diskmax;++i) {
/* save the correct one */
memcpy(buffer_save, buffer[i], block_size);
/* destroy it */
memset(buffer[i], 0x55, block_size);
/* recover */
raid5_recov_data(buffer, diskmax, block_size, i);
/* check */
if (memcmp(buffer_save, buffer[i], block_size) != 0) {
fprintf(stderr, "Failed RAID5 test\n");
exit(EXIT_FAILURE);
}
}
free(buffer_alloc);
free(buffer);
}
static void raid6test(unsigned diskmax, unsigned block_size)
{
void* buffer_alloc;
unsigned char* buffer_aligned;
unsigned char** buffer;
unsigned char* buffer_save0;
unsigned char* buffer_save1;
unsigned char* buffer_zero;
unsigned buffermax;
unsigned i, j;
buffermax = diskmax + 2 + 2 + 1;
buffer_aligned = malloc_nofail_align(buffermax * block_size, &buffer_alloc);
buffer = malloc_nofail(buffermax * sizeof(void*));
for(i=0;i<buffermax;++i) {
buffer[i] = buffer_aligned + i * block_size;
}
buffer_save0 = buffer[buffermax-3];
buffer_save1 = buffer[buffermax-2];
buffer_zero = buffer[buffermax-1];
memset(buffer_zero, 0, block_size);
/* fill with random */
for(i=0;i<diskmax;++i) {
for(j=0;j<block_size;++j)
buffer[i][j] = rand();
}
raid_gen(2, buffer, diskmax, block_size);
/* check 2data */
for(i=0;i<diskmax;++i) {
for(j=i+1;j<diskmax;++j) {
/* save the correct ones */
memcpy(buffer_save0, buffer[i], block_size);
memcpy(buffer_save1, buffer[j], block_size);
/* destroy it */
memset(buffer[i], 0x55, block_size);
memset(buffer[j], 0x55, block_size);
/* recover */
raid6_recov_2data(buffer, diskmax, block_size, i, j, buffer_zero);
/* check */
if (memcmp(buffer_save0, buffer[i], block_size) != 0
|| memcmp(buffer_save1, buffer[j], block_size) != 0
) {
fprintf(stderr, "Failed RAID6 test\n");
exit(EXIT_FAILURE);
}
}
}
/* check datap */
for(i=0;i<diskmax;++i) {
/* the other is the parity */
j = diskmax;
/* save the correct ones */
memcpy(buffer_save0, buffer[i], block_size);
memcpy(buffer_save1, buffer[j], block_size);
/* destroy it */
memset(buffer[i], 0x55, block_size);
memset(buffer[j], 0x55, block_size);
/* recover */
raid6_recov_datap(buffer, diskmax, block_size, i, buffer_zero);
/* check */
if (memcmp(buffer_save0, buffer[i], block_size) != 0
|| memcmp(buffer_save1, buffer[j], block_size) != 0
) {
fprintf(stderr, "Failed RAID6 test\n");
exit(EXIT_FAILURE);
}
}
free(buffer_alloc);
free(buffer);
}
static void hashtest(void)
{
unsigned i;
printf("Self test...\n");
/* large file check */
if (sizeof(off_t) < sizeof(uint64_t)) {
fprintf(stderr, "Missing support for large files\n");
exit(EXIT_FAILURE);
}
for(i=0;TEST_MD5[i].data;++i) {
unsigned char digest[HASH_SIZE];
memhash(HASH_MD5, digest, TEST_MD5[i].data, strlen(TEST_MD5[i].data));
@ -109,3 +230,23 @@ void selftest(void)
}
}
void selftest(void)
{
unsigned i;
printf("Self test...\n");
/* large file check */
if (sizeof(off_t) < sizeof(uint64_t)) {
fprintf(stderr, "Missing support for large files\n");
exit(EXIT_FAILURE);
}
hashtest();
for(i=1;i<19;++i) {
raid5test(i, 4096);
raid6test(i, 4096);
}
}

View file

@ -201,10 +201,10 @@ int main(int argc, char* argv[])
exit(EXIT_FAILURE);
}
selftest();
raid_init();
selftest();
state_init(&state);
state_config(&state, conf, verbose, force_zero, force_empty, expect_unrecoverable, expect_recoverable);

12
speed.c
View file

@ -92,7 +92,7 @@ void speed(void)
gettimeofday(&start, 0);
for(j=0;j<count;++j) {
raid5_int32r2(buffer, diskmax + 1, block_size);
raid5_int32r2(buffer, diskmax, block_size);
}
gettimeofday(&stop, 0);
@ -105,7 +105,7 @@ void speed(void)
if (cpu_has_mmx()) {
gettimeofday(&start, 0);
for(j=0;j<count;++j) {
raid5_mmxr2(buffer, diskmax + 1, block_size);
raid5_mmxr2(buffer, diskmax, block_size);
}
gettimeofday(&stop, 0);
@ -118,7 +118,7 @@ void speed(void)
if (cpu_has_sse2()) {
gettimeofday(&start, 0);
for(j=0;j<count;++j) {
raid5_sse2r2(buffer, diskmax + 1, block_size);
raid5_sse2r2(buffer, diskmax, block_size);
}
gettimeofday(&stop, 0);
@ -131,7 +131,7 @@ void speed(void)
gettimeofday(&start, 0);
for(j=0;j<count;++j) {
raid6_int32r2(buffer, diskmax + 2, block_size);
raid6_int32r2(buffer, diskmax, block_size);
}
gettimeofday(&stop, 0);
@ -144,7 +144,7 @@ void speed(void)
if (cpu_has_mmx()) {
gettimeofday(&start, 0);
for(j=0;j<count;++j) {
raid6_mmxr2(buffer, diskmax + 2, block_size);
raid6_mmxr2(buffer, diskmax, block_size);
}
gettimeofday(&stop, 0);
@ -157,7 +157,7 @@ void speed(void)
if (cpu_has_sse2()) {
gettimeofday(&start, 0);
for(j=0;j<count;++j) {
raid6_sse2r2(buffer, diskmax + 2, block_size);
raid6_sse2r2(buffer, diskmax, block_size);
}
gettimeofday(&stop, 0);

6
sync.c
View file

@ -189,11 +189,7 @@ static int state_sync_process(struct snapraid_state* state, int parity_f, int qa
}
/* compute the parity */
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);
/* write the parity */
ret = parity_write(state->parity, parity_f, i, buffer[diskmax], state->block_size);

8
test-raid6.conf Normal file
View file

@ -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/