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 <behlendorf1@llnl.gov> Signed-off-by: Chunwei Chen <tuxoko@gmail.com> Closes #3943 Closes #3969 Closes #4121
This commit is contained in:
parent
44f547af98
commit
3445a340d5
|
@ -214,6 +214,7 @@ zpl_xattr_list(struct dentry *dentry, char *buffer, size_t buffer_size)
|
||||||
|
|
||||||
crhold(cr);
|
crhold(cr);
|
||||||
cookie = spl_fstrans_mark();
|
cookie = spl_fstrans_mark();
|
||||||
|
rrm_enter_read(&(zsb)->z_teardown_lock, FTAG);
|
||||||
rw_enter(&zp->z_xattr_lock, RW_READER);
|
rw_enter(&zp->z_xattr_lock, RW_READER);
|
||||||
|
|
||||||
if (zsb->z_use_sa && zp->z_is_sa) {
|
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:
|
out:
|
||||||
|
|
||||||
rw_exit(&zp->z_xattr_lock);
|
rw_exit(&zp->z_xattr_lock);
|
||||||
|
rrm_exit(&(zsb)->z_teardown_lock, FTAG);
|
||||||
spl_fstrans_unmark(cookie);
|
spl_fstrans_unmark(cookie);
|
||||||
crfree(cr);
|
crfree(cr);
|
||||||
|
|
||||||
|
@ -339,15 +341,18 @@ static int
|
||||||
zpl_xattr_get(struct inode *ip, const char *name, void *value, size_t size)
|
zpl_xattr_get(struct inode *ip, const char *name, void *value, size_t size)
|
||||||
{
|
{
|
||||||
znode_t *zp = ITOZ(ip);
|
znode_t *zp = ITOZ(ip);
|
||||||
|
zfs_sb_t *zsb = ZTOZSB(zp);
|
||||||
cred_t *cr = CRED();
|
cred_t *cr = CRED();
|
||||||
fstrans_cookie_t cookie;
|
fstrans_cookie_t cookie;
|
||||||
int error;
|
int error;
|
||||||
|
|
||||||
crhold(cr);
|
crhold(cr);
|
||||||
cookie = spl_fstrans_mark();
|
cookie = spl_fstrans_mark();
|
||||||
|
rrm_enter_read(&(zsb)->z_teardown_lock, FTAG);
|
||||||
rw_enter(&zp->z_xattr_lock, RW_READER);
|
rw_enter(&zp->z_xattr_lock, RW_READER);
|
||||||
error = __zpl_xattr_get(ip, name, value, size, cr);
|
error = __zpl_xattr_get(ip, name, value, size, cr);
|
||||||
rw_exit(&zp->z_xattr_lock);
|
rw_exit(&zp->z_xattr_lock);
|
||||||
|
rrm_exit(&(zsb)->z_teardown_lock, FTAG);
|
||||||
spl_fstrans_unmark(cookie);
|
spl_fstrans_unmark(cookie);
|
||||||
crfree(cr);
|
crfree(cr);
|
||||||
|
|
||||||
|
@ -493,6 +498,7 @@ zpl_xattr_set(struct inode *ip, const char *name, const void *value,
|
||||||
|
|
||||||
crhold(cr);
|
crhold(cr);
|
||||||
cookie = spl_fstrans_mark();
|
cookie = spl_fstrans_mark();
|
||||||
|
rrm_enter_read(&(zsb)->z_teardown_lock, FTAG);
|
||||||
rw_enter(&ITOZ(ip)->z_xattr_lock, RW_WRITER);
|
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);
|
error = zpl_xattr_set_dir(ip, name, value, size, flags, cr);
|
||||||
out:
|
out:
|
||||||
rw_exit(&ITOZ(ip)->z_xattr_lock);
|
rw_exit(&ITOZ(ip)->z_xattr_lock);
|
||||||
|
rrm_exit(&(zsb)->z_teardown_lock, FTAG);
|
||||||
spl_fstrans_unmark(cookie);
|
spl_fstrans_unmark(cookie);
|
||||||
crfree(cr);
|
crfree(cr);
|
||||||
ASSERT3S(error, <=, 0);
|
ASSERT3S(error, <=, 0);
|
||||||
|
|
Loading…
Reference in New Issue