Fix some locking cases where we ask for exclusively locked vnode, but we get

shared locked vnode in instead when vfs.lookup_shared is set to 1.

Discussed with:	kib, kris
Tested by:	kris
Approved by:	re (kensmith)
This commit is contained in:
Pawel Jakub Dawidek 2007-09-21 10:16:56 +00:00
parent e84091cb52
commit b4d7e2983c
2 changed files with 25 additions and 4 deletions

View File

@ -314,6 +314,7 @@ cache_lookup(dvp, vpp, cnp)
struct componentname *cnp;
{
struct namecache *ncp;
struct thread *td;
u_int32_t hash;
int error, ltype;
@ -321,6 +322,7 @@ cache_lookup(dvp, vpp, cnp)
cnp->cn_flags &= ~MAKEENTRY;
return (0);
}
td = cnp->cn_thread;
retry:
CACHE_LOCK();
numcalls++;
@ -419,18 +421,29 @@ cache_lookup(dvp, vpp, cnp)
if (dvp == *vpp) { /* lookup on "." */
VREF(*vpp);
CACHE_UNLOCK();
/*
* When we lookup "." we still can be asked to lock it
* differently...
*/
ltype = cnp->cn_lkflags & (LK_SHARED | LK_EXCLUSIVE);
if (ltype == VOP_ISLOCKED(*vpp, td))
return (-1);
else if (ltype == LK_EXCLUSIVE)
vn_lock(*vpp, LK_UPGRADE | LK_RETRY, td);
return (-1);
}
ltype = 0; /* silence gcc warning */
if (cnp->cn_flags & ISDOTDOT) {
ltype = VOP_ISLOCKED(dvp, cnp->cn_thread);
VOP_UNLOCK(dvp, 0, cnp->cn_thread);
ltype = VOP_ISLOCKED(dvp, td);
VOP_UNLOCK(dvp, 0, td);
}
VI_LOCK(*vpp);
CACHE_UNLOCK();
error = vget(*vpp, cnp->cn_lkflags | LK_INTERLOCK, cnp->cn_thread);
error = vget(*vpp, cnp->cn_lkflags | LK_INTERLOCK, td);
if (cnp->cn_flags & ISDOTDOT)
vn_lock(dvp, ltype | LK_RETRY, cnp->cn_thread);
vn_lock(dvp, ltype | LK_RETRY, td);
if ((cnp->cn_flags & ISLASTCN) && (cnp->cn_lkflags & LK_EXCLUSIVE))
ASSERT_VOP_ELOCKED(*vpp, "cache_lookup");
if (error) {
*vpp = NULL;
goto retry;

View File

@ -772,6 +772,14 @@ lookup(struct nameidata *ndp)
if ((cnp->cn_flags & LOCKLEAF) == 0)
VOP_UNLOCK(dp, 0, td);
success:
/*
* Because of lookup_shared we may have the vnode shared locked, but
* the caller may want it to be exclusively locked.
*/
if ((cnp->cn_flags & (ISLASTCN | LOCKSHARED | LOCKLEAF)) ==
(ISLASTCN | LOCKLEAF) && VOP_ISLOCKED(dp, td) != LK_EXCLUSIVE) {
vn_lock(dp, LK_UPGRADE | LK_RETRY, td);
}
if (vfslocked && dvfslocked)
VFS_UNLOCK_GIANT(dvfslocked); /* Only need one */
if (vfslocked || dvfslocked)