Adds complete Cauchy RAID support and removes the mixed Power RAID.

This commit is contained in:
Andrea Mazzoleni 2013-10-25 18:03:05 +02:00
parent 136af37e96
commit 9a5978eb22
20 changed files with 6111 additions and 72023 deletions

20
HISTORY
View file

@ -1,23 +1,13 @@
SnapRAID HISTORY
================
6.0 2013/10 (BETA)
==================
* Added support for unlimted RAIDQP (QuadParity), RAIDPP (Penta Parity)
and RAIDHP (Hexa parity).
We use a Cauchy parity generator that gets good performance only if
SSSE3 is available.
5.0 2013/10 (BETA)
==================
* Added support for RAIDTP (Triple Parity) and RAIDQP (Quad Parity).
For Quad Parity, and only for Quad Parity, there is a limitation of
using no more than 21 data disks. This is a conseguence of the
high performant parity generator matrix used.
Extensions supporting more disks and more parities are possible,
but not yet implemented.
* Faster parity computation for SSSE3 and AVX CPUs.
* Faster parity computation for ARM 64 bit CPUs.
* Added support for RAIDTP (Triple Parity), RAIDQP (Quad Parity),
RAIDPP (Penta Parity) and RAIDHP (Hexa parity).
Take cate that to get good performance you need a CPU with
SSSE3 support.
* Faster RAID5 and RAID6 computation for ARM 64 bit CPUs.
* In sync, if a silent error is found, directly mark the block as bad
like in scrub, and continue with the sync process.
* Sort files by inode when listing the directory. This improves

View file

@ -1,6 +1,6 @@
bin_PROGRAMS = snapraid
check_PROGRAMS = mktest
EXTRA_PROGRAMS = mktables
EXTRA_PROGRAMS = mktables mkcombos
snapraid_SOURCES = \
snapraid.c \
@ -31,6 +31,9 @@ snapraid_SOURCES = \
mktables_SOURCES = \
mktables.c
mkcombos_SOURCES = \
mkcombos.c
noinst_HEADERS = \
portable.h \
snapraid.h \
@ -63,6 +66,7 @@ noinst_HEADERS = \
cpu.h \
raid.h \
tables.h \
combos.h \
import.h \
mingw.h \
unix.h
@ -76,7 +80,7 @@ EXTRA_DIST = \
noautomake.sh \
README AUTHORS HISTORY INSTALL COPYING TODO TEST INSTALL.windows \
snapraid.d snapraid.1 snapraid.txt \
test-raid5.conf test-raid6.conf test-raidTP.conf test-raidQP.conf test-raidQP-hole.conf \
test-raid5.conf test-raid6.conf test-raidTP.conf test-raidQP.conf test-raidPP.conf test-raidHP.conf test-raidHP-hole.conf \
snapraid.conf.example \
configure.windows-x86 configure.windows-x64 snapraid.conf.example.windows \
acinclude.m4
@ -100,12 +104,14 @@ maintainer-clean-local:
CHECKFLAGS = --test-skip-device --test-skip-self
CHECKCOUNT = 2000
CHECKSIZE = 4000
CONF = $(srcdir)/test-raidQP.conf
HOLE = $(srcdir)/test-raidQP-hole.conf
CONF = $(srcdir)/test-raidHP.conf
HOLE = $(srcdir)/test-raidHP-hole.conf
RAID5 = $(srcdir)/test-raid5.conf
RAID6 = $(srcdir)/test-raid6.conf
RAIDTP = $(srcdir)/test-raidTP.conf
RAIDQP = $(srcdir)/test-raidQP.conf
RAIDPP = $(srcdir)/test-raidPP.conf
RAIDHP = $(srcdir)/test-raidHP.conf
check-local:
rm -rf test
@ -116,6 +122,7 @@ check-local:
mkdir test/disk3
mkdir test/disk4
mkdir test/disk5
mkdir test/disk6
mkdir test/pool
echo --- Help
$(TESTENV) ./snapraid$(EXEEXT) --test-skip-device -h
@ -133,7 +140,7 @@ endif
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(CONF) check
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(CONF) -a check
echo --- Create some files, sync and check in RAID5/6/TP
$(TESTENV) ./mktest$(EXEEXT) generate 1 5 $(CHECKCOUNT) $(CHECKSIZE)
$(TESTENV) ./mktest$(EXEEXT) generate 1 6 $(CHECKCOUNT) $(CHECKSIZE)
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -v -c $(CONF) diff > output.log
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -v -c $(CONF) diff -G > output.log
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(CONF) --test-force-order-physical sync
@ -149,7 +156,7 @@ endif
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(CONF) check
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(CONF) -a check
echo --- Create some more files, hardlinks and empty directories, sync RAID5 and check
$(TESTENV) ./mktest$(EXEEXT) generate 2 5 $(CHECKCOUNT) $(CHECKSIZE)
$(TESTENV) ./mktest$(EXEEXT) generate 2 6 $(CHECKCOUNT) $(CHECKSIZE)
mkdir test/disk1/empty1
mkdir test/disk2/empty2
mkdir test/disk3/empty3a
@ -174,7 +181,7 @@ endif
if HAVE_SYMLINK
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(RAID5) pool
endif
echo --- Extend RAID5 to RAIDQP with fix and check
echo --- Extend RAID5 to max parity with fix and check
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) --test-expect-recoverable -c $(CONF) check -l test.log
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(CONF) fix -l test.log
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(RAID5) check
@ -224,7 +231,7 @@ endif
rm test/disk2/2*
rm test/disk3/3*
rm test/disk4/4*
$(TESTENV) ./mktest$(EXEEXT) generate 3 5 $(CHECKCOUNT) $(CHECKSIZE)
$(TESTENV) ./mktest$(EXEEXT) generate 3 6 $(CHECKCOUNT) $(CHECKSIZE)
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(CONF) sync
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(CONF) check
echo --- Move some files, sync and check
@ -240,9 +247,7 @@ endif
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) --test-expect-recoverable -c $(RAID5) check -l test.log
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(RAID5) fix -l test.log
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(CONF) check
echo --- Sync after all the fixes with an empty drive to test --force-empty
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) --force-empty -c $(CONF) sync
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(CONF) check
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(CONF) sync
echo --- Delete two disks, fix and check with RAID6
rm -r test/disk1
mkdir test/disk1
@ -260,13 +265,42 @@ endif
mkdir test/disk2
rm -r test/disk3
mkdir test/disk3
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) --test-expect-unrecoverable -c $(RAID5) fix -l test.log
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) --test-expect-unrecoverable -c $(RAID6) fix -l test.log
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) --test-expect-recoverable -c $(RAIDTP) check -l test.log
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(RAIDTP) fix -l test.log
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(CONF) check
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(CONF) sync
echo --- Delete four disks, fix and check with RAIDQP
rm -r test/disk3
mkdir test/disk3
rm -r test/disk4
mkdir test/disk4
rm -r test/disk5
mkdir test/disk5
rm -r test/disk6
mkdir test/disk6
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) --test-expect-unrecoverable -c $(RAIDTP) fix -l test.log
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) --test-expect-recoverable -c $(RAIDQP) check -l test.log
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(RAIDQP) fix -l test.log
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(CONF) check
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(CONF) sync
echo --- Delete five disks, fix and check with RAIDPP
rm -r test/disk1
mkdir test/disk1
rm -r test/disk2
mkdir test/disk2
rm -r test/disk3
mkdir test/disk3
rm -r test/disk5
mkdir test/disk5
rm -r test/disk6
mkdir test/disk6
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) --test-expect-unrecoverable -c $(RAIDQP) fix -l test.log
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) --test-expect-recoverable -c $(RAIDPP) check -l test.log
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(RAIDPP) fix -l test.log
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(CONF) check
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(CONF) sync
echo --- Delete six disks, fix and check with RAIDHP
rm -r test/disk1
mkdir test/disk1
rm -r test/disk2
@ -275,11 +309,13 @@ endif
mkdir test/disk3
rm -r test/disk4
mkdir test/disk4
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) --test-expect-unrecoverable -c $(RAID5) fix -l test.log
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) --test-expect-unrecoverable -c $(RAID6) fix -l test.log
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) --test-expect-unrecoverable -c $(RAIDTP) fix -l test.log
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) --test-expect-recoverable -c $(RAIDQP) check -l test.log
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(RAIDQP) fix -l test.log
rm -r test/disk5
mkdir test/disk5
rm -r test/disk6
mkdir test/disk6
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) --test-expect-unrecoverable -c $(RAIDPP) fix -l test.log
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) --test-expect-recoverable -c $(RAIDHP) check -l test.log
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(RAIDHP) fix -l test.log
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(CONF) check
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(CONF) sync
echo --- Delete some files and create some new, sync and check in multiple steps using text content
@ -293,7 +329,7 @@ endif
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(CONF) --test-force-content-text test-rewrite
cmp test/content test/content.txt
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(CONF) check -l test.log
$(TESTENV) ./mktest$(EXEEXT) generate 4 5 $(CHECKCOUNT) $(CHECKSIZE)
$(TESTENV) ./mktest$(EXEEXT) generate 4 6 $(CHECKCOUNT) $(CHECKSIZE)
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(CONF) -t 100 sync --test-force-content-text
cp test/content test/content.txt
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) -c $(CONF) test-rewrite

294
check.c
View file

