From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: Rohan Puri Date: Sat, 28 Jul 2018 18:32:12 +0530 Subject: [PATCH] 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 Signed-off-by: Rohan Puri Closes #7751 Closes #7752 (Cherry-picked from fd7265c646f40e364396af5014bbb83e809e124a) Signed-off-by: Stoiko Ivanov Signed-off-by: Stoiko Ivanov --- module/zfs/zfs_ctldir.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/module/zfs/zfs_ctldir.c b/module/zfs/zfs_ctldir.c index bf5a1d059..2964b65a6 100644 --- a/module/zfs/zfs_ctldir.c +++ b/module/zfs/zfs_ctldir.c @@ -358,8 +358,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); @@ -570,13 +568,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;