summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJoshua M. Clulow <jmc@oxide.computer>2022-11-10 11:36:16 -0800
committerJoshua M. Clulow <josh@sysmgr.org>2022-11-10 11:36:17 -0800
commit8b26092d555bd1deaacf79ea64da374602aefb65 (patch)
tree78276ecbb871e076e9a362fbef63911045ec32fe
parent6eeafb34dceabceff80ed689002b6dc3e060f498 (diff)
downloadillumos-gate-8b26092d555bd1deaacf79ea64da374602aefb65.tar.gz
15137 ZFS should allow direct import of a root pool from a /devices path
15122 vdev_disk_preroot_fini can race against the child vdev open taskq Reviewed by: Robert Mustacchi <rm@fingolfin.org> Reviewed by: Toomas Soome <tsoome@me.com> Reviewed by: Dan Cross <cross@oxidecomputer.com> Approved by: Dan McDonald <danmcd@mnx.io>
-rw-r--r--usr/src/uts/common/fs/zfs/spa.c16
-rw-r--r--usr/src/uts/common/fs/zfs/sys/vdev_impl.h3
-rw-r--r--usr/src/uts/common/fs/zfs/vdev_disk.c40
-rw-r--r--usr/src/uts/common/fs/zfs/zfs_vfsops.c19
4 files changed, 72 insertions, 6 deletions
diff --git a/usr/src/uts/common/fs/zfs/spa.c b/usr/src/uts/common/fs/zfs/spa.c
index d390e39195..d6e230fbb4 100644
--- a/usr/src/uts/common/fs/zfs/spa.c
+++ b/usr/src/uts/common/fs/zfs/spa.c
@@ -5479,6 +5479,21 @@ spa_import_rootpool(char *devpath, char *devid, uint64_t pool_guid,
char *pname;
int error;
const char *altdevpath = NULL;
+ const char *rdpath = NULL;
+
+ if ((rdpath = vdev_disk_preroot_force_path()) != NULL) {
+ /*
+ * We expect to import a single-vdev pool from a specific
+ * device such as a ramdisk device. We don't care what the
+ * pool label says.
+ */
+ config = spa_generate_rootconf(rdpath, NULL, &guid, 0);
+ if (config != NULL) {
+ goto configok;
+ }
+ cmn_err(CE_NOTE, "Cannot use root disk device '%s'", rdpath);
+ return (SET_ERROR(EIO));
+ }
/*
* Read the label from the boot device and generate a configuration.
@@ -5521,6 +5536,7 @@ spa_import_rootpool(char *devpath, char *devid, uint64_t pool_guid,
return (SET_ERROR(EIO));
}
+configok:
VERIFY(nvlist_lookup_string(config, ZPOOL_CONFIG_POOL_NAME,
&pname) == 0);
VERIFY(nvlist_lookup_uint64(config, ZPOOL_CONFIG_POOL_TXG, &txg) == 0);
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 d1341dbcd3..d542368e7c 100644
--- a/usr/src/uts/common/fs/zfs/sys/vdev_impl.h
+++ b/usr/src/uts/common/fs/zfs/sys/vdev_impl.h
@@ -588,9 +588,10 @@ typedef struct vdev_buf {
* 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_init(const char *);
extern void vdev_disk_preroot_fini(void);
extern const char *vdev_disk_preroot_lookup(uint64_t, uint64_t);
+extern const char *vdev_disk_preroot_force_path(void);
#ifdef __cplusplus
}
diff --git a/usr/src/uts/common/fs/zfs/vdev_disk.c b/usr/src/uts/common/fs/zfs/vdev_disk.c
index 1cdc458974..cd5e80d769 100644
--- a/usr/src/uts/common/fs/zfs/vdev_disk.c
+++ b/usr/src/uts/common/fs/zfs/vdev_disk.c
@@ -301,6 +301,7 @@ vdev_disk_open(vdev_t *vd, uint64_t *psize, uint64_t *max_psize,
int otyp;
boolean_t validate_devid = B_FALSE;
uint64_t capacity = 0, blksz = 0, pbsize;
+ const char *rdpath = vdev_disk_preroot_force_path();
/*
* We must have a pathname, and it must be absolute.
@@ -328,7 +329,8 @@ vdev_disk_open(vdev_t *vd, uint64_t *psize, uint64_t *max_psize,
/*
* Allow bypassing the devid.
*/
- if (vd->vdev_devid != NULL && vdev_disk_bypass_devid) {
+ if (vd->vdev_devid != NULL &&
+ (vdev_disk_bypass_devid || rdpath != NULL)) {
vdev_dbgmsg(vd, "vdev_disk_open, devid %s bypassed",
vd->vdev_devid);
spa_strfree(vd->vdev_devid);
@@ -364,6 +366,17 @@ vdev_disk_open(vdev_t *vd, uint64_t *psize, uint64_t *max_psize,
error = EINVAL; /* presume failure */
+ if (rdpath != NULL) {
+ /*
+ * We have been asked to open only a specific root device, and
+ * to fail otherwise.
+ */
+ error = ldi_open_by_name((char *)rdpath, spa_mode(spa), kcred,
+ &dvd->vd_lh, zfs_li);
+ validate_devid = B_TRUE;
+ goto rootdisk_only;
+ }
+
if (vd->vdev_path != NULL) {
if (vd->vdev_wholedisk == -1ULL) {
size_t len = strlen(vd->vdev_path) + 3;
@@ -501,6 +514,7 @@ vdev_disk_open(vdev_t *vd, uint64_t *psize, uint64_t *max_psize,
}
}
+rootdisk_only:
if (error != 0) {
vd->vdev_stat.vs_aux = VDEV_AUX_OPEN_FAILED;
vdev_dbgmsg(vd, "vdev_disk_open: failed to open [error=%d]",
@@ -1158,6 +1172,7 @@ vdev_disk_read_rootlabel(const char *devpath, const char *devid,
struct veb {
list_t veb_ents;
boolean_t veb_scanned;
+ char *veb_force_path;
};
struct veb_ent {
@@ -1269,8 +1284,22 @@ vdev_disk_preroot_lookup(uint64_t pool_guid, uint64_t vdev_guid)
return (path);
}
+const char *
+vdev_disk_preroot_force_path(void)
+{
+ const char *force_path = NULL;
+
+ mutex_enter(&veb_lock);
+ if (veb != NULL) {
+ force_path = veb->veb_force_path;
+ }
+ mutex_exit(&veb_lock);
+
+ return (force_path);
+}
+
void
-vdev_disk_preroot_init(void)
+vdev_disk_preroot_init(const char *force_path)
{
mutex_init(&veb_lock, NULL, MUTEX_DEFAULT, NULL);
@@ -1279,6 +1308,9 @@ vdev_disk_preroot_init(void)
list_create(&veb->veb_ents, sizeof (struct veb_ent),
offsetof(struct veb_ent, vebe_link));
veb->veb_scanned = B_FALSE;
+ if (force_path != NULL) {
+ veb->veb_force_path = spa_strdup(force_path);
+ }
}
void
@@ -1295,6 +1327,10 @@ vdev_disk_preroot_fini(void)
kmem_free(vebe, sizeof (*vebe));
}
+ if (veb->veb_force_path != NULL) {
+ spa_strfree(veb->veb_force_path);
+ }
+
kmem_free(veb, sizeof (*veb));
veb = NULL;
}
diff --git a/usr/src/uts/common/fs/zfs/zfs_vfsops.c b/usr/src/uts/common/fs/zfs/zfs_vfsops.c
index 87b4b36a7d..288dc93e3c 100644
--- a/usr/src/uts/common/fs/zfs/zfs_vfsops.c
+++ b/usr/src/uts/common/fs/zfs/zfs_vfsops.c
@@ -1767,6 +1767,7 @@ zfs_mountroot(vfs_t *vfsp, enum whymountroot why)
vnode_t *vp = NULL;
char *zfs_bootfs;
char *zfs_devid;
+ char *zfs_rootdisk_path;
uint64_t zfs_bootpool;
uint64_t zfs_bootvdev;
@@ -1806,12 +1807,19 @@ zfs_mountroot(vfs_t *vfsp, enum whymountroot why)
zfs_bootvdev = spa_get_bootprop_uint64("zfs-bootvdev", 0);
/*
+ * If we have been given a root disk override path, we want to
+ * ignore device paths from the pool configuration and use only
+ * the specific path we were given in the boot properties.
+ */
+ zfs_rootdisk_path = spa_get_bootprop("zfs-rootdisk-path");
+
+ /*
* 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();
+ vdev_disk_preroot_init(zfs_rootdisk_path);
error = spa_import_rootpool(rootfs.bo_name, zfs_devid,
zfs_bootpool, zfs_bootvdev);
@@ -1820,6 +1828,7 @@ zfs_mountroot(vfs_t *vfsp, enum whymountroot why)
if (error != 0) {
spa_free_bootprop(zfs_bootfs);
+ spa_free_bootprop(zfs_rootdisk_path);
vdev_disk_preroot_fini();
cmn_err(CE_NOTE, "spa_import_rootpool: error %d",
error);
@@ -1828,6 +1837,7 @@ zfs_mountroot(vfs_t *vfsp, enum whymountroot why)
if (error = zfs_parse_bootfs(zfs_bootfs, rootfs.bo_name)) {
spa_free_bootprop(zfs_bootfs);
+ spa_free_bootprop(zfs_rootdisk_path);
vdev_disk_preroot_fini();
cmn_err(CE_NOTE, "zfs_parse_bootfs: error %d",
error);
@@ -1835,10 +1845,12 @@ zfs_mountroot(vfs_t *vfsp, enum whymountroot why)
}
spa_free_bootprop(zfs_bootfs);
- vdev_disk_preroot_fini();
+ spa_free_bootprop(zfs_rootdisk_path);
- if (error = vfs_lock(vfsp))
+ if ((error = vfs_lock(vfsp)) != 0) {
+ vdev_disk_preroot_fini();
return (error);
+ }
if (error = zfs_domount(vfsp, rootfs.bo_name)) {
cmn_err(CE_NOTE, "zfs_domount: error %d", error);
@@ -1864,6 +1876,7 @@ zfs_mountroot(vfs_t *vfsp, enum whymountroot why)
vfs_add((struct vnode *)0, vfsp,
(vfsp->vfs_flag & VFS_RDONLY) ? MS_RDONLY : 0);
out:
+ vdev_disk_preroot_fini();
vfs_unlock(vfsp);
return (error);
} else if (why == ROOT_REMOUNT) {