diff options
author | Joshua M. Clulow <jmc@joyent.com> | 2020-04-02 00:16:22 -0700 |
---|---|---|
committer | Joshua M. Clulow <josh@sysmgr.org> | 2020-04-02 00:16:56 -0700 |
commit | 30c304d9746f4a048a7de56d31333b0fa8e43dee (patch) | |
tree | ca4f81799f06cebd4c6e06629c886b70d695a8c9 | |
parent | 476d5ff73c235a63ab06a9852e510910a3ce1793 (diff) | |
download | illumos-joyent-30c304d9746f4a048a7de56d31333b0fa8e43dee.tar.gz |
7119 boot should handle change in physical path to ZFS root devices
Reviewed by: Toomas Soome <tsoome@me.com>
Approved by: Dan McDonald <danmcd@joyent.com>
-rw-r--r-- | usr/src/uts/common/fs/zfs/spa.c | 46 | ||||
-rw-r--r-- | usr/src/uts/common/fs/zfs/sys/spa.h | 4 | ||||
-rw-r--r-- | usr/src/uts/common/fs/zfs/sys/spa_boot.h | 5 | ||||
-rw-r--r-- | usr/src/uts/common/fs/zfs/sys/vdev_impl.h | 9 | ||||
-rw-r--r-- | usr/src/uts/common/fs/zfs/vdev_disk.c | 181 | ||||
-rw-r--r-- | usr/src/uts/common/fs/zfs/zfs_vfsops.c | 68 | ||||
-rw-r--r-- | usr/src/uts/common/os/devcfg.c | 103 | ||||
-rw-r--r-- | usr/src/uts/common/sys/ddi_implfuncs.h | 11 | ||||
-rw-r--r-- | usr/src/uts/intel/zfs/spa_boot.c | 11 | ||||
-rw-r--r-- | usr/src/uts/sparc/zfs/spa_boot.c | 11 |
10 files changed, 418 insertions, 31 deletions
diff --git a/usr/src/uts/common/fs/zfs/spa.c b/usr/src/uts/common/fs/zfs/spa.c index 60f739d1b4..daf4df1624 100644 --- a/usr/src/uts/common/fs/zfs/spa.c +++ b/usr/src/uts/common/fs/zfs/spa.c @@ -31,6 +31,7 @@ * Copyright (c) 2017, Intel Corporation. * Copyright (c) 2017 Datto Inc. * Copyright 2018 OmniOS Community Edition (OmniOSce) Association. + * Copyright 2020 Joshua M. Clulow <josh@sysmgr.org> */ /* @@ -5358,10 +5359,9 @@ spa_create(const char *pool, nvlist_t *nvroot, nvlist_t *props, * Get the root pool information from the root disk, then import the root pool * during the system boot up time. */ -extern int vdev_disk_read_rootlabel(char *, char *, nvlist_t **); - static nvlist_t * -spa_generate_rootconf(char *devpath, char *devid, uint64_t *guid) +spa_generate_rootconf(const char *devpath, const char *devid, uint64_t *guid, + uint64_t pool_guid) { nvlist_t *config; nvlist_t *nvtop, *nvroot; @@ -5379,6 +5379,19 @@ spa_generate_rootconf(char *devpath, char *devid, uint64_t *guid) &pgid) == 0); VERIFY(nvlist_lookup_uint64(config, ZPOOL_CONFIG_GUID, guid) == 0); + if (pool_guid != 0 && pool_guid != pgid) { + /* + * The boot loader provided a pool GUID, but it does not match + * the one we found in the label. Return failure so that we + * can fall back to the full device scan. + */ + zfs_dbgmsg("spa_generate_rootconf: loader pool guid %llu != " + "label pool guid %llu", (u_longlong_t)pool_guid, + (u_longlong_t)pgid); + nvlist_free(config); + return (NULL); + } + /* * Put this pool's top-level vdevs into a root vdev. */ @@ -5445,7 +5458,8 @@ spa_alt_rootvdev(vdev_t *vd, vdev_t **avd, uint64_t *txg) * "/pci@1f,0/ide@d/disk@0,0:a" */ int -spa_import_rootpool(char *devpath, char *devid) +spa_import_rootpool(char *devpath, char *devid, uint64_t pool_guid, + uint64_t vdev_guid) { spa_t *spa; vdev_t *rvd, *bvd, *avd = NULL; @@ -5453,11 +5467,12 @@ spa_import_rootpool(char *devpath, char *devid) uint64_t guid, txg; char *pname; int error; + const char *altdevpath = NULL; /* * Read the label from the boot device and generate a configuration. */ - config = spa_generate_rootconf(devpath, devid, &guid); + config = spa_generate_rootconf(devpath, devid, &guid, pool_guid); #if defined(_OBP) && defined(_KERNEL) if (config == NULL) { if (strstr(devpath, "/iscsi/ssd") != NULL) { @@ -5467,6 +5482,27 @@ spa_import_rootpool(char *devpath, char *devid) } } #endif + + /* + * We were unable to import the pool using the /devices path or devid + * provided by the boot loader. This may be the case if the boot + * device has been connected to a different location in the system, or + * if a new boot environment has changed the driver used to access the + * boot device. + * + * Attempt an exhaustive scan of all visible block devices to see if we + * can locate an alternative /devices path with a label that matches + * the expected pool and vdev GUID. + */ + if (config == NULL && (altdevpath = + vdev_disk_preroot_lookup(pool_guid, vdev_guid)) != NULL) { + cmn_err(CE_NOTE, "Original /devices path (%s) not available; " + "ZFS is trying an alternate path (%s)", devpath, + altdevpath); + config = spa_generate_rootconf(altdevpath, NULL, &guid, + pool_guid); + } + if (config == NULL) { cmn_err(CE_NOTE, "Cannot read the pool label from '%s'", devpath); diff --git a/usr/src/uts/common/fs/zfs/sys/spa.h b/usr/src/uts/common/fs/zfs/sys/spa.h index e017462613..31faac4f77 100644 --- a/usr/src/uts/common/fs/zfs/sys/spa.h +++ b/usr/src/uts/common/fs/zfs/sys/spa.h @@ -28,6 +28,7 @@ * Copyright 2019 Joyent, Inc. * Copyright (c) 2017 Datto Inc. * Copyright (c) 2017, Intel Corporation. + * Copyright 2020 Joshua M. Clulow <josh@sysmgr.org> */ #ifndef _SYS_SPA_H @@ -759,7 +760,8 @@ extern int spa_get_stats(const char *pool, nvlist_t **config, char *altroot, size_t buflen); extern int spa_create(const char *pool, nvlist_t *nvroot, nvlist_t *props, nvlist_t *zplprops, struct dsl_crypto_params *dcp); -extern int spa_import_rootpool(char *devpath, char *devid); +extern int spa_import_rootpool(char *devpath, char *devid, uint64_t pool_guid, + uint64_t vdev_guid); extern int spa_import(const char *pool, nvlist_t *config, nvlist_t *props, uint64_t flags); extern nvlist_t *spa_tryimport(nvlist_t *tryconfig); diff --git a/usr/src/uts/common/fs/zfs/sys/spa_boot.h b/usr/src/uts/common/fs/zfs/sys/spa_boot.h index 8df5072a55..b1b100e17e 100644 --- a/usr/src/uts/common/fs/zfs/sys/spa_boot.h +++ b/usr/src/uts/common/fs/zfs/sys/spa_boot.h @@ -25,6 +25,7 @@ /* * Copyright (c) 2012 by Delphix. All rights reserved. + * Copyright 2020 Joshua M. Clulow <josh@sysmgr.org> */ #ifndef _SYS_SPA_BOOT_H @@ -36,8 +37,8 @@ extern "C" { #endif -extern char *spa_get_bootprop(char *prop); -extern void spa_free_bootprop(char *prop); +extern char *spa_get_bootprop(const char *propname); +extern void spa_free_bootprop(char *propval); extern void spa_arch_init(void); diff --git a/usr/src/uts/common/fs/zfs/sys/vdev_impl.h b/usr/src/uts/common/fs/zfs/sys/vdev_impl.h index b9b538455c..3004872de7 100644 --- a/usr/src/uts/common/fs/zfs/sys/vdev_impl.h +++ b/usr/src/uts/common/fs/zfs/sys/vdev_impl.h @@ -24,6 +24,7 @@ * Copyright (c) 2011, 2019 by Delphix. All rights reserved. * Copyright (c) 2017, Intel Corporation. * Copyright 2019 Joyent, Inc. + * Copyright 2020 Joshua M. Clulow <josh@sysmgr.org> */ #ifndef _SYS_VDEV_IMPL_H @@ -555,6 +556,14 @@ typedef struct vdev_buf { zio_t *vb_io; /* pointer back to the original zio_t */ } vdev_buf_t; +/* + * Support routines used during boot from a ZFS pool + */ +extern int vdev_disk_read_rootlabel(const char *, const char *, nvlist_t **); +extern void vdev_disk_preroot_init(void); +extern void vdev_disk_preroot_fini(void); +extern const char *vdev_disk_preroot_lookup(uint64_t, uint64_t); + #ifdef __cplusplus } #endif diff --git a/usr/src/uts/common/fs/zfs/vdev_disk.c b/usr/src/uts/common/fs/zfs/vdev_disk.c index dcb61064eb..ffbe1ede69 100644 --- a/usr/src/uts/common/fs/zfs/vdev_disk.c +++ b/usr/src/uts/common/fs/zfs/vdev_disk.c @@ -23,6 +23,7 @@ * Copyright (c) 2012, 2018 by Delphix. All rights reserved. * Copyright 2016 Nexenta Systems, Inc. All rights reserved. * Copyright 2020 Joyent, Inc. + * Copyright 2020 Joshua M. Clulow <josh@sysmgr.org> */ #include <sys/zfs_context.h> @@ -362,7 +363,6 @@ vdev_disk_open(vdev_t *vd, uint64_t *psize, uint64_t *max_psize, error = EINVAL; /* presume failure */ if (vd->vdev_path != NULL) { - if (vd->vdev_wholedisk == -1ULL) { size_t len = strlen(vd->vdev_path) + 3; char *buf = kmem_alloc(len, KM_SLEEP); @@ -477,6 +477,28 @@ vdev_disk_open(vdev_t *vd, uint64_t *psize, uint64_t *max_psize, } } + /* + * If this is early in boot, a sweep of available block devices may + * locate an alternative path that we can try. + */ + if (error != 0) { + const char *altdevpath = vdev_disk_preroot_lookup( + spa_guid(spa), vd->vdev_guid); + + if (altdevpath != NULL) { + vdev_dbgmsg(vd, "Trying alternate preroot path (%s)", + altdevpath); + + validate_devid = B_TRUE; + + if ((error = ldi_open_by_name((char *)altdevpath, + spa_mode(spa), kcred, &dvd->vd_lh, zfs_li)) != 0) { + vdev_dbgmsg(vd, "Failed to open by preroot " + "path (%s)", altdevpath); + } + } + } + if (error != 0) { vd->vdev_stat.vs_aux = VDEV_AUX_OPEN_FAILED; vdev_dbgmsg(vd, "vdev_disk_open: failed to open [error=%d]", @@ -1055,7 +1077,8 @@ vdev_ops_t vdev_disk_ops = { * the device, and construct a configuration nvlist. */ int -vdev_disk_read_rootlabel(char *devpath, char *devid, nvlist_t **config) +vdev_disk_read_rootlabel(const char *devpath, const char *devid, + nvlist_t **config) { ldi_handle_t vd_lh; vdev_label_t *label; @@ -1068,7 +1091,7 @@ vdev_disk_read_rootlabel(char *devpath, char *devid, nvlist_t **config) /* * Read the device label and build the nvlist. */ - if (devid != NULL && ddi_devid_str_decode(devid, &tmpdevid, + if (devid != NULL && ddi_devid_str_decode((char *)devid, &tmpdevid, &minor_name) == 0) { error = ldi_open_by_devid(tmpdevid, minor_name, FREAD, kcred, &vd_lh, zfs_li); @@ -1076,9 +1099,10 @@ vdev_disk_read_rootlabel(char *devpath, char *devid, nvlist_t **config) ddi_devid_str_free(minor_name); } - if (error && (error = ldi_open_by_name(devpath, FREAD, kcred, &vd_lh, - zfs_li))) + if (error != 0 && (error = ldi_open_by_name((char *)devpath, FREAD, + kcred, &vd_lh, zfs_li)) != 0) { return (error); + } if (ldi_get_size(vd_lh, &s)) { (void) ldi_close(vd_lh, FREAD, kcred); @@ -1128,3 +1152,150 @@ vdev_disk_read_rootlabel(char *devpath, char *devid, nvlist_t **config) return (error); } + +struct veb { + list_t veb_ents; + boolean_t veb_scanned; +}; + +struct veb_ent { + uint64_t vebe_pool_guid; + uint64_t vebe_vdev_guid; + + char *vebe_devpath; + + list_node_t vebe_link; +}; + +static kmutex_t veb_lock; +static struct veb *veb; + +static int +vdev_disk_preroot_scan_walk(const char *devpath, void *arg) +{ + int r; + nvlist_t *cfg = NULL; + uint64_t pguid = 0, vguid = 0; + + /* + * Attempt to read the label from this block device. + */ + if ((r = vdev_disk_read_rootlabel(devpath, NULL, &cfg)) != 0) { + /* + * Many of the available block devices will represent slices or + * partitions of disks, or may represent disks that are not at + * all initialised with ZFS. As this is a best effort + * mechanism to locate an alternate path to a particular vdev, + * we will ignore any failures and keep scanning. + */ + return (PREROOT_WALK_BLOCK_DEVICES_NEXT); + } + + /* + * Determine the pool and vdev GUID read from the label for this + * device. Both values must be present and have a non-zero value. + */ + if (nvlist_lookup_uint64(cfg, ZPOOL_CONFIG_POOL_GUID, &pguid) != 0 || + nvlist_lookup_uint64(cfg, ZPOOL_CONFIG_GUID, &vguid) != 0 || + pguid == 0 || vguid == 0) { + /* + * This label was not complete. + */ + goto out; + } + + /* + * Keep track of all of the GUID-to-devpath mappings we find so that + * vdev_disk_preroot_lookup() can search them. + */ + struct veb_ent *vebe = kmem_zalloc(sizeof (*vebe), KM_SLEEP); + vebe->vebe_pool_guid = pguid; + vebe->vebe_vdev_guid = vguid; + vebe->vebe_devpath = spa_strdup(devpath); + + list_insert_tail(&veb->veb_ents, vebe); + +out: + nvlist_free(cfg); + return (PREROOT_WALK_BLOCK_DEVICES_NEXT); +} + +const char * +vdev_disk_preroot_lookup(uint64_t pool_guid, uint64_t vdev_guid) +{ + if (pool_guid == 0 || vdev_guid == 0) { + /* + * If we aren't provided both a pool and a vdev GUID, we cannot + * perform a lookup. + */ + return (NULL); + } + + mutex_enter(&veb_lock); + if (veb == NULL) { + /* + * If vdev_disk_preroot_fini() has been called already, there + * is nothing we can do. + */ + mutex_exit(&veb_lock); + return (NULL); + } + + /* + * We want to perform at most one scan of all block devices per boot. + */ + if (!veb->veb_scanned) { + cmn_err(CE_NOTE, "Performing full ZFS device scan!"); + + preroot_walk_block_devices(vdev_disk_preroot_scan_walk, NULL); + + veb->veb_scanned = B_TRUE; + } + + const char *path = NULL; + for (struct veb_ent *vebe = list_head(&veb->veb_ents); vebe != NULL; + vebe = list_next(&veb->veb_ents, vebe)) { + if (vebe->vebe_pool_guid == pool_guid && + vebe->vebe_vdev_guid == vdev_guid) { + path = vebe->vebe_devpath; + break; + } + } + + mutex_exit(&veb_lock); + + return (path); +} + +void +vdev_disk_preroot_init(void) +{ + mutex_init(&veb_lock, NULL, MUTEX_DEFAULT, NULL); + + VERIFY3P(veb, ==, NULL); + veb = kmem_zalloc(sizeof (*veb), KM_SLEEP); + list_create(&veb->veb_ents, sizeof (struct veb_ent), + offsetof(struct veb_ent, vebe_link)); + veb->veb_scanned = B_FALSE; +} + +void +vdev_disk_preroot_fini(void) +{ + mutex_enter(&veb_lock); + + if (veb != NULL) { + while (!list_is_empty(&veb->veb_ents)) { + struct veb_ent *vebe = list_remove_head(&veb->veb_ents); + + spa_strfree(vebe->vebe_devpath); + + kmem_free(vebe, sizeof (*vebe)); + } + + kmem_free(veb, sizeof (*veb)); + veb = NULL; + } + + mutex_exit(&veb_lock); +} diff --git a/usr/src/uts/common/fs/zfs/zfs_vfsops.c b/usr/src/uts/common/fs/zfs/zfs_vfsops.c index 1314204765..d504e15457 100644 --- a/usr/src/uts/common/fs/zfs/zfs_vfsops.c +++ b/usr/src/uts/common/fs/zfs/zfs_vfsops.c @@ -24,6 +24,7 @@ * Copyright (c) 2014 Integros [integros.com] * Copyright 2016 Nexenta Systems, Inc. All rights reserved. * Copyright 2019 Joyent, Inc. + * Copyright 2020 Joshua M. Clulow <josh@sysmgr.org> */ /* Portions Copyright 2010 Robert Milkowski */ @@ -63,10 +64,12 @@ #include <sys/zfs_ctldir.h> #include <sys/zfs_fuid.h> #include <sys/bootconf.h> +#include <sys/ddi.h> #include <sys/sunddi.h> #include <sys/dnlc.h> #include <sys/dmu_objset.h> #include <sys/spa_boot.h> +#include <sys/vdev_impl.h> #include "zfs_comutil.h" int zfsfstype; @@ -1710,6 +1713,36 @@ zfs_mount_label_policy(vfs_t *vfsp, char *osname) return (retv); } +/* + * Load a string-valued boot property and attempt to convert it to a 64-bit + * unsigned integer. If the value is not present, or the conversion fails, + * return the provided default value. + */ +static uint64_t +spa_get_bootprop_uint64(const char *name, uint64_t defval) +{ + char *propval; + u_longlong_t r; + int e; + + if ((propval = spa_get_bootprop(name)) == NULL) { + /* + * The property does not exist. + */ + return (defval); + } + + e = ddi_strtoull(propval, NULL, 10, &r); + + spa_free_bootprop(propval); + + /* + * If the conversion succeeded, return the value. If there was any + * kind of failure, just return the default value. + */ + return (e == 0 ? r : defval); +} + static int zfs_mountroot(vfs_t *vfsp, enum whymountroot why) { @@ -1720,6 +1753,8 @@ zfs_mountroot(vfs_t *vfsp, enum whymountroot why) vnode_t *vp = NULL; char *zfs_bootfs; char *zfs_devid; + uint64_t zfs_bootpool; + uint64_t zfs_bootvdev; ASSERT(vfsp); @@ -1731,6 +1766,7 @@ zfs_mountroot(vfs_t *vfsp, enum whymountroot why) if (why == ROOT_INIT) { if (zfsrootdone++) return (SET_ERROR(EBUSY)); + /* * the process of doing a spa_load will require the * clock to be set before we could (for example) do @@ -1745,23 +1781,47 @@ zfs_mountroot(vfs_t *vfsp, enum whymountroot why) return (SET_ERROR(EINVAL)); } zfs_devid = spa_get_bootprop("diskdevid"); - error = spa_import_rootpool(rootfs.bo_name, zfs_devid); - if (zfs_devid) - spa_free_bootprop(zfs_devid); - if (error) { + + /* + * The boot loader may also provide us with the GUID for both + * the pool and the nominated boot vdev. A GUID value of 0 is + * explicitly invalid (see "spa_change_guid()"), so we use this + * as a sentinel value when no GUID is present. + */ + zfs_bootpool = spa_get_bootprop_uint64("zfs-bootpool", 0); + zfs_bootvdev = spa_get_bootprop_uint64("zfs-bootvdev", 0); + + /* + * Initialise the early boot device rescan mechanism. A scan + * will not actually be performed unless we need to do so in + * order to find the correct /devices path for a relocated + * device. + */ + vdev_disk_preroot_init(); + + error = spa_import_rootpool(rootfs.bo_name, zfs_devid, + zfs_bootpool, zfs_bootvdev); + + spa_free_bootprop(zfs_devid); + + if (error != 0) { spa_free_bootprop(zfs_bootfs); + vdev_disk_preroot_fini(); cmn_err(CE_NOTE, "spa_import_rootpool: error %d", error); return (error); } + if (error = zfs_parse_bootfs(zfs_bootfs, rootfs.bo_name)) { spa_free_bootprop(zfs_bootfs); + vdev_disk_preroot_fini(); cmn_err(CE_NOTE, "zfs_parse_bootfs: error %d", error); return (error); } spa_free_bootprop(zfs_bootfs); + vdev_disk_preroot_fini(); if (error = vfs_lock(vfsp)) return (error); diff --git a/usr/src/uts/common/os/devcfg.c b/usr/src/uts/common/os/devcfg.c index cbcc4db3d8..c82a8f86d9 100644 --- a/usr/src/uts/common/os/devcfg.c +++ b/usr/src/uts/common/os/devcfg.c @@ -24,6 +24,7 @@ * Copyright 2012 Garrett D'Amore <garrett@damore.org>. All rights reserved. * Copyright (c) 2013, Joyent, Inc. All rights reserved. * Copyright (c) 2016 by Delphix. All rights reserved. + * Copyright 2020 Joshua M. Clulow <josh@sysmgr.org> */ #include <sys/note.h> @@ -62,6 +63,7 @@ #include <sys/varargs.h> #include <sys/modhash.h> #include <sys/instance.h> +#include <sys/sysevent/eventdefs.h> #if defined(__amd64) && !defined(__xpv) #include <sys/iommulib.h> @@ -1486,12 +1488,12 @@ postattach_node(dev_info_t *dip) /* * Plumbing during postattach may fail because of the * underlying device is not ready. This will fail ndi_devi_config() - * in dv_filldir() and a warning message is issued. The message - * from here will explain what happened + * in dv_filldir(). */ if (rval != DACF_SUCCESS) { - cmn_err(CE_WARN, "Postattach failed for %s%d\n", - ddi_driver_name(dip), ddi_get_instance(dip)); + NDI_CONFIG_DEBUG((CE_CONT, "postattach_node: %s%d (%p) " + "postattach failed\n", ddi_driver_name(dip), + ddi_get_instance(dip), (void *)dip)); return (DDI_FAILURE); } @@ -3560,7 +3562,6 @@ walk_devs(dev_info_t *dip, int (*f)(dev_info_t *, void *), void *arg, * They include, but not limited to, _init(9e), _fini(9e), probe(9e), * attach(9e), and detach(9e). */ - void ddi_walk_devs(dev_info_t *dip, int (*f)(dev_info_t *, void *), void *arg) { @@ -3580,7 +3581,6 @@ ddi_walk_devs(dev_info_t *dip, int (*f)(dev_info_t *, void *), void *arg) * * N.B. The same restrictions from ddi_walk_devs() apply. */ - void e_ddi_walk_driver(char *drv, int (*f)(dev_info_t *, void *), void *arg) { @@ -3609,6 +3609,91 @@ e_ddi_walk_driver(char *drv, int (*f)(dev_info_t *, void *), void *arg) UNLOCK_DEV_OPS(&dnp->dn_lock); } +struct preroot_walk_block_devices_arg { + int (*prwb_func)(const char *, void *); + void *prwb_arg; +}; + +static int +preroot_walk_block_devices_walker(dev_info_t *dip, void *arg) +{ + struct preroot_walk_block_devices_arg *prwb = arg; + + if (i_ddi_devi_class(dip) == NULL || + strcmp(i_ddi_devi_class(dip), ESC_DISK) != 0) { + /* + * We do not think that this is a disk. + */ + return (DDI_WALK_CONTINUE); + } + + for (struct ddi_minor_data *md = DEVI(dip)->devi_minor; md != NULL; + md = md->next) { + if (md->ddm_spec_type != S_IFBLK) { + /* + * We don't want the raw version of any block device. + */ + continue; + } + + /* + * The node type taxonomy is hierarchical, with each level + * separated by colons. Nodes of interest are either of the + * BLOCK type, or are prefixed with that type. + */ + if (strcmp(md->ddm_node_type, DDI_NT_BLOCK) != 0 && + strncmp(md->ddm_node_type, DDI_NT_BLOCK ":", + strlen(DDI_NT_BLOCK ":")) != 0) { + /* + * This minor node does not represent a block device. + */ + continue; + } + + char buf[MAXPATHLEN]; + int r; + if ((r = prwb->prwb_func(ddi_pathname_minor(md, buf), + prwb->prwb_arg)) == PREROOT_WALK_BLOCK_DEVICES_CANCEL) { + /* + * The consumer does not need any more minor nodes. + */ + return (DDI_WALK_TERMINATE); + } + VERIFY3S(r, ==, PREROOT_WALK_BLOCK_DEVICES_NEXT); + } + + return (DDI_WALK_CONTINUE); +} + +/* + * Private routine for ZFS when it needs to attach and scan all of the block + * device minors in the system while looking for vdev labels. + * + * The callback function accepts a physical device path and the context + * argument (arg) passed to this function; it should return + * PREROOT_WALK_BLOCK_DEVICES_NEXT when more devices are required and + * PREROOT_WALK_BLOCK_DEVICES_CANCEL to stop the walk. + */ +void +preroot_walk_block_devices(int (*callback)(const char *, void *), void *arg) +{ + /* + * First, force everything which can attach to do so. The device class + * is not derived until at least one minor mode is created, so we + * cannot walk the device tree looking for a device class of ESC_DISK + * until everything is attached. + */ + (void) ndi_devi_config(ddi_root_node(), NDI_CONFIG | NDI_DEVI_PERSIST | + NDI_NO_EVENT | NDI_DRV_CONF_REPROBE); + + struct preroot_walk_block_devices_arg prwb; + prwb.prwb_func = callback; + prwb.prwb_arg = arg; + + ddi_walk_devs(ddi_root_node(), preroot_walk_block_devices_walker, + &prwb); +} + /* * argument to i_find_devi, a devinfo node search callback function. */ @@ -3823,8 +3908,8 @@ ddi_is_pci_dip(dev_info_t *dip) * to ioc's bus_config entry point. */ int -resolve_pathname(char *pathname, - dev_info_t **dipp, dev_t *devtp, int *spectypep) +resolve_pathname(char *pathname, dev_info_t **dipp, dev_t *devtp, + int *spectypep) { int error; dev_info_t *parent, *child; @@ -9055,7 +9140,7 @@ out: char * ddi_curr_redirect(char *curr) { - char *alias; + char *alias; int i; if (ddi_aliases_present == B_FALSE) diff --git a/usr/src/uts/common/sys/ddi_implfuncs.h b/usr/src/uts/common/sys/ddi_implfuncs.h index e7d218844b..522366aa23 100644 --- a/usr/src/uts/common/sys/ddi_implfuncs.h +++ b/usr/src/uts/common/sys/ddi_implfuncs.h @@ -25,6 +25,7 @@ */ /* * Copyright 2012 Garrett D'Amore <garrett@damore.org>. All rights reserved. + * Copyright 2020 Joshua M. Clulow <josh@sysmgr.org> */ #ifndef _SYS_DDI_IMPLFUNCS_H @@ -237,6 +238,16 @@ extern void i_ddi_decr_locked_memory(proc_t *, rctl_qty_t); */ extern void translate_devid(dev_info_t *dip); +/* + * Support routine for file systems that need to scan block devices searching + * for a label as part of mounting the root file system. + */ +extern void preroot_walk_block_devices(int (*)(const char *, void *), void *); + +#define PREROOT_WALK_BLOCK_DEVICES_NEXT 1 +#define PREROOT_WALK_BLOCK_DEVICES_CANCEL 2 + + #endif /* _KERNEL */ #ifdef __cplusplus diff --git a/usr/src/uts/intel/zfs/spa_boot.c b/usr/src/uts/intel/zfs/spa_boot.c index adbcffaef0..abcaac7ce9 100644 --- a/usr/src/uts/intel/zfs/spa_boot.c +++ b/usr/src/uts/intel/zfs/spa_boot.c @@ -26,6 +26,7 @@ /* * Copyright (c) 2012 by Delphix. All rights reserved. + * Copyright 2020 Joshua M. Clulow <josh@sysmgr.org> */ #include <sys/zio.h> @@ -36,19 +37,25 @@ extern int zfs_deadman_enabled; char * -spa_get_bootprop(char *propname) +spa_get_bootprop(const char *propname) { char *value; if (ddi_prop_lookup_string(DDI_DEV_T_ANY, ddi_root_node(), - DDI_PROP_DONTPASS, propname, &value) != DDI_SUCCESS) + DDI_PROP_DONTPASS, (char *)propname, &value) != DDI_SUCCESS) { return (NULL); + } + return (value); } void spa_free_bootprop(char *value) { + if (value == NULL) { + return; + } + ddi_prop_free(value); } diff --git a/usr/src/uts/sparc/zfs/spa_boot.c b/usr/src/uts/sparc/zfs/spa_boot.c index 71a8b9ed51..8109d0e2f9 100644 --- a/usr/src/uts/sparc/zfs/spa_boot.c +++ b/usr/src/uts/sparc/zfs/spa_boot.c @@ -26,6 +26,7 @@ /* * Copyright (c) 2012 by Delphix. All rights reserved. + * Copyright 2020 Joshua M. Clulow <josh@sysmgr.org> */ #include <sys/zio.h> @@ -35,7 +36,7 @@ extern int zfs_deadman_enabled; char * -spa_get_bootprop(char *propname) +spa_get_bootprop(const char *propname) { int proplen; char *value; @@ -54,9 +55,13 @@ spa_get_bootprop(char *propname) } void -spa_free_bootprop(char *propname) +spa_free_bootprop(char *propval) { - kmem_free(propname, strlen(propname) + 1); + if (propval == NULL) { + return; + } + + kmem_free(propval, strlen(propval) + 1); } void |