/*
* 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.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#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);
BUG_ON(size % 64 != 0);
raid_par_ptr[np - 1](nd, size, v);
}
/**
* Inverts the square matrix M of size nxn into V.
* We use Gauss elimination to invert.
*/
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 0 */
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.
*
* Note that all the other parities not in the ip[] vector
* are destroyed.
*/
void raid_delta_gen(int nr, const int *id, const int *ip, int nd, size_t size, void **v)
{
void *p[RAID_PARITY_MAX];
void *pa[RAID_PARITY_MAX];
int i;
for (i = 0; i < nr; ++i) {
/* keep a copy of the parity buffer */
p[i] = v[nd+ip[i]];
/* buffer for missing data blocks */
pa[i] = v[id[i]];
/* set at zero the missing data blocks */
v[id[i]] = raid_zero_block;
/* compute the parity over the missing data blocks */
v[nd+ip[i]] = pa[i];
}
/* recompute the minimal parity required */
raid_par(nd, ip[nr - 1] + 1, size, v);
for (i = 0; i < nr; ++i) {
/* restore disk buffers as before */
v[id[i]] = pa[i];
/* restore parity buffers as before */
v[nd+ip[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_rec1_par1(const 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_par(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] nd);
BUG_ON(nrd + nrp > np);
BUG_ON(size % 64 != 0);
/* if failed data is present */
if (nrd != 0) {
int iu[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 (ip[j] == i) {
/* this parity has to be recovered */
++j;
} else {
/* this parity is used for recovering */
iu[k] = i;
++k;
}
}
/* recover the data, and limit the parity use to needed ones */
raid_rec_ptr[nrd - 1](nrd, id, iu, nd, size, v);
}
/* recompute all the parities up to the last bad one */
if (nrp != 0)
raid_par(nd, ip[nrp - 1] + 1, size, v);
}
void raid_rec_dataonly(int nr, const int *id, const int *ip, int nd, size_t size, void **v)
{
BUG_ON(nr > nd);
BUG_ON(size % 64 != 0);
/* if failed data is present */
if (nr != 0) {
/* if there is a waste buffer, use it to save unused parities */
if (raid_waste_block != 0) {
void *p[RAID_PARITY_MAX];
int i, j;
int np;
/* number of parities used */
np = ip[nr - 1] + 1;
/* set unused parities to the waste buffer */
for (i = 0, j = 0; i < np; ++i) {
p[i] = v[nd+i];
if (ip[j] == i) {
/* this parity is used for recovering */
++j;
} else {
/* this parity is not used */
v[nd+i] = raid_waste_block;
}
}
raid_rec_ptr[nr - 1](nr, id, ip, nd, size, v);
/* restore pointers */
for (i = 0; i < np; ++i)
v[nd+i] = p[i];
} else {
raid_rec_ptr[nr - 1](nr, id, ip, nd, size, v);
}
}
}