summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJoshua M. Clulow <jmc@joyent.com>2020-04-02 00:16:22 -0700
committerJoshua M. Clulow <josh@sysmgr.org>2020-04-02 00:16:56 -0700
commit30c304d9746f4a048a7de56d31333b0fa8e43dee (patch)
treeca4f81799f06cebd4c6e06629c886b70d695a8c9
parent476d5ff73c235a63ab06a9852e510910a3ce1793 (diff)
downloadillumos-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.c46
-rw-r--r--usr/src/uts/common/fs/zfs/sys/spa.h4
-rw-r--r--usr/src/uts/common/fs/zfs/sys/spa_boot.h5
-rw-r--r--usr/src/uts/common/fs/zfs/sys/vdev_impl.h9
-rw-r--r--usr/src/uts/common/fs/zfs/vdev_disk.c181
-rw-r--r--usr/src/uts/common/fs/zfs/zfs_vfsops.c68
-rw-r--r--usr/src/uts/common/os/devcfg.c103
-rw-r--r--usr/src/uts/common/sys/ddi_implfuncs.h11
-rw-r--r--usr/src/uts/intel/zfs/spa_boot.c11
-rw-r--r--usr/src/uts/sparc/zfs/spa_boot.c11
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