]> git.ipfire.org Git - thirdparty/grub.git/commitdiff
Fix RAIDZ(2).
authorVladimir 'phcoder' Serbinenko <phcoder@gmail.com>
Tue, 1 Nov 2011 19:05:28 +0000 (20:05 +0100)
committerVladimir 'phcoder' Serbinenko <phcoder@gmail.com>
Tue, 1 Nov 2011 19:05:28 +0000 (20:05 +0100)
* grub-core/fs/zfs/zfs.c (grub_zfs_device_desc): New member ashift.
(fill_vdev_info_real): Set ashift.
(read_device): Rewrite RAIDZ part based on reverse engineering.

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

index 27c18655aba446086c83e0f58fd782a7121e66df..e112868f01484a14fc56aa15ce8ca44ba1d71a64 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,11 @@
+2011-11-01  Vladimir Serbinenko  <phcoder@gmail.com>
+
+       Fix RAIDZ(2).
+
+       * grub-core/fs/zfs/zfs.c (grub_zfs_device_desc): New member ashift.
+       (fill_vdev_info_real): Set ashift.
+       (read_device): Rewrite RAIDZ part based on reverse engineering.
+
 2011-10-31  Vladimir Serbinenko  <phcoder@gmail.com>
 
        * grub-core/fs/btrfs.c (grub_btrfs_extent_read): Add sanity check and
index 9bd68222ae23da94efdc02746b5bf19bb2513658..c7fd130188e31c4cd3df100ee8d95b6050e185cf 100644 (file)
@@ -152,6 +152,7 @@ struct grub_zfs_device_desc
 
   /* Valid only for RAIDZ.  */
   unsigned nparity;
+  unsigned ashift;
 
   /* Valid only for leaf devices.  */
   grub_device_t dev;
@@ -516,6 +517,9 @@ fill_vdev_info_real (struct grub_zfs_data *data,
          if (!grub_zfs_nvlist_lookup_uint64 (nvlist, "nparity", &par))
            return grub_error (GRUB_ERR_BAD_FS, "couldn't find raidz parity");
          fill->nparity = par;
+         if (!grub_zfs_nvlist_lookup_uint64 (nvlist, "ashift", &par))
+           return grub_error (GRUB_ERR_BAD_FS, "couldn't find raidz ashift");
+         fill->ashift = par;
        }
 
       nelm = grub_zfs_nvlist_lookup_nvlist_array_get_nelm (nvlist, ZPOOL_CONFIG_CHILDREN);
@@ -882,38 +886,76 @@ read_device (grub_uint64_t offset, struct grub_zfs_device_desc *desc,
       }
     case DEVICE_RAIDZ:
       {
-       grub_uint64_t sector;
-       grub_uint32_t bsize;
        unsigned c = 0;
+       grub_uint64_t high;
+       grub_uint64_t devn;
+       grub_uint64_t redundancy_strip = 0, m;
+       grub_uint64_t redundancy_strip2 = 0;
+       grub_uint32_t s;
+
+       /* (4,1) -> 2, (3,1) -> 1 */
+       if (desc->nparity == 1)
+         s = asize + desc->n_children - 2;
+       else
+         s = asize + desc->n_children - 3;
+       high = grub_divmod64 ((offset >> desc->ashift),
+                             desc->n_children, &m);
 
-       bsize = (asize + desc->nparity) / desc->n_children;
-       sector = offset >> 9;
+       if (desc->nparity == 1)
+         {
+           redundancy_strip = m;
+           redundancy_strip += ((offset >> (desc->ashift + 11)) & 1);
+           if (redundancy_strip == desc->n_children)
+             redundancy_strip = 0;
+           redundancy_strip2 = redundancy_strip;
+         }
+       else
+         {
+           redundancy_strip = m;
+           redundancy_strip2 = m + 1;
+           if (redundancy_strip2 == desc->n_children)
+             redundancy_strip2 = 0;
+         }
+       grub_dprintf ("zfs", "rs = %x, %llx\n",
+                     (int) redundancy_strip,
+                     (unsigned long long) high);
        while (len > 0)
          {
            grub_size_t csize;
-           grub_uint64_t high;
-           grub_uint64_t devn;
+           grub_uint32_t bsize;
            grub_err_t err;
-           high = grub_divmod64 (sector + (asize > 2) + c, desc->n_children,
-                                 &devn);
-           csize = bsize << 9;
+           bsize = s / desc->n_children;
+             
+           while (1)
+             {
+               high = grub_divmod64 ((offset >> desc->ashift) + c,
+                                     desc->n_children, &devn);
+               if (devn != redundancy_strip && devn != redundancy_strip2)
+                 break;
+               c++;
+             }
+           csize = bsize << desc->ashift;
            if (csize > len)
              csize = len;
+
            grub_dprintf ("zfs", "RAIDZ mapping 0x%" PRIxGRUB_UINT64_T
-                         "+%d+%u -> (0x%" PRIxGRUB_UINT64_T ", 0x%"
+                         "+%u (%d, %d) -> (0x%" PRIxGRUB_UINT64_T ", 0x%"
                          PRIxGRUB_UINT64_T ")\n",
-                         sector,(asize > 2), c, high, devn);
-           err = read_device (high << 9, &desc->children[devn],
+                         offset >> desc->ashift, c, asize, bsize, high,
+                         devn);
+           err = read_device ((high << desc->ashift)
+                              | (offset & ((1 << desc->ashift) - 1)),
+                              &desc->children[devn],
                               bsize, csize, buf);
            if (err)
              return err;
            c++;
+           s--;
            buf = (char *) buf + csize;
            len -= csize;
          }
-       return GRUB_ERR_NONE;       
       }
-      return GRUB_ERR_NONE;
+      return GRUB_ERR_NONE;        
     }
   return grub_error (GRUB_ERR_BAD_FS, "unsupported device type");
 }