Avoid double-unlock or double unreference for ndp->ni_dvp when the vnode dp

lock upgrade right after the 'success' label fails.

In collaboration with:	pho
MFC after:	1 week
This commit is contained in:
Konstantin Belousov 2012-01-01 18:45:59 +00:00
parent f8f33f6ab6
commit cdb7a43117

View File

@ -508,12 +508,14 @@ lookup(struct nameidata *ndp)
int dvfslocked; /* VFS Giant state for parent */ int dvfslocked; /* VFS Giant state for parent */
int tvfslocked; int tvfslocked;
int lkflags_save; int lkflags_save;
int ni_dvp_unlocked;
/* /*
* Setup: break out flag bits into variables. * Setup: break out flag bits into variables.
*/ */
dvfslocked = (ndp->ni_cnd.cn_flags & GIANTHELD) != 0; dvfslocked = (ndp->ni_cnd.cn_flags & GIANTHELD) != 0;
vfslocked = 0; vfslocked = 0;
ni_dvp_unlocked = 0;
ndp->ni_cnd.cn_flags &= ~GIANTHELD; ndp->ni_cnd.cn_flags &= ~GIANTHELD;
wantparent = cnp->cn_flags & (LOCKPARENT | WANTPARENT); wantparent = cnp->cn_flags & (LOCKPARENT | WANTPARENT);
KASSERT(cnp->cn_nameiop == LOOKUP || wantparent, KASSERT(cnp->cn_nameiop == LOOKUP || wantparent,
@ -861,8 +863,10 @@ lookup(struct nameidata *ndp)
/* /*
* Symlink code always expects an unlocked dvp. * Symlink code always expects an unlocked dvp.
*/ */
if (ndp->ni_dvp != ndp->ni_vp) if (ndp->ni_dvp != ndp->ni_vp) {
VOP_UNLOCK(ndp->ni_dvp, 0); VOP_UNLOCK(ndp->ni_dvp, 0);
ni_dvp_unlocked = 1;
}
goto success; goto success;
} }
@ -909,14 +913,17 @@ lookup(struct nameidata *ndp)
VREF(ndp->ni_startdir); VREF(ndp->ni_startdir);
} }
if (!wantparent) { if (!wantparent) {
ni_dvp_unlocked = 2;
if (ndp->ni_dvp != dp) if (ndp->ni_dvp != dp)
vput(ndp->ni_dvp); vput(ndp->ni_dvp);
else else
vrele(ndp->ni_dvp); vrele(ndp->ni_dvp);
VFS_UNLOCK_GIANT(dvfslocked); VFS_UNLOCK_GIANT(dvfslocked);
dvfslocked = 0; dvfslocked = 0;
} else if ((cnp->cn_flags & LOCKPARENT) == 0 && ndp->ni_dvp != dp) } else if ((cnp->cn_flags & LOCKPARENT) == 0 && ndp->ni_dvp != dp) {
VOP_UNLOCK(ndp->ni_dvp, 0); VOP_UNLOCK(ndp->ni_dvp, 0);
ni_dvp_unlocked = 1;
}
if (cnp->cn_flags & AUDITVNODE1) if (cnp->cn_flags & AUDITVNODE1)
AUDIT_ARG_VNODE1(dp); AUDIT_ARG_VNODE1(dp);
@ -945,10 +952,12 @@ lookup(struct nameidata *ndp)
return (0); return (0);
bad2: bad2:
if (dp != ndp->ni_dvp) if (ni_dvp_unlocked != 2) {
vput(ndp->ni_dvp); if (dp != ndp->ni_dvp && !ni_dvp_unlocked)
else vput(ndp->ni_dvp);
vrele(ndp->ni_dvp); else
vrele(ndp->ni_dvp);
}
bad: bad:
if (!dpunlocked) if (!dpunlocked)
vput(dp); vput(dp);