]> git.ipfire.org Git - thirdparty/grub.git/commitdiff
Support third redundancy strip on raidz3.
authorVladimir 'phcoder' Serbinenko <phcoder@gmail.com>
Fri, 4 Nov 2011 14:19:23 +0000 (15:19 +0100)
committerVladimir 'phcoder' Serbinenko <phcoder@gmail.com>
Fri, 4 Nov 2011 14:19:23 +0000 (15:19 +0100)
* 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.

ChangeLog
grub-core/fs/zfs/zfs.c

index 48c43467bd87372e0392fdea645cccf7bd8eb377..70dc4e4f31f20873dc93cdc252ba1a300b0bead0 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -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.
index 9c06574e9b37a9b32eb22f1c579346a5a9a942d5..f892290a1c98867f4e1c4a0c4d1360aacabd6fd2 100644 (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 (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]));
+         }
+       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);
+             }
+         }
 
-           bufs[0][i] = gf_mul (det0, det_inv);
-           bufs[1][i] = gf_mul (det1, det_inv);
+       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]);
+             }
          }
-       break;
+       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;
       }