From e0c0ea3dcc4e7e0fa6de1843e65a7c88cdeca66a Mon Sep 17 00:00:00 2001 From: Andrea Mazzoleni Date: Thu, 27 Aug 2026 19:19:31 +0200 Subject: [PATCH] Optimize scalar 8-bit RAID parity generation and recovery in int.c --- cmdline/speed.c | 18 +- raid/NOTES | 52 ++- raid/int.c | 846 +++++++++++++++++++++++++++++++++++------- raid/internal.h | 6 +- raid/raid.c | 60 --- raid/test.c | 13 +- raid/test/speedtest.c | 10 +- 7 files changed, 777 insertions(+), 228 deletions(-) diff --git a/cmdline/speed.c b/cmdline/speed.c index 2e1dd36e..7f7a933e 100644 --- a/cmdline/speed.c +++ b/cmdline/speed.c @@ -1218,7 +1218,7 @@ void speed_rec(int nd, void** v, int size, int delta, int period) printf("RAID polynomial functions used for recovering with 'fix':\n"); printf("%8s", ""); printf("%8s", "best"); - printf("%8s", "int8"); + printf("%8s", "int"); #ifdef CONFIG_NEON printf("%8s", "neon"); #endif @@ -1911,9 +1911,7 @@ void speed_rec(int nd, void** v, int size, int delta, int period) fflush(stdout); SPEED_START { - /* ensure to use same hardware in the delta step */ - raid_gen_force(3, raid_gen3_int8); - raid_recX_int8(3, id, ip, nd, size, v); + raid_rec3_int8(3, id, ip, nd, size, v); } SPEED_STOP printf("%8" PRIu64, ds / dt); @@ -2004,9 +2002,7 @@ void speed_rec(int nd, void** v, int size, int delta, int period) fflush(stdout); SPEED_START { - /* ensure to use same hardware in the delta step */ - raid_gen_force(4, raid_gen4_int8); - raid_recX_int8(4, id, ip, nd, size, v); + raid_rec4_int8(4, id, ip, nd, size, v); } SPEED_STOP printf("%8" PRIu64, ds / dt); @@ -2097,9 +2093,7 @@ void speed_rec(int nd, void** v, int size, int delta, int period) fflush(stdout); SPEED_START { - /* ensure to use same hardware in the delta step */ - raid_gen_force(5, raid_gen5_int8); - raid_recX_int8(5, id, ip, nd, size, v); + raid_rec5_int8(5, id, ip, nd, size, v); } SPEED_STOP printf("%8" PRIu64, ds / dt); @@ -2190,9 +2184,7 @@ void speed_rec(int nd, void** v, int size, int delta, int period) fflush(stdout); SPEED_START { - /* ensure to use same hardware in the delta step */ - raid_gen_force(6, raid_gen6_int8); - raid_recX_int8(6, id, ip, nd, size, v); + raid_rec6_int8(6, id, ip, nd, size, v); } SPEED_STOP printf("%8" PRIu64, ds / dt); diff --git a/raid/NOTES b/raid/NOTES index 3d084d6b..d2cbe1b8 100644 --- a/raid/NOTES +++ b/raid/NOTES @@ -45,6 +45,9 @@ Generation: * RAID GFNI generation uses direct Cauchy coefficients. Their 8-byte affine representations are precomputed in a disk-major table, avoiding a dependent Cauchy-coefficient -> affine-matrix lookup in the hot generation loop. + Similarly, scalar int8 generation precomputes per-disk 2D multiplication table + pointers T[d][p] before the hot loop to eliminate two-level dependent table + lookups. * AVX512 128-byte processing, non-GFNI AVX512BW Horner, standalone VPTERNLOGQ transformations, VPBLENDVB, cached parity stores, and large @@ -69,8 +72,8 @@ Recovery: AArch32 NEON uses fused syndrome generation for all non-P REC1 and REC2 cases and for REC3..REC6. SSSE3 and SSSE3 extended use it for REC3..REC6. -* Scalar recovery, SSSE3 REC1/REC2, and AArch32 NEON P+Q continue to use - raid_delta_gen(). +* Scalar REC1/REC2, SSSE3 REC1/REC2, and AArch32 NEON P+Q continue to use + raid_delta_gen(), while scalar REC3..REC6 uses fused syndrome recovery. * Non-GFNI fused recovery splits every completed syndrome into low/high nibbles once and reuses the split values across all reconstructed outputs. @@ -86,13 +89,14 @@ Recovery: * Specialized P+Q rec2of2 recovery reduces 4 GF multiplications to 2 GF multiplications and 1 XOR; AVX512BW, GFNI, AVX2, and AArch64 NEON handle this - directly inside the 1-pass fused kernel, while scalar int8, SSSE3, SSSE3ext, - and AArch32 NEON backends implement dedicated rec2of2 helpers. + directly inside the 1-pass fused kernel, while SSSE3, SSSE3ext, + and AArch32 NEON backends implement dedicated rec2of2 helpers, and scalar int8 + handles it via has_p in raid_rec2_int8. -* When P is among the selected parity blocks, optimized SIMD RECX paths - reconstruct only nr - 1 missing blocks through the inverse matrix and derive - the final missing block from the raw P delta by XORing the blocks already - reconstructed. +* When P is among the selected parity blocks, optimized SIMD and scalar RECX + paths reconstruct only nr - 1 missing blocks through the inverse matrix and + derive the final missing block from the raw P delta by XORing the blocks + already reconstructed. Architecture and validation: @@ -251,8 +255,8 @@ REC2OF2: directly in the 1-pass fused kernel, which computes P with XOR, Q with GF multiplication, and derives the second missing block via XOR. -* Scalar int8, SSSE3, SSSE3ext, and AArch32 NEON call their specialized - raid_rec2of2_*() delta helpers. +* SSSE3, SSSE3ext, and AArch32 NEON call their specialized + raid_rec2of2_*() delta helpers, while scalar int8 leverages has_p in raid_rec2_int8. * This makes the row a direct benchmark for the specialized P+Q recovery implementation. @@ -271,8 +275,8 @@ REC2OF3 and REC2OF4: REC3OF3 through REC6OF6: * These rows measure multi-failure recovery for each supported failure count - using the first N parity levels. SIMD columns use fused recovery; the scalar - column retains raid_delta_gen(). + using the first N parity levels. SIMD backends and scalar int8 use fused + syndrome recovery. Delta generation: @@ -316,7 +320,7 @@ raid_delta_gen() fusion XOR streaming within the fused kernel (no GF matrix multiplication), while on scalar, SSSE3, and AVX2 it uses the raid_rec1of1() pointer remapping path. -* raid_delta_gen() remains in the scalar implementation, SSSE3 REC1/REC2, and +* raid_delta_gen() remains in scalar REC1/REC2, SSSE3 REC1/REC2, and AArch32 NEON P+Q. @@ -390,6 +394,11 @@ Generation: GFNI direct coefficients difference is VGF2P8AFFINEQB with an 8-byte RAID matrix versus VGF2P8MULB with an AES coefficient byte. +* Scalar int8 generation (raid_gen2_int8 .. raid_gen6_int8) similarly precomputes + per-disk multiplication table pointers T[d][p] in a 2D array before entering + the hot loop, replacing the double-indexed raid_gfmul[d0][raid_gfgen[j][d]] + lookup with a single direct table access T[d][p][d0]. + Generation: coefficient residency --------------------------------- @@ -695,8 +704,8 @@ REC2 status: * Generic REC2 uses the fused path across AVX2, AVX512BW, GFNI, and NEON. * AVX2, AVX512BW, GFNI, and AArch64 NEON handle P+Q recovery directly inside the - 1-pass fused kernel, while scalar int8, SSSE3, SSSE3ext, and AArch32 NEON - backends implement dedicated rec2of2 delta helpers. + 1-pass fused kernel, while SSSE3, SSSE3ext, and AArch32 NEON backends implement + dedicated rec2of2 delta helpers, and scalar int8 handles it via has_p. Specialized rec2of2 P+Q @@ -717,7 +726,7 @@ Backends implement this specialization as follows: derivation directly into the unified 1-pass fused kernel (P is accumulated via XOR, Q via GF multiplication, Dy is solved with inverse coefficients, and Dx derived by XOR with Pdelta). -* Scalar int8 (raid_rec2of2_int8) +* Scalar int8 (has_p branch in raid_rec2_int8) * SSSE3 and SSSE3 ext (raid_rec2of2_ssse3, raid_rec2of2_ssse3ext) * AArch32 NEON (raid_rec2of2_neon32) @@ -782,6 +791,10 @@ Users include: * AVX512BW RECX * SSSE3 REC3..REC6 * AArch64 and AArch32 NEON REC1..REC6 +* Scalar int8 RECX (raid_recX_int8), which precomputes 256-byte raid_gfmul + pointers S[s][j] and R[j][k], and structures inner loops with explicit + if (nr >= N) branches so the compiler fully unrolls syndrome accumulation + and inverse-matrix reconstruction when nr is a compile-time constant. The remaining cost is that the low/high tables are still loaded or broadcast for every coefficient of every data chunk. @@ -1032,9 +1045,10 @@ Short list of ideas already tried or rejected existing traversal instead. * raid_delta_gen() + REC fusion: - implemented for all SIMD REC1..REC6 on AVX512BW, GFNI, and AArch64 NEON, and for - all AVX2 REC except rec1of1; SSSE3 retains delta for REC1/REC2; only scalar - paths, SSSE3 REC1/REC2, and AArch32 NEON P+Q retain raid_delta_gen(). + implemented for all SIMD REC1..REC6 on AVX512BW, GFNI, and AArch64 NEON, for + all AVX2 REC except rec1of1, and for scalar int8 REC3..REC6; SSSE3 retains delta + for REC1/REC2; only scalar REC1/REC2, SSSE3 REC1/REC2, and AArch32 NEON P+Q retain + raid_delta_gen(). * Software prefetch: no strong reason to add it given the existing long streams and proven diff --git a/raid/int.c b/raid/int.c index 62d7fbb7..018bd429 100644 --- a/raid/int.c +++ b/raid/int.c @@ -6,6 +6,8 @@ /* * Generate one parity block (RAID5 with XOR) using 32bit C implementation. + * + * Uses 16-byte chunks across four 32-bit words. */ void raid_gen1_int32(int nd, size_t size, void **vv) { @@ -16,24 +18,34 @@ void raid_gen1_int32(int nd, size_t size, void **vv) uint32_t p0; uint32_t p1; + uint32_t p2; + uint32_t p3; l = nd - 1; p = v[nd]; - for (i = 0; i < size; i += 8) { + for (i = 0; i < size; i += 16) { p0 = v_read32(&v[l][i]); p1 = v_read32(&v[l][i + 4]); + p2 = v_read32(&v[l][i + 8]); + p3 = v_read32(&v[l][i + 12]); for (d = l - 1; d >= 0; --d) { p0 ^= v_read32(&v[d][i]); p1 ^= v_read32(&v[d][i + 4]); + p2 ^= v_read32(&v[d][i + 8]); + p3 ^= v_read32(&v[d][i + 12]); } v_write32(&p[i], p0); v_write32(&p[i + 4], p1); + v_write32(&p[i + 8], p2); + v_write32(&p[i + 12], p3); } } /* * Generate one parity block (RAID5 with XOR) using 64bit C implementation. + * + * Uses 32-byte chunks across four 64-bit words. */ void raid_gen1_int64(int nd, size_t size, void **vv) { @@ -44,24 +56,34 @@ void raid_gen1_int64(int nd, size_t size, void **vv) uint64_t p0; uint64_t p1; + uint64_t p2; + uint64_t p3; l = nd - 1; p = v[nd]; - for (i = 0; i < size; i += 16) { + for (i = 0; i < size; i += 32) { p0 = v_read64(&v[l][i]); p1 = v_read64(&v[l][i + 8]); + p2 = v_read64(&v[l][i + 16]); + p3 = v_read64(&v[l][i + 24]); for (d = l - 1; d >= 0; --d) { p0 ^= v_read64(&v[d][i]); p1 ^= v_read64(&v[d][i + 8]); + p2 ^= v_read64(&v[d][i + 16]); + p3 ^= v_read64(&v[d][i + 24]); } v_write64(&p[i], p0); v_write64(&p[i + 8], p1); + v_write64(&p[i + 16], p2); + v_write64(&p[i + 24], p3); } } /* * Generate two parity blocks (RAID6 with powers of 2 or 3) using 32bit C implementation. + * + * Uses Horner's method with 8-byte chunks across two 32-bit words. */ static __always_inline void raid_gen2_int32_gen(int nd, size_t size, void **vv, int generator) { @@ -110,18 +132,10 @@ static __always_inline void raid_gen2_int32_gen(int nd, size_t size, void **vv, } } -void raid_gen2_int32_raid(int nd, size_t size, void **vv) -{ - raid_gen2_int32_gen(nd, size, vv, 2); -} - -void raid_gen2_int32_aes(int nd, size_t size, void **vv) -{ - raid_gen2_int32_gen(nd, size, vv, 3); -} - /* * Generate two parity blocks (RAID6 with powers of 2 or 3) using 64bit C implementation. + * + * Uses Horner's method with 16-byte chunks across two 64-bit words. */ static __always_inline void raid_gen2_int64_gen(int nd, size_t size, void **vv, int generator) { @@ -170,23 +184,10 @@ static __always_inline void raid_gen2_int64_gen(int nd, size_t size, void **vv, } } -void raid_gen2_int64_raid(int nd, size_t size, void **vv) -{ - raid_gen2_int64_gen(nd, size, vv, 2); -} - -void raid_gen2_int64_aes(int nd, size_t size, void **vv) -{ - raid_gen2_int64_gen(nd, size, vv, 3); -} - -void raid_gen2_int8(int nd, size_t size, void **vv) -{ - raid_gen_ref(nd, 2, size, vv); -} - /* - * Generate three parity blocks with powers of 2^-1 using 32bit C implementation. + * Generate three parity blocks (with powers of 2^-1) using 32bit C implementation. + * + * Uses Horner's method with 8-byte chunks across two 32-bit words. */ void raid_genz_int32_raid(int nd, size_t size, void **vv) { @@ -239,7 +240,9 @@ void raid_genz_int32_raid(int nd, size_t size, void **vv) } /* - * Generate three parity blocks with powers of 2^-1 using 64bit C implementation. + * Generate three parity blocks (with powers of 2^-1) using 64bit C implementation. + * + * Uses Horner's method with 16-byte chunks across two 64-bit words. */ void raid_genz_int64_raid(int nd, size_t size, void **vv) { @@ -292,12 +295,61 @@ void raid_genz_int64_raid(int nd, size_t size, void **vv) } /* - * Generate three parity blocks with Cauchy matrix using 8bit C implementation. + * Generate two parity blocks (RAID6 with Cauchy matrix) using 8bit C implementation. * - * Note that instead of a generic multiplication table, likely resulting - * in multiple cache misses, a precomputed table could be used. - * But this is only a kind of reference function, and we are not really - * interested in speed. + * Precomputes per-disk multiplication tables and processes 2-byte chunks. + */ +void raid_gen2_int8(int nd, size_t size, void **vv) +{ + uint8_t **v = (uint8_t **)vv; + uint8_t *p; + uint8_t *q; + const uint8_t *T[RAID_DATA_MAX]; + int d, l; + size_t i; + + uint8_t d0, d1, q0, q1, p0, p1; + + l = nd - 1; + p = v[nd]; + q = v[nd + 1]; + + for (d = 1; d <= l; ++d) + T[d] = table(A(1, d)); + + for (i = 0; i < size; i += 2) { + p0 = q0 = 0; + p1 = q1 = 0; + for (d = l; d > 0; --d) { + d0 = v[d][i]; + d1 = v[d][i + 1]; + + p0 ^= d0; + p1 ^= d1; + q0 ^= T[d][d0]; + q1 ^= T[d][d1]; + } + + /* first disk with all coefficients at 1 */ + d0 = v[0][i]; + d1 = v[0][i + 1]; + + p0 ^= d0; + p1 ^= d1; + q0 ^= d0; + q1 ^= d1; + + p[i] = p0; + p[i + 1] = p1; + q[i] = q0; + q[i + 1] = q1; + } +} + +/* + * Generate three parity blocks (with Cauchy matrix) using 8bit C implementation. + * + * Precomputes per-disk multiplication tables and processes 2-byte chunks. */ void raid_gen3_int8(int nd, size_t size, void **vv) { @@ -305,46 +357,61 @@ void raid_gen3_int8(int nd, size_t size, void **vv) uint8_t *p; uint8_t *q; uint8_t *r; + const uint8_t *T[RAID_DATA_MAX][2]; int d, l; size_t i; - uint8_t d0, r0, q0, p0; + uint8_t d0, d1, r0, r1, q0, q1, p0, p1; l = nd - 1; p = v[nd]; q = v[nd + 1]; r = v[nd + 2]; - for (i = 0; i < size; i += 1) { + for (d = 1; d <= l; ++d) { + T[d][0] = table(A(1, d)); + T[d][1] = table(A(2, d)); + } + + for (i = 0; i < size; i += 2) { p0 = q0 = r0 = 0; + p1 = q1 = r1 = 0; for (d = l; d > 0; --d) { d0 = v[d][i]; + d1 = v[d][i + 1]; p0 ^= d0; - q0 ^= raid_gfmul[d0][raid_gfgen[1][d]]; - r0 ^= raid_gfmul[d0][raid_gfgen[2][d]]; + p1 ^= d1; + q0 ^= T[d][0][d0]; + q1 ^= T[d][0][d1]; + r0 ^= T[d][1][d0]; + r1 ^= T[d][1][d1]; } /* first disk with all coefficients at 1 */ d0 = v[0][i]; + d1 = v[0][i + 1]; p0 ^= d0; + p1 ^= d1; q0 ^= d0; + q1 ^= d1; r0 ^= d0; + r1 ^= d1; p[i] = p0; + p[i + 1] = p1; q[i] = q0; + q[i + 1] = q1; r[i] = r0; + r[i + 1] = r1; } } /* - * Generate four parity blocks with Cauchy matrix using 8bit C implementation. + * Generate four parity blocks (with Cauchy matrix) using 8bit C implementation. * - * Note that instead of a generic multiplication table, likely resulting - * in multiple cache misses, a precomputed table could be used. - * But this is only a kind of reference function, and we are not really - * interested in speed. + * Precomputes per-disk multiplication tables and processes 2-byte chunks. */ void raid_gen4_int8(int nd, size_t size, void **vv) { @@ -353,10 +420,11 @@ void raid_gen4_int8(int nd, size_t size, void **vv) uint8_t *q; uint8_t *r; uint8_t *s; + const uint8_t *T[RAID_DATA_MAX][3]; int d, l; size_t i; - uint8_t d0, s0, r0, q0, p0; + uint8_t d0, d1, s0, s1, r0, r1, q0, q1, p0, p1; l = nd - 1; p = v[nd]; @@ -364,39 +432,57 @@ void raid_gen4_int8(int nd, size_t size, void **vv) r = v[nd + 2]; s = v[nd + 3]; - for (i = 0; i < size; i += 1) { + for (d = 1; d <= l; ++d) { + T[d][0] = table(A(1, d)); + T[d][1] = table(A(2, d)); + T[d][2] = table(A(3, d)); + } + + for (i = 0; i < size; i += 2) { p0 = q0 = r0 = s0 = 0; + p1 = q1 = r1 = s1 = 0; for (d = l; d > 0; --d) { d0 = v[d][i]; + d1 = v[d][i + 1]; p0 ^= d0; - q0 ^= raid_gfmul[d0][raid_gfgen[1][d]]; - r0 ^= raid_gfmul[d0][raid_gfgen[2][d]]; - s0 ^= raid_gfmul[d0][raid_gfgen[3][d]]; + p1 ^= d1; + q0 ^= T[d][0][d0]; + q1 ^= T[d][0][d1]; + r0 ^= T[d][1][d0]; + r1 ^= T[d][1][d1]; + s0 ^= T[d][2][d0]; + s1 ^= T[d][2][d1]; } /* first disk with all coefficients at 1 */ d0 = v[0][i]; + d1 = v[0][i + 1]; p0 ^= d0; + p1 ^= d1; q0 ^= d0; + q1 ^= d1; r0 ^= d0; + r1 ^= d1; s0 ^= d0; + s1 ^= d1; p[i] = p0; + p[i + 1] = p1; q[i] = q0; + q[i + 1] = q1; r[i] = r0; + r[i + 1] = r1; s[i] = s0; + s[i + 1] = s1; } } /* - * Generate five parity blocks with Cauchy matrix using 8bit C implementation. + * Generate five parity blocks (with Cauchy matrix) using 8bit C implementation. * - * Note that instead of a generic multiplication table, likely resulting - * in multiple cache misses, a precomputed table could be used. - * But this is only a kind of reference function, and we are not really - * interested in speed. + * Precomputes per-disk multiplication tables and processes 2-byte chunks. */ void raid_gen5_int8(int nd, size_t size, void **vv) { @@ -406,10 +492,11 @@ void raid_gen5_int8(int nd, size_t size, void **vv) uint8_t *r; uint8_t *s; uint8_t *t; + const uint8_t *T[RAID_DATA_MAX][4]; int d, l; size_t i; - uint8_t d0, t0, s0, r0, q0, p0; + uint8_t d0, d1, t0, t1, s0, s1, r0, r1, q0, q1, p0, p1; l = nd - 1; p = v[nd]; @@ -418,42 +505,64 @@ void raid_gen5_int8(int nd, size_t size, void **vv) s = v[nd + 3]; t = v[nd + 4]; - for (i = 0; i < size; i += 1) { + for (d = 1; d <= l; ++d) { + T[d][0] = table(A(1, d)); + T[d][1] = table(A(2, d)); + T[d][2] = table(A(3, d)); + T[d][3] = table(A(4, d)); + } + + for (i = 0; i < size; i += 2) { p0 = q0 = r0 = s0 = t0 = 0; + p1 = q1 = r1 = s1 = t1 = 0; for (d = l; d > 0; --d) { d0 = v[d][i]; + d1 = v[d][i + 1]; p0 ^= d0; - q0 ^= raid_gfmul[d0][raid_gfgen[1][d]]; - r0 ^= raid_gfmul[d0][raid_gfgen[2][d]]; - s0 ^= raid_gfmul[d0][raid_gfgen[3][d]]; - t0 ^= raid_gfmul[d0][raid_gfgen[4][d]]; + p1 ^= d1; + q0 ^= T[d][0][d0]; + q1 ^= T[d][0][d1]; + r0 ^= T[d][1][d0]; + r1 ^= T[d][1][d1]; + s0 ^= T[d][2][d0]; + s1 ^= T[d][2][d1]; + t0 ^= T[d][3][d0]; + t1 ^= T[d][3][d1]; } /* first disk with all coefficients at 1 */ d0 = v[0][i]; + d1 = v[0][i + 1]; p0 ^= d0; + p1 ^= d1; q0 ^= d0; + q1 ^= d1; r0 ^= d0; + r1 ^= d1; s0 ^= d0; + s1 ^= d1; t0 ^= d0; + t1 ^= d1; p[i] = p0; + p[i + 1] = p1; q[i] = q0; + q[i + 1] = q1; r[i] = r0; + r[i + 1] = r1; s[i] = s0; + s[i + 1] = s1; t[i] = t0; + t[i + 1] = t1; } } /* - * Generate six parity blocks with Cauchy matrix using 8bit C implementation. + * Generate six parity blocks (with Cauchy matrix) using 8bit C implementation. * - * Note that instead of a generic multiplication table, likely resulting - * in multiple cache misses, a precomputed table could be used. - * But this is only a kind of reference function, and we are not really - * interested in speed. + * Precomputes per-disk multiplication tables and processes 2-byte chunks. */ void raid_gen6_int8(int nd, size_t size, void **vv) { @@ -464,10 +573,11 @@ void raid_gen6_int8(int nd, size_t size, void **vv) uint8_t *s; uint8_t *t; uint8_t *u; + const uint8_t *T[RAID_DATA_MAX][5]; int d, l; size_t i; - uint8_t d0, u0, t0, s0, r0, q0, p0; + uint8_t d0, d1, u0, u1, t0, t1, s0, s1, r0, r1, q0, q1, p0, p1; l = nd - 1; p = v[nd]; @@ -477,41 +587,70 @@ void raid_gen6_int8(int nd, size_t size, void **vv) t = v[nd + 4]; u = v[nd + 5]; - for (i = 0; i < size; i += 1) { + for (d = 1; d <= l; ++d) { + T[d][0] = table(A(1, d)); + T[d][1] = table(A(2, d)); + T[d][2] = table(A(3, d)); + T[d][3] = table(A(4, d)); + T[d][4] = table(A(5, d)); + } + + for (i = 0; i < size; i += 2) { p0 = q0 = r0 = s0 = t0 = u0 = 0; + p1 = q1 = r1 = s1 = t1 = u1 = 0; for (d = l; d > 0; --d) { d0 = v[d][i]; + d1 = v[d][i + 1]; p0 ^= d0; - q0 ^= raid_gfmul[d0][raid_gfgen[1][d]]; - r0 ^= raid_gfmul[d0][raid_gfgen[2][d]]; - s0 ^= raid_gfmul[d0][raid_gfgen[3][d]]; - t0 ^= raid_gfmul[d0][raid_gfgen[4][d]]; - u0 ^= raid_gfmul[d0][raid_gfgen[5][d]]; + p1 ^= d1; + q0 ^= T[d][0][d0]; + q1 ^= T[d][0][d1]; + r0 ^= T[d][1][d0]; + r1 ^= T[d][1][d1]; + s0 ^= T[d][2][d0]; + s1 ^= T[d][2][d1]; + t0 ^= T[d][3][d0]; + t1 ^= T[d][3][d1]; + u0 ^= T[d][4][d0]; + u1 ^= T[d][4][d1]; } /* first disk with all coefficients at 1 */ d0 = v[0][i]; + d1 = v[0][i + 1]; p0 ^= d0; + p1 ^= d1; q0 ^= d0; + q1 ^= d1; r0 ^= d0; + r1 ^= d1; s0 ^= d0; + s1 ^= d1; t0 ^= d0; + t1 ^= d1; u0 ^= d0; + u1 ^= d1; p[i] = p0; + p[i + 1] = p1; q[i] = q0; + q[i + 1] = q1; r[i] = r0; + r[i + 1] = r1; s[i] = s0; + s[i + 1] = s1; t[i] = t0; + t[i + 1] = t1; u[i] = u0; + u[i + 1] = u1; } } /* * Recover failure of one data block at index id[0] using parity at index - * ip[0] for any RAID level. + * ip[0] for any RAID level using 8bit C implementation. * * Starting from the equation: * @@ -565,21 +704,17 @@ void raid_rec1_int8(int nr, int *id, int *ip, int nd, size_t size, void **vv) /* * Recover failure of two data blocks at indexes id[0],id[1] using parity at - * indexes ip[0],ip[1] for any RAID level. + * indexes ip[0],ip[1] for any RAID level using 8bit C implementation. * - * Starting from the equations: - * - * Pd = A[ip[0],id[0]] * Dx + A[ip[0],id[1]] * Dy - * Qd = A[ip[1],id[0]] * Dx + A[ip[1],id[1]] * Dy - * - * we solve inverting the coefficients matrix. + * If P is available, reconstructs only the first missing block through the + * inverse matrix and derives the second missing block from the P delta by XOR. */ -void raid_rec2_int8(int nr, int *id, int *ip, int nd, size_t size, void **vv) +static __always_inline void raid_rec2_int8_gen(int nr, int has_p, int *id, int *ip, int nd, size_t size, void **vv) { uint8_t **v = (uint8_t **)vv; uint8_t *p; - uint8_t *pa; uint8_t *q; + uint8_t *pa; uint8_t *qa; const uint8_t *T[2][2]; uint8_t G[2 * 2]; @@ -589,12 +724,6 @@ void raid_rec2_int8(int nr, int *id, int *ip, int nd, size_t size, void **vv) (void)nr; /* unused, it's always 2 */ - /* if it's RAID6, recovering with P and Q uses the faster function */ - if (ip[0] == 0 && ip[1] == 1) { - raid_rec2of2_int8(id, ip, nd, size, vv); - return; - } - /* setup the coefficients matrix */ for (j = 0; j < 2; ++j) for (k = 0; k < 2; ++k) @@ -604,7 +733,7 @@ void raid_rec2_int8(int nr, int *id, int *ip, int nd, size_t size, void **vv) raid_invert(G, V, 2); /* get multiplication tables */ - for (j = 0; j < 2; ++j) + for (j = 0; j < 2 - has_p; ++j) for (k = 0; k < 2; ++k) T[j][k] = table(V[j * 2 + k]); @@ -622,35 +751,46 @@ void raid_rec2_int8(int nr, int *id, int *ip, int nd, size_t size, void **vv) uint8_t Qd = q[i] ^ qa[i]; /* reconstruct */ - pa[i] = T[0][0][Pd] ^ T[0][1][Qd]; - qa[i] = T[1][0][Pd] ^ T[1][1][Qd]; + if (has_p) { + uint8_t Dx = T[0][0][Pd] ^ T[0][1][Qd]; + + pa[i] = Dx; + qa[i] = Pd ^ Dx; + } else { + pa[i] = T[0][0][Pd] ^ T[0][1][Qd]; + qa[i] = T[1][0][Pd] ^ T[1][1][Qd]; + } } } /* * Recover failure of N data blocks at indexes id[N] using parity at indexes - * ip[N] for any RAID level. + * ip[N] for any RAID level using 8bit C implementation. * - * Starting from the N equations, with 0<=i= N) branches for compile-time loop unrolling. * - * we solve inverting the coefficients matrix. - * - * Note that referring to previous equations you have: - * PD[0] = Pd, PD[1] = Qd, PD[2] = Rd, ... - * D[0] = Dx, D[1] = Dy, D[2] = Dz, ... + * If P is available, reconstructs only nr - 1 missing blocks through the + * inverse matrix and derives the last missing block from the P delta by XOR. */ -void raid_recX_int8(int nr, int *id, int *ip, int nd, size_t size, void **vv) +static __always_inline void raid_recX_int8(int nr, int has_p, int *id, int *ip, int nd, size_t size, void **vv) { uint8_t **v = (uint8_t **)vv; uint8_t *p[RAID_PARITY_MAX]; uint8_t *pa[RAID_PARITY_MAX]; - const uint8_t *T[RAID_PARITY_MAX][RAID_PARITY_MAX]; + uint8_t *src[RAID_DATA_MAX]; uint8_t G[RAID_PARITY_MAX * RAID_PARITY_MAX]; uint8_t V[RAID_PARITY_MAX * RAID_PARITY_MAX]; + const uint8_t *S[RAID_DATA_MAX][RAID_PARITY_MAX]; + const uint8_t *R[RAID_PARITY_MAX][RAID_PARITY_MAX]; size_t i; - int j, k; + int d, j, k, s; + int ns; + + BUG_ON(nr < 1 || nr > RAID_PARITY_MAX); /* setup the coefficients matrix */ for (j = 0; j < nr; ++j) @@ -660,37 +800,489 @@ void raid_recX_int8(int nr, int *id, int *ip, int nd, size_t size, void **vv) /* invert it to solve the system of linear equations */ raid_invert(G, V, nr); - /* get multiplication tables */ - for (j = 0; j < nr; ++j) - for (k = 0; k < nr; ++k) - T[j][k] = table(V[j * nr + k]); - - /* compute delta parity */ - raid_delta_gen(nr, id, ip, nd, size, vv); - - for (j = 0; j < nr; ++j) { - p[j] = v[nd + ip[j]]; - pa[j] = v[id[j]]; + /* setup selected parity and destination pointers */ + p[0] = v[nd + ip[0]]; + pa[0] = v[id[0]]; + if (nr >= 2) { + p[1] = v[nd + ip[1]]; + pa[1] = v[id[1]]; + } + if (nr >= 3) { + p[2] = v[nd + ip[2]]; + pa[2] = v[id[2]]; + } + if (nr >= 4) { + p[3] = v[nd + ip[3]]; + pa[3] = v[id[3]]; + } + if (nr >= 5) { + p[4] = v[nd + ip[4]]; + pa[4] = v[id[4]]; + } + if (nr >= 6) { + p[5] = v[nd + ip[5]]; + pa[5] = v[id[5]]; } - for (i = 0; i < size; ++i) { - uint8_t PD[RAID_PARITY_MAX]; + /* build the compact surviving-data list and its syndrome tables */ + ns = 0; + k = 0; + for (d = 0; d < nd; ++d) { + if (k < nr && d == id[k]) { + ++k; + continue; + } - /* delta */ - for (j = 0; j < nr; ++j) - PD[j] = p[j][i] ^ pa[j][i]; + src[ns] = v[d]; - /* reconstruct */ - for (j = 0; j < nr; ++j) { - uint8_t b = 0; + S[ns][0] = table(A(ip[0], d)); + if (nr >= 2) + S[ns][1] = table(A(ip[1], d)); + if (nr >= 3) + S[ns][2] = table(A(ip[2], d)); + if (nr >= 4) + S[ns][3] = table(A(ip[3], d)); + if (nr >= 5) + S[ns][4] = table(A(ip[4], d)); + if (nr >= 6) + S[ns][5] = table(A(ip[5], d)); - for (k = 0; k < nr; ++k) - b ^= T[j][k][PD[k]]; - pa[j][i] = b; + ++ns; + } + + BUG_ON(k != nr); + BUG_ON(ns != nd - nr); + + /* precompute inverse-matrix multiplication table pointers */ + if (!has_p) { + R[0][0] = table(V[0 * nr + 0]); + if (nr >= 2) + R[0][1] = table(V[0 * nr + 1]); + if (nr >= 3) + R[0][2] = table(V[0 * nr + 2]); + if (nr >= 4) + R[0][3] = table(V[0 * nr + 3]); + if (nr >= 5) + R[0][4] = table(V[0 * nr + 4]); + if (nr >= 6) + R[0][5] = table(V[0 * nr + 5]); + } + if (nr >= 2) { + int rj = 1 - has_p; + + R[rj][0] = table(V[rj * nr + 0]); + if (nr >= 2) + R[rj][1] = table(V[rj * nr + 1]); + if (nr >= 3) + R[rj][2] = table(V[rj * nr + 2]); + if (nr >= 4) + R[rj][3] = table(V[rj * nr + 3]); + if (nr >= 5) + R[rj][4] = table(V[rj * nr + 4]); + if (nr >= 6) + R[rj][5] = table(V[rj * nr + 5]); + } + if (nr >= 3) { + int rj = 2 - has_p; + + R[rj][0] = table(V[rj * nr + 0]); + if (nr >= 2) + R[rj][1] = table(V[rj * nr + 1]); + if (nr >= 3) + R[rj][2] = table(V[rj * nr + 2]); + if (nr >= 4) + R[rj][3] = table(V[rj * nr + 3]); + if (nr >= 5) + R[rj][4] = table(V[rj * nr + 4]); + if (nr >= 6) + R[rj][5] = table(V[rj * nr + 5]); + } + if (nr >= 4) { + int rj = 3 - has_p; + + R[rj][0] = table(V[rj * nr + 0]); + if (nr >= 2) + R[rj][1] = table(V[rj * nr + 1]); + if (nr >= 3) + R[rj][2] = table(V[rj * nr + 2]); + if (nr >= 4) + R[rj][3] = table(V[rj * nr + 3]); + if (nr >= 5) + R[rj][4] = table(V[rj * nr + 4]); + if (nr >= 6) + R[rj][5] = table(V[rj * nr + 5]); + } + if (nr >= 5) { + int rj = 4 - has_p; + + R[rj][0] = table(V[rj * nr + 0]); + if (nr >= 2) + R[rj][1] = table(V[rj * nr + 1]); + if (nr >= 3) + R[rj][2] = table(V[rj * nr + 2]); + if (nr >= 4) + R[rj][3] = table(V[rj * nr + 3]); + if (nr >= 5) + R[rj][4] = table(V[rj * nr + 4]); + if (nr >= 6) + R[rj][5] = table(V[rj * nr + 5]); + } + if (nr >= 6) { + int rj = 5 - has_p; + + R[rj][0] = table(V[rj * nr + 0]); + if (nr >= 2) + R[rj][1] = table(V[rj * nr + 1]); + if (nr >= 3) + R[rj][2] = table(V[rj * nr + 2]); + if (nr >= 4) + R[rj][3] = table(V[rj * nr + 3]); + if (nr >= 5) + R[rj][4] = table(V[rj * nr + 4]); + if (nr >= 6) + R[rj][5] = table(V[rj * nr + 5]); + } + + for (i = 0; i < size; i += 2) { + uint8_t syn0a, syn1a, syn2a, syn3a, syn4a, syn5a; + uint8_t syn0b, syn1b, syn2b, syn3b, syn4b, syn5b; + uint8_t p_deltaa, p_deltab; + + /* load syndromes from parity */ + syn0a = p[0][i]; + syn0b = p[0][i + 1]; + if (nr >= 2) { + syn1a = p[1][i]; + syn1b = p[1][i + 1]; + } + if (nr >= 3) { + syn2a = p[2][i]; + syn2b = p[2][i + 1]; + } + if (nr >= 4) { + syn3a = p[3][i]; + syn3b = p[3][i + 1]; + } + if (nr >= 5) { + syn4a = p[4][i]; + syn4b = p[4][i + 1]; + } + if (nr >= 6) { + syn5a = p[5][i]; + syn5b = p[5][i + 1]; + } + + /* accumulate syndromes over surviving data */ + for (s = 0; s < ns; ++s) { + uint8_t d0 = src[s][i]; + uint8_t d1 = src[s][i + 1]; + + if (has_p) { + syn0a ^= d0; + syn0b ^= d1; + } else { + syn0a ^= S[s][0][d0]; + syn0b ^= S[s][0][d1]; + } + + if (nr >= 2) { + syn1a ^= S[s][1][d0]; + syn1b ^= S[s][1][d1]; + } + if (nr >= 3) { + syn2a ^= S[s][2][d0]; + syn2b ^= S[s][2][d1]; + } + if (nr >= 4) { + syn3a ^= S[s][3][d0]; + syn3b ^= S[s][3][d1]; + } + if (nr >= 5) { + syn4a ^= S[s][4][d0]; + syn4b ^= S[s][4][d1]; + } + if (nr >= 6) { + syn5a ^= S[s][5][d0]; + syn5b ^= S[s][5][d1]; + } + } + + p_deltaa = syn0a; + p_deltab = syn0b; + + /* reconstruct missing blocks via inverse matrix */ + if (!has_p) { + uint8_t ba = R[0][0][syn0a]; + uint8_t bb = R[0][0][syn0b]; + + if (nr >= 2) { + ba ^= R[0][1][syn1a]; + bb ^= R[0][1][syn1b]; + } + if (nr >= 3) { + ba ^= R[0][2][syn2a]; + bb ^= R[0][2][syn2b]; + } + if (nr >= 4) { + ba ^= R[0][3][syn3a]; + bb ^= R[0][3][syn3b]; + } + if (nr >= 5) { + ba ^= R[0][4][syn4a]; + bb ^= R[0][4][syn4b]; + } + if (nr >= 6) { + ba ^= R[0][5][syn5a]; + bb ^= R[0][5][syn5b]; + } + + pa[0][i] = ba; + pa[0][i + 1] = bb; + } + if (nr >= 2) { + int rj = 1 - has_p; + uint8_t ba = R[rj][0][syn0a]; + uint8_t bb = R[rj][0][syn0b]; + + if (nr >= 2) { + ba ^= R[rj][1][syn1a]; + bb ^= R[rj][1][syn1b]; + } + if (nr >= 3) { + ba ^= R[rj][2][syn2a]; + bb ^= R[rj][2][syn2b]; + } + if (nr >= 4) { + ba ^= R[rj][3][syn3a]; + bb ^= R[rj][3][syn3b]; + } + if (nr >= 5) { + ba ^= R[rj][4][syn4a]; + bb ^= R[rj][4][syn4b]; + } + if (nr >= 6) { + ba ^= R[rj][5][syn5a]; + bb ^= R[rj][5][syn5b]; + } + + if (has_p) { + p_deltaa ^= ba; + p_deltab ^= bb; + } + + pa[rj][i] = ba; + pa[rj][i + 1] = bb; + } + if (nr >= 3) { + int rj = 2 - has_p; + uint8_t ba = R[rj][0][syn0a]; + uint8_t bb = R[rj][0][syn0b]; + + if (nr >= 2) { + ba ^= R[rj][1][syn1a]; + bb ^= R[rj][1][syn1b]; + } + if (nr >= 3) { + ba ^= R[rj][2][syn2a]; + bb ^= R[rj][2][syn2b]; + } + if (nr >= 4) { + ba ^= R[rj][3][syn3a]; + bb ^= R[rj][3][syn3b]; + } + if (nr >= 5) { + ba ^= R[rj][4][syn4a]; + bb ^= R[rj][4][syn4b]; + } + if (nr >= 6) { + ba ^= R[rj][5][syn5a]; + bb ^= R[rj][5][syn5b]; + } + + if (has_p) { + p_deltaa ^= ba; + p_deltab ^= bb; + } + + pa[rj][i] = ba; + pa[rj][i + 1] = bb; + } + if (nr >= 4) { + int rj = 3 - has_p; + uint8_t ba = R[rj][0][syn0a]; + uint8_t bb = R[rj][0][syn0b]; + + if (nr >= 2) { + ba ^= R[rj][1][syn1a]; + bb ^= R[rj][1][syn1b]; + } + if (nr >= 3) { + ba ^= R[rj][2][syn2a]; + bb ^= R[rj][2][syn2b]; + } + if (nr >= 4) { + ba ^= R[rj][3][syn3a]; + bb ^= R[rj][3][syn3b]; + } + if (nr >= 5) { + ba ^= R[rj][4][syn4a]; + bb ^= R[rj][4][syn4b]; + } + if (nr >= 6) { + ba ^= R[rj][5][syn5a]; + bb ^= R[rj][5][syn5b]; + } + + if (has_p) { + p_deltaa ^= ba; + p_deltab ^= bb; + } + + pa[rj][i] = ba; + pa[rj][i + 1] = bb; + } + if (nr >= 5) { + int rj = 4 - has_p; + uint8_t ba = R[rj][0][syn0a]; + uint8_t bb = R[rj][0][syn0b]; + + if (nr >= 2) { + ba ^= R[rj][1][syn1a]; + bb ^= R[rj][1][syn1b]; + } + if (nr >= 3) { + ba ^= R[rj][2][syn2a]; + bb ^= R[rj][2][syn2b]; + } + if (nr >= 4) { + ba ^= R[rj][3][syn3a]; + bb ^= R[rj][3][syn3b]; + } + if (nr >= 5) { + ba ^= R[rj][4][syn4a]; + bb ^= R[rj][4][syn4b]; + } + if (nr >= 6) { + ba ^= R[rj][5][syn5a]; + bb ^= R[rj][5][syn5b]; + } + + if (has_p) { + p_deltaa ^= ba; + p_deltab ^= bb; + } + + pa[rj][i] = ba; + pa[rj][i + 1] = bb; + } + if (nr >= 6) { + int rj = 5 - has_p; + uint8_t ba = R[rj][0][syn0a]; + uint8_t bb = R[rj][0][syn0b]; + + if (nr >= 2) { + ba ^= R[rj][1][syn1a]; + bb ^= R[rj][1][syn1b]; + } + if (nr >= 3) { + ba ^= R[rj][2][syn2a]; + bb ^= R[rj][2][syn2b]; + } + if (nr >= 4) { + ba ^= R[rj][3][syn3a]; + bb ^= R[rj][3][syn3b]; + } + if (nr >= 5) { + ba ^= R[rj][4][syn4a]; + bb ^= R[rj][4][syn4b]; + } + if (nr >= 6) { + ba ^= R[rj][5][syn5a]; + bb ^= R[rj][5][syn5b]; + } + + if (has_p) { + p_deltaa ^= ba; + p_deltab ^= bb; + } + + pa[rj][i] = ba; + pa[rj][i + 1] = bb; + } + + if (has_p) { + pa[nr - 1][i] = p_deltaa; + pa[nr - 1][i + 1] = p_deltab; } } } +void raid_gen2_int32_raid(int nd, size_t size, void **vv) +{ + raid_gen2_int32_gen(nd, size, vv, 2); +} + +void raid_gen2_int32_aes(int nd, size_t size, void **vv) +{ + raid_gen2_int32_gen(nd, size, vv, 3); +} + +void raid_gen2_int64_raid(int nd, size_t size, void **vv) +{ + raid_gen2_int64_gen(nd, size, vv, 2); +} + +void raid_gen2_int64_aes(int nd, size_t size, void **vv) +{ + raid_gen2_int64_gen(nd, size, vv, 3); +} + +void raid_rec2_int8(int nr, int *id, int *ip, int nd, size_t size, void **vv) +{ + BUG_ON(nr != 2); + + if (ip[0] == 0) + raid_rec2_int8_gen(2, 1, id, ip, nd, size, vv); + else + raid_rec2_int8_gen(2, 0, id, ip, nd, size, vv); +} + +void raid_rec3_int8(int nr, int *id, int *ip, int nd, size_t size, void **vv) +{ + BUG_ON(nr != 3); + if (ip[0] == 0) + raid_recX_int8(3, 1, id, ip, nd, size, vv); + else + raid_recX_int8(3, 0, id, ip, nd, size, vv); +} + +void raid_rec4_int8(int nr, int *id, int *ip, int nd, size_t size, void **vv) +{ + BUG_ON(nr != 4); + if (ip[0] == 0) + raid_recX_int8(4, 1, id, ip, nd, size, vv); + else + raid_recX_int8(4, 0, id, ip, nd, size, vv); +} + +void raid_rec5_int8(int nr, int *id, int *ip, int nd, size_t size, void **vv) +{ + BUG_ON(nr != 5); + if (ip[0] == 0) + raid_recX_int8(5, 1, id, ip, nd, size, vv); + else + raid_recX_int8(5, 0, id, ip, nd, size, vv); +} + +void raid_rec6_int8(int nr, int *id, int *ip, int nd, size_t size, void **vv) +{ + BUG_ON(nr != 6); + if (ip[0] == 0) + raid_recX_int8(6, 1, id, ip, nd, size, vv); + else + raid_recX_int8(6, 0, id, ip, nd, size, vv); +} + void raid_register_int(void) { raid_gen_register(RAID_ALGO_CAUCHY_PAR2, "int8", raid_gen2_int8, RAID_POLY_ANY); @@ -713,8 +1305,8 @@ void raid_register_int(void) raid_rec_register(RAID_ALGO_CAUCHY_PAR1, "int8", raid_rec1_int8, RAID_POLY_ANY); raid_rec_register(RAID_ALGO_CAUCHY_PAR2, "int8", raid_rec2_int8, RAID_POLY_ANY); - raid_rec_register(RAID_ALGO_CAUCHY_PAR3, "int8", raid_recX_int8, RAID_POLY_ANY); - raid_rec_register(RAID_ALGO_CAUCHY_PAR4, "int8", raid_recX_int8, RAID_POLY_ANY); - raid_rec_register(RAID_ALGO_CAUCHY_PAR5, "int8", raid_recX_int8, RAID_POLY_ANY); - raid_rec_register(RAID_ALGO_CAUCHY_PAR6, "int8", raid_recX_int8, RAID_POLY_ANY); + raid_rec_register(RAID_ALGO_CAUCHY_PAR3, "int8", raid_rec3_int8, RAID_POLY_ANY); + raid_rec_register(RAID_ALGO_CAUCHY_PAR4, "int8", raid_rec4_int8, RAID_POLY_ANY); + raid_rec_register(RAID_ALGO_CAUCHY_PAR5, "int8", raid_rec5_int8, RAID_POLY_ANY); + raid_rec_register(RAID_ALGO_CAUCHY_PAR6, "int8", raid_rec6_int8, RAID_POLY_ANY); } diff --git a/raid/internal.h b/raid/internal.h index b9052390..1558aa92 100644 --- a/raid/internal.h +++ b/raid/internal.h @@ -91,7 +91,6 @@ void raid_gen_ref(int nd, int np, size_t size, void **vv); void raid_invert(uint8_t *M, uint8_t *V, int n); void raid_delta_gen(int nr, int *id, int *ip, int nd, size_t size, void **v); void raid_rec1of1(int *id, int nd, size_t size, void **v); -void raid_rec2of2_int8(int *id, int *ip, int nd, size_t size, void **vv); void raid_gen1_int32(int nd, size_t size, void **vv); void raid_gen1_int64(int nd, size_t size, void **vv); void raid_gen1_sse2(int nd, size_t size, void **vv); @@ -168,7 +167,10 @@ void raid_gen6_avx2gfni_aes(int nd, size_t size, void **vv); void raid_gen6_avx512gfni_aes(int nd, size_t size, void **vv); void raid_rec1_int8(int nr, int *id, int *ip, int nd, size_t size, void **vv); void raid_rec2_int8(int nr, int *id, int *ip, int nd, size_t size, void **vv); -void raid_recX_int8(int nr, int *id, int *ip, int nd, size_t size, void **vv); +void raid_rec3_int8(int nr, int *id, int *ip, int nd, size_t size, void **vv); +void raid_rec4_int8(int nr, int *id, int *ip, int nd, size_t size, void **vv); +void raid_rec5_int8(int nr, int *id, int *ip, int nd, size_t size, void **vv); +void raid_rec6_int8(int nr, int *id, int *ip, int nd, size_t size, void **vv); void raid_rec1_ssse3(int nr, int *id, int *ip, int nd, size_t size, void **vv); void raid_rec2_ssse3(int nr, int *id, int *ip, int nd, size_t size, void **vv); void raid_rec3_ssse3(int nr, int *id, int *ip, int nd, size_t size, void **vv); diff --git a/raid/raid.c b/raid/raid.c index c2aab876..0e2b1cc7 100644 --- a/raid/raid.c +++ b/raid/raid.c @@ -413,63 +413,3 @@ void raid_rec1of1(int *id, int nd, size_t size, void **v) v[id[0]] = pa; v[nd] = p; } - -/** - * Recover failure of two data blocks for PAR2. - * - * Starting from the equations: - * - * Pd = Dx + Dy - * Qd = g^id[0] * Dx + g^id[1] * Dy - * - * and solving we get: - * - * 1 g^(-id[0]) - * Dy = ------------------- * Pd + ------------------- * Qd - * g^(id[1]-id[0]) + 1 g^(id[1]-id[0]) + 1 - * - * Dx = Dy + Pd - * - * with conditions: - * - * g^id[0] != 0 - * g^(id[1]-id[0]) + 1 != 0 - * - * That are always satisfied for any 0<=id[0]