diff options
author | mav <mav@FreeBSD.org> | 2016-10-28 18:18:53 +0000 |
---|---|---|
committer | mav <mav@FreeBSD.org> | 2016-10-28 18:18:53 +0000 |
commit | c8c4c6244daef57c7b854dfe5de8a10d8675634b (patch) | |
tree | a8e6cb6833101513ffe3bbb9ecea29fca0736409 /sys/cddl/contrib/opensolaris/uts/common | |
parent | c60f65ebc02c81225657a336afc72d7d08f4fe36 (diff) | |
download | FreeBSD-src-c8c4c6244daef57c7b854dfe5de8a10d8675634b.zip FreeBSD-src-c8c4c6244daef57c7b854dfe5de8a10d8675634b.tar.gz |
MFC r294329 (by asomers): Disallow zvol-backed ZFS pools
Using zvols as backing devices for ZFS pools is fraught with panics and
deadlocks. For example, attempting to online a missing device in the
presence of a zvol can cause a panic when vdev_geom tastes the zvol. Better
to completely disable vdev_geom from ever opening a zvol. The solution
relies on setting a thread-local variable during vdev_geom_open, and
returning EOPNOTSUPP during zvol_open if that thread-local variable is set.
Remove the check for MUTEX_HELD(&zfsdev_state_lock) in zvol_open. Its intent
was to prevent a recursive mutex acquisition panic. However, the new check
for the thread-local variable also fixes that problem.
Also, fix a panic in vdev_geom_taste_orphan. For an unknown reason, this
function was set to panic. But it can occur that a device disappears during
tasting, and it causes no problems to ignore this departure.
Diffstat (limited to 'sys/cddl/contrib/opensolaris/uts/common')
4 files changed, 34 insertions, 28 deletions
diff --git a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/vdev_impl.h b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/vdev_impl.h index 6f3013d..dc5b5be 100644 --- a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/vdev_impl.h +++ b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/vdev_impl.h @@ -381,6 +381,7 @@ extern void vdev_set_min_asize(vdev_t *vd); */ /* zdb uses this tunable, so it must be declared here to make lint happy. */ extern int zfs_vdev_cache_size; +extern uint_t zfs_geom_probe_vdev_key; #ifdef illumos /* diff --git a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/vdev_geom.c b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/vdev_geom.c index ac994f5..deb2bfd 100644 --- a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/vdev_geom.c +++ b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/vdev_geom.c @@ -63,6 +63,13 @@ TUNABLE_INT("vfs.zfs.vdev.bio_delete_disable", &vdev_geom_bio_delete_disable); SYSCTL_INT(_vfs_zfs_vdev, OID_AUTO, bio_delete_disable, CTLFLAG_RW, &vdev_geom_bio_delete_disable, 0, "Disable BIO_DELETE"); +/* + * Thread local storage used to indicate when a thread is probing geoms + * for their guids. If NULL, this thread is not tasting geoms. If non NULL, + * it is looking for a replacement for the vdev_t* that is its value. + */ +uint_t zfs_geom_probe_vdev_key; + static void vdev_geom_set_rotation_rate(vdev_t *vd, struct g_consumer *cp) { @@ -329,9 +336,8 @@ vdev_geom_io(struct g_consumer *cp, int cmd, void *data, off_t offset, off_t siz static void vdev_geom_taste_orphan(struct g_consumer *cp) { - - KASSERT(1 == 0, ("%s called while tasting %s.", __func__, - cp->provider->name)); + ZFS_LOG(0, "WARNING: Orphan %s while tasting its VDev GUID.", + cp->provider->name); } static int @@ -578,7 +584,6 @@ vdev_geom_attach_by_guids(vdev_t *vd) g_topology_assert(); zgp = g_new_geomf(&zfs_vdev_class, "zfs::vdev::taste"); - /* This orphan function should be never called. */ zgp->orphan = vdev_geom_taste_orphan; zcp = g_new_consumer(zgp); @@ -714,6 +719,9 @@ vdev_geom_open(vdev_t *vd, uint64_t *psize, uint64_t *max_psize, size_t bufsize; int error; + /* Set the TLS to indicate downstack that we should not access zvols*/ + VERIFY(tsd_set(zfs_geom_probe_vdev_key, vd) == 0); + /* * We must have a pathname, and it must be absolute. */ @@ -764,6 +772,9 @@ vdev_geom_open(vdev_t *vd, uint64_t *psize, uint64_t *max_psize, } } + /* Clear the TLS now that tasting is done */ + VERIFY(tsd_set(zfs_geom_probe_vdev_key, NULL) == 0); + if (cp == NULL) { ZFS_LOG(1, "Provider %s not found.", vd->vdev_path); error = ENOENT; diff --git a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_ioctl.c b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_ioctl.c index aea3c9e..d76ba07 100644 --- a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_ioctl.c +++ b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_ioctl.c @@ -207,6 +207,7 @@ extern void zfs_fini(void); uint_t zfs_fsyncer_key; extern uint_t rrw_tsd_key; static uint_t zfs_allow_log_key; +extern uint_t zfs_geom_probe_vdev_key; typedef int zfs_ioc_legacy_func_t(zfs_cmd_t *); typedef int zfs_ioc_func_t(const char *, nvlist_t *, nvlist_t *); @@ -6735,6 +6736,7 @@ zfs__init(void) tsd_create(&zfs_fsyncer_key, NULL); tsd_create(&rrw_tsd_key, rrw_tsd_destroy); tsd_create(&zfs_allow_log_key, zfs_allow_log_destroy); + tsd_create(&zfs_geom_probe_vdev_key, NULL); printf("ZFS storage pool version: features support (" SPA_VERSION_STRING ")\n"); root_mount_rel(zfs_root_token); diff --git a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zvol.c b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zvol.c index 77d951e..d1427b0 100644 --- a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zvol.c +++ b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zvol.c @@ -1123,36 +1123,30 @@ zvol_open(struct g_provider *pp, int flag, int count) return (err); } #else /* !illumos */ - boolean_t locked = B_FALSE; - - /* - * Protect against recursively entering spa_namespace_lock - * when spa_open() is used for a pool on a (local) ZVOL(s). - * This is needed since we replaced upstream zfsdev_state_lock - * with spa_namespace_lock in the ZVOL code. - * We are using the same trick as spa_open(). - * Note that calls in zvol_first_open which need to resolve - * pool name to a spa object will enter spa_open() - * recursively, but that function already has all the - * necessary protection. - */ - if (!MUTEX_HELD(&zfsdev_state_lock)) { - mutex_enter(&zfsdev_state_lock); - locked = B_TRUE; + if (tsd_get(zfs_geom_probe_vdev_key) != NULL) { + /* + * if zfs_geom_probe_vdev_key is set, that means that zfs is + * attempting to probe geom providers while looking for a + * replacement for a missing VDEV. In this case, the + * spa_namespace_lock will not be held, but it is still illegal + * to use a zvol as a vdev. Deadlocks can result if another + * thread has spa_namespace_lock + */ + return (EOPNOTSUPP); } + mutex_enter(&zfsdev_state_lock); + zv = pp->private; if (zv == NULL) { - if (locked) - mutex_exit(&zfsdev_state_lock); + mutex_exit(&zfsdev_state_lock); return (SET_ERROR(ENXIO)); } if (zv->zv_total_opens == 0) { err = zvol_first_open(zv); if (err) { - if (locked) - mutex_exit(&zfsdev_state_lock); + mutex_exit(&zfsdev_state_lock); return (err); } pp->mediasize = zv->zv_volsize; @@ -1186,8 +1180,7 @@ zvol_open(struct g_provider *pp, int flag, int count) mutex_exit(&zfsdev_state_lock); #else zv->zv_total_opens += count; - if (locked) - mutex_exit(&zfsdev_state_lock); + mutex_exit(&zfsdev_state_lock); #endif return (err); @@ -1197,8 +1190,7 @@ out: #ifdef illumos mutex_exit(&zfsdev_state_lock); #else - if (locked) - mutex_exit(&zfsdev_state_lock); + mutex_exit(&zfsdev_state_lock); #endif return (err); } |