diff options
author | pjd <pjd@FreeBSD.org> | 2007-05-25 22:23:38 +0000 |
---|---|---|
committer | pjd <pjd@FreeBSD.org> | 2007-05-25 22:23:38 +0000 |
commit | f9fef47d890708fdf9c930956ec9957d1eb3431e (patch) | |
tree | 771377b46816c640072b1234d5d63b6c3b5f65bd /sys/kern/vfs_cache.c | |
parent | 29aee808dff1626ff8e4f4cf47d359c598b53403 (diff) | |
download | FreeBSD-src-f9fef47d890708fdf9c930956ec9957d1eb3431e.zip FreeBSD-src-f9fef47d890708fdf9c930956ec9957d1eb3431e.tar.gz |
To avoid a deadlock when handling .. directory during a lookup, we unlock
parent vnode and relock it after locking child vnode. The problem was that
we always relock it exclusively, even when it was share-locked.
Discussed with: jeff
Diffstat (limited to 'sys/kern/vfs_cache.c')
-rw-r--r-- | sys/kern/vfs_cache.c | 9 |
1 files changed, 6 insertions, 3 deletions
diff --git a/sys/kern/vfs_cache.c b/sys/kern/vfs_cache.c index 088425e..a174c45 100644 --- a/sys/kern/vfs_cache.c +++ b/sys/kern/vfs_cache.c @@ -315,7 +315,7 @@ cache_lookup(dvp, vpp, cnp) { struct namecache *ncp; u_int32_t hash; - int error; + int error, ltype; if (!doingcache) { cnp->cn_flags &= ~MAKEENTRY; @@ -421,13 +421,16 @@ success: CACHE_UNLOCK(); return (-1); } - if (cnp->cn_flags & ISDOTDOT) + ltype = 0; /* silence gcc warning */ + if (cnp->cn_flags & ISDOTDOT) { + ltype = VOP_ISLOCKED(dvp, cnp->cn_thread); VOP_UNLOCK(dvp, 0, cnp->cn_thread); + } VI_LOCK(*vpp); CACHE_UNLOCK(); error = vget(*vpp, cnp->cn_lkflags | LK_INTERLOCK, cnp->cn_thread); if (cnp->cn_flags & ISDOTDOT) - vn_lock(dvp, LK_EXCLUSIVE | LK_RETRY, cnp->cn_thread); + vn_lock(dvp, ltype | LK_RETRY, cnp->cn_thread); if (error) { *vpp = NULL; goto retry; |