diff options
author | Brian Behlendorf <[email protected]> | 2015-12-21 09:27:24 -0800 |
---|---|---|
committer | Brian Behlendorf <[email protected]> | 2015-12-22 16:59:20 -0800 |
commit | 2ebc7b72b3989ac53c1753f79eaf71d95419c103 (patch) | |
tree | bbbe40243e34ff75c20edc451ce1c0f89f3f50d5 /module/zfs/zpl_xattr.c | |
parent | 228b461b564532123efbf3f6c595494c8cbe7793 (diff) |
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 <[email protected]>
Signed-off-by: Chunwei Chen <[email protected]>
Closes #3943
Closes #3969
Closes #4121
Diffstat (limited to 'module/zfs/zpl_xattr.c')
-rw-r--r-- | module/zfs/zpl_xattr.c | 7 |
1 files changed, 7 insertions, 0 deletions
diff --git a/module/zfs/zpl_xattr.c b/module/zfs/zpl_xattr.c index d224078ef..d9d067305 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); |