diff options
author | pjd <pjd@FreeBSD.org> | 2009-09-24 15:49:15 +0000 |
---|---|---|
committer | pjd <pjd@FreeBSD.org> | 2009-09-24 15:49:15 +0000 |
commit | 534a0875d3f4fbdd8697194f7919403b3e61fb34 (patch) | |
tree | 5fb2a3298c141850549c51b8288e6e87624140ec /sys/cddl/contrib | |
parent | 9485b3f20f55236d1fb8b9e6ac48a4cd0ae9c4e2 (diff) | |
download | FreeBSD-src-534a0875d3f4fbdd8697194f7919403b3e61fb34.zip FreeBSD-src-534a0875d3f4fbdd8697194f7919403b3e61fb34.tar.gz |
Close race in zfs_zget(). We have to increase usecount first and then
check for VI_DOOMED flag. Before this change vnode could be reclaimed
between checking for the flag and increasing usecount.
MFC after: 3 days
Diffstat (limited to 'sys/cddl/contrib')
-rw-r--r-- | sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_znode.c | 28 |
1 files changed, 19 insertions, 9 deletions
diff --git a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_znode.c b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_znode.c index 082198f..c43a850 100644 --- a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_znode.c +++ b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_znode.c @@ -890,17 +890,25 @@ again: if (zp->z_unlinked) { err = ENOENT; } else { - if ((vp = ZTOV(zp)) != NULL) { - VI_LOCK(vp); + int dying = 0; + + vp = ZTOV(zp); + if (vp == NULL) + dying = 1; + else { + VN_HOLD(vp); if ((vp->v_iflag & VI_DOOMED) != 0) { - VI_UNLOCK(vp); - vp = NULL; - } else - VI_UNLOCK(vp); + dying = 1; + /* + * Don't VN_RELE() vnode here, because + * it can call vn_lock() which creates + * LOR between vnode lock and znode + * lock. We will VN_RELE() the vnode + * after droping znode lock. + */ + } } - if (vp != NULL) - VN_HOLD(vp); - else { + if (dying) { if (first) { ZFS_LOG(1, "dying znode detected (zp=%p)", zp); first = 0; @@ -912,6 +920,8 @@ again: dmu_buf_rele(db, NULL); mutex_exit(&zp->z_lock); ZFS_OBJ_HOLD_EXIT(zfsvfs, obj_num); + if (vp != NULL) + VN_RELE(vp); tsleep(zp, 0, "zcollide", 1); goto again; } |