/* * 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); }