From f8303d29630c4f1b23b9e6e315284fe851dd9811 Mon Sep 17 00:00:00 2001 From: Andrea Mazzoleni Date: Tue, 17 Sep 2013 23:05:44 +0200 Subject: [PATCH 1/4] Adds preliminary support for QuadParity. --- mktables.c | 16 + raid.c | 983 ++++++++++++++++++++++++++++++++++++++++++++++++++++- raid.h | 24 ++ selftest.c | 264 +++++++++++++- speed.c | 48 ++- state.c | 5 +- state.h | 6 +- tables.h | 36 ++ 8 files changed, 1363 insertions(+), 19 deletions(-) diff --git a/mktables.c b/mktables.c index 15089baf..ee5d7c0b 100644 --- a/mktables.c +++ b/mktables.c @@ -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"); diff --git a/raid.c b/raid.c index 93df72dc..bd470a85 100644 --- a/raid.c +++ b/raid.c @@ -708,7 +708,6 @@ void raidTP_sse2r1(unsigned char** buffer, unsigned diskmax, unsigned size) } #endif - #if defined(__x86_64__) /* * RAIDTP unrolled-by-2 SSE2 implementation @@ -794,6 +793,411 @@ void raidTP_sse2r2(unsigned char** buffer, unsigned diskmax, unsigned size) } #endif +/* + * RAIDQP unrolled-by-2 C implementation + */ +void raidQP_int32r2(unsigned char** buffer, unsigned diskmax, unsigned size) +{ + unsigned char* p; + unsigned char* q; + unsigned char* r; + unsigned char* s; + int z, z0; + unsigned d; + + uint32_t wd0, ws0, wr0, wq0, wp0, w10, w20; + uint32_t wd1, ws1, wr1, wq1, wp1, w11, w21; + + z0 = diskmax - 1; + p = buffer[diskmax]; + q = buffer[diskmax+1]; + r = buffer[diskmax+2]; + s = buffer[diskmax+3]; + + for(d=0;d= 0;--z) { + wd0 = *(uint32_t *)&buffer[z][d+0*4]; + wd1 = *(uint32_t *)&buffer[z][d+1*4]; + + /* wp += wd */ + wp0 ^= wd0; + wp1 ^= wd1; + + /* w1 = wq * {02} */ + w20 = MASK(wq0); + w21 = MASK(wq1); + w10 = SHLBYTE(wq0); + w11 = SHLBYTE(wq1); + w20 &= NBYTES(0x1d); + w21 &= NBYTES(0x1d); + w10 ^= w20; + w11 ^= w21; + + /* wq = w1 + wd */ + wq0 = w10 ^ wd0; + wq1 = w11 ^ wd1; + + /* w1 = wr * {02} */ + w20 = MASK(wr0); + w21 = MASK(wr1); + w10 = SHLBYTE(wr0); + w11 = SHLBYTE(wr1); + w20 &= NBYTES(0x1d); + w21 &= NBYTES(0x1d); + w10 ^= w20; + w11 ^= w21; + + /* w1 = w1 * {02} */ + w20 = MASK(w10); + w21 = MASK(w11); + w10 = SHLBYTE(w10); + w11 = SHLBYTE(w11); + w20 &= NBYTES(0x1d); + w21 &= NBYTES(0x1d); + w10 ^= w20; + w11 ^= w21; + + /* wr = w1 + wd */ + wr0 = w10 ^ wd0; + wr1 = w11 ^ wd1; + + /* w1 = ws * {02} */ + w20 = MASK(ws0); + w21 = MASK(ws1); + w10 = SHLBYTE(ws0); + w11 = SHLBYTE(ws1); + w20 &= NBYTES(0x1d); + w21 &= NBYTES(0x1d); + w10 ^= w20; + w11 ^= w21; + + /* w1 = w1 * {02} */ + w20 = MASK(w10); + w21 = MASK(w11); + w10 = SHLBYTE(w10); + w11 = SHLBYTE(w11); + w20 &= NBYTES(0x1d); + w21 &= NBYTES(0x1d); + w10 ^= w20; + w11 ^= w21; + + /* w1 = w1 * {02} */ + w20 = MASK(w10); + w21 = MASK(w11); + w10 = SHLBYTE(w10); + w11 = SHLBYTE(w11); + w20 &= NBYTES(0x1d); + w21 &= NBYTES(0x1d); + w10 ^= w20; + w11 ^= w21; + + /* ws = w1 + wd */ + ws0 = w10 ^ wd0; + ws1 = w11 ^ wd1; + + } + *(uint32_t *)&p[d+4*0] = wp0; + *(uint32_t *)&p[d+4*1] = wp1; + *(uint32_t *)&q[d+4*0] = wq0; + *(uint32_t *)&q[d+4*1] = wq1; + *(uint32_t *)&r[d+4*0] = wr0; + *(uint32_t *)&r[d+4*1] = wr1; + *(uint32_t *)&s[d+4*0] = ws0; + *(uint32_t *)&s[d+4*1] = ws1; + } +} + +#if defined(__i386__) || defined(__x86_64__) +/* + * RAIDQP unrolled-by-1 MMX implementation + */ +void raidQP_mmxr1(unsigned char** buffer, unsigned diskmax, unsigned size) +{ + unsigned char* p; + unsigned char* q; + unsigned char* r; + unsigned char* s; + int z, z0; + unsigned d; + + z0 = diskmax - 1; + p = buffer[diskmax]; + q = buffer[diskmax+1]; + r = buffer[diskmax+2]; + s = buffer[diskmax+3]; + + asm volatile("movq %0,%%mm7" : : "m" (raid6_mmx_constants.x1d)); + asm volatile("pxor %mm4,%mm4"); + asm volatile("pxor %mm5,%mm5"); + + for(d=0;d=0;--z) { + asm volatile("pcmpgtb %mm1,%mm4"); + asm volatile("pcmpgtb %mm3,%mm5"); + asm volatile("paddb %mm1,%mm1"); + asm volatile("paddb %mm3,%mm3"); + asm volatile("pand %mm7,%mm4"); + asm volatile("pand %mm7,%mm5"); + asm volatile("pxor %mm4,%mm1"); + asm volatile("pxor %mm5,%mm3"); + + asm volatile("pxor %mm4,%mm4"); + asm volatile("pxor %mm5,%mm5"); + + asm volatile("pcmpgtb %mm2,%mm4"); + asm volatile("pcmpgtb %mm3,%mm5"); + asm volatile("paddb %mm2,%mm2"); + asm volatile("paddb %mm3,%mm3"); + asm volatile("pand %mm7,%mm4"); + asm volatile("pand %mm7,%mm5"); + asm volatile("pxor %mm4,%mm2"); + asm volatile("pxor %mm5,%mm3"); + + asm volatile("pxor %mm4,%mm4"); + asm volatile("pxor %mm5,%mm5"); + + asm volatile("pcmpgtb %mm2,%mm4"); + asm volatile("pcmpgtb %mm3,%mm5"); + asm volatile("paddb %mm2,%mm2"); + asm volatile("paddb %mm3,%mm3"); + asm volatile("pand %mm7,%mm4"); + asm volatile("pand %mm7,%mm5"); + asm volatile("pxor %mm4,%mm2"); + asm volatile("pxor %mm5,%mm3"); + + asm volatile("movq %0,%%mm6" : : "m" (buffer[z][d])); + asm volatile("pxor %mm6,%mm0"); + asm volatile("pxor %mm6,%mm1"); + asm volatile("pxor %mm6,%mm2"); + asm volatile("pxor %mm6,%mm3"); + + asm volatile("pxor %mm4,%mm4"); + asm volatile("pxor %mm5,%mm5"); + } + asm volatile("movq %%mm0,%0" : "=m" (p[d])); + asm volatile("movq %%mm1,%0" : "=m" (q[d])); + asm volatile("movq %%mm2,%0" : "=m" (r[d])); + asm volatile("movq %%mm3,%0" : "=m" (s[d])); + } + + asm volatile("emms" : : : "memory"); +} +#endif + +#if defined(__i386__) || defined(__x86_64__) +/* + * RAIDQP unrolled-by-1 SSE2 implementation + */ +void raidQP_sse2r1(unsigned char** buffer, unsigned diskmax, unsigned size) +{ + unsigned char* p; + unsigned char* q; + unsigned char* r; + unsigned char* s; + int z, z0; + unsigned d; + + z0 = diskmax - 1; + p = buffer[diskmax]; + q = buffer[diskmax+1]; + r = buffer[diskmax+2]; + s = buffer[diskmax+3]; + + asm volatile("movdqa %0,%%xmm7" : : "m" (raid6_sse_constants.x1d[0])); + asm volatile("pxor %xmm4,%xmm4"); + asm volatile("pxor %xmm5,%xmm5"); + + for(d=0;d=0;--z) { + asm volatile("pcmpgtb %xmm1,%xmm4"); + asm volatile("pcmpgtb %xmm3,%xmm5"); + asm volatile("paddb %xmm1,%xmm1"); + asm volatile("paddb %xmm3,%xmm3"); + asm volatile("pand %xmm7,%xmm4"); + asm volatile("pand %xmm7,%xmm5"); + asm volatile("pxor %xmm4,%xmm1"); + asm volatile("pxor %xmm5,%xmm3"); + + asm volatile("pxor %xmm4,%xmm4"); + asm volatile("pxor %xmm5,%xmm5"); + + asm volatile("pcmpgtb %xmm2,%xmm4"); + asm volatile("pcmpgtb %xmm3,%xmm5"); + asm volatile("paddb %xmm2,%xmm2"); + asm volatile("paddb %xmm3,%xmm3"); + asm volatile("pand %xmm7,%xmm4"); + asm volatile("pand %xmm7,%xmm5"); + asm volatile("pxor %xmm4,%xmm2"); + asm volatile("pxor %xmm5,%xmm3"); + + asm volatile("pxor %xmm4,%xmm4"); + asm volatile("pxor %xmm5,%xmm5"); + + asm volatile("pcmpgtb %xmm2,%xmm4"); + asm volatile("pcmpgtb %xmm3,%xmm5"); + asm volatile("paddb %xmm2,%xmm2"); + asm volatile("paddb %xmm3,%xmm3"); + asm volatile("pand %xmm7,%xmm4"); + asm volatile("pand %xmm7,%xmm5"); + asm volatile("pxor %xmm4,%xmm2"); + asm volatile("pxor %xmm5,%xmm3"); + + asm volatile("movdqa %0,%%xmm6" : : "m" (buffer[z][d])); + asm volatile("pxor %xmm6,%xmm0"); + asm volatile("pxor %xmm6,%xmm1"); + asm volatile("pxor %xmm6,%xmm2"); + asm volatile("pxor %xmm6,%xmm3"); + + asm volatile("pxor %xmm4,%xmm4"); + asm volatile("pxor %xmm5,%xmm5"); + } + asm volatile("movntdq %%xmm0,%0" : "=m" (p[d])); + asm volatile("movntdq %%xmm1,%0" : "=m" (q[d])); + asm volatile("movntdq %%xmm2,%0" : "=m" (r[d])); + asm volatile("movntdq %%xmm3,%0" : "=m" (s[d])); + } + + asm volatile("sfence" : : : "memory"); +} +#endif + +#if defined(__x86_64__) +/* + * RAIDQP unrolled-by-2 SSE2 implementation + * Note that it uses all the 16 registers, meaning that x64 is required. + */ +void raidQP_sse2r2(unsigned char** buffer, unsigned diskmax, unsigned size) +{ + unsigned char* p; + unsigned char* q; + unsigned char* r; + unsigned char* s; + int z, z0; + unsigned d; + + z0 = diskmax - 1; + p = buffer[diskmax]; + q = buffer[diskmax+1]; + r = buffer[diskmax+2]; + s = buffer[diskmax+3]; + + asm volatile("movdqa %0,%%xmm7" : : "m" (raid6_sse_constants.x1d[0])); + asm volatile("pxor %xmm4,%xmm4"); + asm volatile("pxor %xmm5,%xmm5"); + asm volatile("pxor %xmm12,%xmm12"); + asm volatile("pxor %xmm13,%xmm13"); + + for(d=0;d=0;--z) { + asm volatile("pcmpgtb %xmm1,%xmm4"); + asm volatile("pcmpgtb %xmm3,%xmm5"); + asm volatile("pcmpgtb %xmm9,%xmm12"); + asm volatile("pcmpgtb %xmm11,%xmm13"); + asm volatile("paddb %xmm1,%xmm1"); + asm volatile("paddb %xmm3,%xmm3"); + asm volatile("paddb %xmm9,%xmm9"); + asm volatile("paddb %xmm11,%xmm11"); + asm volatile("pand %xmm7,%xmm4"); + asm volatile("pand %xmm7,%xmm5"); + asm volatile("pand %xmm7,%xmm12"); + asm volatile("pand %xmm7,%xmm13"); + asm volatile("pxor %xmm4,%xmm1"); + asm volatile("pxor %xmm5,%xmm3"); + asm volatile("pxor %xmm12,%xmm9"); + asm volatile("pxor %xmm13,%xmm11"); + + asm volatile("pxor %xmm4,%xmm4"); + asm volatile("pxor %xmm5,%xmm5"); + asm volatile("pxor %xmm12,%xmm12"); + asm volatile("pxor %xmm13,%xmm13"); + + asm volatile("pcmpgtb %xmm2,%xmm4"); + asm volatile("pcmpgtb %xmm3,%xmm5"); + asm volatile("pcmpgtb %xmm10,%xmm12"); + asm volatile("pcmpgtb %xmm11,%xmm13"); + asm volatile("paddb %xmm2,%xmm2"); + asm volatile("paddb %xmm3,%xmm3"); + asm volatile("paddb %xmm10,%xmm10"); + asm volatile("paddb %xmm11,%xmm11"); + asm volatile("pand %xmm7,%xmm4"); + asm volatile("pand %xmm7,%xmm5"); + asm volatile("pand %xmm7,%xmm12"); + asm volatile("pand %xmm7,%xmm13"); + asm volatile("pxor %xmm4,%xmm2"); + asm volatile("pxor %xmm5,%xmm3"); + asm volatile("pxor %xmm12,%xmm10"); + asm volatile("pxor %xmm13,%xmm11"); + + asm volatile("pxor %xmm4,%xmm4"); + asm volatile("pxor %xmm5,%xmm5"); + asm volatile("pxor %xmm12,%xmm12"); + asm volatile("pxor %xmm13,%xmm13"); + + asm volatile("pcmpgtb %xmm2,%xmm4"); + asm volatile("pcmpgtb %xmm3,%xmm5"); + asm volatile("pcmpgtb %xmm10,%xmm12"); + asm volatile("pcmpgtb %xmm11,%xmm13"); + asm volatile("paddb %xmm2,%xmm2"); + asm volatile("paddb %xmm3,%xmm3"); + asm volatile("paddb %xmm10,%xmm10"); + asm volatile("paddb %xmm11,%xmm11"); + asm volatile("pand %xmm7,%xmm4"); + asm volatile("pand %xmm7,%xmm5"); + asm volatile("pand %xmm7,%xmm12"); + asm volatile("pand %xmm7,%xmm13"); + asm volatile("pxor %xmm4,%xmm2"); + asm volatile("pxor %xmm5,%xmm3"); + asm volatile("pxor %xmm12,%xmm10"); + asm volatile("pxor %xmm13,%xmm11"); + + asm volatile("movdqa %0,%%xmm6" : : "m" (buffer[z][d])); + asm volatile("movdqa %0,%%xmm14" : : "m" (buffer[z][d+16])); + asm volatile("pxor %xmm6,%xmm0"); + asm volatile("pxor %xmm6,%xmm1"); + asm volatile("pxor %xmm6,%xmm2"); + asm volatile("pxor %xmm6,%xmm3"); + asm volatile("pxor %xmm14,%xmm8"); + asm volatile("pxor %xmm14,%xmm9"); + asm volatile("pxor %xmm14,%xmm10"); + asm volatile("pxor %xmm14,%xmm11"); + + asm volatile("pxor %xmm4,%xmm4"); + asm volatile("pxor %xmm5,%xmm5"); + asm volatile("pxor %xmm12,%xmm12"); + asm volatile("pxor %xmm13,%xmm13"); + } + asm volatile("movntdq %%xmm0,%0" : "=m" (p[d])); + asm volatile("movntdq %%xmm8,%0" : "=m" (p[d+16])); + asm volatile("movntdq %%xmm1,%0" : "=m" (q[d])); + asm volatile("movntdq %%xmm9,%0" : "=m" (q[d+16])); + asm volatile("movntdq %%xmm2,%0" : "=m" (r[d])); + asm volatile("movntdq %%xmm10,%0" : "=m" (r[d+16])); + asm volatile("movntdq %%xmm3,%0" : "=m" (s[d])); + asm volatile("movntdq %%xmm11,%0" : "=m" (s[d+16])); + } + + asm volatile("sfence" : : : "memory"); +} +#endif + /****************************************************************************/ /* generic computation */ @@ -801,6 +1205,7 @@ void raidTP_sse2r2(unsigned char** buffer, unsigned diskmax, unsigned size) static void (*raid5_gen)(unsigned char** buffer, unsigned diskmax, unsigned size); static void (*raid6_gen)(unsigned char** buffer, unsigned diskmax, unsigned size); static void (*raidTP_gen)(unsigned char** buffer, unsigned diskmax, unsigned size); +static void (*raidQP_gen)(unsigned char** buffer, unsigned diskmax, unsigned size); void raid_gen(unsigned level, unsigned char** buffer, unsigned diskmax, unsigned size) { @@ -808,6 +1213,7 @@ void raid_gen(unsigned level, unsigned char** buffer, unsigned diskmax, unsigned case 1 : raid5_gen(buffer, diskmax, size); break; case 2 : raid6_gen(buffer, diskmax, size); break; case 3 : raidTP_gen(buffer, diskmax, size); break; + case 4 : raidQP_gen(buffer, diskmax, size); break; } } @@ -914,45 +1320,68 @@ static inline unsigned char mul(unsigned char a, unsigned char b) */ static inline unsigned char inv(unsigned char a) { -#ifdef CHECKER if (a == 0) { fprintf(stderr, "GF division by zero\n"); exit(EXIT_FAILURE); } -#endif + return gfinv[a]; } /** * GF 2^a. - * Not defined for a == 255. */ static inline unsigned char pow2(int v) { -#ifdef CHECKER if (v < 0 || v > 254) { fprintf(stderr, "GF invalid exponent\n"); exit(EXIT_FAILURE); } -#endif + return gfexp2[v]; } - /** * GF 4^a. - * Not defined for a == 255. */ static inline unsigned char pow4(int v) { -#ifdef CHECKER if (v < 0 || v > 254) { fprintf(stderr, "GF invalid exponent\n"); exit(EXIT_FAILURE); } -#endif + return gfexp4[v]; } +/** + * GF 8^a. + */ +static inline unsigned char pow8(int v) +{ + if (v < 0 || v > 254) { + fprintf(stderr, "GF invalid exponent\n"); + exit(EXIT_FAILURE); + } + + return gfexp8[v]; +} + +/** + * GF 2^b^a. + */ +static inline unsigned char pown(int b, int a) +{ + switch (b) { + case 0 : return 1; + case 1 : return pow2(a); + case 2 : return pow4(a); + case 3 : return pow8(a); + } + + fprintf(stderr, "GF invalid base\n"); + exit(EXIT_FAILURE); +} + /** * GF multiplication table. */ @@ -1480,6 +1909,536 @@ void raidTP_recov_3data(unsigned char** dptrs, unsigned diskmax, unsigned size, } } +/****************************************************************************/ +/* matrix recovering */ + +/** + * Inverts the square matrix M of size nxn into V. + * We use Gauss elimination to invert. + */ +static void invert(unsigned char* M, unsigned char* V, unsigned n) +{ + unsigned i,j,k; + + /* set the identity matrix in V */ + for(i=0;iblock_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 +340,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); diff --git a/state.h b/state.h index 6a0bd5e4..871ec87a 100644 --- a/state.h +++ b/state.h @@ -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); diff --git a/tables.h b/tables.h index b710e19b..927a8820 100644 --- a/tables.h +++ b/tables.h @@ -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 */ From 9af05bce98b985e0f2077a839203dcb49faba207 Mon Sep 17 00:00:00 2001 From: Andrea Mazzoleni Date: Wed, 18 Sep 2013 21:56:06 +0200 Subject: [PATCH 2/4] Uses a more generic strategy for recoving. --- check.c | 485 ++++++++++++++++++++++++++------------------------------ 1 file changed, 224 insertions(+), 261 deletions(-) diff --git a/check.c b/check.c index c2ee95d3..545024cb 100644 --- a/check.c +++ b/check.c @@ -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;jlevel, 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;iblock_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;jlevel, 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;iblock_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;jlevel, 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;iblock_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;iblock_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;jlevel, 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;iblock_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;jlevel, 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;iblock_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;iblock_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;jlevel, 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;jlevel, 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;jlevel, 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;llevel;++l) + buffer_recov[l] = buffer[diskmax + state->level + l]; + for(;l Date: Wed, 18 Sep 2013 23:06:07 +0200 Subject: [PATCH 3/4] Removes the specific recovering functions. We now always use the generic ones. --- raid.c | 261 ++++++++++++++----------------------- raid.h | 44 +------ selftest.c | 368 +++-------------------------------------------------- 3 files changed, 122 insertions(+), 551 deletions(-) diff --git a/raid.c b/raid.c index bd470a85..e584d61e 100644 --- a/raid.c +++ b/raid.c @@ -1232,8 +1232,8 @@ void raid_gen(unsigned level, unsigned char** buffer, unsigned diskmax, unsigned * This method is also the same used by ZFS to implement its RAIDTP * support. * - * Note that the same extension is not possible for Quad Parity because - * in the approach used in the paper we don't have the guarantee to always have + * To support RAIDQP (Quad Parity) we go futher using also the parity generator "8". + * Note that in this case we don't have the guarantee to always have * a system of independent linear equations, and for Quad Parity in some cases * the system is not solvable. * @@ -1258,18 +1258,18 @@ void raid_gen(unsigned level, unsigned char** buffer, unsigned diskmax, unsigned * Q = sum(2^i * Di) 0<=i RAID_QUAD_DATA_LIMIT && COMBO3[n].use[2] >= 3) + continue; + /* save */ a = buffer[i]; b = buffer[j]; @@ -520,7 +195,7 @@ static void raidQPtest(unsigned diskmax, unsigned block_size) || memcmp(buffer_test1, b, block_size) != 0 || memcmp(buffer_test2, c, block_size) != 0 ) { - fprintf(stderr, "Failed RAIDQP test 1\n"); + fprintf(stderr, "Failed RAID test\n"); exit(EXIT_FAILURE); } @@ -539,7 +214,7 @@ static void raidQPtest(unsigned diskmax, unsigned block_size) /* check 2data */ for(i=0;i Date: Wed, 18 Sep 2013 23:16:39 +0200 Subject: [PATCH 4/4] Enforces the 21 data disk limit. --- raid.h | 9 +++++++-- selftest.c | 4 ++-- state.c | 12 +++++++++--- 3 files changed, 18 insertions(+), 7 deletions(-) diff --git a/raid.h b/raid.h index 14fb1194..9a7c74ad 100644 --- a/raid.h +++ b/raid.h @@ -22,9 +22,14 @@ /* raid */ /** - * Max number of data disks for quad parity. + * Max number of data disks for RAID6. */ -#define RAID_QUAD_DATA_LIMIT 21 +#define RAID6_DATA_LIMIT 21 + +/** + * Max number of data disks for RAIDQP. + */ +#define RAIDQP_DATA_LIMIT 21 /** * Syndrome computation. diff --git a/selftest.c b/selftest.c index 3e2204b3..bbc99c66 100644 --- a/selftest.c +++ b/selftest.c @@ -168,8 +168,8 @@ static void raidtest(unsigned diskmax, unsigned block_size) unsigned char* c; unsigned char* p; - /* this is the point where quad parity fails over RAID_QUAD_DATA_LIMIT data disks */ - if (diskmax > RAID_QUAD_DATA_LIMIT && COMBO3[n].use[2] >= 3) + /* 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 */ diff --git a/state.c b/state.c index 320df9e1..d6942f50 100644 --- a/state.c +++ b/state.c @@ -23,6 +23,7 @@ #include "util.h" #include "parity.h" #include "cpu.h" +#include "raid.h" const char* lev_name(unsigned l) { @@ -176,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); }