]> granicus.if.org Git - zfs/commitdiff
Fix deadlock between zfs umount & snapentry_expire
authorRohan Puri <rohan.puri15@gmail.com>
Sat, 28 Jul 2018 13:02:12 +0000 (18:32 +0530)
committerBrian Behlendorf <behlendorf1@llnl.gov>
Wed, 1 Aug 2018 22:00:02 +0000 (15:00 -0700)
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 <behlendorf1@llnl.gov>
Signed-off-by: Rohan Puri <rohan.puri15@gmail.com>
Closes #7751
Closes #7752

module/zfs/zfs_ctldir.c

index baa72863e68ef1cea97f93b655c449d13a05b39c..8a35c99471c57ef0fab7af3ab0167bf3a71622e1 100644 (file)
@@ -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;