From c416528eaab6c8cd255e63a1505d4e348ff18b6e Mon Sep 17 00:00:00 2001 From: Kent Overstreet Date: Fri, 23 Nov 2018 00:44:20 -0500 Subject: snapraid --- raid/raid.c | 586 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 586 insertions(+) create mode 100644 raid/raid.c (limited to 'raid/raid.c') diff --git a/raid/raid.c b/raid/raid.c new file mode 100644 index 0000000..3052675 --- /dev/null +++ b/raid/raid.c @@ -0,0 +1,586 @@ +/* + * Copyright (C) 2013 Andrea Mazzoleni + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "internal.h" +#include "gf.h" + +/* + * This is a RAID implementation working in the Galois Field GF(2^8) with + * the primitive polynomial x^8 + x^4 + x^3 + x^2 + 1 (285 decimal), and + * supporting up to six parity levels. + * + * For RAID5 and RAID6 it works as as described in the H. Peter Anvin's + * paper "The mathematics of RAID-6" [1]. Please refer to this paper for a + * complete explanation. + * + * To support triple parity, it was first evaluated and then dropped, an + * extension of the same approach, with additional parity coefficients set + * as powers of 2^-1, with equations: + * + * P = sum(Di) + * Q = sum(2^i * Di) + * R = sum(2^-i * Di) with 0<=i RAID_PARITY_MAX); + + raid_gen_ptr[np - 1](nd, size, v); +} + +/** + * Inverts the square matrix M of size nxn into V. + * + * This is not a general matrix inversion because we assume the matrix M + * to have all the square submatrix not singular. + * We use Gauss elimination to invert. + * + * @M Matrix to invert with @n rows and @n columns. + * @V Destination matrix where the result is put. + * @n Number of rows and columns of the matrix. + */ +void raid_invert(uint8_t *M, uint8_t *V, int n) +{ + int i, j, k; + + /* set the identity matrix in V */ + for (i = 0; i < n; ++i) + for (j = 0; j < n; ++j) + V[i * n + j] = i == j; + + /* for each element in the diagonal */ + for (k = 0; k < n; ++k) { + uint8_t f; + + /* the diagonal element cannot be 0 because */ + /* we are inverting matrices with all the square */ + /* submatrices not singular */ + BUG_ON(M[k * n + k] == 0); + + /* make the diagonal element to be 1 */ + f = inv(M[k * n + k]); + for (j = 0; j < n; ++j) { + M[k * n + j] = mul(f, M[k * n + j]); + V[k * n + j] = mul(f, V[k * n + j]); + } + + /* make all the elements over and under the diagonal */ + /* to be zero */ + for (i = 0; i < n; ++i) { + if (i == k) + continue; + f = M[i * n + k]; + for (j = 0; j < n; ++j) { + M[i * n + j] ^= mul(f, M[k * n + j]); + V[i * n + j] ^= mul(f, V[k * n + j]); + } + } + } +} + +/** + * Computes the parity without the missing data blocks + * and store it in the buffers of such data blocks. + * + * This is the parity expressed as Pa,Qa,Ra,Sa,Ta,Ua in the equations. + */ +void raid_delta_gen(int nr, int *id, int *ip, int nd, size_t size, void **v) +{ + void *p[RAID_PARITY_MAX]; + void *pa[RAID_PARITY_MAX]; + int i, j; + int np; + void *latest; + + /* total number of parities we are going to process */ + /* they are both the used and the unused ones */ + np = ip[nr - 1] + 1; + + /* latest missing data block */ + latest = v[id[nr - 1]]; + + /* setup pointers for delta computation */ + for (i = 0, j = 0; i < np; ++i) { + /* keep a copy of the original parity vector */ + p[i] = v[nd + i]; + + if (ip[j] == i) { + /* + * Set used parities to point to the missing + * data blocks. + * + * The related data blocks are instead set + * to point to the "zero" buffer. + */ + + /* the latest parity to use ends the for loop and */ + /* then it cannot happen to process more of them */ + BUG_ON(j >= nr); + + /* buffer for missing data blocks */ + pa[j] = v[id[j]]; + + /* set at zero the missing data blocks */ + v[id[j]] = raid_zero_block; + + /* compute the parity over the missing data blocks */ + v[nd + i] = pa[j]; + + /* check for the next used entry */ + ++j; + } else { + /* + * Unused parities are going to be rewritten with + * not significative data, becase we don't have + * functions able to compute only a subset of + * parities. + * + * To avoid this, we reuse parity buffers, + * assuming that all the parity functions write + * parities in order. + * + * We assign the unused parity block to the same + * block of the latest used parity that we know it + * will be written. + * + * This means that this block will be written + * multiple times and only the latest write will + * contain the correct data. + */ + v[nd + i] = latest; + } + } + + /* all the parities have to be processed */ + BUG_ON(j != nr); + + /* recompute the parity, note that np may be smaller than the */ + /* total number of parities available */ + raid_gen(nd, np, size, v); + + /* restore data buffers as before */ + for (j = 0; j < nr; ++j) + v[id[j]] = pa[j]; + + /* restore parity buffers as before */ + for (i = 0; i < np; ++i) + v[nd + i] = p[i]; +} + +/** + * Recover failure of one data block for PAR1. + * + * Starting from the equation: + * + * Pd = Dx + * + * and solving we get: + * + * Dx = Pd + */ +void raid_rec1of1(int *id, int nd, size_t size, void **v) +{ + void *p; + void *pa; + + /* for PAR1 we can directly compute the missing block */ + /* and we don't need to use the zero buffer */ + p = v[nd]; + pa = v[id[0]]; + + /* use the parity as missing data block */ + v[id[0]] = p; + + /* compute the parity over the missing data block */ + v[nd] = pa; + + /* compute */ + raid_gen(nd, 1, size, v); + + /* restore as before */ + v[id[0]] = pa; + v[nd] = p; +} + +/** + * Recover failure of two data blocks for PAR2. + * + * Starting from the equations: + * + * Pd = Dx + Dy + * Qd = 2^id[0] * Dx + 2^id[1] * Dy + * + * and solving we get: + * + * 1 2^(-id[0]) + * Dy = ------------------- * Pd + ------------------- * Qd + * 2^(id[1]-id[0]) + 1 2^(id[1]-id[0]) + 1 + * + * Dx = Dy + Pd + * + * with conditions: + * + * 2^id[0] != 0 + * 2^(id[1]-id[0]) + 1 != 0 + * + * That are always satisfied for any 0<=id[0] np); + BUG_ON(np > RAID_PARITY_MAX); + + /* enforce order in index vector */ + BUG_ON(nr >= 2 && ir[0] >= ir[1]); + BUG_ON(nr >= 3 && ir[1] >= ir[2]); + BUG_ON(nr >= 4 && ir[2] >= ir[3]); + BUG_ON(nr >= 5 && ir[3] >= ir[4]); + BUG_ON(nr >= 6 && ir[4] >= ir[5]); + + /* enforce limit on index vector */ + BUG_ON(nr > 0 && ir[nr-1] >= nd + np); + + /* count the number of data blocks to recover */ + nrd = 0; + while (nrd < nr && ir[nrd] < nd) + ++nrd; + + /* all the remaining are parity */ + nrp = nr - nrd; + + /* enforce limit on number of failures */ + BUG_ON(nrd > nd); + BUG_ON(nrp > np); + + /* if failed data is present */ + if (nrd != 0) { + int ip[RAID_PARITY_MAX]; + int i, j, k; + + /* setup the vector of parities to use */ + for (i = 0, j = 0, k = 0; i < np; ++i) { + if (j < nrp && ir[nrd + j] == nd + i) { + /* this parity has to be recovered */ + ++j; + } else { + /* this parity is used for recovering */ + ip[k] = i; + ++k; + } + } + + /* recover the nrd data blocks specified in ir[], */ + /* using the first nrd parity in ip[] for recovering */ + raid_rec_ptr[nrd - 1](nrd, ir, ip, nd, size, v); + } + + /* recompute all the parities up to the last bad one */ + if (nrp != 0) + raid_gen(nd, ir[nr - 1] - nd + 1, size, v); +} + +void raid_data(int nr, int *id, int *ip, int nd, size_t size, void **v) +{ + /* enforce limit on size */ + BUG_ON(size % 64 != 0); + + /* enforce limit on number of failures */ + BUG_ON(nr > nd); + BUG_ON(nr > RAID_PARITY_MAX); + + /* enforce order in index vector for data */ + BUG_ON(nr >= 2 && id[0] >= id[1]); + BUG_ON(nr >= 3 && id[1] >= id[2]); + BUG_ON(nr >= 4 && id[2] >= id[3]); + BUG_ON(nr >= 5 && id[3] >= id[4]); + BUG_ON(nr >= 6 && id[4] >= id[5]); + + /* enforce limit on index vector for data */ + BUG_ON(nr > 0 && id[nr-1] >= nd); + + /* enforce order in index vector for parity */ + BUG_ON(nr >= 2 && ip[0] >= ip[1]); + BUG_ON(nr >= 3 && ip[1] >= ip[2]); + BUG_ON(nr >= 4 && ip[2] >= ip[3]); + BUG_ON(nr >= 5 && ip[3] >= ip[4]); + BUG_ON(nr >= 6 && ip[4] >= ip[5]); + + /* if failed data is present */ + if (nr != 0) + raid_rec_ptr[nr - 1](nr, id, ip, nd, size, v); +} + -- cgit v1.2.3