@ -24,6 +24,7 @@
#include "parity.h"
#include "handle.h"
#include "raid.h"
#include "combos.h"
/****************************************************************************/
/* check */
@ -89,59 +90,6 @@ static int blockcmp(struct snapraid_state* state, int rehash, struct snapraid_bl
return 0;
}
/**
* Combinations of 1 of 4 elements.
*/
static const struct combo1 {
int a;
} COMBO1[] = {
{ 0 },
{ 1 },
{ 2 },
{ 3 },
};
/**
* Combinations of 2 of 4 elements.
*/
static const struct combo2 {
int a;
int b;
} COMBO2[] = {
{ 0, 1 },
{ 0, 2 },
{ 0, 3 },
{ 1, 2 },
{ 1, 3 },
{ 2, 3 }
};
/**
* Combinations of 3 of 4 elements.
*/
static const struct combo3 {
int a;
int b;
int c;
} COMBO3[] = {
{ 0, 1, 2 },
{ 0, 1, 3 },
{ 0, 2, 3 },
{ 1, 2, 3 }
};
/**
* Combinations of 4 of 4 elements.
*/
static const struct combo4 {
int a;
int b;
int c;
int d;
} COMBO4[] = {
{ 0, 1, 2, 3 },
};
/**
* Checks if the hash of at least one failed block is now matching.
*/
@ -164,7 +112,7 @@ static int is_hash_matching(struct snapraid_state* state, int rehash, unsigned d
/* if we checked something, and no block failed the check */
if (hash_checked && j==failed_count) {
/* recompute all the redundancy information */
raid_gen(RAID_POWER, state->level, buffer, diskmax, state->block_size);
raid_gen(state->level, buffer, diskmax, state->block_size);
return 1;
}
@ -177,12 +125,12 @@ static int is_hash_matching(struct snapraid_state* state, int rehash, unsigned d
static int is_parity_matching(struct snapraid_state* state, unsigned diskmax, unsigned i, unsigned char** buffer, unsigned char** buffer_recov)
{
/* recompute parity, note that we don't need parity over i */
raid_gen(RAID_POWER, i + 1, buffer, diskmax, state->block_size);
raid_gen(i + 1, buffer, diskmax, state->block_size);
/* if the recovered parity block matches */
if (memcmp(buffer[diskmax+i], buffer_recov[i], state->block_size) == 0) {
/* recompute all the redundancy information */
raid_gen(RAID_POWER, state->level, buffer, diskmax, state->block_size);
raid_gen(state->level, buffer, diskmax, state->block_size);
return 1;
}
@ -192,24 +140,33 @@ static int is_parity_matching(struct snapraid_state* state, unsigned diskmax, un
/**
* Repair errors.
* Returns <0 if failure for missing strategy, >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.
* If success, the parity are computed in the buffer variable.
*/
static int repair_step(struct snapraid_state* state, int rehash, unsigned pos, unsigned diskmax, struct failed_struct* failed, unsigned* failed_map, unsigned failed_count, unsigned char** buffer, unsigned char** buffer_recov, unsigned char* buffer_zero)
{
unsigned i, j;
int error;
int has_hash;
int kind = RAID_POWER;
int d[LEV_MAX];
/* no fix required */
if (failed_count == 0) {
/* recompute only parity and qarity */
raid_gen(kind, state->level, buffer, diskmax, state->block_size);
/* recompute only the parity */
raid_gen(state->level, buffer, diskmax, state->block_size);
return 0;
}
/* if too many error, we don't have any strategy */
if (failed_count > state->level) {
return -1;
}
error = 0;
/* setup vector of failed disk indexes */
for(i=0;i<failed_count;++i)
d[i] = failed[failed_map[i]].index;
/* check if there is at least a failed block that can be checked for correctness using the hash */
/* if there isn't, we have to sacrifice a parity block to check that the result is correct */
has_hash = 0;
@ -218,158 +175,79 @@ static int repair_step(struct snapraid_state* state, int rehash, unsigned pos, u
has_hash = 1;
}
/* recover one block with hash */
if (failed_count == 1 && has_hash) {
for(i=0;i<sizeof(COMBO1)/sizeof(COMBO1[0]);++i) {
/* if we don't have an hash, but we have an extra parity */
if (!has_hash && failed_count < LEV_MAX) {
/* number of parity to use, the last one is used for checking and not for recovering */
unsigned l = failed_count + 1;
for(i=0;combo[l-1][i][0]>=0;++i) {
int* c = combo[l-1][i];
/* if parity is missing, do nothing */
if (buffer_recov[COMBO1[i].a] == 0)
for(j=0;j<l;++j) {
if (buffer_recov[c[j]] == 0)
break;
}
if (j != l)
continue;
/* copy the redundancy to use */
memcpy(buffer[diskmax+COMBO1[i].a], buffer_recov[COMBO1[i].a], state->block_size);
/* copy the parity to use, one less because the last is used for checking */
for(j=0;j<l-1;++j) {
memcpy(buffer[diskmax+c[j]], buffer_recov[c[j]], state->block_size);
}
/* recover data */
raid_recov_1data(kind, failed[failed_map[0]].index, COMBO1[i].a, buffer, diskmax, buffer_zero, state->block_size);
/* recover using one less parity */
raid_recov(l-1, d, c, buffer, diskmax, buffer_zero, state->block_size);
/* use the remaining parity to check the result */
if (is_parity_matching(state, diskmax, c[l-1], buffer, buffer_recov))
return 0;
/* log */
fprintf(stdlog, "parity_error:%u:", pos);
for(j=0;j<l;++j) {
if (j != 0)
fprintf(stdlog, "/");
fprintf(stdlog, "%s", lev_config_name(c[j]));
}
fprintf(stdlog, ": Data error\n");
++error;
}
}
if (has_hash && failed_count <= LEV_MAX) {
/* number of parities to use */
unsigned l = failed_count;
for(i=0;combo[l-1][i][0]>=0;++i) {
int* c = combo[l-1][i];
/* if parity is missing, do nothing */
for(j=0;j<l;++j) {
if (buffer_recov[c[j]] == 0)
break;
}
if (j != l)
continue;
/* copy the parity to use */
for(j=0;j<l;++j) {
memcpy(buffer[diskmax+c[j]], buffer_recov[c[j]], state->block_size);
}
/* recover */
raid_recov(l, d, c, buffer, diskmax, buffer_zero, state->block_size);
/* use the hash to check the result */
if (is_hash_matching(state, rehash, diskmax, failed, failed_map, failed_count, buffer, buffer_zero))
return 0;
fprintf(stdlog, "parity_error:%u:%s: Data error\n", pos, lev_config_name(COMBO1[i].a));
++error;
}
}
/* recover one block without hash */
if (failed_count == 1 && !has_hash) {
for(i=0;i<sizeof(COMBO2)/sizeof(COMBO2[0]);++i) {
/* if parity is missing, do nothing */
if (buffer_recov[COMBO2[i].a] == 0 || buffer_recov[COMBO2[i].b] == 0)
continue;
/* copy the redundancy to use */
memcpy(buffer[diskmax+COMBO2[i].a], buffer_recov[COMBO2[i].a], state->block_size);
/* recover data */
raid_recov_1data(kind, failed[failed_map[0]].index, COMBO2[i].a, buffer, diskmax, buffer_zero, state->block_size);
if (is_parity_matching(state, diskmax, COMBO2[i].b, buffer, buffer_recov))
return 0;
fprintf(stdlog, "parity_error:%u:%s/%s: Data error\n", pos, lev_config_name(COMBO2[i].a), lev_config_name(COMBO2[i].b));
++error;
}
}
/* recover two blocks with hash */
if (failed_count == 2 && has_hash) {
for(i=0;i<sizeof(COMBO2)/sizeof(COMBO2[0]);++i) {
/* if parity is missing, do nothing */
if (buffer_recov[COMBO2[i].a] == 0 || buffer_recov[COMBO2[i].b] == 0)
continue;
/* copy the redundancy to use */
memcpy(buffer[diskmax+COMBO2[i].a], buffer_recov[COMBO2[i].a], state->block_size);
memcpy(buffer[diskmax+COMBO2[i].b], buffer_recov[COMBO2[i].b], state->block_size);
/* recover data */
raid_recov_2data(kind, failed[failed_map[0]].index, failed[failed_map[1]].index, COMBO2[i].a, COMBO2[i].b, buffer, diskmax, buffer_zero, state->block_size);
if (is_hash_matching(state, rehash, diskmax, failed, failed_map, failed_count, buffer, buffer_zero))
return 0;
fprintf(stdlog, "parity_error:%u:%s/%s: Data error\n", pos, lev_config_name(COMBO2[i].a), lev_config_name(COMBO2[i].b));
++error;
}
}
/* recover two blocks without hash */
if (failed_count == 2 && !has_hash) {
for(i=0;i<sizeof(COMBO3)/sizeof(COMBO3[0]);++i) {
/* if parity is missing, do nothing */
if (buffer_recov[COMBO3[i].a] == 0 || buffer_recov[COMBO3[i].b] == 0 || buffer_recov[COMBO3[i].c] == 0)
continue;
/* copy the redundancy to use */
memcpy(buffer[diskmax+COMBO3[i].a], buffer_recov[COMBO3[i].a], state->block_size);
memcpy(buffer[diskmax+COMBO3[i].b], buffer_recov[COMBO3[i].b], state->block_size);
/* recover data */
raid_recov_2data(kind, failed[failed_map[0]].index, failed[failed_map[1]].index, COMBO3[i].a, COMBO3[i].b, buffer, diskmax, buffer_zero, state->block_size);
if (is_parity_matching(state, diskmax, COMBO3[i].c, buffer, buffer_recov))
return 0;
fprintf(stdlog, "parity_error:%u:%s/%s/%s: Data error\n", pos, lev_config_name(COMBO3[i].a), lev_config_name(COMBO3[i].b), lev_config_name(COMBO3[i].c));
++error;
}
}
/* recover three blocks with hash */
if (failed_count == 3 && has_hash) {
for(i=0;i<sizeof(COMBO3)/sizeof(COMBO3[0]);++i) {
/* if parity is missing, do nothing */
if (buffer_recov[COMBO3[i].a] == 0 || buffer_recov[COMBO3[i].b] == 0 || buffer_recov[COMBO3[i].c] == 0)
continue;
/* copy the redundancy to use */
memcpy(buffer[diskmax+COMBO3[i].a], buffer_recov[COMBO3[i].a], state->block_size);
memcpy(buffer[diskmax+COMBO3[i].b], buffer_recov[COMBO3[i].b], state->block_size);
memcpy(buffer[diskmax+COMBO3[i].c], buffer_recov[COMBO3[i].c], state->block_size);
/* recover data */
raid_recov_3data(kind, failed[failed_map[0]].index, failed[failed_map[1]].index, failed[failed_map[2]].index, COMBO3[i].a, COMBO3[i].b, COMBO3[i].c, buffer, diskmax, buffer_zero, state->block_size);
if (is_hash_matching(state, rehash, diskmax, failed, failed_map, failed_count, buffer, buffer_zero))
return 0;
fprintf(stdlog, "parity_error:%u:%s/%s/%s: Data error\n", pos, lev_config_name(COMBO3[i].a), lev_config_name(COMBO3[i].b), lev_config_name(COMBO3[i].c));
++error;
}
}
/* recover three blocks without hash */
if (failed_count == 3 && !has_hash) {
for(i=0;i<sizeof(COMBO4)/sizeof(COMBO4[0]);++i) {
/* if parity is missing, do nothing */
if (buffer_recov[COMBO4[i].a] == 0 || buffer_recov[COMBO4[i].b] == 0 || buffer_recov[COMBO4[i].c] == 0 || buffer_recov[COMBO4[i].d] == 0)
continue;
/* copy the redundancy to use */
memcpy(buffer[diskmax+COMBO4[i].a], buffer_recov[COMBO4[i].a], state->block_size);
memcpy(buffer[diskmax+COMBO4[i].b], buffer_recov[COMBO4[i].b], state->block_size);
memcpy(buffer[diskmax+COMBO4[i].c], buffer_recov[COMBO4[i].c], state->block_size);
/* recover data */
raid_recov_3data(kind, failed[failed_map[0]].index, failed[failed_map[1]].index, failed[failed_map[2]].index, COMBO4[i].a, COMBO4[i].b, COMBO4[i].c, buffer, diskmax, buffer_zero, state->block_size);
if (is_parity_matching(state, diskmax, COMBO4[i].d, buffer, buffer_recov))
return 0;
fprintf(stdlog, "parity_error:%u:%s/%s/%s/%s: Data error\n", pos, lev_config_name(COMBO4[i].a), lev_config_name(COMBO4[i].b), lev_config_name(COMBO4[i].c), lev_config_name(COMBO4[i].d));
++error;
}
}
/* recover four blocks with hash */
if (failed_count == 4 && has_hash) {
for(i=0;i<sizeof(COMBO4)/sizeof(COMBO4[0]);++i) {
/* if parity is missing, do nothing */
if (buffer_recov[COMBO4[i].a] == 0 || buffer_recov[COMBO4[i].b] == 0 || buffer_recov[COMBO4[i].c] == 0 || buffer_recov[COMBO4[i].d] == 0)
continue;
/* copy the redundancy to use */
memcpy(buffer[diskmax+COMBO4[i].a], buffer_recov[COMBO4[i].a], state->block_size);
memcpy(buffer[diskmax+COMBO4[i].b], buffer_recov[COMBO4[i].b], state->block_size);
memcpy(buffer[diskmax+COMBO4[i].c], buffer_recov[COMBO4[i].c], state->block_size);
memcpy(buffer[diskmax+COMBO4[i].d], buffer_recov[COMBO4[i].d], state->block_size);
/* recover data */
raid_recov_4data(kind, failed[failed_map[0]].index, failed[failed_map[1]].index, failed[failed_map[2]].index, failed[failed_map[3]].index, COMBO4[i].a, COMBO4[i].b, COMBO4[i].c, COMBO4[i].d, buffer, diskmax, buffer_zero, state->block_size);
if (is_hash_matching(state, rehash, diskmax, failed, failed_map, failed_count, buffer, buffer_zero))
return 0;
fprintf(stdlog, "parity_error:%u:%s/%s/%s/%s: Data error\n", pos, lev_config_name(COMBO4[i].a), lev_config_name(COMBO4[i].b), lev_config_name(COMBO4[i].c), lev_config_name(COMBO4[i].d));
/* log */
fprintf(stdlog, "parity_error:%u:", pos);
for(j=0;j<l;++j) {
if (j != 0)
fprintf(stdlog, "/");
fprintf(stdlog, "%s", lev_config_name(c[j]));
}
fprintf(stdlog, ": Data error\n");
++error;
}
}
@ -440,8 +318,8 @@ static int repair(struct snapraid_state* state, int rehash, unsigned pos, unsign
/* if nothing to fix */
if (!something_to_recover) {
/* recompute only parity and qarity */
raid_gen(RAID_POWER, state->level, buffer, diskmax, state->block_size);
/* recompute only the parity */
raid_gen(state->level, buffer, diskmax, state->block_size);
return 0;
}
@ -686,8 +564,10 @@ static int state_check_process(struct snapraid_state* state, int check, int fix,
/* keep track of the number of failed blocks */
failed_count = 0;
/* if we have to use the old hash */
/* get block specific info */
info = info_get(&state->infoarr, i);
/* if we have to use the old hash */
rehash = info_get_rehash(info);
/* for each disk, process the block */

View file

@ -1,6 +1,6 @@
dnl Process this file with autoconf to produce a configure script.
AC_PREREQ([2.65])
AC_INIT([snapraid], [6.0], [], [], [http://snapraid.sourceforge.net])
AC_INIT([snapraid], [5.0], [], [], [http://snapraid.sourceforge.net])
AM_INIT_AUTOMAKE([foreign no-dependencies -Wall])
AC_CONFIG_SRCDIR([snapraid.c])
AC_CONFIG_HEADER([config.h])

1
elem.h
View file

@ -828,7 +828,6 @@ static inline snapraid_info info_set_bad(snapraid_info info)
return info | 0x1;
}
/**
* Marks the block address as with rehash.
*/

View file

@ -1,5 +1,5 @@
/*
* Copyright (C) 2011 Andrea Mazzoleni
* Copyright (C) 2013 Andrea Mazzoleni
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -17,6 +17,9 @@
#include <stdio.h>
/**
* Multiplication in GF(2^8).
*/
unsigned char gfmul(unsigned char a, unsigned char b)
{
unsigned char v;
@ -39,72 +42,96 @@ unsigned char gfmul(unsigned char a, unsigned char b)
return v;
}
/**
* Inversion table in GF(2^8).
*/
unsigned char gfinv[256];
#define POWER_PARITY 4
#define POWER_DISK 255
/**
* Setup the power matrix used to generate parity.
* The first row is formed by all 1.
* The second row contains power of 2, the third power of 4, and so on...
* Note that this matrix is valid only for up to Quad Parity and for up to 21 data disks.
* Number of parity.
* This is the number of rows of the generation matrix.
*/
void set_power(unsigned char* matrix)
{
int i, j;
unsigned char g;
g = 1;
for(i=0;i<POWER_PARITY;++i) {
unsigned char v = 1;
for(j=0;j<POWER_DISK;++j) {
matrix[i*POWER_DISK+j] = v;
v = gfmul(g, v);
}
g = gfmul(2, g);
}
}
#define CAUCHY_PARITY 6
#define CAUCHY_DISK (256-(CAUCHY_PARITY-1))
#define PARITY 6
/**
* Setup the Extended Cauchy matrix used to generate parity.
* The first row is formed by all 1.
* The second row are power of 2, 2^0, 2^1, 2^2, ...
* The others row are computed.
* Number of disks.
* This is the number of columns of the generation matrix.
*/
#define DISK (257-PARITY)
/**
* Setup the Normalized Extended Cauchy matrix used to generate the parity.
*/
void set_cauchy(unsigned char* matrix)
{
int i, j;
unsigned char cv, rv;
unsigned char inv_x, y;
/* first row all 1, this is an Extended Cauchy matrix */
for(i=0;i<CAUCHY_DISK;++i) {
matrix[0*CAUCHY_DISK+i] = 1;
/*
* First row is formed by all 1.
*
* This is an Extended Cauchy matrix built from a Cauchy matrix
* adding the first row of all 1.
*/
for(i=0;i<DISK;++i) {
matrix[0*DISK+i] = 1;
}
/* second row power of 2, this is the first row of the Cauchy matrix */
cv = 1;
for(i=0;i<CAUCHY_DISK;++i) {
matrix[1*CAUCHY_DISK+i] = cv;
cv = gfmul(2, cv);
/*
* Second row is formed by power of 2^i.
*
* This is the first row of the Cauchy matrix.
*
* Each element of the Cauchy matrix is in the form 1/(xi+yj)
* where all xi, and yj must be different.
*
* Choosing xi = 2^-i and y0 = 0, we obtain for the first row:
*
* 1/(xi+y0) = 1/(2^-i + 0) = 2^i
*
* with 2^-i != 0 for any i
*/
inv_x = 1;
for(i=0;i<DISK;++i) {
matrix[1*DISK+i] = inv_x;
inv_x = gfmul(2, inv_x);
}
/* other rows using a Cauchy matrix */
rv = 2;
for(j=0;j<CAUCHY_PARITY-2;++j) {
unsigned char ce;
/*
* Next rows of the Cauchy matrix.
*
* We continue forming the Cauchy matrix with yj = 2^j obtaining :
*
* 1/(xi+yj) = 1/(2^-i + 2^j)
*
* with xi != yj for any i,j with i>=0,j>=1,i+j<256
*/
y = 2;
for(j=0;j<PARITY-2;++j) {
ce = 1;
for(i=0;i<CAUCHY_DISK;++i) {
cv = gfinv[ce];
matrix[(j+2)*CAUCHY_DISK+i] = gfinv[rv ^ cv];
ce = gfmul(2, ce);
inv_x = 1;
for(i=0;i<DISK;++i) {
unsigned char x = gfinv[inv_x];
matrix[(j+2)*DISK+i] = gfinv[y ^ x];
inv_x = gfmul(2, inv_x);
}
rv = gfmul(2, rv);
y = gfmul(2, y);
}
/*
* Normalizes the matrix multipling each row for
* the inverse of the first element in the row.
*
* This operation doesn't invalidate the property that all the square
* submatrices are not singular.
*/
for(j=0;j<PARITY-2;++j) {
unsigned char f = gfinv[matrix[(j+2)*DISK]];
for(i=0;i<DISK;++i) {
matrix[(j+2)*DISK+i] = gfmul(matrix[(j+2)*DISK+i], f);
}
}
}
@ -128,10 +155,10 @@ int main(void)
{
unsigned char v;
int i, j, k, p;
unsigned char matrix[8 * 256];
unsigned char matrix[PARITY * 256];
printf("/*\n");
printf(" * Copyright (C) 2011 Andrea Mazzoleni\n");
printf(" * Copyright (C) 2013 Andrea Mazzoleni\n");
printf(" *\n");
printf(" * This program is free software: you can redistribute it and/or modify\n");
printf(" * it under the terms of the GNU General Public License as published by\n");
@ -185,22 +212,6 @@ int main(void)
}
printf("};\n\n");
/* 4^a */
printf("const unsigned char __attribute__((aligned(256))) gfexp4[256] =\n");
printf("{\n");
v = 1;
for(i=0;i<256;++i) {
if (i % 8 == 0)
printf("\t");
printf("0x%02x,", v);
v = gfmul(v, 4);
if (i % 8 == 7)
printf("\n");
else
printf(" ");
}
printf("};\n\n");
/* 1/a */
printf("const unsigned char __attribute__((aligned(256))) gfinv[256] =\n");
printf("{\n");
@ -220,115 +231,29 @@ int main(void)
}
printf("};\n\n");
/* x4l */
printf("#define GFX4L { ");
for(i=0;i<16;++i) {
v = gfmul(0x4, i);
printf("0x%02x", (unsigned)v);
if (i != 15)
printf(", ");
}
printf(" }\n");
/* x4h */
printf("#define GFX4H { ");
for(i=0;i<16;++i) {
v = gfmul(0x40, i);
printf("0x%02x", (unsigned)v);
if (i != 15)
printf(", ");
}
printf(" }\n");
/* x8l */
printf("#define GFX8L { ");
for(i=0;i<16;++i) {
v = gfmul(0x8, i);
printf("0x%02x", (unsigned)v);
if (i != 15)
printf(", ");
}
printf(" }\n");
/* x8h */
printf("#define GFX8H { ");
for(i=0;i<16;++i) {
v = gfmul(0x80, i);
printf("0x%02x", (unsigned)v);
if (i != 15)
printf(", ");
}
printf(" }\n");
/* poly */
printf("#define GFPOLY8 { 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d } \n");
printf("#define GFPOLY16 { 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d } \n");
printf("#define GFMASK16 { 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f } \n");
printf("\n");
/* power matrix */
set_power(matrix);
printf("/**\n");
printf(" * Power matrix used to generate parity.\n");
printf(" * This matrix is valid for up to 4 parity with 21 data disks.\n");
printf(" *\n");
for(p=0;p<POWER_PARITY;++p) {
printf(" * ");
for(i=0;i<POWER_DISK;++i) {
printf("%02x ", matrix[p*POWER_DISK+i]);
if (p+1 == 4 && i+1 == 21) {
/* stop at 4 parity and 21 data disks */
break;
}
}
printf("\n");
}
printf(" */\n");
printf("const unsigned char __attribute__((aligned(256))) gfmatrix_power[%u][256] =\n", POWER_PARITY);
printf("{\n");
for(p=0;p<POWER_PARITY;++p) {
printf("\t{\n");
for(i=0;i<POWER_DISK;++i) {
if (i % 8 == 0)
printf("\t\t");
printf("0x%02x,", matrix[p*POWER_DISK+i]);
if (p+1 == 4 && i+1 == 21) {
/* stop at 4 parity and 21 data disks */
break;
}
if (i % 8 == 7)
printf("\n");
else
printf(" ");
}
printf("\n\t},\n");
}
printf("};\n\n");
/* matrix matrix */
/* cauchy matrix */
set_cauchy(matrix);
printf("/**\n");
printf(" * Extended Cauchy matrix used to generate parity.\n");
printf(" * This matrix is valid for up to %u parity with %u data disks.\n", CAUCHY_PARITY, CAUCHY_DISK);
printf(" * This matrix is valid for up to %u parity with %u data disks.\n", PARITY, DISK);
printf(" *\n");
for(p=0;p<CAUCHY_PARITY;++p) {
for(p=0;p<PARITY;++p) {
printf(" * ");
for(i=0;i<CAUCHY_DISK;++i) {
printf("%02x ", matrix[p*CAUCHY_DISK+i]);
for(i=0;i<DISK;++i) {
printf("%02x ", matrix[p*DISK+i]);
}
printf("\n");
}
printf(" */\n");
printf("const unsigned char __attribute__((aligned(256))) gfmatrix_cauchy[%u][256] =\n", CAUCHY_PARITY);
printf("const unsigned char __attribute__((aligned(256))) gfmatrix[%u][256] =\n", PARITY);
printf("{\n");
for(p=0;p<CAUCHY_PARITY;++p) {
for(p=0;p<PARITY;++p) {
printf("\t{\n");
for(i=0;i<CAUCHY_DISK;++i) {
for(i=0;i<DISK;++i) {
if (i % 8 == 0)
printf("\t\t");
printf("0x%02x,", matrix[p*CAUCHY_DISK+i]);
printf("0x%02x,", matrix[p*DISK+i]);
if (i % 8 == 7)
printf("\n");
else
@ -338,47 +263,23 @@ int main(void)
}
printf("};\n\n");
printf("/**\n");
printf(" * Multiplication tables for the Extended Cauchy matrix.\n");
printf(" *\n");
printf(" * Indexes are [DISK][DATA][PARITY - 1].\n");
printf(" * Where DISK is from 0 to %u, DATA from 0 to 255, PARITY from 1 to %u.\n", CAUCHY_DISK - 1, CAUCHY_PARITY - 1);
printf(" */\n");
printf("const unsigned char __attribute__((aligned(256))) gfcauchy_mul[%u][256][%u] =\n", CAUCHY_DISK, np(CAUCHY_PARITY - 1));
printf("{\n");
for(i=0;i<CAUCHY_DISK;++i) {
printf("\t{\n");
for(j=0;j<256;++j) {
printf("\t\t{ ");
for(p=1;p<CAUCHY_PARITY;++p) {
v = gfmul(matrix[p*CAUCHY_DISK+i], j);
printf("0x%02x", (unsigned)v);
if (p != CAUCHY_PARITY - 1)
printf(", ");
}
printf(" },\n");
}
printf("\t},\n");
}
printf("};\n\n");
printf("#if defined(__i386__) || defined(__x86_64__)\n");
printf("/**\n");
printf(" * PSHUFB tables for the Extended Cauchy matrix.\n");
printf(" *\n");
printf(" * Indexes are [DISK][DATA][PARITY - 2].\n");
printf(" * Where DISK is from 0 to %u, DATA from 0 to 255, PARITY from 2 to %u.\n", CAUCHY_DISK - 1, CAUCHY_PARITY - 1);
printf(" * Indexes are [DISK][PARITY - 2][LH].\n");
printf(" * Where DISK is from 0 to %u, PARITY from 2 to %u, LH from 0 to 1.\n", DISK - 1, PARITY - 1);
printf(" */\n");
printf("const unsigned char __attribute__((aligned(256))) gfcauchy_pshufb[%u][%u][2][16] =\n", CAUCHY_DISK, np(CAUCHY_PARITY - 2));
printf("const unsigned char __attribute__((aligned(256))) gfpshufb[%u][%u][2][16] =\n", DISK, np(PARITY - 2));
printf("{\n");
for(i=0;i<CAUCHY_DISK;++i) {
for(i=0;i<DISK;++i) {
printf("\t{\n");
for(p=2;p<CAUCHY_PARITY;++p) {
for(p=2;p<PARITY;++p) {
printf("\t\t{\n");
for(j=0;j<2;++j) {
printf("\t\t\t{ ");
for(k=0;k<16;++k) {
v = gfmul(matrix[p*CAUCHY_DISK+i], k);
v = gfmul(matrix[p*DISK+i], k);
if (j == 1)
v = gfmul(v, 16);
printf("0x%02x", (unsigned)v);

3389
raid.c

File diff suppressed because it is too large Load diff

99
raid.h
View file

@ -22,25 +22,9 @@
/* raid */
/**
* Kind of generator matrix used.
* Max number of data disks.
*/
#define RAID_POWER 0 /**< Power matrix. Fast but no more than 4 parity with 21 data disks */
#define RAID_CAUCHY 1 /**< Cauchy matrix. Slower, but no limit. */
/**
* Max number of data disks for RAID_POWER for up to TripleParity.
*/
#define RAID_POWER_DATA_LIMIT 255
/**
* Max number of data disks for RAID_POWER with QuadParity.
*/
#define RAID_POWER_QP_DATA_LIMIT 21
/**
* Max number of data disks with RAID_CAUCHY.
*/
#define RAID_CAUCHY_DATA_LIMIT 251
#define RAID_DATA_LIMIT 251
/**
* Initializes the RAID system.
@ -48,61 +32,42 @@
void raid_init(void);
/**
* Computes the specified number of parities.
* \param kind Kind of parity. RAID_POWER or RAID_CAUCHY.
* \param level Number of parities to compute.
* Computes the parity.
* \param level Number of parities level to compute. Starting from 1.
* \param vbuf Vector of pointers to the blocks for disks and parities.
* It has (::data + ::level) elements. Each element points to a buffer of ::size bytes.
* \param data Number of data disks.
* \param size Size of the blocks pointed by vbuf.
*/
void raid_gen(unsigned kind, unsigned level, unsigned char** vbuf, unsigned data, unsigned size);
void raid_gen(unsigned level, unsigned char** vbuf, unsigned data, unsigned size);
/*
* Recovers failure of one data block x using parity i.
* Note that only the data block is recovered. The full parity is instead destroyed.
* \param x Index of the data disk to recover. Starting from 0.
* \param i Index of the parity to use for the recovering. Starting from 0.
/**
* Recovers failures of data disks using the specified parities.
* Note that only the data blocks are recovered. The parity is destroyed.
* \param level Number of failed disks. Starting from 1.
* \param d[] Vector of indexes of the data disk to recover. Starting from 0.
* \param p[] Vector of indexes of the parity to use for the recovering. Starting from 0.
* \param vbuf Vector of pointers to the blocks for disks and parities.
* It has (::data + ::i + 1) elements. Each element points to a buffer of ::size bytes.
* It has (::data + ::level) elements. Each element points to a buffer of ::size bytes.
* \param data Number of data disks.
* \param zero Buffer filled with 0 of ::size bytes.
* This buffer is not modified.
* \param zero Buffer filled with 0 of ::size bytes. This buffer is not modified.
* \param size Size of the blocks pointed by vbuf.
*/
void raid_recov_1data(unsigned kind, int x, int i, unsigned char** vbuf, unsigned data, unsigned char* zero, unsigned size);
/*
* Recovers failure of two data blocks x,y using parity i,j.
* Note that only the data blocks are recovered. The full parity is instead destroyed.
*/
void raid_recov_2data(unsigned kind, int x, int y, int i, int j, unsigned char** vbuf, unsigned data, unsigned char* zero, unsigned size);
/*
* Recovers failure of three data blocks x,y,z using parity i,j,k.
* Note that only the data blocks are recovered. The full parity is instead destroyed.
*/
void raid_recov_3data(unsigned kind, int x, int y, int z, int i, int j, int k, unsigned char** vbuf, unsigned data, unsigned char* zero, unsigned size);
/*
* Recovers failure of four data blocks x,y,z,v using parity i,j,k,l.
* Note that only the data blocks are recovered. The full parity is instead destroyed.
*/
void raid_recov_4data(unsigned kind, int x, int y, int z, int v, int i, int j, int k, int l, unsigned char** vbuf, unsigned data, unsigned char* zero, unsigned size);
void raid_recov(unsigned level, int* d, int* p, unsigned char** vbuf, unsigned data, unsigned char* zero, unsigned size);
/**
* Gets the name of the selected function to compute parity.
* Only for testing.
*/
const char* raid5_tag(void);
const char* raid6_tag(void);
const char* raidTP_tag(void);
const char* raidQP_tag(void);
const char* cauchyQP_tag(void);
const char* cauchyPP_tag(void);
const char* cauchyHP_tag(void);
const char* raidPP_tag(void);
const char* raidHP_tag(void);
/**
* Internal specialized parity computation.
* Specialized parity computation.
* Only for testing.
*/
void raid5_int32(unsigned char** vbuf, unsigned data, unsigned size);
@ -114,30 +79,18 @@ void raid6_int64(unsigned char** vbuf, unsigned data, unsigned size);
void raid6_mmx(unsigned char** vbuf, unsigned data, unsigned size);
void raid6_sse2(unsigned char** vbuf, unsigned data, unsigned size);
void raid6_sse2ext(unsigned char** vbuf, unsigned data, unsigned size);
void raidTP_int32(unsigned char** vbuf, unsigned data, unsigned size);
void raidTP_int64(unsigned char** vbuf, unsigned data, unsigned size);
void raidTP_mmx(unsigned char** vbuf, unsigned data, unsigned size);
void raidTP_sse2(unsigned char** vbuf, unsigned data, unsigned size);
void raidTP_sse2ext(unsigned char** vbuf, unsigned data, unsigned size);
void raidTP_int8(unsigned char** vbuf, unsigned data, unsigned size);
void raidTP_ssse3(unsigned char** vbuf, unsigned data, unsigned size);
void raidTP_ssse3ext(unsigned char** vbuf, unsigned data, unsigned size);
void raidQP_int32(unsigned char** vbuf, unsigned data, unsigned size);
void raidQP_int64(unsigned char** vbuf, unsigned data, unsigned size);
void raidQP_mmx(unsigned char** vbuf, unsigned data, unsigned size);
void raidQP_sse2(unsigned char** vbuf, unsigned data, unsigned size);
void raidQP_sse2ext(unsigned char** vbuf, unsigned data, unsigned size);
void raidQP_int8(unsigned char** vbuf, unsigned data, unsigned size);
void raidQP_ssse3(unsigned char** vbuf, unsigned data, unsigned size);
void raidQP_ssse3ext(unsigned char** vbuf, unsigned data, unsigned size);
void raidQP_avxext(unsigned char** vbuf, unsigned data, unsigned size);
void cauchyQP_int8(unsigned char** vbuf, unsigned data, unsigned size);
void cauchyQP_ssse3(unsigned char** vbuf, unsigned data, unsigned size);
void cauchyQP_ssse3ext(unsigned char** vbuf, unsigned data, unsigned size);
void cauchyPP_int8(unsigned char** vbuf, unsigned data, unsigned size);
void cauchyPP_ssse3(unsigned char** vbuf, unsigned data, unsigned size);
void cauchyPP_ssse3ext(unsigned char** vbuf, unsigned data, unsigned size);
void cauchyHP_int8(unsigned char** vbuf, unsigned data, unsigned size);
void cauchyHP_ssse3(unsigned char** vbuf, unsigned data, unsigned size);
void cauchyHP_ssse3ext(unsigned char** vbuf, unsigned data, unsigned size);
void raidPP_int8(unsigned char** vbuf, unsigned data, unsigned size);
void raidPP_ssse3(unsigned char** vbuf, unsigned data, unsigned size);
void raidPP_ssse3ext(unsigned char** vbuf, unsigned data, unsigned size);
void raidHP_int8(unsigned char** vbuf, unsigned data, unsigned size);
void raidHP_ssse3(unsigned char** vbuf, unsigned data, unsigned size);
void raidHP_ssse3ext(unsigned char** vbuf, unsigned data, unsigned size);
#endif

View file

@ -315,7 +315,7 @@ static int state_scrub_process(struct snapraid_state* state, struct snapraid_par
}
/* compute the parity */
raid_gen(RAID_POWER, state->level, buffer, diskmax, state->block_size);
raid_gen(state->level, buffer, diskmax, state->block_size);
/* compare the parity */
for(l=0;l<state->level;++l) {
@ -341,7 +341,7 @@ static int state_scrub_process(struct snapraid_state* state, struct snapraid_par
/* do nothing, as this is a generic error */
/* likely caused by a not synched array */
} else {
/* if rehash is neeed */
/* if rehash is needed */
if (rehash) {
/* store all the new hash already computed */
for(j=0;j<diskmax;++j) {

View file

@ -23,6 +23,7 @@
#include "cpu.h"
#include "elem.h"
#include "state.h"
#include "combos.h"
struct hash_test_vector {
const char* data;
@ -46,280 +47,276 @@ static struct hash_test_vector TEST_SPOOKY2[] = {
{ 0, 0, { 0 } }
};
static void recovtest(unsigned kind, unsigned diskmax, unsigned block_size)
static void recovtest(int diskmax, unsigned block_size)
{
void* buffer_alloc;
unsigned char** buffer;
unsigned char* buffer_test0;
unsigned char* buffer_test1;
unsigned char* buffer_test2;
unsigned char* buffer_test3;
unsigned char* buffer_zero;
unsigned char* buffer_bad;
unsigned char** data;
unsigned char** parity;
unsigned char** test;
unsigned char* data_save[LEV_MAX];
unsigned char* parity_save[LEV_MAX];
unsigned char* zero;
unsigned char* waste;
unsigned buffermax;
unsigned level;
unsigned i, j, k, l;
unsigned n;
int d[LEV_MAX];
int i;
unsigned j;
struct combo1 {
int ignore[3];
int use;
} COMBO1[] = {
{ { 1, 2, 3 }, 0 },
{ { 0, 2, 3 }, 1 },
{ { 0, 1, 3 }, 2 },
{ { 0, 1, 2 }, 3 }
};
struct combo2 {
int ignore[2];
int use[2];
} COMBO2[] = {
{ { 0, 1 }, { 2, 3 } },
{ { 0, 2 }, { 1, 3 } },
{ { 0, 3 }, { 1, 2 } },
{ { 1, 2 }, { 0, 3 } },
{ { 1, 3 }, { 0, 2 } },
{ { 2, 3 }, { 0, 1 } }
};
struct combo3 {
int ignore;
int use[3];
} COMBO3[] = {
{ 0, { 1, 2, 3 } },
{ 1, { 0, 2, 3 } },
{ 2, { 0, 1, 3 } },
{ 3, { 0, 1, 2 } }
};
if (kind == RAID_POWER) {
level = 4;
} else {
level = 4; /* TODO preliminary support limited to 4 parity */
}
buffermax = diskmax + level * 2 + 1 + 1;
buffermax = diskmax + LEV_MAX * 2 + 2;
buffer = malloc_nofail_vector_align(buffermax, block_size, &buffer_alloc);
buffer_test0 = buffer[buffermax-6];
buffer_test1 = buffer[buffermax-5];
buffer_test2 = buffer[buffermax-4];
buffer_test3 = buffer[buffermax-3];
buffer_zero = buffer[buffermax-2];
buffer_bad = buffer[buffermax-1];
memset(buffer_zero, 0, block_size);
memset(buffer_bad, 0xA5, block_size);
data = buffer;
parity = buffer + diskmax;
test = buffer + diskmax + LEV_MAX;
/* fill with random */
for(i=0;i<LEV_MAX;++i)
parity_save[i] = parity[i];
zero = buffer[buffermax-2];
memset(zero, 0, block_size);
waste = buffer[buffermax-1];
/* fill data disk with random */
for(i=0;i<diskmax;++i) {
for(j=0;j<block_size;++j)
buffer[i][j] = rand();
data[i][j] = rand();
}
/* compute the parity */
raid_gen(kind, level, buffer, diskmax, block_size);
raid_gen(LEV_MAX, buffer, diskmax, block_size);
/* check 4data */
for(i=0;i<diskmax;++i) {
for(j=i+1;j<diskmax;++j) {
for(k=j+1;k<diskmax;++k) {
for(l=k+1;l<diskmax;++l) {
unsigned char* a;
unsigned char* b;
unsigned char* c;
unsigned char* d;
/* set all the parity to the waste buffer */
for(i=0;i<LEV_MAX;++i)
parity[i] = waste;
/* enforce the limit */
if (kind == RAID_POWER && diskmax > RAID_POWER_QP_DATA_LIMIT && l >= 3)
continue;
/* save */
a = buffer[i];
b = buffer[j];
c = buffer[k];
d = buffer[l];
/* damage */
buffer[i] = buffer_test0;
buffer[j] = buffer_test1;
buffer[k] = buffer_test2;
buffer[l] = buffer_test3;
/* recover */
raid_recov_4data(kind, i, j, k, l, 0, 1, 2, 3, buffer, diskmax, buffer_zero, block_size);
/* check */
if (memcmp(buffer_test0, a, block_size) != 0
|| memcmp(buffer_test1, b, block_size) != 0
|| memcmp(buffer_test2, c, block_size) != 0
|| memcmp(buffer_test3, d, block_size) != 0
) {
fprintf(stderr, "Failed RAID test\n");
exit(EXIT_FAILURE);
}
/* restore */
buffer[i] = a;
buffer[j] = b;
buffer[k] = c;
buffer[l] = d;
}
/* check 6data */
for(d[0]=0;d[0]<diskmax;++d[0]) {
for(d[1]=d[0]+1;d[1]<diskmax;++d[1]) {
for(d[2]=d[1]+1;d[2]<diskmax;++d[2]) {
for(d[3]=d[2]+1;d[3]<diskmax;++d[3]) {
for(d[4]=d[3]+1;d[4]<diskmax;++d[4]) {
for(d[5]=d[4]+1;d[5]<diskmax;++d[5]) {
for(i=0;combo[5][i][0]>=0;++i) {
/* set */
for(j=0;j<6;++j) {
/* remove the missing data */
data_save[j] = data[d[j]];
data[d[j]] = test[j];
/* set the parity to use */
parity[combo[5][i][j]] = parity_save[combo[5][i][j]];
}
}
}
/* check 3data */
for(i=0;i<diskmax;++i) {
for(j=i+1;j<diskmax;++j) {
for(k=j+1;k<diskmax;++k) {
for(n=0;n<sizeof(COMBO3)/sizeof(COMBO3[0]);++n) {
unsigned char* a;
unsigned char* b;
unsigned char* c;
unsigned char* p;
/* recover */
raid_recov(6, d, combo[5][i], buffer, diskmax, zero, block_size);
/* enforce the limit */
/* this is the point where quad parity fails over RAID_POWER_QP_DATA_LIMIT data disks */
if (kind == RAID_POWER && diskmax > RAID_POWER_QP_DATA_LIMIT && COMBO3[n].use[2] >= 3)
continue;
/* save */
a = buffer[i];
b = buffer[j];
c = buffer[k];
/* damage */
buffer[i] = buffer_test0;
buffer[j] = buffer_test1;
buffer[k] = buffer_test2;
/* ignore parity */
l = diskmax + COMBO3[n].ignore;
p = buffer[l];
buffer[l] = buffer_bad;
/* recover ignoring p */
raid_recov_3data(kind, i, j, k, COMBO3[n].use[0], COMBO3[n].use[1], COMBO3[n].use[2], buffer, diskmax, buffer_zero, block_size);
/* check */
if (memcmp(buffer_test0, a, block_size) != 0
|| memcmp(buffer_test1, b, block_size) != 0
|| memcmp(buffer_test2, c, block_size) != 0
) {
fprintf(stderr, "Failed RAID test\n");
exit(EXIT_FAILURE);
}
/* restore parity */
buffer[l] = p;
/* restore */
buffer[i] = a;
buffer[j] = b;
buffer[k] = c;
}
}
}
}
/* check 2data */
for(i=0;i<diskmax;++i) {
for(j=i+1;j<diskmax;++j) {
for(n=0;n<sizeof(COMBO2)/sizeof(COMBO2[0]);++n) {
unsigned char* a;
unsigned char* b;
unsigned char* p;
unsigned char* q;
/* enforce the limit */
if (kind == RAID_POWER && diskmax > RAID_POWER_QP_DATA_LIMIT && COMBO2[n].use[1] >= 3)
continue;
/* save */
a = buffer[i];
b = buffer[j];
/* damage */
buffer[i] = buffer_test0;
buffer[j] = buffer_test1;
/* ignore parity */
k = diskmax + COMBO2[n].ignore[0];
l = diskmax + COMBO2[n].ignore[1];
p = buffer[k];
q = buffer[l];
buffer[k] = buffer_bad;
buffer[l] = buffer_bad;
/* recover */
raid_recov_2data(kind, i, j, COMBO2[n].use[0], COMBO2[n].use[1], buffer, diskmax, buffer_zero, block_size);
/* check */
if (memcmp(buffer_test0, a, block_size) != 0
|| memcmp(buffer_test1, b, block_size) != 0
) {
/* check */
for(j=0;j<6;++j) {
if (memcmp(test[j], data_save[j], block_size) != 0) {
fprintf(stderr, "Failed RAID test\n");
exit(EXIT_FAILURE);
}
}
/* restore parity */
buffer[k] = p;
buffer[l] = q;
/* restore */
buffer[i] = a;
buffer[j] = b;
/* restore */
for(j=0;j<6;++j) {
/* restore the data */
data[d[j]] = data_save[j];
/* restore the parity */
parity[combo[5][i][j]] = waste;
}
}
}
}
}
}
}
}
/* check 1data */
for(n=0;n<sizeof(COMBO1)/sizeof(COMBO1[0]);++n) {
for(i=0;i<diskmax;++i) {
unsigned char* a;
unsigned char* p;
unsigned char* q;
unsigned char* r;
/* enforce the limit */
if (kind == RAID_POWER && diskmax > RAID_POWER_QP_DATA_LIMIT && COMBO1[n].use >= 3)
continue;
/* save */
a = buffer[i];
/* damage */
buffer[i] = buffer_test0;
/* ignore parity */
j = diskmax + COMBO1[n].ignore[0];
k = diskmax + COMBO1[n].ignore[1];
l = diskmax + COMBO1[n].ignore[2];
p = buffer[j];
q = buffer[k];
r = buffer[l];
buffer[j] = buffer_bad;
buffer[k] = buffer_bad;
buffer[l] = buffer_bad;
/* recover */
raid_recov_1data(kind, i, COMBO1[n].use, buffer, diskmax, buffer_zero, block_size);
/* check */
if (memcmp(buffer_test0, a, block_size) != 0) {
fprintf(stderr, "Failed RAID test\n");
exit(EXIT_FAILURE);
/* check 5data */
for(d[0]=0;d[0]<diskmax;++d[0]) {
for(d[1]=d[0]+1;d[1]<diskmax;++d[1]) {
for(d[2]=d[1]+1;d[2]<diskmax;++d[2]) {
for(d[3]=d[2]+1;d[3]<diskmax;++d[3]) {
for(d[4]=d[3]+1;d[4]<diskmax;++d[4]) {
for(i=0;combo[4][i][0]>=0;++i) {
/* set */
for(j=0;j<5;++j) {
/* remove the missing data */
data_save[j] = data[d[j]];
data[d[j]] = test[j];
/* set the parity to use */
parity[combo[4][i][j]] = parity_save[combo[4][i][j]];
}
/* restore parity */
buffer[j] = p;
buffer[k] = q;
buffer[l] = r;
/* recover */
raid_recov(5, d, combo[4][i], buffer, diskmax, zero, block_size);
/* check */
for(j=0;j<5;++j) {
if (memcmp(test[j], data_save[j], block_size) != 0) {
fprintf(stderr, "Failed RAID test\n");
exit(EXIT_FAILURE);
}
}
/* restore */
buffer[i] = a;
for(j=0;j<5;++j) {
/* restore the data */
data[d[j]] = data_save[j];
/* restore the parity */
parity[combo[4][i][j]] = waste;
}
}
}
}
}
}
}
/* check 4data */
for(d[0]=0;d[0]<diskmax;++d[0]) {
for(d[1]=d[0]+1;d[1]<diskmax;++d[1]) {
for(d[2]=d[1]+1;d[2]<diskmax;++d[2]) {
for(d[3]=d[2]+1;d[3]<diskmax;++d[3]) {
for(i=0;combo[3][i][0]>=0;++i) {
/* set */
for(j=0;j<4;++j) {
/* remove the missing data */
data_save[j] = data[d[j]];
data[d[j]] = test[j];
/* set the parity to use */
parity[combo[3][i][j]] = parity_save[combo[3][i][j]];
}
/* recover */
raid_recov(4, d, combo[3][i], buffer, diskmax, zero, block_size);
/* check */
for(j=0;j<4;++j) {
if (memcmp(test[j], data_save[j], block_size) != 0) {
fprintf(stderr, "Failed RAID test\n");
exit(EXIT_FAILURE);
}
}
/* restore */
for(j=0;j<4;++j) {
/* restore the data */
data[d[j]] = data_save[j];
/* restore the parity */
parity[combo[3][i][j]] = waste;
}
}
}
}
}
}
/* check 3data */
for(d[0]=0;d[0]<diskmax;++d[0]) {
for(d[1]=d[0]+1;d[1]<diskmax;++d[1]) {
for(d[2]=d[1]+1;d[2]<diskmax;++d[2]) {
for(i=0;combo[2][i][0]>=0;++i) {
/* set */
for(j=0;j<3;++j) {
/* remove the missing data */
data_save[j] = data[d[j]];
data[d[j]] = test[j];
/* set the parity to use */
parity[combo[2][i][j]] = parity_save[combo[2][i][j]];
}
/* recover */
raid_recov(3, d, combo[2][i], buffer, diskmax, zero, block_size);
/* check */
for(j=0;j<3;++j) {
if (memcmp(test[j], data_save[j], block_size) != 0) {
fprintf(stderr, "Failed RAID test\n");
exit(EXIT_FAILURE);
}
}
/* restore */
for(j=0;j<3;++j) {
/* restore the data */
data[d[j]] = data_save[j];
/* restore the parity */
parity[combo[2][i][j]] = waste;
}
}
}
}
}
/* check 2data */
for(d[0]=0;d[0]<diskmax;++d[0]) {
for(d[1]=d[0]+1;d[1]<diskmax;++d[1]) {
for(i=0;combo[1][i][0]>=0;++i) {
/* set */
for(j=0;j<2;++j) {
/* remove the missing data */
data_save[j] = data[d[j]];
data[d[j]] = test[j];
/* set the parity to use */
parity[combo[1][i][j]] = parity_save[combo[1][i][j]];
}
/* recover */
raid_recov(2, d, combo[1][i], buffer, diskmax, zero, block_size);
/* check */
for(j=0;j<2;++j) {
if (memcmp(test[j], data_save[j], block_size) != 0) {
fprintf(stderr, "Failed RAID test\n");
exit(EXIT_FAILURE);
}
}
/* restore */
for(j=0;j<2;++j) {
/* restore the data */
data[d[j]] = data_save[j];
/* restore the parity */
parity[combo[1][i][j]] = waste;
}
}
}
}
/* check 1data */
for(d[0]=0;d[0]<diskmax;++d[0]) {
for(i=0;combo[0][i][0]>=0;++i) {
/* set */
for(j=0;j<1;++j) {
/* remove the missing data */
data_save[j] = data[d[j]];
data[d[j]] = test[j];
/* set the parity to use */
parity[combo[0][i][j]] = parity_save[combo[0][i][j]];
}
/* recover */
raid_recov(1, d, combo[0][i], buffer, diskmax, zero, block_size);
/* check */
for(j=0;j<1;++j) {
if (memcmp(test[j], data_save[j], block_size) != 0) {
fprintf(stderr, "Failed RAID test\n");
exit(EXIT_FAILURE);
}
}
/* restore */
for(j=0;j<1;++j) {
/* restore the data */
data[d[j]] = data_save[j];
/* restore the parity */
parity[combo[0][i][j]] = waste;
}
}
}
@ -327,7 +324,7 @@ static void recovtest(unsigned kind, unsigned diskmax, unsigned block_size)
free(buffer);
}
static void gentest(unsigned kind, unsigned diskmax, unsigned block_size)
static void gentest(unsigned diskmax, unsigned block_size)
{
void* buffer_alloc;
unsigned char** buffer;
@ -335,15 +332,8 @@ static void gentest(unsigned kind, unsigned diskmax, unsigned block_size)
unsigned i, j;
void (*map[64])(unsigned char** buffer, unsigned diskmax, unsigned size);
unsigned mac;
unsigned level;
if (kind == RAID_POWER) {
level = 4;
} else {
level = LEV_MAX;
}
buffermax = diskmax + level * 2;
buffermax = diskmax + LEV_MAX * 2;
buffer = malloc_nofail_vector_align(buffermax, block_size, &buffer_alloc);
@ -354,82 +344,49 @@ static void gentest(unsigned kind, unsigned diskmax, unsigned block_size)
}
/* compute the parity */
raid_gen(kind, level, buffer, diskmax, block_size);
raid_gen(LEV_MAX, buffer, diskmax, block_size);
/* copy in back buffers */
for(i=0;i<level;++i)
memcpy(buffer[diskmax + level + i], buffer[diskmax + i], block_size);
for(i=0;i<LEV_MAX;++i)
memcpy(buffer[diskmax + LEV_MAX + i], buffer[diskmax + i], block_size);
/* load all the available functions */
mac = 0;
if (kind == RAID_CAUCHY) {
map[mac++] = cauchyQP_int8;
map[mac++] = cauchyPP_int8;
map[mac++] = cauchyHP_int8;
}
if (kind == RAID_POWER) {
map[mac++] = raid5_int32;
map[mac++] = raid5_int64;
map[mac++] = raid6_int32;
map[mac++] = raid6_int64;
map[mac++] = raidTP_int32;
map[mac++] = raidTP_int64;
map[mac++] = raidQP_int32;
map[mac++] = raidQP_int64;
}
map[mac++] = raidTP_int8;
map[mac++] = raidQP_int8;
map[mac++] = raidPP_int8;
map[mac++] = raidHP_int8;
map[mac++] = raid5_int32;
map[mac++] = raid5_int64;
map[mac++] = raid6_int32;
map[mac++] = raid6_int64;
#if defined(__i386__) || defined(__x86_64__)
if (cpu_has_mmx()) {
if (kind == RAID_POWER) {
map[mac++] = raid5_mmx;
map[mac++] = raid6_mmx;
map[mac++] = raidQP_mmx;
map[mac++] = raidTP_mmx;
}
map[mac++] = raid5_mmx;
map[mac++] = raid6_mmx;
}
if (cpu_has_sse2()) {
if (kind == RAID_POWER) {
map[mac++] = raid5_sse2;
map[mac++] = raid6_sse2;
map[mac++] = raidTP_sse2;
map[mac++] = raidQP_sse2;
map[mac++] = raid5_sse2;
map[mac++] = raid6_sse2;
#if defined(__x86_64__)
map[mac++] = raid6_sse2ext;
map[mac++] = raidTP_sse2ext;
map[mac++] = raidQP_sse2ext;
map[mac++] = raid6_sse2ext;
#endif
}
}
if (cpu_has_ssse3()) {
if (kind == RAID_POWER) {
map[mac++] = raidTP_ssse3;
map[mac++] = raidQP_ssse3;
}
if (kind == RAID_CAUCHY) {
map[mac++] = cauchyQP_ssse3;
map[mac++] = cauchyPP_ssse3;
map[mac++] = cauchyHP_ssse3;
}
map[mac++] = raidTP_ssse3;
map[mac++] = raidQP_ssse3;
map[mac++] = raidPP_ssse3;
map[mac++] = raidHP_ssse3;
#if defined(__x86_64__)
if (kind == RAID_POWER) {
map[mac++] = raidTP_ssse3ext;
map[mac++] = raidQP_ssse3ext;
}
if (kind == RAID_CAUCHY) {
map[mac++] = cauchyQP_ssse3ext;
map[mac++] = cauchyPP_ssse3ext;
map[mac++] = cauchyHP_ssse3ext;
}
#endif
}
if (cpu_has_avx()) {
#if defined(__x86_64__)
if (kind == RAID_POWER) {
map[mac++] = raidQP_avxext;
}
map[mac++] = raidTP_ssse3ext;
map[mac++] = raidQP_ssse3ext;
map[mac++] = raidPP_ssse3ext;
map[mac++] = raidHP_ssse3ext;
#endif
}
#endif
@ -440,8 +397,8 @@ static void gentest(unsigned kind, unsigned diskmax, unsigned block_size)
map[j](buffer, diskmax, block_size);
/* check it */
for(i=0;i<level;++i) {
if (memcmp(buffer[diskmax + level + i], buffer[diskmax + i], block_size) != 0) {
for(i=0;i<LEV_MAX;++i) {
if (memcmp(buffer[diskmax + LEV_MAX + i], buffer[diskmax + i], block_size) != 0) {
fprintf(stderr, "Failed RAID test\n");
exit(EXIT_FAILURE);
}
@ -565,9 +522,7 @@ void selftest()
hashtest();
crc32ctest();
gentest(RAID_POWER, 24, 256 * 1024);
gentest(RAID_CAUCHY, 32, 256 * 1024);
recovtest(RAID_POWER, 24, 2048);
recovtest(RAID_CAUCHY, 32, 2048);
gentest(64, 256);
recovtest(16, 64);
}

322
speed.c
View file

@ -121,35 +121,12 @@ void speed(void)
printf("\n");
printf("Expected fastest hash is ");
#if defined(__i386__) || defined(__x86_64__)
if (sizeof(void*) == 4 && !cpu_has_slowmult())
printf("murmur3");
else
printf("spooky2");
#else
if (sizeof(void*) == 4)
printf("murmur3");
else
printf("spooky2");
#endif
printf("\n");
printf("Expected fastest power5 is %s\n", raid5_tag());
printf("Expected fastest power6 is %s\n", raid6_tag());
printf("Expected fastest powerTP is %s\n", raidTP_tag());
printf("Expected fastest powerQP is %s\n", raidQP_tag());
printf("If these expectations are false, please report it in the SnapRAID forum\n");
printf("\n");
printf("Speed test using %d buffers of %u bytes, for a total of %u KiB.\n", diskmax, block_size, (diskmax + 3) * block_size / 1024);
printf("All values are in MiB/s.\n");
printf("Speed test using %u buffers of %u bytes, for a total of %u KiB.\n", diskmax, block_size, diskmax * block_size / 1024);
printf("All values are in MiB/s of data processed, not counting written parity.\n");
printf("\n");
printf("Memory write speed using the C memset() function:\n");
printf("%10s", "memset");
printf("%8s", "memset");
fflush(stdout);
SPEED_START {
@ -158,14 +135,14 @@ void speed(void)
}
} SPEED_STOP
printf(" %6"PRIu64, ds / dt);
printf("%7"PRIu64, ds / dt);
printf("\n");
printf("\n");
/* crc table */
printf("CRC used to check the content file integrity:\n");
printf("%10s", "table");
printf("%8s", "table");
fflush(stdout);
SPEED_START {
@ -174,10 +151,10 @@ void speed(void)
}
} SPEED_STOP
printf(" %6"PRIu64, ds / dt);
printf("%7"PRIu64, ds / dt);
printf("\n");
printf("%10s", "intel");
printf("%8s", "intel");
fflush(stdout);
#if defined(__i386__) || defined(__x86_64__)
@ -188,7 +165,7 @@ void speed(void)
}
} SPEED_STOP
printf(" %6"PRIu64, ds / dt);
printf("%7"PRIu64, ds / dt);
}
#endif
printf("\n");
@ -197,7 +174,24 @@ void speed(void)
/* hash table */
printf("Hash used to check the data blocks integrity:\n");
printf("%10s", "murmur3");
printf("%8s", "");
printf("%8s", "best");
printf("%8s", "murmur3");
printf("%8s", "spooky2");
printf("\n");
printf("%8s", "hash");
#if defined(__i386__) || defined(__x86_64__)
if (sizeof(void*) == 4 && !cpu_has_slowmult())
printf("%8s", "murmur3");
else
printf("%8s", "spooky2");
#else
if (sizeof(void*) == 4)
printf("%8s", "murmur3");
else
printf("%8s", "spooky2");
#endif
fflush(stdout);
SPEED_START {
@ -206,9 +200,7 @@ void speed(void)
}
} SPEED_STOP
printf(" %6"PRIu64, ds / dt);
printf("\n");
printf("%10s", "spooky2");
printf("%8"PRIu64, ds / dt);
fflush(stdout);
SPEED_START {
@ -217,48 +209,49 @@ void speed(void)
}
} SPEED_STOP
printf(" %6"PRIu64, ds / dt);
printf("%8"PRIu64, ds / dt);
printf("\n");
printf("\n");
/* RAID table */
printf("RAID functions used to compute the parity:\n");
printf("%10s", "");
printf(" %6s", "int8");
printf(" %6s", "int32");
printf(" %6s", "int64");
printf("%8s", "");
printf("%7s", "best");
printf("%7s", "int8");
printf("%7s", "int32");
printf("%7s", "int64");
#if defined(__i386__) || defined(__x86_64__)
printf(" %6s", "mmx");
printf(" %6s", "sse2");
printf("%7s", "mmx");
printf("%7s", "sse2");
#if defined(__x86_64__)
printf(" %6s", "sse2ext");
printf("%7s", "sse2e");
#endif
printf(" %6s", "ssse3");
printf("%7s", "ssse3");
#if defined(__x86_64__)
printf(" %6s", "ssse3ext");
printf(" %6s", "avxext");
printf("%7s", "ssse3e");
#endif
#endif
printf("\n");
/* RAID5 */
printf("%10s", "power5");
printf("%8s", "raid5");
printf("%7s", raid5_tag());
fflush(stdout);
printf(" %6s", "");
printf("%7s", "");
SPEED_START {
raid5_int32(buffer, diskmax, block_size);
} SPEED_STOP
printf(" %6"PRIu64, ds / dt);
printf("%7"PRIu64, ds / dt);
fflush(stdout);
SPEED_START {
raid5_int64(buffer, diskmax, block_size);
} SPEED_STOP
printf(" %6"PRIu64, ds / dt);
printf("%7"PRIu64, ds / dt);
fflush(stdout);
#if defined(__i386__) || defined(__x86_64__)
@ -267,7 +260,7 @@ void speed(void)
raid5_mmx(buffer, diskmax, block_size);
} SPEED_STOP
printf(" %6"PRIu64, ds / dt);
printf("%7"PRIu64, ds / dt);
fflush(stdout);
}
@ -276,30 +269,31 @@ void speed(void)
raid5_sse2(buffer, diskmax, block_size);
} SPEED_STOP
printf(" %6"PRIu64, ds / dt);
printf("%7"PRIu64, ds / dt);
fflush(stdout);
}
#endif
printf("\n");
/* RAID6 */
printf("%10s", "power6");
printf("%8s", "raid6");
printf("%7s", raid6_tag());
fflush(stdout);
printf(" %6s", "");
printf("%7s", "");
SPEED_START {
raid6_int32(buffer, diskmax, block_size);
} SPEED_STOP
printf(" %6"PRIu64, ds / dt);
printf("%7"PRIu64, ds / dt);
fflush(stdout);
SPEED_START {
raid6_int64(buffer, diskmax, block_size);
} SPEED_STOP
printf(" %6"PRIu64, ds / dt);
printf("%7"PRIu64, ds / dt);
fflush(stdout);
#if defined(__i386__) || defined(__x86_64__)
@ -308,7 +302,7 @@ void speed(void)
raid6_mmx(buffer, diskmax, block_size);
} SPEED_STOP
printf(" %6"PRIu64, ds / dt);
printf("%7"PRIu64, ds / dt);
fflush(stdout);
}
@ -317,7 +311,7 @@ void speed(void)
raid6_sse2(buffer, diskmax, block_size);
} SPEED_STOP
printf(" %6"PRIu64, ds / dt);
printf("%7"PRIu64, ds / dt);
fflush(stdout);
#if defined(__x86_64__)
@ -325,7 +319,7 @@ void speed(void)
raid6_sse2ext(buffer, diskmax, block_size);
} SPEED_STOP
printf(" %6"PRIu64, ds / dt);
printf("%7"PRIu64, ds / dt);
fflush(stdout);
#endif
}
@ -333,59 +327,39 @@ void speed(void)
printf("\n");
/* RAIDTP */
printf("%10s", "powerTP");
fflush(stdout);
printf(" %6s", "");
SPEED_START {
raidTP_int32(buffer, diskmax, block_size);
} SPEED_STOP
printf(" %6"PRIu64, ds / dt);
printf("%8s", "raidTP");
printf("%7s", raidTP_tag());
fflush(stdout);
SPEED_START {
raidTP_int64(buffer, diskmax, block_size);
raidTP_int8(buffer, diskmax, block_size);
} SPEED_STOP
printf(" %6"PRIu64, ds / dt);
printf("%7"PRIu64, ds / dt);
fflush(stdout);
#if defined(__i386__) || defined(__x86_64__)
printf("%7s", "");
printf("%7s", "");
if (cpu_has_mmx()) {
SPEED_START {
raidTP_mmx(buffer, diskmax, block_size);
} SPEED_STOP
printf(" %6"PRIu64, ds / dt);
fflush(stdout);
printf("%7s", "");
}
if (cpu_has_sse2()) {
SPEED_START {
raidTP_sse2(buffer, diskmax, block_size);
} SPEED_STOP
printf(" %6"PRIu64, ds / dt);
fflush(stdout);
printf("%7s", "");
#if defined(__x86_64__)
SPEED_START {
raidTP_sse2ext(buffer, diskmax, block_size);
} SPEED_STOP
printf(" %6"PRIu64, ds / dt);
fflush(stdout);
printf("%7s", "");
#endif
}
#if defined(__i386__) || defined(__x86_64__)
if (cpu_has_ssse3()) {
SPEED_START {
raidTP_ssse3(buffer, diskmax, block_size);
} SPEED_STOP
printf(" %6"PRIu64, ds / dt);
printf("%7"PRIu64, ds / dt);
fflush(stdout);
#if defined(__x86_64__)
@ -393,7 +367,7 @@ void speed(void)
raidTP_ssse3ext(buffer, diskmax, block_size);
} SPEED_STOP
printf(" %6"PRIu64, ds / dt);
printf("%7"PRIu64, ds / dt);
fflush(stdout);
#endif
}
@ -401,59 +375,39 @@ void speed(void)
printf("\n");
/* RAIDQP */
printf("%10s", "powerQP");
fflush(stdout);
printf(" %6s", "");
SPEED_START {
raidQP_int32(buffer, diskmax, block_size);
} SPEED_STOP
printf(" %6"PRIu64, ds / dt);
printf("%8s", "raidQP");
printf("%7s", raidQP_tag());
fflush(stdout);
SPEED_START {
raidQP_int64(buffer, diskmax, block_size);
raidQP_int8(buffer, diskmax, block_size);
} SPEED_STOP
printf(" %6"PRIu64, ds / dt);
printf("%7"PRIu64, ds / dt);
fflush(stdout);
#if defined(__i386__) || defined(__x86_64__)
printf("%7s", "");
printf("%7s", "");
if (cpu_has_mmx()) {
SPEED_START {
raidQP_mmx(buffer, diskmax, block_size);
} SPEED_STOP
printf(" %6"PRIu64, ds / dt);
fflush(stdout);
printf("%7s", "");
}
if (cpu_has_sse2()) {
SPEED_START {
raidQP_sse2(buffer, diskmax, block_size);
} SPEED_STOP
printf(" %6"PRIu64, ds / dt);
fflush(stdout);
printf("%7s", "");
#if defined(__x86_64__)
SPEED_START {
raidQP_sse2ext(buffer, diskmax, block_size);
} SPEED_STOP
printf(" %6"PRIu64, ds / dt);
fflush(stdout);
printf("%7s", "");
#endif
}
#if defined(__i386__) || defined(__x86_64__)
if (cpu_has_ssse3()) {
SPEED_START {
raidQP_ssse3(buffer, diskmax, block_size);
} SPEED_STOP
printf(" %6"PRIu64, ds / dt);
printf("%7"PRIu64, ds / dt);
fflush(stdout);
#if defined(__x86_64__)
@ -461,159 +415,103 @@ void speed(void)
raidQP_ssse3ext(buffer, diskmax, block_size);
} SPEED_STOP
printf(" %6"PRIu64, ds / dt);
fflush(stdout);
#endif
}
if (cpu_has_avx()) {
#if defined(__x86_64__)
SPEED_START {
raidQP_avxext(buffer, diskmax, block_size);
} SPEED_STOP
printf(" %6"PRIu64, ds / dt);
fflush(stdout);
#endif
}
#endif
printf("\n");
/* CAUCHYQP */
printf("%10s", "cauchyQP");
fflush(stdout);
SPEED_START {
cauchyQP_int8(buffer, diskmax, block_size);
} SPEED_STOP
printf(" %6"PRIu64, ds / dt);
fflush(stdout);
printf(" %6s", "");
printf(" %6s", "");
if (cpu_has_mmx()) {
printf(" %6s", "");
}
if (cpu_has_sse2()) {
printf(" %6s", "");
#if defined(__x86_64__)
printf(" %6s", "");
#endif
}
#if defined(__i386__) || defined(__x86_64__)
if (cpu_has_ssse3()) {
SPEED_START {
cauchyQP_ssse3(buffer, diskmax, block_size);
} SPEED_STOP
printf(" %6"PRIu64, ds / dt);
fflush(stdout);
#if defined(__x86_64__)
SPEED_START {
cauchyQP_ssse3ext(buffer, diskmax, block_size);
} SPEED_STOP
printf(" %6"PRIu64, ds / dt);
printf("%7"PRIu64, ds / dt);
fflush(stdout);
#endif
}
#endif
printf("\n");
/* CAUCHYPP */
printf("%10s", "cauchyPP");
/* RAIDPP */
printf("%8s", "raidPP");
printf("%7s", raidPP_tag());
fflush(stdout);
SPEED_START {
cauchyPP_int8(buffer, diskmax, block_size);
raidPP_int8(buffer, diskmax, block_size);
} SPEED_STOP
printf(" %6"PRIu64, ds / dt);
printf("%7"PRIu64, ds / dt);
fflush(stdout);
printf(" %6s", "");
printf(" %6s", "");
printf("%7s", "");
printf("%7s", "");
if (cpu_has_mmx()) {
printf(" %6s", "");
printf("%7s", "");
}
if (cpu_has_sse2()) {
printf(" %6s", "");
printf("%7s", "");
#if defined(__x86_64__)
printf(" %6s", "");
printf("%7s", "");
#endif
}
#if defined(__i386__) || defined(__x86_64__)
if (cpu_has_ssse3()) {
SPEED_START {
cauchyPP_ssse3(buffer, diskmax, block_size);
raidPP_ssse3(buffer, diskmax, block_size);
} SPEED_STOP
printf(" %6"PRIu64, ds / dt);
printf("%7"PRIu64, ds / dt);
fflush(stdout);
#if defined(__x86_64__)
SPEED_START {
cauchyPP_ssse3ext(buffer, diskmax, block_size);
raidPP_ssse3ext(buffer, diskmax, block_size);
} SPEED_STOP
printf(" %6"PRIu64, ds / dt);
printf("%7"PRIu64, ds / dt);
fflush(stdout);
#endif
}
#endif
printf("\n");
/* CAUCHYHP */
printf("%10s", "cauchyHP");
/* RAIDHP */
printf("%8s", "raidHP");
printf("%7s", raidHP_tag());
fflush(stdout);
SPEED_START {
cauchyHP_int8(buffer, diskmax, block_size);
raidHP_int8(buffer, diskmax, block_size);
} SPEED_STOP
printf(" %6"PRIu64, ds / dt);
printf("%7"PRIu64, ds / dt);
fflush(stdout);
printf(" %6s", "");
printf(" %6s", "");
printf("%7s", "");
printf("%7s", "");
if (cpu_has_mmx()) {
printf(" %6s", "");
printf("%7s", "");
}
if (cpu_has_sse2()) {
printf(" %6s", "");
printf("%7s", "");
#if defined(__x86_64__)
printf(" %6s", "");
printf("%7s", "");
#endif
}
#if defined(__i386__) || defined(__x86_64__)
if (cpu_has_ssse3()) {
SPEED_START {
cauchyHP_ssse3(buffer, diskmax, block_size);
raidHP_ssse3(buffer, diskmax, block_size);
} SPEED_STOP
printf(" %6"PRIu64, ds / dt);
printf("%7"PRIu64, ds / dt);
fflush(stdout);
#if defined(__x86_64__)
SPEED_START {
cauchyHP_ssse3ext(buffer, diskmax, block_size);
raidHP_ssse3ext(buffer, diskmax, block_size);
} SPEED_STOP
printf(" %6"PRIu64, ds / dt);
printf("%7"PRIu64, ds / dt);
fflush(stdout);
#endif
}
@ -621,6 +519,10 @@ void speed(void)
printf("\n");
printf("\n");
printf("If the 'best' expectations are wrong, please report it in the SnapRAID forum\n");
printf("\n");
free(buffer_alloc);
free(buffer);
}

67
state.c
View file

@ -53,6 +53,20 @@ const char* lev_config_name(unsigned l)
return 0;
}
const char* lev_raid_name(unsigned n)
{
switch (n) {
case 1 : return "raid5";
case 2 : return "raid6";
case 3 : return "raidTP";
case 4 : return "raidQP";
case 5 : return "raidPP";
case 6 : return "raidHP";
}
return 0;
}
void state_init(struct snapraid_state* state)
{
unsigned l;
@ -183,17 +197,6 @@ static void state_config_check(struct snapraid_state* state, const char* path)
++diskcount;
}
if (state->level >= 2 && diskcount > RAID_POWER_DATA_LIMIT) {
/* RAID6 parity works for up to 255 drives, no more */
fprintf(stderr, "Too many disks for RAID6. No more than %u.\n", RAID_POWER_DATA_LIMIT);
exit(EXIT_FAILURE);
}
if (state->level >= 4 && diskcount > RAID_POWER_QP_DATA_LIMIT) {
/* RAIDQP parity works for up to 21 drives, no more */
fprintf(stderr, "Too many disks for RAIDQP. No more than %u.\n", RAID_POWER_QP_DATA_LIMIT);
exit(EXIT_FAILURE);
}
#ifdef _WIN32
for(l=0;l<state->level;++l) {
if (state->parity_device[l] == 0) {
@ -267,6 +270,13 @@ static void state_config_check(struct snapraid_state* state, const char* path)
exit(EXIT_FAILURE);
}
}
#if defined(__i386__) || defined(__x86_64__)
if (!cpu_has_ssse3())
#endif
if (state->level > 2) {
fprintf(stderr, "WARNING! Your CPU doesn't have a fast implementation beyond RAID6.\n");
}
}
void state_config(struct snapraid_state* state, const char* path, const char* command, struct snapraid_option* opt)
@ -341,7 +351,13 @@ void state_config(struct snapraid_state* state, const char* path, const char* co
exit(EXIT_FAILURE);
}
state->block_size *= 1024;
} else if (strcmp(tag, "parity") == 0 || strcmp(tag, "q-parity") == 0 || strcmp(tag, "r-parity") == 0 || strcmp(tag, "s-parity") == 0) {
} else if (strcmp(tag, "parity") == 0
|| strcmp(tag, "q-parity") == 0
|| strcmp(tag, "r-parity") == 0
|| strcmp(tag, "s-parity") == 0
|| strcmp(tag, "t-parity") == 0
|| strcmp(tag, "u-parity") == 0
) {
char device[PATH_MAX];
char* slash;
struct stat st;
@ -352,6 +368,8 @@ void state_config(struct snapraid_state* state, const char* path, const char* co
case 'q' : l = 1; break;
case 'r' : l = 2; break;
case 's' : l = 3; break;
case 't' : l = 4; break;
case 'u' : l = 5; break;
default:
fprintf(stderr, "Invalid '%s' specification in '%s' at line %u\n", tag, path, line);
exit(EXIT_FAILURE);
@ -652,16 +670,9 @@ void state_config(struct snapraid_state* state, const char* path, const char* co
fprintf(stdlog, "disk:%s:%s\n", disk->name, disk->dir);
}
switch (state->level) {
case 1 : fprintf(stdlog, "mode:raid5\n"); break;
case 2 : fprintf(stdlog, "mode:raid6\n"); break;
case 3 : fprintf(stdlog, "mode:raidTP\n"); break;
case 4 : fprintf(stdlog, "mode:raidQP\n"); break;
}
for(l=0;l<state->level;++l) {
fprintf(stdlog, "mode:%s\n", lev_raid_name(state->level));
for(l=0;l<state->level;++l)
fprintf(stdlog, "%s:%s\n", lev_config_name(l), state->parity_path[l]);
}
if (state->pool[0] != 0)
fprintf(stdlog, "pool:%s\n", state->pool);
fflush(stdlog);
@ -675,6 +686,7 @@ static void state_map(struct snapraid_state* state)
unsigned hole;
tommy_node* i;
unsigned uuid_mismatch;
unsigned diskcount;
/* removes all the mapping without a disk */
/* this happens when a disk is removed from the configuration file */
@ -805,6 +817,19 @@ static void state_map(struct snapraid_state* state)
fprintf(stderr, "and you have to restore the mount points at the state of the latest sync.\n");
exit(EXIT_FAILURE);
}
/* count the number of data disks, including holes left after removing some */
diskcount = 0;
for(i=state->maplist;i!=0;i=i->next) {
struct snapraid_map* map = i->data;
if (map->position + 1 > diskcount)
diskcount = map->position + 1;
}
if (diskcount > RAID_DATA_LIMIT) {
fprintf(stderr, "Too many datadisks. No more than %u.\n", RAID_DATA_LIMIT);
exit(EXIT_FAILURE);
}
}
/**

View file

@ -134,7 +134,6 @@ int state_status(struct snapraid_state* state)
count = 0;
rehash = 0;
unsynched_blocks = 0;
for(i=0;i<blockmax;++i) {
int one_invalid;
int one_valid;

8
sync.c
View file

@ -175,8 +175,10 @@ static int state_sync_process(struct snapraid_state* state, struct snapraid_pari
error_on_this_block = 0;
silent_error_on_this_block = 0;
/* if we have to use the old hash */
/* get block specific info */
info = info_get(&state->infoarr, i);
/* if we have to use the old hash */
rehash = info_get_rehash(info);
/* it could happens that all the blocks are EMPTY/BLK and CHG but with the hash */
@ -374,7 +376,7 @@ static int state_sync_process(struct snapraid_state* state, struct snapraid_pari
/* updates the parity only if really needed */
if (parity_needs_to_be_updated) {
/* compute the parity */
raid_gen(RAID_POWER, state->level, buffer, diskmax, state->block_size);
raid_gen(state->level, buffer, diskmax, state->block_size);
/* write the parity */
for(l=0;l<state->level;++l) {
@ -430,7 +432,7 @@ static int state_sync_process(struct snapraid_state* state, struct snapraid_pari
info_set(&state->infoarr, i, info_make(now, 0, 0));
}
} else if (silent_error_on_this_block) {
/* set the error status keeping the existing time and hash */
/* set the error status keeping the other info */
info_set(&state->infoarr, i, info_set_bad(info));
}

72960
tables.h

File diff suppressed because it is too large Load diff

View file

@ -7,6 +7,7 @@ disk disk2 test/disk2/
disk disk3 test/disk3/
disk disk4 test/disk4/
disk disk5 test/disk5/
disk disk6 test/disk6/
include *.hidden
exclude *.unrecoverable
pool test/pool

View file

@ -9,6 +9,7 @@ disk disk2 test/disk2/
disk disk3 test/disk3/
disk disk4 test/disk4/
disk disk5 test/disk5/
disk disk6 test/disk6/
include *.hidden
exclude *.unrecoverable

View file

@ -1,16 +0,0 @@
block_size 1
parity test/parity
q-parity test/q-parity
r-parity test/r-parity
s-parity test/s-parity
content test/content
content test/p-content
content test/q-content
content test/r-content
disk disk1 test/disk1/
disk disk3 test/disk3/
disk disk4 test/disk4/
disk disk5 test/disk5/
include *.hidden
exclude *.unrecoverable

View file

@ -7,11 +7,13 @@ content test/content
content test/p-content
content test/q-content
content test/r-content
content test/s-content
disk disk1 test/disk1/
disk disk2 test/disk2/
disk disk3 test/disk3/
disk disk4 test/disk4/
disk disk5 test/disk5/
disk disk6 test/disk6/
include *.hidden
exclude *.unrecoverable

View file

@ -11,6 +11,7 @@ disk disk2 test/disk2/
disk disk3 test/disk3/
disk disk4 test/disk4/
disk disk5 test/disk5/
disk disk6 test/disk6/
include *.hidden
exclude *.unrecoverable