From 3445a340d54f4209eb13cf9a59b01ef571006fc4 Mon Sep 17 00:00:00 2001 From: Brian Behlendorf Date: Mon, 21 Dec 2015 09:27:24 -0800 Subject: [PATCH] Fix z_xattr_lock/z_teardown_lock inversion There exists a lock inversion between the z_xattr_lock and the z_teardown_lock. Resolve this by taking the z_teardown_lock in all registered xattr callbacks prior to taking the z_xattr_lock. This ensures the locks are always taken is the same order thus preventing a deadlock. Note the z_teardown_lock is taken again in zfs_lookup() and this is safe because the z_teardown lock is a re-entrant read reader/writer lock. * 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 Signed-off-by: Chunwei Chen Closes #3943 Closes #3969 Closes #4121 --- module/zfs/zpl_xattr.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/module/zfs/zpl_xattr.c b/module/zfs/zpl_xattr.c index d224078efc..d9d0673051 100644 --- a/module/zfs/zpl_xattr.c +++ b/module/zfs/zpl_xattr.c @@ -214,6 +214,7 @@ zpl_xattr_list(struct dentry *dentry, char *buffer, size_t buffer_size) crhold(cr); cookie = spl_fstrans_mark(); + rrm_enter_read(&(zsb)->z_teardown_lock, FTAG); rw_enter(&zp->z_xattr_lock, RW_READER); if (zsb->z_use_sa && zp->z_is_sa) { @@ -230,6 +231,7 @@ zpl_xattr_list(struct dentry *dentry, char *buffer, size_t buffer_size) out: rw_exit(&zp->z_xattr_lock); + rrm_exit(&(zsb)->z_teardown_lock, FTAG); spl_fstrans_unmark(cookie); crfree(cr); @@ -339,15 +341,18 @@ static int zpl_xattr_get(struct inode *ip, const char *name, void *value, size_t size) { znode_t *zp = ITOZ(ip); + zfs_sb_t *zsb = ZTOZSB(zp); cred_t *cr = CRED(); fstrans_cookie_t cookie; int error; crhold(cr); cookie = spl_fstrans_mark(); + rrm_enter_read(&(zsb)->z_teardown_lock, FTAG); rw_enter(&zp->z_xattr_lock, RW_READER); error = __zpl_xattr_get(ip, name, value, size, cr); rw_exit(&zp->z_xattr_lock); + rrm_exit(&(zsb)->z_teardown_lock, FTAG); spl_fstrans_unmark(cookie); crfree(cr); @@ -493,6 +498,7 @@ zpl_xattr_set(struct inode *ip, const char *name, const void *value, crhold(cr); cookie = spl_fstrans_mark(); + rrm_enter_read(&(zsb)->z_teardown_lock, FTAG); rw_enter(&ITOZ(ip)->z_xattr_lock, RW_WRITER); /* @@ -530,6 +536,7 @@ zpl_xattr_set(struct inode *ip, const char *name, const void *value, error = zpl_xattr_set_dir(ip, name, value, size, flags, cr); out: rw_exit(&ITOZ(ip)->z_xattr_lock); + rrm_exit(&(zsb)->z_teardown_lock, FTAG); spl_fstrans_unmark(cookie); crfree(cr); ASSERT3S(error, <=, 0);