Check for RW_WRITE_HELD in zfs_inactive

Before read locking z_teardown_inactive_lock, we need to check if we have
already had write lock on it. Otherwise, we would deadlock on ourself when
doing rollback:

zfs_ioc_rollback
->zfs_suspend_fs (z_teardown_inactive_lock, RW_WRITER)
->zfs_resume_fs->zfs_rezget->zfs_iput_async->iput-> ...
  ->zfs_inactive (z_teardown_inactive_lock, RW_READER)

Signed-off-by: Chunwei Chen <tuxoko@gmail.com>
Signed-off-by: Brian Behlendorf <behlendorf1@llnl.gov>
Closes #2869
This commit is contained in:
tuxoko 2015-09-01 20:02:48 +08:00 committed by Brian Behlendorf
parent 97771edaca
commit cafbd2aca3

View File

@ -4096,10 +4096,16 @@ zfs_inactive(struct inode *ip)
znode_t *zp = ITOZ(ip);
zfs_sb_t *zsb = ITOZSB(ip);
int error;
int need_unlock = 0;
rw_enter(&zsb->z_teardown_inactive_lock, RW_READER);
/* Only read lock if we haven't already write locked, e.g. rollback */
if (!RW_WRITE_HELD(&zsb->z_teardown_inactive_lock)) {
need_unlock = 1;
rw_enter(&zsb->z_teardown_inactive_lock, RW_READER);
}
if (zp->z_sa_hdl == NULL) {
rw_exit(&zsb->z_teardown_inactive_lock);
if (need_unlock)
rw_exit(&zsb->z_teardown_inactive_lock);
return;
}
@ -4122,7 +4128,8 @@ zfs_inactive(struct inode *ip)
}
zfs_zinactive(zp);
rw_exit(&zsb->z_teardown_inactive_lock);
if (need_unlock)
rw_exit(&zsb->z_teardown_inactive_lock);
}
EXPORT_SYMBOL(zfs_inactive);