Merge branch 'quadparity'

This commit is contained in:
Andrea Mazzoleni 2013-09-18 23:18:15 +02:00
commit 7a1fdf151b
9 changed files with 1602 additions and 713 deletions

485
check.c
View file

@ -89,6 +89,106 @@ 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, 4 },
};
/**
* Checks if the hash of at least one failed block is now matching.
*/
static int is_hash_matching(struct snapraid_state* state, int rehash, unsigned diskmax, struct failed_struct* failed, unsigned* failed_map, unsigned failed_count, unsigned char** buffer, unsigned char* buffer_zero)
{
unsigned j;
int hash_checked;
hash_checked = 0; /* keep track if we check at least one block */
/* check if the recovered blocks are OK */
for(j=0;j<failed_count;++j) {
if (block_has_updated_hash(failed[failed_map[j]].block)) { /* if the block can be checked */
hash_checked = 1;
if (blockcmp(state, rehash, failed[failed_map[j]].block, buffer[failed[failed_map[j]].index], buffer_zero) != 0)
break;
}
}
/* if we checked something, and no block failed the check */
if (hash_checked && j==failed_count) {
/* recompute all the redundancy information */
raid_gen(state->level, buffer, diskmax, state->block_size);
return 1;
}
return 0;
}
/**
* Checks if specified parity is now matching with a recomputed one.
*/
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(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(state->level, buffer, diskmax, state->block_size);
return 1;
}
return 0;
}
/**
* Repair errors.
* Returns <0 if failure for missing strategy, >0 if data is wrong and we cannot rebuild correctly, 0 on success.
@ -96,13 +196,9 @@ static int blockcmp(struct snapraid_state* state, int rehash, struct snapraid_bl
*/
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 j;
int error = 0;
unsigned i, j;
int error;
int has_hash;
int hash_checked;
unsigned char* buffer_parity = buffer_recov[0];
unsigned char* buffer_qarity = buffer_recov[1];
unsigned char* buffer_rarity = buffer_recov[2];
/* no fix required */
if (failed_count == 0) {
@ -111,6 +207,8 @@ static int repair_step(struct snapraid_state* state, int rehash, unsigned pos, u
return 0;
}
error = 0;
/* 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;
@ -119,288 +217,160 @@ static int repair_step(struct snapraid_state* state, int rehash, unsigned pos, u
has_hash = 1;
}
/* RAID5 recovering from p */
if (failed_count == 1 && buffer_parity != 0 && has_hash) {
/* copy the redundancy to use */
memcpy(buffer[diskmax], buffer_parity, state->block_size);
/* recover one block with hash */
if (failed_count == 1 && has_hash) {
for(i=0;i<sizeof(COMBO1)/sizeof(COMBO1[0]);++i) {
/* if parity is missing, do nothing */
if (buffer_recov[COMBO1[i].a] == 0)
continue;
/* recover data */
raid5_recov_data(buffer, diskmax, state->block_size, failed[failed_map[0]].index);
/* copy the redundancy to use */
memcpy(buffer[diskmax+COMBO1[i].a], buffer_recov[COMBO1[i].a], state->block_size);
hash_checked = 0; /* keep track if we check at least one block */
/* recover data */
raid_recov_1data(buffer, diskmax, state->block_size, failed[failed_map[0]].index, COMBO1[i].a, buffer_zero);
/* check if the recovered blocks are OK */
for(j=0;j<failed_count;++j) {
if (block_has_updated_hash(failed[failed_map[j]].block)) { /* if the block can be checked */
hash_checked = 1;
if (blockcmp(state, rehash, failed[failed_map[j]].block, buffer[failed[failed_map[j]].index], buffer_zero) != 0)
break;
}
if (is_hash_matching(state, rehash, diskmax, failed, failed_map, failed_count, buffer, buffer_zero))
return 0;
fprintf(stdlog, "error:%u:%s: Data error\n", pos, lev_config_name(COMBO1[i].a));
++error;
}
/* if we checked something, and no block failed the check */
if (hash_checked && j==failed_count) {
/* recompute all the redundancy information */
raid_gen(state->level, buffer, diskmax, state->block_size);
return 0;
}
fprintf(stdlog, "error:%u:parity: Data error\n", pos);
++error;
}
/* RAID6 recovering from q, ignoring p */
if (failed_count == 1 && buffer_qarity != 0 && has_hash) {
/* copy the redundancy to use */
memcpy(buffer[diskmax+1], buffer_qarity, state->block_size);
/* 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;
/* recover data */
raid6_recov_datap(buffer, diskmax, state->block_size, failed[failed_map[0]].index, buffer_zero);
/* copy the redundancy to use */
memcpy(buffer[diskmax+COMBO2[i].a], buffer_recov[COMBO2[i].a], state->block_size);
hash_checked = 0; /* keep track if we check at least one block */
/* recover data */
raid_recov_1data(buffer, diskmax, state->block_size, failed[failed_map[0]].index, COMBO2[i].a, buffer_zero);
/* check if the recovered blocks are OK */
for(j=0;j<failed_count;++j) {
if (block_has_updated_hash(failed[failed_map[j]].block)) { /* if the block can be checked */
hash_checked = 1;
if (blockcmp(state, rehash, failed[failed_map[j]].block, buffer[failed[failed_map[j]].index], buffer_zero) != 0)
break;
}
if (is_parity_matching(state, diskmax, COMBO2[i].b, buffer, buffer_recov))
return 0;
fprintf(stdlog, "error:%u:%s/%s: Data error\n", pos, lev_config_name(COMBO2[i].a), lev_config_name(COMBO2[i].b));
++error;
}
/* if we checked something, and no block failed the check */
if (hash_checked && j==failed_count) {
/* recompute all the redundancy information */
raid_gen(state->level, buffer, diskmax, state->block_size);
return 0;
}
fprintf(stdlog, "error:%u:q-parity: Data error\n", pos);
++error;
}
/* RAID6 recovering from q, using p to check the result */
if (failed_count == 1 && buffer_parity != 0 && buffer_qarity != 0 && !has_hash) {
/* copy the redundancy to use */
memcpy(buffer[diskmax+1], buffer_qarity, state->block_size);
/* 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;
/* recover data */
raid6_recov_datap(buffer, diskmax, state->block_size, failed[failed_map[0]].index, buffer_zero);
/* 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);
/* recompute parity with RAID5 to get p from it */
raid_gen(1, buffer, diskmax, state->block_size);
/* recover data */
raid_recov_2data(buffer, diskmax, state->block_size, failed[failed_map[0]].index, failed[failed_map[1]].index, COMBO2[i].a, COMBO2[i].b, buffer_zero);
/* if the recovered parity block matches */
if (memcmp(buffer[diskmax], buffer_parity, state->block_size) == 0) {
/* recompute all the redundancy information */
raid_gen(state->level, buffer, diskmax, state->block_size);
return 0;
if (is_hash_matching(state, rehash, diskmax, failed, failed_map, failed_count, buffer, buffer_zero))
return 0;
fprintf(stdlog, "error:%u:%s/%s: Data error\n", pos, lev_config_name(COMBO2[i].a), lev_config_name(COMBO2[i].b));
++error;
}
fprintf(stdlog, "error:%u:parity/q-parity: Data error\n", pos);
++error;
}
/* RAID6 recovering from p+q */
if (failed_count == 2 && buffer_parity != 0 && buffer_qarity != 0 && has_hash) {
/* copy the redundancy to use */
memcpy(buffer[diskmax], buffer_parity, state->block_size);
memcpy(buffer[diskmax+1], buffer_qarity, state->block_size);
/* 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;
/* recover 2 data */
raid6_recov_2data(buffer, diskmax, state->block_size, failed[failed_map[0]].index, failed[failed_map[1]].index, buffer_zero);
/* 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);
hash_checked = 0; /* keep track if we check at least one block */
/* recover data */
raid_recov_2data(buffer, diskmax, state->block_size, failed[failed_map[0]].index, failed[failed_map[1]].index, COMBO3[i].a, COMBO3[i].b, buffer_zero);
/* check if the recovered blocks are OK */
for(j=0;j<failed_count;++j) {
if (block_has_updated_hash(failed[failed_map[j]].block)) { /* if the block can be checked */
hash_checked = 1;
if (blockcmp(state, rehash, failed[failed_map[j]].block, buffer[failed[failed_map[j]].index], buffer_zero) != 0)
break;
}
if (is_parity_matching(state, diskmax, COMBO3[i].c, buffer, buffer_recov))
return 0;
fprintf(stdlog, "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;
}
/* if we checked something, and no block failed the check */
if (hash_checked && j==failed_count) {
/* recompute all the redundancy information */
raid_gen(state->level, buffer, diskmax, state->block_size);
return 0;
}
fprintf(stdlog, "error:%u:parity/q-parity: Data error\n", pos);
++error;
}
/* RAIDTP recovering from r, ignoring p,q */
if (failed_count == 1 && buffer_rarity != 0 && has_hash) {
/* copy the redundancy to use */
memcpy(buffer[diskmax+2], buffer_rarity, state->block_size);
/* 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;
/* recover data */
raidTP_recov_datapq(buffer, diskmax, state->block_size, failed[failed_map[0]].index, buffer_zero);
/* 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);
hash_checked = 0; /* keep track if we check at least one block */
/* recover data */
raid_recov_3data(buffer, diskmax, state->block_size, 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_zero);
/* check if the recovered blocks are OK */
for(j=0;j<failed_count;++j) {
if (block_has_updated_hash(failed[failed_map[j]].block)) { /* if the block can be checked */
hash_checked = 1;
if (blockcmp(state, rehash, failed[failed_map[j]].block, buffer[failed[failed_map[j]].index], buffer_zero) != 0)
break;
}
if (is_hash_matching(state, rehash, diskmax, failed, failed_map, failed_count, buffer, buffer_zero))
return 0;
fprintf(stdlog, "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;
}
/* if we checked something, and no block failed the check */
if (hash_checked && j==failed_count) {
/* recompute all the redundancy information */
raid_gen(state->level, buffer, diskmax, state->block_size);
return 0;
}
fprintf(stdlog, "error:%u:rarity: Data error\n", pos);
++error;
}
/* RAIDTP recovering from r, using p or q to check the result */
if (failed_count == 1 && (buffer_parity != 0 || buffer_qarity != 0) && buffer_rarity != 0 && !has_hash) {
/* copy the redundancy to use */
memcpy(buffer[diskmax+2], buffer_rarity, state->block_size);
/* 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;
/* recover data */
raidTP_recov_datapq(buffer, diskmax, state->block_size, failed[failed_map[0]].index, buffer_zero);
/* 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);
/* recompute parity with RAID6 to get p and q from it */
raid_gen(2, buffer, diskmax, state->block_size);
/* recover data */
raid_recov_3data(buffer, diskmax, state->block_size, 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_zero);
/* if the recovered parity block matches */
if ((buffer_parity != 0 && memcmp(buffer[diskmax], buffer_parity, state->block_size) == 0)
|| (buffer_qarity != 0 && memcmp(buffer[diskmax+1], buffer_qarity, state->block_size) == 0)
) {
/* recompute all the redundancy information */
raid_gen(state->level, buffer, diskmax, state->block_size);
return 0;
if (is_parity_matching(state, diskmax, COMBO4[i].d, buffer, buffer_recov))
return 0;
fprintf(stdlog, "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;
}
fprintf(stdlog, "error:%u:parity/qarity/rarity: Data error\n", pos);
++error;
}
/* RAIDTP recovering from p+r */
if (failed_count == 2 && buffer_parity != 0 && buffer_rarity != 0 && has_hash) {
/* copy the redundancy to use */
memcpy(buffer[diskmax], buffer_parity, state->block_size);
memcpy(buffer[diskmax+2], buffer_rarity, state->block_size);
/* 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;
/* recover 2 data */
raidTP_recov_2dataq(buffer, diskmax, state->block_size, failed[failed_map[0]].index, failed[failed_map[1]].index, buffer_zero);
/* 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);
hash_checked = 0; /* keep track if we check at least one block */
/* recover data */
raid_recov_4data(buffer, diskmax, state->block_size, 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_zero);
/* check if the recovered blocks are OK */
for(j=0;j<failed_count;++j) {
if (block_has_updated_hash(failed[failed_map[j]].block)) { /* if the block can be checked */
hash_checked = 1;
if (blockcmp(state, rehash, failed[failed_map[j]].block, buffer[failed[failed_map[j]].index], buffer_zero) != 0)
break;
}
if (is_hash_matching(state, rehash, diskmax, failed, failed_map, failed_count, buffer, buffer_zero))
return 0;
fprintf(stdlog, "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;
}
/* if we checked something, and no block failed the check */
if (hash_checked && j==failed_count) {
/* recompute all the redundancy information */
raid_gen(state->level, buffer, diskmax, state->block_size);
return 0;
}
fprintf(stdlog, "error:%u:parity/rarity: Data error\n", pos);
++error;
}
/* RAIDTP recovering from q+r */
if (failed_count == 2 && buffer_qarity != 0 && buffer_rarity != 0 && has_hash) {
/* copy the redundancy to use */
memcpy(buffer[diskmax+1], buffer_qarity, state->block_size);
memcpy(buffer[diskmax+2], buffer_rarity, state->block_size);
/* recover 2 data */
raidTP_recov_2datap(buffer, diskmax, state->block_size, failed[failed_map[0]].index, failed[failed_map[1]].index, buffer_zero);
hash_checked = 0; /* keep track if we check at least one block */
/* check if the recovered blocks are OK */
for(j=0;j<failed_count;++j) {
if (block_has_updated_hash(failed[failed_map[j]].block)) { /* if the block can be checked */
hash_checked = 1;
if (blockcmp(state, rehash, failed[failed_map[j]].block, buffer[failed[failed_map[j]].index], buffer_zero) != 0)
break;
}
}
/* if we checked something, and no block failed the check */
if (hash_checked && j==failed_count) {
/* recompute all the redundancy information */
raid_gen(state->level, buffer, diskmax, state->block_size);
return 0;
}
fprintf(stdlog, "error:%u:qarity/rarity: Data error\n", pos);
++error;
}
/* RAIDTP recovering from q+r, using p to check the result */
if (failed_count == 2 && buffer_parity != 0 && buffer_qarity != 0 && buffer_rarity != 0 && !has_hash) {
/* copy the redundancy to use */
memcpy(buffer[diskmax+1], buffer_qarity, state->block_size);
memcpy(buffer[diskmax+2], buffer_rarity, state->block_size);
/* recover data */
raidTP_recov_2datap(buffer, diskmax, state->block_size, failed[failed_map[0]].index, failed[failed_map[1]].index, buffer_zero);
/* recompute parity with RAID5 to get p from it */
raid_gen(1, buffer, diskmax, state->block_size);
/* if the recovered parity block matches */
if (buffer_parity != 0 && memcmp(buffer[diskmax], buffer_parity, state->block_size) == 0) {
/* recompute all the redundancy information */
raid_gen(state->level, buffer, diskmax, state->block_size);
return 0;
}
fprintf(stdlog, "error:%u:parity/qarity/rarity: Data error\n", pos);
++error;
}
/* RAIDTP recovering from p+q+r */
if (failed_count == 3 && buffer_parity != 0 && buffer_qarity != 0 && buffer_rarity != 0 && has_hash) {
/* copy the redundancy to use */
memcpy(buffer[diskmax], buffer_parity, state->block_size);
memcpy(buffer[diskmax+1], buffer_qarity, state->block_size);
memcpy(buffer[diskmax+2], buffer_rarity, state->block_size);
/* recover 3 data */
raidTP_recov_3data(buffer, diskmax, state->block_size, failed[failed_map[0]].index, failed[failed_map[1]].index, failed[failed_map[2]].index, buffer_zero);
hash_checked = 0; /* keep track if we check at least one block */
/* check if the recovered blocks are OK */
for(j=0;j<failed_count;++j) {
if (block_has_updated_hash(failed[failed_map[j]].block)) { /* if the block can be checked */
hash_checked = 1;
if (blockcmp(state, rehash, failed[failed_map[j]].block, buffer[failed[failed_map[j]].index], buffer_zero) != 0)
break;
}
}
/* if we checked something, and no block failed the check */
if (hash_checked && j==failed_count) {
/* recompute all the redundancy information */
raid_gen(state->level, buffer, diskmax, state->block_size);
return 0;
}
fprintf(stdlog, "error:%u:parity/qarity/rarity: Data error\n", pos);
++error;
}
/* return the number of failed attempts, or -1 if no strategy */
@ -914,19 +884,12 @@ static int state_check_process(struct snapraid_state* state, int check, int fix,
unsigned char* buffer_zero;
/* buffers for parity read and not computed */
if (state->level == 1) {
buffer_recov[0] = buffer[diskmax + 1];
buffer_recov[1] = 0;
buffer_recov[2] = 0;
} else if (state->level == 2) {
buffer_recov[0] = buffer[diskmax + 2];
buffer_recov[1] = buffer[diskmax + 3];
buffer_recov[2] = 0;
} else {
buffer_recov[0] = buffer[diskmax + 3];
buffer_recov[1] = buffer[diskmax + 4];
buffer_recov[2] = buffer[diskmax + 5];
}
for(l=0;l<state->level;++l)
buffer_recov[l] = buffer[diskmax + state->level + l];
for(;l<LEV_MAX;++l)
buffer_recov[l] = 0;
/* the zero buffer is the last one */
buffer_zero = buffer[buffermax-1];
/* read the parity */

View file

@ -98,6 +98,22 @@ int main(void)
}
printf("};\n\n");
/* 8^a */
printf("const unsigned char __attribute__((aligned(256))) gfexp8[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, 8);
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");

1208
raid.c

File diff suppressed because it is too large Load diff

49
raid.h
View file

@ -21,49 +21,40 @@
/****************************************************************************/
/* raid */
/**
* Max number of data disks for RAID6.
*/
#define RAID6_DATA_LIMIT 21
/**
* Max number of data disks for RAIDQP.
*/
#define RAIDQP_DATA_LIMIT 21
/**
* Syndrome computation.
*/
void raid_gen(unsigned level, unsigned char** buffer, unsigned diskmax, unsigned size);
/*
* Recover failure of one data block.
* Recover failure of one data block x using parity i.
*/
void raid5_recov_data(unsigned char** dptrs, unsigned diskmax, unsigned size, int faila);
void raid_recov_1data(unsigned char** dptrs, unsigned diskmax, unsigned size, int x, int i, unsigned char* zero);
/*
* Recover two failed data blocks.
* Recover failure of two data blocks x,y using parity i,j.
*/
void raid6_recov_2data(unsigned char** dptrs, unsigned diskmax, unsigned size, int faila, int failb, unsigned char* zero);
void raid_recov_2data(unsigned char** dptrs, unsigned diskmax, unsigned size, int x, int y, int i, int j, unsigned char* zero);
/*
* Recover failure of one data block ignoring P.
* Note that P content is not recovered.
* Recover failure of three data blocks x,y,z using parity i,j,k.
*/
void raid6_recov_datap(unsigned char** dptrs, unsigned diskmax, unsigned size, int faila, unsigned char* zero);
void raid_recov_3data(unsigned char** dptrs, unsigned diskmax, unsigned size, int x, int y, int z, int i, int j, int k, unsigned char* zero);
/*
* Recover failure of one data block ignoring P and Q.
* Note that P and Q content is not recovered.
* Recover failure of four data blocks x,y,z,v using parity i,j,k,l.
*/
void raidTP_recov_datapq(unsigned char** dptrs, unsigned diskmax, unsigned size, int faila, unsigned char* zero);
/*
* Recover failure of two data blocks ignoring P.
* Note that P content is not recovered.
*/
void raidTP_recov_2datap(unsigned char** dptrs, unsigned diskmax, unsigned size, int faila, int failb, unsigned char* zero);
/*
* Recover failure of two data blocks ignoring Q.
* Note that Q content is not recovered.
*/
void raidTP_recov_2dataq(unsigned char** dptrs, unsigned diskmax, unsigned size, int faila, int failb, unsigned char* zero);
/*
* Recover failure of three data blocks.
*/
void raidTP_recov_3data(unsigned char** dptrs, unsigned diskmax, unsigned size, int faila, int failb, int failc, unsigned char* zero);
void raid_recov_4data(unsigned char** dptrs, unsigned diskmax, unsigned size, int x, int y, int z, int v, int i, int j, int k, int l, unsigned char* zero);
/**
* Initializes the RAID system.
@ -87,6 +78,10 @@ void raidTP_int32r2(unsigned char** dptr, unsigned diskmax, unsigned size);
void raidTP_mmxr1(unsigned char** buffer, unsigned diskmax, unsigned size);
void raidTP_sse2r1(unsigned char** dptr, unsigned diskmax, unsigned size);
void raidTP_sse2r2(unsigned char** dptr, unsigned diskmax, unsigned size);
void raidQP_int32r2(unsigned char** dptr, unsigned diskmax, unsigned size);
void raidQP_mmxr1(unsigned char** buffer, unsigned diskmax, unsigned size);
void raidQP_sse2r1(unsigned char** dptr, unsigned diskmax, unsigned size);
void raidQP_sse2r2(unsigned char** buffer, unsigned diskmax, unsigned size);
#endif

View file

@ -20,6 +20,8 @@
#include "snapraid.h"
#include "util.h"
#include "raid.h"
#include "elem.h"
#include "state.h"
struct hash_test_vector {
const char* data;
@ -43,170 +45,60 @@ static struct hash_test_vector TEST_SPOOKY2[] = {
{ 0, 0, { 0 } }
};
static void raid5test(unsigned diskmax, unsigned block_size)
{
void* buffer_alloc;
unsigned char** buffer;
unsigned char* buffer_test;
unsigned buffermax;
unsigned i, j;
buffermax = diskmax + 1 + 1;
buffer = malloc_nofail_vector_align(buffermax, block_size, &buffer_alloc);
buffer_test = buffer[buffermax-1];
/* fill with random */
for(i=0;i<diskmax;++i) {
for(j=0;j<block_size;++j)
buffer[i][j] = rand();
}
/* compute the parity */
raid_gen(1, buffer, diskmax, block_size);
for(i=0;i<diskmax;++i) {
unsigned char* a;
/* save */
a = buffer[i];
/* damage */
buffer[i] = buffer_test;
/* recover */
raid5_recov_data(buffer, diskmax, block_size, i);
/* restore */
buffer[i] = a;
/* check */
if (memcmp(buffer_test, 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;
unsigned char* buffer_test0;
unsigned char* buffer_test1;
unsigned char* buffer_zero;
unsigned char* buffer_bad;
unsigned buffermax;
unsigned i, j;
buffermax = diskmax + 2 + 2 + 1 + 1;
buffer = malloc_nofail_vector_align(buffermax, block_size, &buffer_alloc);
buffer_test0 = buffer[buffermax-4];
buffer_test1 = 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);
/* fill with random */
for(i=0;i<diskmax;++i) {
for(j=0;j<block_size;++j)
buffer[i][j] = rand();
}
/* compute the parity */
raid_gen(2, buffer, diskmax, block_size);
/* check 2data */
for(i=0;i<diskmax;++i) {
for(j=i+1;j<diskmax;++j) {
unsigned char* a;
unsigned char* b;
/* save */
a = buffer[i];
b = buffer[j];
/* damage */
buffer[i] = buffer_test0;
buffer[j] = buffer_test1;
/* recover */
raid6_recov_2data(buffer, diskmax, block_size, i, j, buffer_zero);
/* restore */
buffer[i] = a;
buffer[j] = b;
/* check */
if (memcmp(buffer_test0, buffer[i], block_size) != 0
|| memcmp(buffer_test1, buffer[j], block_size) != 0
) {
fprintf(stderr, "Failed RAID6 test\n");
exit(EXIT_FAILURE);
}
}
}
/* check datap */
for(i=0;i<diskmax;++i) {
unsigned char* a;
unsigned char* p;
/* other is p */
j = diskmax;
/* save */
a = buffer[i];
p = buffer[j];
/* damage */
buffer[i] = buffer_test0;
buffer[j] = buffer_bad;
/* recover */
raid6_recov_datap(buffer, diskmax, block_size, i, buffer_zero);
/* restore */
buffer[i] = a;
buffer[j] = p;
/* check */
if (memcmp(buffer_test0, buffer[i], block_size) != 0) {
fprintf(stderr, "Failed RAID6 test\n");
exit(EXIT_FAILURE);
}
}
free(buffer_alloc);
free(buffer);
}
static void raidTPtest(unsigned diskmax, unsigned block_size)
static void raidtest(unsigned 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 buffermax;
unsigned i, j, k;
unsigned i, j, k, l;
unsigned n;
buffermax = diskmax + 3 + 3 + 1 + 1;
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 } }
};
buffermax = diskmax + LEV_MAX * 2 + 1 + 1;
buffer = malloc_nofail_vector_align(buffermax, block_size, &buffer_alloc);
buffer_test0 = buffer[buffermax-5];
buffer_test1 = buffer[buffermax-4];
buffer_test2 = buffer[buffermax-3];
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);
@ -219,154 +111,194 @@ static void raidTPtest(unsigned diskmax, unsigned block_size)
}
/* compute the parity */
raid_gen(3, buffer, diskmax, block_size);
raid_gen(LEV_MAX, buffer, diskmax, block_size);
/* check 3data */
/* check 4data */
for(i=0;i<diskmax;++i) {
for(j=i+1;j<diskmax;++j) {
for(k=j+1;k<diskmax;++k) {
unsigned char* a;
unsigned char* b;
unsigned char* c;
for(l=k+1;l<diskmax;++l) {
unsigned char* a;
unsigned char* b;
unsigned char* c;
unsigned char* d;
/* save */
a = buffer[i];
b = buffer[j];
c = buffer[k];
/* 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;
/* damage */
buffer[i] = buffer_test0;
buffer[j] = buffer_test1;
buffer[k] = buffer_test2;
buffer[l] = buffer_test3;
/* recover */
raidTP_recov_3data(buffer, diskmax, block_size, i, j, k, buffer_zero);
/* recover */
raid_recov_4data(buffer, diskmax, block_size, i, j, k, l, 0, 1, 2, 3, buffer_zero);
/* restore */
buffer[i] = a;
buffer[j] = b;
buffer[k] = c;
/* 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);
}
/* check */
if (memcmp(buffer_test0, buffer[i], block_size) != 0
|| memcmp(buffer_test1, buffer[j], block_size) != 0
|| memcmp(buffer_test2, buffer[k], block_size) != 0
) {
fprintf(stderr, "Failed RAIDTP test\n");
exit(EXIT_FAILURE);
/* restore */
buffer[i] = a;
buffer[j] = b;
buffer[k] = c;
buffer[l] = d;
}
}
}
}
/* check 2dataq */
/* check 3data */
for(i=0;i<diskmax;++i) {
for(j=i+1;j<diskmax;++j) {
unsigned char* a;
unsigned char* b;
unsigned char* q;
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;
/* other is q */
k = diskmax + 1;
/* this is the point where quad parity fails over RAIDQP_DATA_LIMIT data disks */
if (diskmax > RAIDQP_DATA_LIMIT && COMBO3[n].use[2] >= 3)
continue;
/* save */
a = buffer[i];
b = buffer[j];
q = buffer[k];
/* save */
a = buffer[i];
b = buffer[j];
c = buffer[k];
/* damage */
buffer[i] = buffer_test0;
buffer[j] = buffer_test1;
buffer[k] = buffer_bad;
/* damage */
buffer[i] = buffer_test0;
buffer[j] = buffer_test1;
buffer[k] = buffer_test2;
/* recover */
raidTP_recov_2dataq(buffer, diskmax, block_size, i, j, buffer_zero);
/* ignore parity */
l = diskmax + COMBO3[n].ignore;
p = buffer[l];
buffer[l] = buffer_bad;
/* restore */
buffer[i] = a;
buffer[j] = b;
buffer[k] = q;
/* recover ignoring p */
raid_recov_3data(buffer, diskmax, block_size, i, j, k, COMBO3[n].use[0], COMBO3[n].use[1], COMBO3[n].use[2], buffer_zero);
/* check */
if (memcmp(buffer_test0, buffer[i], block_size) != 0
|| memcmp(buffer_test1, buffer[j], block_size) != 0
) {
fprintf(stderr, "Failed RAIDTP test\n");
exit(EXIT_FAILURE);
/* 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 2datap */
/* 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;
/* 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(buffer, diskmax, block_size, i, j, COMBO2[n].use[0], COMBO2[n].use[1], buffer_zero);
/* check */
if (memcmp(buffer_test0, a, block_size) != 0
|| memcmp(buffer_test1, b, 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;
}
}
}
/* check 1data */
for(n=0;n<sizeof(COMBO1)/sizeof(COMBO1[0]);++n) {
for(i=0;i<diskmax;++i) {
unsigned char* a;
unsigned char* b;
unsigned char* p;
/* other is p */
k = diskmax;
unsigned char* q;
unsigned char* r;
/* save */
a = buffer[i];
b = buffer[j];
p = buffer[k];
/* damage */
buffer[i] = buffer_test0;
buffer[j] = buffer_test1;
/* 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 */
raidTP_recov_2datap(buffer, diskmax, block_size, i, j, buffer_zero);
raid_recov_1data(buffer, diskmax, block_size, i, COMBO1[n].use, buffer_zero);
/* check */
if (memcmp(buffer_test0, a, block_size) != 0) {
fprintf(stderr, "Failed RAID test\n");
exit(EXIT_FAILURE);
}
/* restore parity */
buffer[j] = p;
buffer[k] = q;
buffer[l] = r;
/* restore */
buffer[i] = a;
buffer[j] = b;
buffer[k] = p;
/* check */
if (memcmp(buffer_test0, buffer[i], block_size) != 0
|| memcmp(buffer_test1, buffer[j], block_size) != 0
) {
fprintf(stderr, "Failed RAIDTP test\n");
exit(EXIT_FAILURE);
}
}
}
/* check datapq */
for(i=0;i<diskmax;++i) {
unsigned char* a;
unsigned char* p;
unsigned char* q;
/* others are p,q */
j = diskmax;
k = diskmax + 1;
/* save */
a = buffer[i];
p = buffer[j];
q = buffer[k];
/* damage */
buffer[i] = buffer_test0;
buffer[j] = buffer_bad;
buffer[k] = buffer_bad;
/* recover */
raidTP_recov_datapq(buffer, diskmax, block_size, i, buffer_zero);
/* restore */
buffer[i] = a;
buffer[j] = p;
buffer[k] = q;
/* check */
if (memcmp(buffer_test0, buffer[i], block_size) != 0) {
fprintf(stderr, "Failed RAIDTP test\n");
exit(EXIT_FAILURE);
}
}
@ -489,8 +421,6 @@ void selftest(int gui)
hashtest();
crc32ctest();
raid5test(33, 2048);
raid6test(33, 2048);
raidTPtest(33, 2048);
raidtest(24, 2048);
}

48
speed.c
View file

@ -66,10 +66,10 @@ void speed(void)
void* buffer_alloc;
unsigned char** buffer;
buffer = malloc_nofail_vector_align(diskmax + 3, block_size, &buffer_alloc);
buffer = malloc_nofail_vector_align(diskmax + 4, block_size, &buffer_alloc);
/* initialize with fixed data */
for(i=0;i<diskmax+3;++i) {
for(i=0;i<diskmax+4;++i) {
memset(buffer[i], i, block_size);
}
for(i=0;i<HASH_SIZE;++i)
@ -161,6 +161,18 @@ void speed(void)
printf("sse2x2");
#else
printf("sse2x1");
#endif
else if (cpu_has_mmx())
printf("mmxx1");
else
printf("int32x2");
printf("\n");
printf("Expected fastest RAIDQP is ");
if (cpu_has_sse2())
#if defined(__x86_64__)
printf("sse2x2");
#else
printf("sse2x1");
#endif
else if (cpu_has_mmx())
printf("mmxx1");
@ -325,6 +337,38 @@ void speed(void)
}
#endif
SPEED_START {
raidQP_int32r2(buffer, diskmax, block_size);
} SPEED_STOP
printf("RAIDQP int32x2 %"PRIu64" [MB/s]\n", ds / dt);
#if defined(__i386__) || defined(__x86_64__)
if (cpu_has_mmx()) {
SPEED_START {
raidQP_mmxr1(buffer, diskmax, block_size);
} SPEED_STOP
printf("RAIDQP mmxr1 %"PRIu64" [MB/s]\n", ds / dt);
}
if (cpu_has_sse2()) {
SPEED_START {
raidQP_sse2r1(buffer, diskmax, block_size);
} SPEED_STOP
printf("RAIDQP sse2x1 %"PRIu64" [MB/s]\n", ds / dt);
#if defined(__x86_64__)
SPEED_START {
raidQP_sse2r2(buffer, diskmax, block_size);
} SPEED_STOP
printf("RAIDQP sse2x2 %"PRIu64" [MB/s]\n", ds / dt);
#endif
}
#endif
free(buffer_alloc);
free(buffer);
}

17
state.c
View file

@ -23,6 +23,7 @@
#include "util.h"
#include "parity.h"
#include "cpu.h"
#include "raid.h"
const char* lev_name(unsigned l)
{
@ -30,6 +31,7 @@ const char* lev_name(unsigned l)
case 0 : return "Parity";
case 1 : return "Q-Parity";
case 2 : return "R-Parity";
case 3 : return "S-Parity";
}
return 0;
@ -41,6 +43,7 @@ const char* lev_config_name(unsigned l)
case 0 : return "parity";
case 1 : return "q-parity";
case 2 : return "r-parity";
case 3 : return "s-parity";
}
return 0;
@ -174,9 +177,14 @@ static void state_config_check(struct snapraid_state* state, const char* path)
++diskcount;
}
if (diskcount > 255) {
/* RAID6 P/Q parity works for up to 255 drives, no more */
fprintf(stderr, "Too many disks. No more than 255.\n");
if (state->level >= 2 && diskcount > RAID6_DATA_LIMIT) {
/* RAID6 parity works for up to 255 drives, no more */
fprintf(stderr, "Too many disks for RAID6. No more than %u.\n", RAID6_DATA_LIMIT);
exit(EXIT_FAILURE);
}
if (state->level >= 4 && diskcount > RAIDQP_DATA_LIMIT) {
/* RAIDQP parity works for up to 21 drives, no more */
fprintf(stderr, "Too many disks for RAIDQP. No more than %u.\n", RAIDQP_DATA_LIMIT);
exit(EXIT_FAILURE);
}
@ -328,7 +336,7 @@ 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) {
} else if (strcmp(tag, "parity") == 0 || strcmp(tag, "q-parity") == 0 || strcmp(tag, "r-parity") == 0 || strcmp(tag, "s-parity") == 0) {
char device[PATH_MAX];
char* slash;
struct stat st;
@ -338,6 +346,7 @@ void state_config(struct snapraid_state* state, const char* path, const char* co
case 'p' : l = 0; break;
case 'q' : l = 1; break;
case 'r' : l = 2; break;
case 's' : l = 3; break;
default:
fprintf(stderr, "Invalid '%s' specification in '%s' at line %u\n", tag, path, line);
exit(EXIT_FAILURE);

View file

@ -24,15 +24,15 @@
/**
* Max level of parity supported.
*/
#define LEV_MAX 3
#define LEV_MAX 4
/**
* Returns the parity name: Parity, Q-Parity, R-Parity.
* Returns the parity name: Parity, Q-Parity, R-Parity, S-Parity.
*/
const char* lev_name(unsigned level);
/**
* Returns the parity name used in the config file: parity, q-parity, r-parity.
* Returns the parity name used in the config file: parity, q-parity, r-parity, s-parity.
*/
const char* lev_config_name(unsigned level);

View file

@ -8795,6 +8795,42 @@ const unsigned char __attribute__((aligned(256))) gfexp4[256] =
0x58, 0x7d, 0xe9, 0x83, 0x36, 0xd8, 0x47, 0x01,
};
const unsigned char __attribute__((aligned(256))) gfexp8[256] =
{
0x01, 0x08, 0x40, 0x3a, 0xcd, 0x26, 0x2d, 0x75,
0x8f, 0x0c, 0x60, 0x27, 0x25, 0x35, 0xb5, 0xc1,
0x46, 0x0a, 0x50, 0xba, 0xb9, 0xa1, 0x61, 0x2f,
0x65, 0x0f, 0x78, 0xe7, 0x6b, 0x7f, 0xdf, 0xb6,
0xd9, 0x86, 0x44, 0x1a, 0xd0, 0xce, 0x3e, 0xed,
0x3b, 0xc5, 0x66, 0x17, 0xb8, 0xa9, 0x21, 0x15,
0xa8, 0x29, 0x55, 0x92, 0xe4, 0x73, 0xbf, 0x91,
0xfc, 0xb3, 0xf1, 0xdb, 0x96, 0xc4, 0x6e, 0x57,
0x82, 0x64, 0x07, 0x38, 0xdd, 0xa6, 0x59, 0xf2,
0xc3, 0x56, 0x8a, 0x24, 0x3d, 0xf5, 0xfb, 0x8b,
0x2c, 0x7d, 0xcf, 0x36, 0xad, 0x01, 0x08, 0x40,
0x3a, 0xcd, 0x26, 0x2d, 0x75, 0x8f, 0x0c, 0x60,
0x27, 0x25, 0x35, 0xb5, 0xc1, 0x46, 0x0a, 0x50,
0xba, 0xb9, 0xa1, 0x61, 0x2f, 0x65, 0x0f, 0x78,
0xe7, 0x6b, 0x7f, 0xdf, 0xb6, 0xd9, 0x86, 0x44,
0x1a, 0xd0, 0xce, 0x3e, 0xed, 0x3b, 0xc5, 0x66,
0x17, 0xb8, 0xa9, 0x21, 0x15, 0xa8, 0x29, 0x55,
0x92, 0xe4, 0x73, 0xbf, 0x91, 0xfc, 0xb3, 0xf1,
0xdb, 0x96, 0xc4, 0x6e, 0x57, 0x82, 0x64, 0x07,
0x38, 0xdd, 0xa6, 0x59, 0xf2, 0xc3, 0x56, 0x8a,
0x24, 0x3d, 0xf5, 0xfb, 0x8b, 0x2c, 0x7d, 0xcf,
0x36, 0xad, 0x01, 0x08, 0x40, 0x3a, 0xcd, 0x26,
0x2d, 0x75, 0x8f, 0x0c, 0x60, 0x27, 0x25, 0x35,
0xb5, 0xc1, 0x46, 0x0a, 0x50, 0xba, 0xb9, 0xa1,
0x61, 0x2f, 0x65, 0x0f, 0x78, 0xe7, 0x6b, 0x7f,
0xdf, 0xb6, 0xd9, 0x86, 0x44, 0x1a, 0xd0, 0xce,
0x3e, 0xed, 0x3b, 0xc5, 0x66, 0x17, 0xb8, 0xa9,
0x21, 0x15, 0xa8, 0x29, 0x55, 0x92, 0xe4, 0x73,
0xbf, 0x91, 0xfc, 0xb3, 0xf1, 0xdb, 0x96, 0xc4,
0x6e, 0x57, 0x82, 0x64, 0x07, 0x38, 0xdd, 0xa6,
0x59, 0xf2, 0xc3, 0x56, 0x8a, 0x24, 0x3d, 0xf5,
0xfb, 0x8b, 0x2c, 0x7d, 0xcf, 0x36, 0xad, 0x01,
};
const unsigned char __attribute__((aligned(256))) gfinv[256] =
{
/* note that the first element is not significative */