Mercurial > illumos > illumos-gate
changeset 9790:e276ee006ff6
6747441 GRUB/vdev_get_bootpath, spa_get_rootconf, zpool_get_physpath should take care of spare vdev
6844158 assertion failed: vd->vdev_ops == &vdev_mirror_ops, file: ../../common/fs/zfs/zvol.c, line: 1054
6846024 allow grub to findroot /boot/grub/menu.lst if opening menu.lst from the boot device fails
author | Lin Ling <Lin.Ling@Sun.COM> |
---|---|
date | Thu, 04 Jun 2009 17:15:02 -0700 |
parents | 33dc583075ed |
children | 61b4ddda82ec |
files | usr/src/grub/grub-0.97/stage2/builtins.c usr/src/grub/grub-0.97/stage2/fsys_zfs.c usr/src/lib/libzfs/common/libzfs_pool.c usr/src/uts/common/fs/zfs/spa.c usr/src/uts/common/fs/zfs/sys/spa.h usr/src/uts/common/fs/zfs/sys/spa_boot.h usr/src/uts/common/fs/zfs/sys/vdev_impl.h usr/src/uts/common/fs/zfs/vdev.c usr/src/uts/common/fs/zfs/zvol.c |
diffstat | 9 files changed, 222 insertions(+), 211 deletions(-) [+] |
line wrap: on
line diff
--- a/usr/src/grub/grub-0.97/stage2/builtins.c Thu Jun 04 15:12:01 2009 -0700 +++ b/usr/src/grub/grub-0.97/stage2/builtins.c Thu Jun 04 17:15:02 2009 -0700 @@ -1619,7 +1619,7 @@ grub_sprintf(bootsign, "%s/%s", BOOTSIGN_DIR, arg); filename = bootsign; goto harddisk; - } else if (for_root) { + } else if (for_root && !grub_strchr(arg, '/')) { /* Boot signature without partition/slice information */ grub_sprintf(bootsign, "%s/%s", BOOTSIGN_DIR, arg); filename = bootsign; @@ -4163,11 +4163,6 @@ return 1; } - if (grub_strchr(arg, '/')) { - errnum = ERR_BAD_ARGUMENT; - return 1; - } - find_best_root = 1; best_drive = 0; best_part = 0;
--- a/usr/src/grub/grub-0.97/stage2/fsys_zfs.c Thu Jun 04 15:12:01 2009 -0700 +++ b/usr/src/grub/grub-0.97/stage2/fsys_zfs.c Thu Jun 04 17:15:02 2009 -0700 @@ -1140,11 +1140,12 @@ } /* - * Get a list of valid vdev pathname from the boot device. + * Get a valid vdev pathname/devid from the boot device. * The caller should already allocate MAXPATHLEN memory for bootpath and devid. */ -int -vdev_get_bootpath(char *nv, uint64_t inguid, char *devid, char *bootpath) +static int +vdev_get_bootpath(char *nv, uint64_t inguid, char *devid, char *bootpath, + int is_spare) { char type[16]; @@ -1165,6 +1166,15 @@ if (guid != inguid) return (ERR_NO_BOOTPATH); + /* for a spare vdev, pick the disk labeled with "is_spare" */ + if (is_spare) { + uint64_t spare = 0; + (void) nvlist_lookup_value(nv, ZPOOL_CONFIG_IS_SPARE, + &spare, DATA_TYPE_UINT64, NULL); + if (!spare) + return (ERR_NO_BOOTPATH); + } + if (nvlist_lookup_value(nv, ZPOOL_CONFIG_PHYS_PATH, bootpath, DATA_TYPE_STRING, NULL) != 0) bootpath[0] = '\0'; @@ -1179,7 +1189,9 @@ return (0); - } else if (strcmp(type, VDEV_TYPE_MIRROR) == 0) { + } else if (strcmp(type, VDEV_TYPE_MIRROR) == 0 || + strcmp(type, VDEV_TYPE_REPLACING) == 0 || + (is_spare = (strcmp(type, VDEV_TYPE_SPARE) == 0))) { int nelm, i; char *child; @@ -1192,7 +1204,7 @@ child_i = nvlist_array(child, i); if (vdev_get_bootpath(child_i, inguid, devid, - bootpath) == 0) + bootpath, is_spare) == 0) return (0); } } @@ -1259,7 +1271,7 @@ if (nvlist_lookup_value(nvlist, ZPOOL_CONFIG_GUID, &diskguid, DATA_TYPE_UINT64, NULL)) return (ERR_FSYS_CORRUPT); - if (vdev_get_bootpath(nv, diskguid, outdevid, outpath)) + if (vdev_get_bootpath(nv, diskguid, outdevid, outpath, 0)) return (ERR_NO_BOOTPATH); return (0); }
--- a/usr/src/lib/libzfs/common/libzfs_pool.c Thu Jun 04 15:12:01 2009 -0700 +++ b/usr/src/lib/libzfs/common/libzfs_pool.c Thu Jun 04 17:15:02 2009 -0700 @@ -1504,10 +1504,10 @@ } /* - * Helper function for zpool_get_config_physpath(). + * Helper function for zpool_get_physpaths(). */ static int -vdev_get_physpath(nvlist_t *config, char *physpath, size_t physpath_size, +vdev_get_one_physpath(nvlist_t *config, char *physpath, size_t physpath_size, size_t *bytes_written) { size_t bytes_left, pos, rsz; @@ -1535,6 +1535,57 @@ return (0); } +static int +vdev_get_physpaths(nvlist_t *nv, char *physpath, size_t phypath_size, + size_t *rsz, boolean_t is_spare) +{ + char *type; + int ret; + + if (nvlist_lookup_string(nv, ZPOOL_CONFIG_TYPE, &type) != 0) + return (EZFS_INVALCONFIG); + + if (strcmp(type, VDEV_TYPE_DISK) == 0) { + /* + * An active spare device has ZPOOL_CONFIG_IS_SPARE set. + * For a spare vdev, we only want to boot from the active + * spare device. + */ + if (is_spare) { + uint64_t spare = 0; + (void) nvlist_lookup_uint64(nv, ZPOOL_CONFIG_IS_SPARE, + &spare); + if (!spare) + return (EZFS_INVALCONFIG); + } + + if (vdev_online(nv)) { + if ((ret = vdev_get_one_physpath(nv, physpath, + phypath_size, rsz)) != 0) + return (ret); + } + } else if (strcmp(type, VDEV_TYPE_MIRROR) == 0 || + strcmp(type, VDEV_TYPE_REPLACING) == 0 || + (is_spare = (strcmp(type, VDEV_TYPE_SPARE) == 0))) { + nvlist_t **child; + uint_t count; + int i, ret; + + if (nvlist_lookup_nvlist_array(nv, + ZPOOL_CONFIG_CHILDREN, &child, &count) != 0) + return (EZFS_INVALCONFIG); + + for (i = 0; i < count; i++) { + ret = vdev_get_physpaths(child[i], physpath, + phypath_size, rsz, is_spare); + if (ret == EZFS_NOSPC) + return (ret); + } + } + + return (EZFS_POOL_INVALARG); +} + /* * Get phys_path for a root pool config. * Return 0 on success; non-zero on failure. @@ -1545,10 +1596,8 @@ size_t rsz; nvlist_t *vdev_root; nvlist_t **child; - nvlist_t **child2; uint_t count; char *type; - int j, ret; rsz = 0; @@ -1569,40 +1618,8 @@ pool_uses_efi(vdev_root)) return (EZFS_POOL_INVALARG); - if (nvlist_lookup_string(child[0], ZPOOL_CONFIG_TYPE, &type) != 0) - return (EZFS_INVALCONFIG); - - if (strcmp(type, VDEV_TYPE_DISK) == 0) { - if (vdev_online(child[0])) { - if ((ret = vdev_get_physpath(child[0], physpath, - phypath_size, &rsz)) != 0) - return (ret); - } - } else if (strcmp(type, VDEV_TYPE_MIRROR) == 0) { - - if (nvlist_lookup_nvlist_array(child[0], - ZPOOL_CONFIG_CHILDREN, &child2, &count) != 0) - return (EZFS_INVALCONFIG); - - for (j = 0; j < count; j++) { - if (nvlist_lookup_string(child2[j], ZPOOL_CONFIG_TYPE, - &type) != 0) - return (EZFS_INVALCONFIG); - - if (strcmp(type, VDEV_TYPE_DISK) != 0) - return (EZFS_POOL_INVALARG); - - if (vdev_online(child2[j])) { - ret = vdev_get_physpath(child2[j], - physpath, phypath_size, &rsz); - - if (ret == EZFS_NOSPC) - return (ret); - } - } - } else { - return (EZFS_POOL_INVALARG); - } + (void) vdev_get_physpaths(child[0], physpath, phypath_size, &rsz, + B_FALSE); /* No online devices */ if (rsz == 0) @@ -1938,6 +1955,14 @@ (void) fprintf(stderr, dgettext(TEXT_DOMAIN, "Please " "be sure to invoke %s to make '%s' bootable.\n"), BOOTCMD, new_disk); + + /* + * XXX need a better way to prevent user from + * booting up a half-baked vdev. + */ + (void) fprintf(stderr, dgettext(TEXT_DOMAIN, "Make " + "sure to wait until resilver is done " + "before rebooting.\n")); } return (0); }
--- a/usr/src/uts/common/fs/zfs/spa.c Thu Jun 04 15:12:01 2009 -0700 +++ b/usr/src/uts/common/fs/zfs/spa.c Thu Jun 04 17:15:02 2009 -0700 @@ -2279,29 +2279,36 @@ #ifdef _KERNEL /* - * Build a "root" vdev for a top level vdev read in from a rootpool - * device label. + * Get the root pool information from the root disk, then import the root pool + * during the system boot up time. */ -static void -spa_build_rootpool_config(nvlist_t *config) +extern int vdev_disk_read_rootlabel(char *, char *, nvlist_t **); + +static nvlist_t * +spa_generate_rootconf(char *devpath, char *devid, uint64_t *guid) { + nvlist_t *config; nvlist_t *nvtop, *nvroot; uint64_t pgid; + if (vdev_disk_read_rootlabel(devpath, devid, &config) != 0) + return (NULL); + /* * Add this top-level vdev to the child array. */ - VERIFY(nvlist_lookup_nvlist(config, ZPOOL_CONFIG_VDEV_TREE, &nvtop) - == 0); - VERIFY(nvlist_lookup_uint64(config, ZPOOL_CONFIG_POOL_GUID, &pgid) - == 0); + VERIFY(nvlist_lookup_nvlist(config, ZPOOL_CONFIG_VDEV_TREE, + &nvtop) == 0); + VERIFY(nvlist_lookup_uint64(config, ZPOOL_CONFIG_POOL_GUID, + &pgid) == 0); + VERIFY(nvlist_lookup_uint64(config, ZPOOL_CONFIG_GUID, guid) == 0); /* * Put this pool's top-level vdevs into a root vdev. */ VERIFY(nvlist_alloc(&nvroot, NV_UNIQUE_NAME, KM_SLEEP) == 0); - VERIFY(nvlist_add_string(nvroot, ZPOOL_CONFIG_TYPE, VDEV_TYPE_ROOT) - == 0); + VERIFY(nvlist_add_string(nvroot, ZPOOL_CONFIG_TYPE, + VDEV_TYPE_ROOT) == 0); VERIFY(nvlist_add_uint64(nvroot, ZPOOL_CONFIG_ID, 0ULL) == 0); VERIFY(nvlist_add_uint64(nvroot, ZPOOL_CONFIG_GUID, pgid) == 0); VERIFY(nvlist_add_nvlist_array(nvroot, ZPOOL_CONFIG_CHILDREN, @@ -2313,129 +2320,42 @@ */ VERIFY(nvlist_add_nvlist(config, ZPOOL_CONFIG_VDEV_TREE, nvroot) == 0); nvlist_free(nvroot); + return (config); } /* - * Get the root pool information from the root disk, then import the root pool - * during the system boot up time. + * Walk the vdev tree and see if we can find a device with "better" + * configuration. A configuration is "better" if the label on that + * device has a more recent txg. */ -extern int vdev_disk_read_rootlabel(char *, char *, nvlist_t **); - -int -spa_check_rootconf(char *devpath, char *devid, nvlist_t **bestconf, - uint64_t *besttxg) -{ - nvlist_t *config; - uint64_t txg; - int error; - - if (error = vdev_disk_read_rootlabel(devpath, devid, &config)) - return (error); - - VERIFY(nvlist_lookup_uint64(config, ZPOOL_CONFIG_POOL_TXG, &txg) == 0); - - if (bestconf != NULL) - *bestconf = config; - else - nvlist_free(config); - *besttxg = txg; - return (0); -} - -boolean_t -spa_rootdev_validate(nvlist_t *nv) -{ - uint64_t ival; - - if (nvlist_lookup_uint64(nv, ZPOOL_CONFIG_OFFLINE, &ival) == 0 || - nvlist_lookup_uint64(nv, ZPOOL_CONFIG_FAULTED, &ival) == 0 || - nvlist_lookup_uint64(nv, ZPOOL_CONFIG_REMOVED, &ival) == 0) - return (B_FALSE); - - return (B_TRUE); -} - - -/* - * Given the boot device's physical path or devid, check if the device - * is in a valid state. If so, return the configuration from the vdev - * label. - */ -int -spa_get_rootconf(char *devpath, char *devid, nvlist_t **bestconf) +static void +spa_alt_rootvdev(vdev_t *vd, vdev_t **avd, uint64_t *txg) { - nvlist_t *conf = NULL; - uint64_t txg = 0; - nvlist_t *nvtop, **child; - char *type; - char *bootpath = NULL; - uint_t children, c; - char *tmp; - int error; - - if (devpath && ((tmp = strchr(devpath, ' ')) != NULL)) - *tmp = '\0'; - if (error = spa_check_rootconf(devpath, devid, &conf, &txg)) { - cmn_err(CE_NOTE, "error reading device label"); - return (error); - } - if (txg == 0) { - cmn_err(CE_NOTE, "this device is detached"); - nvlist_free(conf); - return (EINVAL); - } - - VERIFY(nvlist_lookup_nvlist(conf, ZPOOL_CONFIG_VDEV_TREE, - &nvtop) == 0); - VERIFY(nvlist_lookup_string(nvtop, ZPOOL_CONFIG_TYPE, &type) == 0); - - if (strcmp(type, VDEV_TYPE_DISK) == 0) { - if (spa_rootdev_validate(nvtop)) { - goto out; - } else { - nvlist_free(conf); - return (EINVAL); + int c; + + for (c = 0; c < vd->vdev_children; c++) + spa_alt_rootvdev(vd->vdev_child[c], avd, txg); + + if (vd->vdev_ops->vdev_op_leaf) { + nvlist_t *label; + uint64_t label_txg; + + if (vdev_disk_read_rootlabel(vd->vdev_physpath, vd->vdev_devid, + &label) != 0) + return; + + VERIFY(nvlist_lookup_uint64(label, ZPOOL_CONFIG_POOL_TXG, + &label_txg) == 0); + + /* + * Do we have a better boot device? + */ + if (label_txg > *txg) { + *txg = label_txg; + *avd = vd; } + nvlist_free(label); } - - ASSERT(strcmp(type, VDEV_TYPE_MIRROR) == 0); - - VERIFY(nvlist_lookup_nvlist_array(nvtop, ZPOOL_CONFIG_CHILDREN, - &child, &children) == 0); - - /* - * Go thru vdevs in the mirror to see if the given device - * has the most recent txg. Only the device with the most - * recent txg has valid information and should be booted. - */ - for (c = 0; c < children; c++) { - char *cdevid, *cpath; - uint64_t tmptxg; - - cpath = NULL; - cdevid = NULL; - (void) nvlist_lookup_string(child[c], ZPOOL_CONFIG_PHYS_PATH, - &cpath); - (void) nvlist_lookup_string(child[c], ZPOOL_CONFIG_DEVID, - &cdevid); - if (cpath == NULL && cdevid == NULL) - return (EINVAL); - if ((spa_check_rootconf(cpath, cdevid, NULL, - &tmptxg) == 0) && (tmptxg > txg)) { - txg = tmptxg; - VERIFY(nvlist_lookup_string(child[c], - ZPOOL_CONFIG_PATH, &bootpath) == 0); - } - } - - /* Does the best device match the one we've booted from? */ - if (bootpath) { - cmn_err(CE_NOTE, "try booting from '%s'", bootpath); - return (EINVAL); - } -out: - *bestconf = conf; - return (0); } /* @@ -2453,24 +2373,25 @@ int spa_import_rootpool(char *devpath, char *devid) { - nvlist_t *conf = NULL; + spa_t *spa; + vdev_t *rvd, *bvd, *avd = NULL; + nvlist_t *config, *nvtop; + uint64_t guid, txg; char *pname; int error; - spa_t *spa; /* - * Get the vdev pathname and configuation from the most - * recently updated vdev (highest txg). + * Read the label from the boot device and generate a configuration. */ - if (error = spa_get_rootconf(devpath, devid, &conf)) - goto msg_out; - - /* - * Add type "root" vdev to the config. - */ - spa_build_rootpool_config(conf); - - VERIFY(nvlist_lookup_string(conf, ZPOOL_CONFIG_POOL_NAME, &pname) == 0); + if ((config = spa_generate_rootconf(devpath, devid, &guid)) == NULL) { + cmn_err(CE_NOTE, "Can not read the pool label from '%s'", + devpath); + return (EIO); + } + + VERIFY(nvlist_lookup_string(config, ZPOOL_CONFIG_POOL_NAME, + &pname) == 0); + VERIFY(nvlist_lookup_uint64(config, ZPOOL_CONFIG_POOL_TXG, &txg) == 0); mutex_enter(&spa_namespace_lock); if ((spa = spa_lookup(pname)) != NULL) { @@ -2482,24 +2403,72 @@ } spa = spa_add(pname, NULL); - spa->spa_is_root = B_TRUE; - VERIFY(nvlist_dup(conf, &spa->spa_config, 0) == 0); + + /* + * Build up a vdev tree based on the boot device's label config. + */ + VERIFY(nvlist_lookup_nvlist(config, ZPOOL_CONFIG_VDEV_TREE, + &nvtop) == 0); + spa_config_enter(spa, SCL_ALL, FTAG, RW_WRITER); + error = spa_config_parse(spa, &rvd, nvtop, NULL, 0, + VDEV_ALLOC_ROOTPOOL); + spa_config_exit(spa, SCL_ALL, FTAG); + if (error) { + mutex_exit(&spa_namespace_lock); + nvlist_free(config); + cmn_err(CE_NOTE, "Can not parse the config for pool '%s'", + pname); + return (error); + } + + /* + * Get the boot vdev. + */ + if ((bvd = vdev_lookup_by_guid(rvd, guid)) == NULL) { + cmn_err(CE_NOTE, "Can not find the boot vdev for guid %llu", + (u_longlong_t)guid); + error = ENOENT; + goto out; + } + + /* + * Determine if there is a better boot device. + */ + avd = bvd; + spa_alt_rootvdev(rvd, &avd, &txg); + if (avd != bvd) { + cmn_err(CE_NOTE, "The boot device is 'degraded'. Please " + "try booting from '%s'", avd->vdev_path); + error = EINVAL; + goto out; + } + + /* + * If the boot device is part of a spare vdev then ensure that + * we're booting off the active spare. + */ + if (bvd->vdev_parent->vdev_ops == &vdev_spare_ops && + !bvd->vdev_isspare) { + cmn_err(CE_NOTE, "The boot device is currently spared. Please " + "try booting from '%s'", + bvd->vdev_parent->vdev_child[1]->vdev_path); + error = EINVAL; + goto out; + } + + VERIFY(nvlist_dup(config, &spa->spa_config, 0) == 0); + error = 0; +out: + spa_config_enter(spa, SCL_ALL, FTAG, RW_WRITER); + vdev_free(rvd); + spa_config_exit(spa, SCL_ALL, FTAG); mutex_exit(&spa_namespace_lock); - nvlist_free(conf); - return (0); - -msg_out: - cmn_err(CE_NOTE, "\n" - " *************************************************** \n" - " * This device is not bootable! * \n" - " * It is either offlined or detached or faulted. * \n" - " * Please try to boot from a different device. * \n" - " *************************************************** "); - + nvlist_free(config); return (error); } + #endif /*
--- a/usr/src/uts/common/fs/zfs/sys/spa.h Thu Jun 04 15:12:01 2009 -0700 +++ b/usr/src/uts/common/fs/zfs/sys/spa.h Thu Jun 04 17:15:02 2009 -0700 @@ -324,9 +324,6 @@ char *altroot, size_t buflen); extern int spa_create(const char *pool, nvlist_t *config, nvlist_t *props, const char *history_str, nvlist_t *zplprops); -extern int spa_check_rootconf(char *devpath, char *devid, - nvlist_t **bestconf, uint64_t *besttxg); -extern boolean_t spa_rootdev_validate(nvlist_t *nv); extern int spa_import_rootpool(char *devpath, char *devid); extern int spa_import(const char *pool, nvlist_t *config, nvlist_t *props); extern int spa_import_verbatim(const char *, nvlist_t *, nvlist_t *);
--- a/usr/src/uts/common/fs/zfs/sys/spa_boot.h Thu Jun 04 15:12:01 2009 -0700 +++ b/usr/src/uts/common/fs/zfs/sys/spa_boot.h Thu Jun 04 17:15:02 2009 -0700 @@ -19,15 +19,13 @@ * CDDL HEADER END */ /* - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #ifndef _SYS_SPA_BOOT_H #define _SYS_SPA_BOOT_H -#pragma ident "%Z%%M% %I% %E% SMI" - #include <sys/nvpair.h> #ifdef __cplusplus @@ -36,7 +34,6 @@ extern char *spa_get_bootprop(char *prop); extern void spa_free_bootprop(char *prop); -extern int spa_get_rootconf(char *devpath, char *devid, nvlist_t **bestconf_p); #ifdef __cplusplus }
--- a/usr/src/uts/common/fs/zfs/sys/vdev_impl.h Thu Jun 04 15:12:01 2009 -0700 +++ b/usr/src/uts/common/fs/zfs/sys/vdev_impl.h Thu Jun 04 17:15:02 2009 -0700 @@ -239,6 +239,7 @@ #define VDEV_ALLOC_ADD 1 #define VDEV_ALLOC_SPARE 2 #define VDEV_ALLOC_L2CACHE 3 +#define VDEV_ALLOC_ROOTPOOL 4 /* * Allocate or free a vdev
--- a/usr/src/uts/common/fs/zfs/vdev.c Thu Jun 04 15:12:01 2009 -0700 +++ b/usr/src/uts/common/fs/zfs/vdev.c Thu Jun 04 17:15:02 2009 -0700 @@ -373,6 +373,9 @@ } else if (alloctype == VDEV_ALLOC_L2CACHE) { if (nvlist_lookup_uint64(nv, ZPOOL_CONFIG_GUID, &guid) != 0) return (EINVAL); + } else if (alloctype == VDEV_ALLOC_ROOTPOOL) { + if (nvlist_lookup_uint64(nv, ZPOOL_CONFIG_GUID, &guid) != 0) + return (EINVAL); } /* @@ -475,13 +478,23 @@ * If we're a leaf vdev, try to load the DTL object and other state. */ if (vd->vdev_ops->vdev_op_leaf && - (alloctype == VDEV_ALLOC_LOAD || alloctype == VDEV_ALLOC_L2CACHE)) { + (alloctype == VDEV_ALLOC_LOAD || alloctype == VDEV_ALLOC_L2CACHE || + alloctype == VDEV_ALLOC_ROOTPOOL)) { if (alloctype == VDEV_ALLOC_LOAD) { (void) nvlist_lookup_uint64(nv, ZPOOL_CONFIG_DTL, &vd->vdev_dtl_smo.smo_object); (void) nvlist_lookup_uint64(nv, ZPOOL_CONFIG_UNSPARE, &vd->vdev_unspare); } + + if (alloctype == VDEV_ALLOC_ROOTPOOL) { + uint64_t spare = 0; + + if (nvlist_lookup_uint64(nv, ZPOOL_CONFIG_IS_SPARE, + &spare) == 0 && spare) + spa_spare_add(vd); + } + (void) nvlist_lookup_uint64(nv, ZPOOL_CONFIG_OFFLINE, &vd->vdev_offline);
--- a/usr/src/uts/common/fs/zfs/zvol.c Thu Jun 04 15:12:01 2009 -0700 +++ b/usr/src/uts/common/fs/zfs/zvol.c Thu Jun 04 17:15:02 2009 -0700 @@ -1051,7 +1051,9 @@ int numerrors = 0; for (c = 0; c < vd->vdev_children; c++) { - ASSERT(vd->vdev_ops == &vdev_mirror_ops); + ASSERT(vd->vdev_ops == &vdev_mirror_ops || + vd->vdev_ops == &vdev_replacing_ops || + vd->vdev_ops == &vdev_spare_ops); int err = zvol_dumpio_vdev(vd->vdev_child[c], addr, offset, size, doread, isdump); if (err != 0) {