aboutsummaryrefslogtreecommitdiffstats
path: root/module/zfs/zfs_ctldir.c
diff options
context:
space:
mode:
authorRohan Puri <[email protected]>2018-07-28 18:32:12 +0530
committerBrian Behlendorf <[email protected]>2018-08-01 15:00:02 -0700
commitfd7265c646f40e364396af5014bbb83e809e124a (patch)
tree4f4d7f280ce699992cbb0224a2951c2eb101d0d4 /module/zfs/zfs_ctldir.c
parent492f64e941e3d6b947d1cc387a1a380c0c738b09 (diff)
Fix deadlock between zfs umount & snapentry_expire
zfs umount -> zfsctl_destroy() takes the zfs_snapshot_lock as a writer and calls zfsctl_snapshot_unmount_cancel(), which waits for snapentry_expire() if present (when snap is automounted). This snapentry_expire() itself then waits for zfs_snapshot_lock as a reader, resulting in a deadlock. The fix is to only hold the zfs_snapshot_lock over the tree lookup and removal. After a successful lookup the lock can be dropped and zfs_snapentry_t will remain valid until the reference taken by the lookup is released. Reviewed-by: Brian Behlendorf <[email protected]> Signed-off-by: Rohan Puri <[email protected]> Closes #7751 Closes #7752
Diffstat (limited to 'module/zfs/zfs_ctldir.c')
-rw-r--r--module/zfs/zfs_ctldir.c11
1 files changed, 5 insertions, 6 deletions
diff --git a/module/zfs/zfs_ctldir.c b/module/zfs/zfs_ctldir.c
index baa72863e..8a35c9947 100644
--- a/module/zfs/zfs_ctldir.c
+++ b/module/zfs/zfs_ctldir.c
@@ -355,8 +355,6 @@ snapentry_expire(void *data)
static void
zfsctl_snapshot_unmount_cancel(zfs_snapentry_t *se)
{
- ASSERT(RW_LOCK_HELD(&zfs_snapshot_lock));
-
if (taskq_cancel_id(system_delay_taskq, se->se_taskqid) == 0) {
se->se_taskqid = TASKQID_INVALID;
zfsctl_snapshot_rele(se);
@@ -567,13 +565,14 @@ zfsctl_destroy(zfsvfs_t *zfsvfs)
uint64_t objsetid = dmu_objset_id(zfsvfs->z_os);
rw_enter(&zfs_snapshot_lock, RW_WRITER);
- if ((se = zfsctl_snapshot_find_by_objsetid(spa, objsetid))
- != NULL) {
- zfsctl_snapshot_unmount_cancel(se);
+ se = zfsctl_snapshot_find_by_objsetid(spa, objsetid);
+ if (se != NULL)
zfsctl_snapshot_remove(se);
+ rw_exit(&zfs_snapshot_lock);
+ if (se != NULL) {
+ zfsctl_snapshot_unmount_cancel(se);
zfsctl_snapshot_rele(se);
}
- rw_exit(&zfs_snapshot_lock);
} else if (zfsvfs->z_ctldir) {
iput(zfsvfs->z_ctldir);
zfsvfs->z_ctldir = NULL;