From 6b32ef572f754efc3f9edb20d022450f8e6b02d9 Mon Sep 17 00:00:00 2001 From: Brian Behlendorf Date: Wed, 16 Dec 2015 14:17:49 -0800 Subject: [PATCH] Fix z_xattr_lock/z_teardown_lock lock inversion There exists a lock inversion between the z_xattr_lock and the z_teardown_lock. Detect this case and return EBUSY so zfs_resume_fs() will mark the inode stale and it can be safely revalidated on next access. * process-1 zpl_xattr_get -> Takes zp->z_xattr_lock __zpl_xattr_get zfs_lookup -> Takes zsb->z_teardown_lock in ZFS_ENTER macro * process-2 zfs_ioc_recv -> Takes zsb->z_teardown_lock in zfs_suspend_fs() zfs_resume_fs zfs_rezget -> Takes zp->z_xattr_lock Signed-off-by: Brian Behlendorf Issue #3969 --- module/zfs/zfs_znode.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/module/zfs/zfs_znode.c b/module/zfs/zfs_znode.c index bf360f1bc6..6fda78e5f4 100644 --- a/module/zfs/zfs_znode.c +++ b/module/zfs/zfs_znode.c @@ -1012,7 +1012,16 @@ zfs_rezget(znode_t *zp) } mutex_exit(&zp->z_acl_lock); - rw_enter(&zp->z_xattr_lock, RW_WRITER); + /* + * Lock inversion with zpl_xattr_get->__zpl_xattr_get->zfs_lookup + * between z_xattr_lock and z_teardown_lock. Detect this case and + * return EBUSY so zfs_resume_fs() will mark the inode stale and it + * will safely be revalidated on next access. + */ + err = rw_tryenter(&zp->z_xattr_lock, RW_WRITER); + if (!err) + return (SET_ERROR(EBUSY)); + if (zp->z_xattr_cached) { nvlist_free(zp->z_xattr_cached); zp->z_xattr_cached = NULL;