]> git.ipfire.org Git - thirdparty/util-linux.git/commitdiff
zfs: make less syscalls
authorMichal Humpula <michal.humpula@hudrydum.cz>
Sat, 28 Feb 2015 20:19:42 +0000 (21:19 +0100)
committerMichal Humpula <michal.humpula@hudrydum.cz>
Sat, 28 Feb 2015 20:19:42 +0000 (21:19 +0100)
libblkid/src/superblocks/zfs.c

index 4a64a03de53518b01253f18104bbdd088fd64bba..9b5601e0abb808dc02db813cdf8f738ca38238cc 100644 (file)
@@ -20,6 +20,7 @@
 #define VDEV_LABEL_NVPAIR      ( 16 * 1024ULL)
 #define VDEV_LABEL_SIZE                (256 * 1024ULL)
 #define UBERBLOCK_SIZE         1024ULL
+#define UBERBLOCKS_COUNT   128
 
 /* #include <sys/uberblock_impl.h> */
 #define UBERBLOCK_MAGIC         0x00bab10c              /* oo-ba-bloc!  */
@@ -32,7 +33,6 @@ struct zfs_uberblock {
        char            ub_rootbp;      /* MOS objset_phys_t            */
 } __attribute__((packed));
 
-#define ZFS_TRIES      512
 #define ZFS_WANT        4
 
 #define DATA_TYPE_UINT64 8
@@ -163,61 +163,82 @@ static void zfs_extract_guid_name(blkid_probe pr, loff_t offset)
 #define zdebug(fmt, ...)       do {} while(0)
 /*#define zdebug(fmt, a...)    fprintf(stderr, fmt, ##a)*/
 
-/* ZFS has 128x1kB host-endian root blocks, stored in 2 areas (labels)
- * at the start of the disk, and 2 areas at the end of the disk.
- */
+static int find_uberblocks(const void *label, loff_t *ub_offset, int *swap_endian)
+{
+       uint64_t swab_magic = swab64(UBERBLOCK_MAGIC);
+       struct zfs_uberblock *ub;
+       int i, found = 0;
+       loff_t offset = VDEV_LABEL_UBERBLOCK;
+
+       for (i = 0; i < UBERBLOCKS_COUNT; i++, offset += UBERBLOCK_SIZE) {
+               ub = (struct zfs_uberblock *)(label + offset);
+
+               if (ub->ub_magic == UBERBLOCK_MAGIC) {
+                       *ub_offset = offset;
+                       *swap_endian = 0;
+                       found++;
+                       zdebug("probe_zfs: found little-endian uberblock at %llu\n", offset >> 10);
+               }
+
+               if (ub->ub_magic == swab_magic) {
+                       *ub_offset = offset;
+                       *swap_endian = 1;
+                       found++;
+                       zdebug("probe_zfs: found big-endian uberblock at %llu\n", offset >> 10);
+               }
+  }
+
+  return found;
+}
+
+/* ZFS has 128x1kB host-endian root blocks, stored in 2 areas at the start
+ * of the disk, and 2 areas at the end of the disk.  Check only some of them...
+ * #4 (@ 132kB) is the first one written on a new filesystem. */
 static int probe_zfs(blkid_probe pr, const struct blkid_idmag *mag)
 {
        uint64_t swab_magic = swab64(UBERBLOCK_MAGIC);
+       int swab_endian = 0;
        struct zfs_uberblock *ub;
-       int swab_endian;
        loff_t offset, ub_offset = 0;
-       int tried;
-       int found;
+       int label_no, found = 0, found_in_label;
+       void *label;
        loff_t blk_align = (pr->size % (256 * 1024ULL));
 
        zdebug("probe_zfs\n");
-       /* Look for at least 4 uberblocks to ensure a positive match.
-          Begin with Label 0 (L0) at the start of the block device. */
-       for (tried = found = 0, offset = VDEV_LABEL_UBERBLOCK;
-            found < ZFS_WANT && tried < ZFS_TRIES;
-            tried++, offset += UBERBLOCK_SIZE)
-       {
-               /* Leave L0 to try other labels */
-               switch(tried) {
-               case 128: // jump to L1, just after L0
-                       offset = VDEV_LABEL_SIZE + VDEV_LABEL_UBERBLOCK;
+       /* Look for at least 4 uberblocks to ensure a positive match */
+       for (label_no = 0; label_no < 4; label_no++) {
+               switch(label_no) {
+               case 0: // jump to L0
+                       offset = 0;
                        break;
-               case 256: // jump to L2 near the far end of the block device
-                       offset = pr->size - 2 * VDEV_LABEL_SIZE + VDEV_LABEL_UBERBLOCK - blk_align;
-                       zdebug("probe_zfs: l2 offset %llu\n", offset >> 10);
+               case 1: // jump to L1
+                       offset = VDEV_LABEL_SIZE;
                        break;
-               case 384: // jump to L3 at the furthest end of the block device
-                       offset = pr->size - VDEV_LABEL_SIZE + VDEV_LABEL_UBERBLOCK - blk_align;
-                       zdebug("probe_zfs: l3 offset %llu\n", offset >> 10);
+               case 2: // jump to L2
+                       offset = pr->size - 2 * VDEV_LABEL_SIZE - blk_align;
+                       break;
+               case 3: // jump to L3
+                       offset = pr->size - VDEV_LABEL_SIZE - blk_align;
                        break;
                }
 
-               ub = (struct zfs_uberblock *)
-                       blkid_probe_get_buffer(pr, offset,
-                                              sizeof(struct zfs_uberblock));
-               if (ub == NULL)
+               label = blkid_probe_get_buffer(pr, offset, VDEV_LABEL_SIZE);
+               if (label == NULL)
                        return errno ? -errno : 1;
 
-               if (ub->ub_magic == UBERBLOCK_MAGIC) {
-                       ub_offset = offset;
-                       found++;
-                       zdebug("probe_zfs: found little-endian uberblock at %llu\n", offset >> 10);
-               }
+               found_in_label = find_uberblocks(label, &ub_offset, &swab_endian);
 
-               if ((swab_endian = (ub->ub_magic == swab_magic))) {
-                       ub_offset = offset;
-                       found++;
-                       zdebug("probe_zfs: found big-endian uberblock at %llu\n", offset >> 10);
+               if (found_in_label > 0) {
+                       found+= found_in_label;
+                       ub = (struct zfs_uberblock *)(label + ub_offset);
+                       ub_offset += offset;
+
+                       if (found >= ZFS_WANT)
+                               break;
                }
        }
 
-       if (found < 4)
+       if (found < ZFS_WANT)
                return 1;
 
        /* If we found the 4th uberblock, then we will have exited from the