Support third redundancy strip on raidz3.

* grub-core/fs/zfs/zfs.c (recovery): Add Gauss for general case.
	Return error on singularity. All users updated.
	(read_device): Don't stop on 3rd failure on raidz3.
This commit is contained in:
Vladimir 'phcoder' Serbinenko 2011-11-04 15:19:23 +01:00
parent 8622923b66
commit c2fd16cacb
2 changed files with 123 additions and 25 deletions

View file

@ -1,3 +1,11 @@
2011-11-04 Vladimir Serbinenko <phcoder@gmail.com>
Support third redundancy strip on raidz3.
* grub-core/fs/zfs/zfs.c (recovery): Add Gauss for general case.
Return error on singularity. All users updated.
(read_device): Don't stop on 3rd failure on raidz3.
2011-11-04 Vladimir Serbinenko <phcoder@gmail.com>
Support case-insensitive ZFS subvolumes.

View file

@ -893,14 +893,15 @@ gf_mul (grub_uint8_t a, grub_uint8_t b)
return powx[powx_inv[a] + powx_inv[b]];
}
static inline void
static inline grub_err_t
recovery (grub_uint8_t *bufs[4], grub_size_t s, const int nbufs,
const unsigned *powers,
const int *idx)
{
grub_dprintf ("zfs", "recovering %u bufers\n", nbufs);
/* Now we have */
/* b_i = sum (r_j* (x ** (powers[i] * idx[j])))*/
/* Since nbufs <= 3 let's be lazy. */
/* Let's invert the matrix in question. */
switch (nbufs)
{
/* Easy: r_0 = bufs[0] / (x << (powers[i] * idx[j])). */
@ -909,39 +910,126 @@ recovery (grub_uint8_t *bufs[4], grub_size_t s, const int nbufs,
int add;
grub_uint8_t *a;
if (powers[0] == 0 || idx[0] == 0)
return;
return GRUB_ERR_NONE;
add = 255 - ((powers[0] * idx[0]) % 255);
for (a = bufs[0]; s--; a++)
if (*a)
*a = powx[powx_inv[*a] + add];
return;
return GRUB_ERR_NONE;
}
/* b_0 = r_0 * (x ** (powers[0] * idx[0])) + r_1 * (x ** (powers[0] * idx[1]))
b_1 = r_0 * (x ** (powers[1] * idx[0])) + r_1 * (x ** (powers[1] * idx[1]))
*/
/* Case 2x2: Let's use the determinant formula. */
case 2:
{
grub_uint8_t det, det_inv;
grub_uint8_t det0, det1;
grub_uint8_t matrixinv[2][2];
unsigned i;
/* The determinant is: */
det = (powx[(powers[0] * idx[0] + powers[1] * idx[1]) % 255]
^ powx[(powers[0] * idx[1] + powers[1] * idx[0]) % 255]);
if (det == 0)
return grub_error (GRUB_ERR_BAD_FS, "singular recovery matrix");
det_inv = powx[255 - powx_inv[det]];
matrixinv[0][0] = gf_mul (powx[(powers[1] * idx[1]) % 255], det_inv);
matrixinv[1][1] = gf_mul (powx[(powers[0] * idx[0]) % 255], det_inv);
matrixinv[0][1] = gf_mul (powx[(powers[0] * idx[1]) % 255], det_inv);
matrixinv[1][0] = gf_mul (powx[(powers[1] * idx[0]) % 255], det_inv);
for (i = 0; i < s; i++)
{
det0 = (gf_mul (bufs[0][i], powx[(powers[1] * idx[1]) % 255])
^ gf_mul (bufs[1][i], powx[(powers[0] * idx[1]) % 255]));
det1 = (gf_mul (bufs[0][i], powx[(powers[1] * idx[0]) % 255])
^ gf_mul (bufs[1][i], powx[(powers[0] * idx[0]) % 255]));
grub_uint8_t b0, b1;
b0 = bufs[0][i];
b1 = bufs[1][i];
bufs[0][i] = gf_mul (det0, det_inv);
bufs[1][i] = gf_mul (det1, det_inv);
bufs[0][i] = (gf_mul (b0, matrixinv[0][0])
^ gf_mul (b1, matrixinv[0][1]));
bufs[1][i] = (gf_mul (b0, matrixinv[1][0])
^ gf_mul (b1, matrixinv[1][1]));
}
break;
return GRUB_ERR_NONE;
}
}
/* Otherwise use Gauss. */
default:
{
grub_uint8_t matrix1[nbufs][nbufs], matrix2[nbufs][nbufs];
int i, j, k;
for (i = 0; i < nbufs; i++)
for (j = 0; j < nbufs; j++)
matrix1[i][j] = powx[(powers[i] * idx[j]) % 255];
for (i = 0; i < nbufs; i++)
for (j = 0; j < nbufs; j++)
matrix2[i][j] = 0;
for (i = 0; i < nbufs; i++)
matrix2[i][i] = 1;
for (i = 0; i < nbufs; i++)
{
grub_uint8_t mul;
for (j = i; j < nbufs; j++)
if (matrix1[i][j])
break;
if (j == nbufs)
return grub_error (GRUB_ERR_BAD_FS, "singular recovery matrix");
if (j != i)
{
int xchng;
xchng = j;
for (j = 0; j < nbufs; j++)
{
grub_uint8_t t;
t = matrix1[xchng][j];
matrix1[xchng][j] = matrix1[i][j];
matrix1[i][j] = t;
}
for (j = 0; j < nbufs; j++)
{
grub_uint8_t t;
t = matrix2[xchng][j];
matrix2[xchng][j] = matrix2[i][j];
matrix2[i][j] = t;
}
}
mul = powx[255 - powx_inv[matrix1[i][i]]];
for (j = 0; j < nbufs; j++)
matrix1[i][j] = gf_mul (matrix1[i][j], mul);
for (j = 0; j < nbufs; j++)
matrix2[i][j] = gf_mul (matrix2[i][j], mul);
for (j = i + 1; j < nbufs; j++)
{
mul = matrix1[j][i];
for (k = 0; k < nbufs; k++)
matrix1[j][k] ^= gf_mul (matrix1[i][k], mul);
for (k = 0; k < nbufs; k++)
matrix2[j][k] ^= gf_mul (matrix2[i][k], mul);
}
}
for (i = nbufs - 1; i >= 0; i--)
{
for (j = 0; j < i; j++)
{
grub_uint8_t mul;
mul = matrix1[j][i];
for (k = 0; k < nbufs; k++)
matrix1[j][k] ^= gf_mul (matrix1[i][k], mul);
for (k = 0; k < nbufs; k++)
matrix2[j][k] ^= gf_mul (matrix2[i][k], mul);
}
}
for (i = 0; i < (int) s; i++)
{
grub_uint8_t b[nbufs];
for (j = 0; j < nbufs; j++)
b[j] = bufs[j][i];
for (j = 0; j < nbufs; j++)
{
bufs[j][i] = 0;
for (k = 0; k < nbufs; k++)
bufs[j][i] ^= gf_mul (matrix2[j][k], b[k]);
}
}
return GRUB_ERR_NONE;
}
}
}
static grub_err_t
@ -1040,10 +1128,7 @@ read_device (grub_uint64_t offset, struct grub_zfs_device_desc *desc,
| (offset & ((1 << desc->ashift) - 1)),
&desc->children[devn],
csize, buf);
/* No raidz3 recovery yet. */
if (err
&& failed_devices < desc->nparity
&& failed_devices < 2)
if (err && failed_devices < desc->nparity)
{
recovery_buf[failed_devices] = buf;
recovery_len[failed_devices] = csize;
@ -1066,6 +1151,7 @@ read_device (grub_uint64_t offset, struct grub_zfs_device_desc *desc,
unsigned cur_redundancy_pow = 0;
unsigned n_redundancy = 0;
unsigned i, j;
grub_err_t err;
/* Compute mul. x**s has a period of 255. */
if (powx[0] == 0)
@ -1088,7 +1174,6 @@ read_device (grub_uint64_t offset, struct grub_zfs_device_desc *desc,
n_redundancy < failed_devices;
cur_redundancy_pow++)
{
grub_err_t err;
high = grub_divmod64 ((offset >> desc->ashift)
+ cur_redundancy_pow
+ ((desc->nparity == 1)
@ -1151,10 +1236,15 @@ read_device (grub_uint64_t offset, struct grub_zfs_device_desc *desc,
grub_uint8_t *tmp_recovery_buf[4];
for (j = 0; j < i; j++)
tmp_recovery_buf[j] = recovery_buf[j] + recovery_len[j] - 1;
recovery (tmp_recovery_buf, 1, i, redundancy_pow, recovery_idx);
err = recovery (tmp_recovery_buf, 1, i, redundancy_pow,
recovery_idx);
if (err)
return err;
}
recovery (recovery_buf, recovery_len[failed_devices - 1],
failed_devices, redundancy_pow, recovery_idx);
err = recovery (recovery_buf, recovery_len[failed_devices - 1],
failed_devices, redundancy_pow, recovery_idx);
if (err)
return err;
}
return GRUB_ERR_NONE;
}