aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--sys/boot/efi/boot1/zfs_module.c9
-rw-r--r--sys/boot/efi/loader/main.c11
-rw-r--r--sys/boot/i386/common/drv.h2
-rw-r--r--sys/boot/i386/loader/main.c11
-rw-r--r--sys/boot/i386/zfsboot/zfsboot.c23
-rw-r--r--sys/boot/zfs/libzfs.h1
-rw-r--r--sys/boot/zfs/zfsimpl.c224
7 files changed, 185 insertions, 96 deletions
diff --git a/sys/boot/efi/boot1/zfs_module.c b/sys/boot/efi/boot1/zfs_module.c
index 78d635cde761..363ae342a82f 100644
--- a/sys/boot/efi/boot1/zfs_module.c
+++ b/sys/boot/efi/boot1/zfs_module.c
@@ -40,6 +40,15 @@
static dev_info_t *devices;
+uint64_t
+ldi_get_size(void *priv)
+{
+ dev_info_t *devinfo = priv;
+
+ return (devinfo->dev->Media->BlockSize *
+ (devinfo->dev->Media->LastBlock + 1));
+}
+
static int
vdev_read(vdev_t *vdev, void *priv, off_t off, void *buf, size_t bytes)
{
diff --git a/sys/boot/efi/loader/main.c b/sys/boot/efi/loader/main.c
index 81f4d1b1e80d..f40c71253b1a 100644
--- a/sys/boot/efi/loader/main.c
+++ b/sys/boot/efi/loader/main.c
@@ -28,6 +28,7 @@
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
+#include <sys/disk.h>
#include <sys/param.h>
#include <sys/reboot.h>
#include <sys/boot.h>
@@ -838,4 +839,14 @@ efi_zfs_probe(void)
}
}
}
+
+uint64_t
+ldi_get_size(void *priv)
+{
+ int fd = (uintptr_t) priv;
+ uint64_t size;
+
+ ioctl(fd, DIOCGMEDIASIZE, &size);
+ return (size);
+}
#endif
diff --git a/sys/boot/i386/common/drv.h b/sys/boot/i386/common/drv.h
index e437cb83bf91..c0995df45ea9 100644
--- a/sys/boot/i386/common/drv.h
+++ b/sys/boot/i386/common/drv.h
@@ -36,7 +36,7 @@ struct dsk {
unsigned int slice;
int part;
daddr_t start;
- int init;
+ uint64_t size;
};
int drvread(struct dsk *dskp, void *buf, daddr_t lba, unsigned nblk);
diff --git a/sys/boot/i386/loader/main.c b/sys/boot/i386/loader/main.c
index 7fd4cef81adf..81bc2ff55dd0 100644
--- a/sys/boot/i386/loader/main.c
+++ b/sys/boot/i386/loader/main.c
@@ -38,6 +38,7 @@ __FBSDID("$FreeBSD$");
#include <machine/bootinfo.h>
#include <machine/cpufunc.h>
#include <machine/psl.h>
+#include <sys/disk.h>
#include <sys/reboot.h>
#include <common/drv.h>
@@ -463,4 +464,14 @@ i386_zfs_probe(void)
zfs_probe_dev(devname, NULL);
}
}
+
+uint64_t
+ldi_get_size(void *priv)
+{
+ int fd = (uintptr_t) priv;
+ uint64_t size;
+
+ ioctl(fd, DIOCGMEDIASIZE, &size);
+ return (size);
+}
#endif
diff --git a/sys/boot/i386/zfsboot/zfsboot.c b/sys/boot/i386/zfsboot/zfsboot.c
index fe73f97ef4eb..e76e58fc80e7 100644
--- a/sys/boot/i386/zfsboot/zfsboot.c
+++ b/sys/boot/i386/zfsboot/zfsboot.c
@@ -468,6 +468,23 @@ copy_dsk(struct dsk *dsk)
return (newdsk);
}
+/*
+ * The "layered" ioctl to read disk/partition size. Unfortunately
+ * the zfsboot case is hardest, because we do not have full software
+ * stack available, so we need to do some manual work here.
+ */
+uint64_t
+ldi_get_size(void *priv)
+{
+ struct dsk *dskp = priv;
+ uint64_t size = dskp->size;
+
+ if (dskp->start == 0)
+ size = drvsize(dskp);
+
+ return (size * DEV_BSIZE);
+}
+
static void
probe_drive(struct dsk *dsk)
{
@@ -549,6 +566,7 @@ probe_drive(struct dsk *dsk)
if (memcmp(&ent->ent_type, &freebsd_zfs_uuid,
sizeof(uuid_t)) == 0) {
dsk->start = ent->ent_lba_start;
+ dsk->size = ent->ent_lba_end - ent->ent_lba_start + 1;
dsk->slice = part + 1;
dsk->part = 255;
if (vdev_probe(vdev_read, dsk, NULL) == 0) {
@@ -593,6 +611,7 @@ trymbr:
if (!dp[i].dp_typ)
continue;
dsk->start = dp[i].dp_start;
+ dsk->size = dp[i].dp_size;
dsk->slice = i + 1;
if (vdev_probe(vdev_read, dsk, NULL) == 0) {
dsk = copy_dsk(dsk);
@@ -648,7 +667,7 @@ main(void)
dsk->slice = *(uint8_t *)PTOV(ARGS + 1) + 1;
dsk->part = 0;
dsk->start = 0;
- dsk->init = 0;
+ dsk->size = 0;
bootinfo.bi_version = BOOTINFO_VERSION;
bootinfo.bi_size = sizeof(bootinfo);
@@ -699,7 +718,7 @@ main(void)
dsk->slice = 0;
dsk->part = 0;
dsk->start = 0;
- dsk->init = 0;
+ dsk->size = 0;
probe_drive(dsk);
}
diff --git a/sys/boot/zfs/libzfs.h b/sys/boot/zfs/libzfs.h
index 1b304998867c..d1b4674679e5 100644
--- a/sys/boot/zfs/libzfs.h
+++ b/sys/boot/zfs/libzfs.h
@@ -81,6 +81,7 @@ int zfs_parsedev(struct zfs_devdesc *dev, const char *devspec,
char *zfs_fmtdev(void *vdev);
int zfs_probe_dev(const char *devname, uint64_t *pool_guid);
int zfs_list(const char *name);
+uint64_t ldi_get_size(void *);
void init_zfs_bootenv(char *currdev);
int zfs_bootenv(const char *name);
int zfs_belist_add(const char *name, uint64_t __unused);
diff --git a/sys/boot/zfs/zfsimpl.c b/sys/boot/zfs/zfsimpl.c
index a436fef8b539..0c2c1f86f3d8 100644
--- a/sys/boot/zfs/zfsimpl.c
+++ b/sys/boot/zfs/zfsimpl.c
@@ -68,7 +68,7 @@ static const char *features_for_read[] = {
*/
static spa_list_t zfs_pools;
-static const dnode_phys_t *dnode_cache_obj = NULL;
+static const dnode_phys_t *dnode_cache_obj;
static uint64_t dnode_cache_bn;
static char *dnode_cache_buf;
static char *zap_scratch;
@@ -523,12 +523,11 @@ vdev_init_from_nvlist(const unsigned char *nvlist, vdev_t *pvdev,
int nkids, i, is_new;
uint64_t is_offline, is_faulted, is_degraded, is_removed, isnt_present;
- if (nvlist_find(nvlist, ZPOOL_CONFIG_GUID,
- DATA_TYPE_UINT64, 0, &guid)
- || nvlist_find(nvlist, ZPOOL_CONFIG_ID,
- DATA_TYPE_UINT64, 0, &id)
- || nvlist_find(nvlist, ZPOOL_CONFIG_TYPE,
- DATA_TYPE_STRING, 0, &type)) {
+ if (nvlist_find(nvlist, ZPOOL_CONFIG_GUID, DATA_TYPE_UINT64,
+ NULL, &guid)
+ || nvlist_find(nvlist, ZPOOL_CONFIG_ID, DATA_TYPE_UINT64, NULL, &id)
+ || nvlist_find(nvlist, ZPOOL_CONFIG_TYPE, DATA_TYPE_STRING,
+ NULL, &type)) {
printf("ZFS: can't find vdev details\n");
return (ENOENT);
}
@@ -546,15 +545,15 @@ vdev_init_from_nvlist(const unsigned char *nvlist, vdev_t *pvdev,
is_offline = is_removed = is_faulted = is_degraded = isnt_present = 0;
- nvlist_find(nvlist, ZPOOL_CONFIG_OFFLINE, DATA_TYPE_UINT64, 0,
+ nvlist_find(nvlist, ZPOOL_CONFIG_OFFLINE, DATA_TYPE_UINT64, NULL,
&is_offline);
- nvlist_find(nvlist, ZPOOL_CONFIG_REMOVED, DATA_TYPE_UINT64, 0,
+ nvlist_find(nvlist, ZPOOL_CONFIG_REMOVED, DATA_TYPE_UINT64, NULL,
&is_removed);
- nvlist_find(nvlist, ZPOOL_CONFIG_FAULTED, DATA_TYPE_UINT64, 0,
+ nvlist_find(nvlist, ZPOOL_CONFIG_FAULTED, DATA_TYPE_UINT64, NULL,
&is_faulted);
- nvlist_find(nvlist, ZPOOL_CONFIG_DEGRADED, DATA_TYPE_UINT64, 0,
+ nvlist_find(nvlist, ZPOOL_CONFIG_DEGRADED, DATA_TYPE_UINT64, NULL,
&is_degraded);
- nvlist_find(nvlist, ZPOOL_CONFIG_NOT_PRESENT, DATA_TYPE_UINT64, 0,
+ nvlist_find(nvlist, ZPOOL_CONFIG_NOT_PRESENT, DATA_TYPE_UINT64, NULL,
&isnt_present);
vdev = vdev_find(guid);
@@ -573,17 +572,19 @@ vdev_init_from_nvlist(const unsigned char *nvlist, vdev_t *pvdev,
vdev->v_id = id;
vdev->v_top = pvdev != NULL ? pvdev : vdev;
if (nvlist_find(nvlist, ZPOOL_CONFIG_ASHIFT,
- DATA_TYPE_UINT64, 0, &ashift) == 0)
+ DATA_TYPE_UINT64, NULL, &ashift) == 0) {
vdev->v_ashift = ashift;
- else
+ } else {
vdev->v_ashift = 0;
+ }
if (nvlist_find(nvlist, ZPOOL_CONFIG_NPARITY,
- DATA_TYPE_UINT64, 0, &nparity) == 0)
+ DATA_TYPE_UINT64, NULL, &nparity) == 0) {
vdev->v_nparity = nparity;
- else
+ } else {
vdev->v_nparity = 0;
+ }
if (nvlist_find(nvlist, ZPOOL_CONFIG_PATH,
- DATA_TYPE_STRING, 0, &path) == 0) {
+ DATA_TYPE_STRING, NULL, &path) == 0) {
if (strncmp(path, "/dev/", 5) == 0)
path += 5;
vdev->v_name = strdup(path);
@@ -625,8 +626,8 @@ vdev_init_from_nvlist(const unsigned char *nvlist, vdev_t *pvdev,
vdev->v_state = VDEV_STATE_CANT_OPEN;
}
- rc = nvlist_find(nvlist, ZPOOL_CONFIG_CHILDREN,
- DATA_TYPE_NVLIST_ARRAY, &nkids, &kids);
+ rc = nvlist_find(nvlist, ZPOOL_CONFIG_CHILDREN, DATA_TYPE_NVLIST_ARRAY,
+ &nkids, &kids);
/*
* Its ok if we don't have any kids.
*/
@@ -744,12 +745,17 @@ spa_get_primary_vdev(const spa_t *spa)
#endif
static spa_t *
-spa_create(uint64_t guid)
+spa_create(uint64_t guid, const char *name)
{
spa_t *spa;
- spa = malloc(sizeof(spa_t));
+ if ((spa = malloc(sizeof(spa_t))) == NULL)
+ return (NULL);
memset(spa, 0, sizeof(spa_t));
+ if ((spa->spa_name = strdup(name)) == NULL) {
+ free(spa);
+ return (NULL);
+ }
STAILQ_INIT(&spa->spa_vdevs);
spa->spa_guid = guid;
STAILQ_INSERT_TAIL(&zfs_pools, spa, spa_link);
@@ -905,24 +911,39 @@ spa_all_status(void)
return (ret);
}
+uint64_t
+vdev_label_offset(uint64_t psize, int l, uint64_t offset)
+{
+ uint64_t label_offset;
+
+ if (l < VDEV_LABELS / 2)
+ label_offset = 0;
+ else
+ label_offset = psize - VDEV_LABELS * sizeof (vdev_label_t);
+
+ return (offset + l * sizeof (vdev_label_t) + label_offset);
+}
+
static int
vdev_probe(vdev_phys_read_t *_read, void *read_priv, spa_t **spap)
{
vdev_t vtmp;
vdev_phys_t *vdev_label = (vdev_phys_t *) zap_scratch;
+ vdev_phys_t *tmp_label = zfs_alloc(sizeof(vdev_phys_t));
spa_t *spa;
vdev_t *vdev, *top_vdev, *pool_vdev;
off_t off;
blkptr_t bp;
- const unsigned char *nvlist;
+ const unsigned char *nvlist = NULL;
uint64_t val;
uint64_t guid;
+ uint64_t best_txg = 0;
uint64_t pool_txg, pool_guid;
- uint64_t is_log;
+ uint64_t psize;
const char *pool_name;
const unsigned char *vdevs;
const unsigned char *features;
- int i, rc, is_newer;
+ int i, l, rc, is_newer;
char *upbuf;
const struct uberblock *up;
@@ -933,26 +954,47 @@ vdev_probe(vdev_phys_read_t *_read, void *read_priv, spa_t **spap)
memset(&vtmp, 0, sizeof(vtmp));
vtmp.v_phys_read = _read;
vtmp.v_read_priv = read_priv;
- off = offsetof(vdev_label_t, vl_vdev_phys);
- BP_ZERO(&bp);
- BP_SET_LSIZE(&bp, sizeof(vdev_phys_t));
- BP_SET_PSIZE(&bp, sizeof(vdev_phys_t));
- BP_SET_CHECKSUM(&bp, ZIO_CHECKSUM_LABEL);
- BP_SET_COMPRESS(&bp, ZIO_COMPRESS_OFF);
- DVA_SET_OFFSET(BP_IDENTITY(&bp), off);
- ZIO_SET_CHECKSUM(&bp.blk_cksum, off, 0, 0, 0);
- if (vdev_read_phys(&vtmp, &bp, vdev_label, off, 0))
- return (EIO);
+ psize = P2ALIGN(ldi_get_size(read_priv),
+ (uint64_t)sizeof (vdev_label_t));
- if (vdev_label->vp_nvlist[0] != NV_ENCODE_XDR) {
- return (EIO);
+ for (l = 0; l < VDEV_LABELS; l++) {
+ off = vdev_label_offset(psize, l,
+ offsetof(vdev_label_t, vl_vdev_phys));
+
+ BP_ZERO(&bp);
+ BP_SET_LSIZE(&bp, sizeof(vdev_phys_t));
+ BP_SET_PSIZE(&bp, sizeof(vdev_phys_t));
+ BP_SET_CHECKSUM(&bp, ZIO_CHECKSUM_LABEL);
+ BP_SET_COMPRESS(&bp, ZIO_COMPRESS_OFF);
+ DVA_SET_OFFSET(BP_IDENTITY(&bp), off);
+ ZIO_SET_CHECKSUM(&bp.blk_cksum, off, 0, 0, 0);
+
+ if (vdev_read_phys(&vtmp, &bp, tmp_label, off, 0))
+ continue;
+
+ if (tmp_label->vp_nvlist[0] != NV_ENCODE_XDR)
+ continue;
+
+ nvlist = (const unsigned char *) tmp_label->vp_nvlist + 4;
+ if (nvlist_find(nvlist, ZPOOL_CONFIG_POOL_TXG,
+ DATA_TYPE_UINT64, NULL, &pool_txg) != 0)
+ continue;
+
+ if (best_txg <= pool_txg) {
+ best_txg = pool_txg;
+ memcpy(vdev_label, tmp_label, sizeof (vdev_phys_t));
+ }
}
+ zfs_free(tmp_label, sizeof (vdev_phys_t));
+
+ if (vdev_label->vp_nvlist[0] != NV_ENCODE_XDR)
+ return (EIO);
+
nvlist = (const unsigned char *) vdev_label->vp_nvlist + 4;
- if (nvlist_find(nvlist,
- ZPOOL_CONFIG_VERSION,
- DATA_TYPE_UINT64, 0, &val)) {
+ if (nvlist_find(nvlist, ZPOOL_CONFIG_VERSION, DATA_TYPE_UINT64,
+ NULL, &val) != 0) {
return (EIO);
}
@@ -963,15 +1005,14 @@ vdev_probe(vdev_phys_read_t *_read, void *read_priv, spa_t **spap)
}
/* Check ZFS features for read */
- if (nvlist_find(nvlist,
- ZPOOL_CONFIG_FEATURES_FOR_READ,
- DATA_TYPE_NVLIST, 0, &features) == 0
- && nvlist_check_features_for_read(features) != 0)
+ if (nvlist_find(nvlist, ZPOOL_CONFIG_FEATURES_FOR_READ,
+ DATA_TYPE_NVLIST, NULL, &features) == 0 &&
+ nvlist_check_features_for_read(features) != 0) {
return (EIO);
+ }
- if (nvlist_find(nvlist,
- ZPOOL_CONFIG_POOL_STATE,
- DATA_TYPE_UINT64, 0, &val)) {
+ if (nvlist_find(nvlist, ZPOOL_CONFIG_POOL_STATE, DATA_TYPE_UINT64,
+ NULL, &val) != 0) {
return (EIO);
}
@@ -980,15 +1021,12 @@ vdev_probe(vdev_phys_read_t *_read, void *read_priv, spa_t **spap)
return (EIO);
}
- if (nvlist_find(nvlist,
- ZPOOL_CONFIG_POOL_TXG,
- DATA_TYPE_UINT64, 0, &pool_txg)
- || nvlist_find(nvlist,
- ZPOOL_CONFIG_POOL_GUID,
- DATA_TYPE_UINT64, 0, &pool_guid)
- || nvlist_find(nvlist,
- ZPOOL_CONFIG_POOL_NAME,
- DATA_TYPE_STRING, 0, &pool_name)) {
+ if (nvlist_find(nvlist, ZPOOL_CONFIG_POOL_TXG, DATA_TYPE_UINT64,
+ NULL, &pool_txg) != 0 ||
+ nvlist_find(nvlist, ZPOOL_CONFIG_POOL_GUID, DATA_TYPE_UINT64,
+ NULL, &pool_guid) != 0 ||
+ nvlist_find(nvlist, ZPOOL_CONFIG_POOL_NAME, DATA_TYPE_STRING,
+ NULL, &pool_name) != 0) {
/*
* Cache and spare devices end up here - just ignore
* them.
@@ -997,25 +1035,26 @@ vdev_probe(vdev_phys_read_t *_read, void *read_priv, spa_t **spap)
return (EIO);
}
- is_log = 0;
- (void) nvlist_find(nvlist, ZPOOL_CONFIG_IS_LOG, DATA_TYPE_UINT64, 0,
- &is_log);
- if (is_log)
+ if (nvlist_find(nvlist, ZPOOL_CONFIG_IS_LOG, DATA_TYPE_UINT64,
+ NULL, &val) == 0 && val != 0) {
return (EIO);
+ }
/*
* Create the pool if this is the first time we've seen it.
*/
spa = spa_find_by_guid(pool_guid);
- if (!spa) {
- spa = spa_create(pool_guid);
- spa->spa_name = strdup(pool_name);
+ if (spa == NULL) {
+ spa = spa_create(pool_guid, pool_name);
+ if (spa == NULL)
+ return (ENOMEM);
}
if (pool_txg > spa->spa_txg) {
spa->spa_txg = pool_txg;
is_newer = 1;
- } else
+ } else {
is_newer = 0;
+ }
/*
* Get the vdev tree and create our in-core copy of it.
@@ -1023,23 +1062,21 @@ vdev_probe(vdev_phys_read_t *_read, void *read_priv, spa_t **spap)
* be some kind of alias (overlapping slices, dangerously dedicated
* disks etc).
*/
- if (nvlist_find(nvlist,
- ZPOOL_CONFIG_GUID,
- DATA_TYPE_UINT64, 0, &guid)) {
+ if (nvlist_find(nvlist, ZPOOL_CONFIG_GUID, DATA_TYPE_UINT64,
+ NULL, &guid) != 0) {
return (EIO);
}
vdev = vdev_find(guid);
if (vdev && vdev->v_phys_read) /* Has this vdev already been inited? */
return (EIO);
- if (nvlist_find(nvlist,
- ZPOOL_CONFIG_VDEV_TREE,
- DATA_TYPE_NVLIST, 0, &vdevs)) {
+ if (nvlist_find(nvlist, ZPOOL_CONFIG_VDEV_TREE, DATA_TYPE_NVLIST,
+ NULL, &vdevs)) {
return (EIO);
}
rc = vdev_init_from_nvlist(vdevs, NULL, &top_vdev, is_newer);
- if (rc)
+ if (rc != 0)
return (rc);
/*
@@ -1079,36 +1116,37 @@ vdev_probe(vdev_phys_read_t *_read, void *read_priv, spa_t **spap)
*/
upbuf = zfs_alloc(VDEV_UBERBLOCK_SIZE(vdev));
up = (const struct uberblock *)upbuf;
- for (i = 0;
- i < VDEV_UBERBLOCK_COUNT(vdev);
- i++) {
- off = VDEV_UBERBLOCK_OFFSET(vdev, i);
- BP_ZERO(&bp);
- DVA_SET_OFFSET(&bp.blk_dva[0], off);
- BP_SET_LSIZE(&bp, VDEV_UBERBLOCK_SIZE(vdev));
- BP_SET_PSIZE(&bp, VDEV_UBERBLOCK_SIZE(vdev));
- BP_SET_CHECKSUM(&bp, ZIO_CHECKSUM_LABEL);
- BP_SET_COMPRESS(&bp, ZIO_COMPRESS_OFF);
- ZIO_SET_CHECKSUM(&bp.blk_cksum, off, 0, 0, 0);
-
- if (vdev_read_phys(vdev, &bp, upbuf, off, 0))
- continue;
+ for (l = 0; l < VDEV_LABELS; l++) {
+ for (i = 0; i < VDEV_UBERBLOCK_COUNT(vdev); i++) {
+ off = vdev_label_offset(psize, l,
+ VDEV_UBERBLOCK_OFFSET(vdev, i));
+ BP_ZERO(&bp);
+ DVA_SET_OFFSET(&bp.blk_dva[0], off);
+ BP_SET_LSIZE(&bp, VDEV_UBERBLOCK_SIZE(vdev));
+ BP_SET_PSIZE(&bp, VDEV_UBERBLOCK_SIZE(vdev));
+ BP_SET_CHECKSUM(&bp, ZIO_CHECKSUM_LABEL);
+ BP_SET_COMPRESS(&bp, ZIO_COMPRESS_OFF);
+ ZIO_SET_CHECKSUM(&bp.blk_cksum, off, 0, 0, 0);
+
+ if (vdev_read_phys(vdev, &bp, upbuf, off, 0))
+ continue;
- if (up->ub_magic != UBERBLOCK_MAGIC)
- continue;
- if (up->ub_txg < spa->spa_txg)
- continue;
- if (up->ub_txg > spa->spa_uberblock.ub_txg) {
- spa->spa_uberblock = *up;
- } else if (up->ub_txg == spa->spa_uberblock.ub_txg) {
- if (up->ub_timestamp > spa->spa_uberblock.ub_timestamp)
+ if (up->ub_magic != UBERBLOCK_MAGIC)
+ continue;
+ if (up->ub_txg < spa->spa_txg)
+ continue;
+ if (up->ub_txg > spa->spa_uberblock.ub_txg ||
+ (up->ub_txg == spa->spa_uberblock.ub_txg &&
+ up->ub_timestamp >
+ spa->spa_uberblock.ub_timestamp)) {
spa->spa_uberblock = *up;
+ }
}
}
zfs_free(upbuf, VDEV_UBERBLOCK_SIZE(vdev));
vdev->spa = spa;
- if (spap)
+ if (spap != NULL)
*spap = spa;
return (0);
}