From 8646f60d1b421b48b43405fa286548a0bf3f5da4 Mon Sep 17 00:00:00 2001 From: Michal Humpula Date: Sat, 28 Feb 2015 21:19:42 +0100 Subject: [PATCH 109/116] zfs: make less syscalls Upstream: https://github.com/karelzak/util-linux/commit/e44a4c7ac9522c03b76d8b62ce88b443771fdb0b Addresses: https://bugzilla.redhat.com/show_bug.cgi?id=1392661 Signed-off-by: Karel Zak --- libblkid/src/superblocks/zfs.c | 95 ++++++++++++++++++++++++++---------------- 1 file changed, 58 insertions(+), 37 deletions(-) diff --git a/libblkid/src/superblocks/zfs.c b/libblkid/src/superblocks/zfs.c index 4a64a03..9b5601e 100644 --- a/libblkid/src/superblocks/zfs.c +++ b/libblkid/src/superblocks/zfs.c @@ -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 */ #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 -- 2.9.3