nfs_lookup: vn_lock the vnode returned by cache_lookup_raw

before feeding it to VOP_GETATTR.  it's necessary because the vnode might
be being cleaned by getcleanvnode.

it's an instance of more general races between vnode reclaim and
unlocked VOPs.  however, this one happens somewhat often because it can be
triggered by getnewvnode rather than revoke.
This commit is contained in:
yamt 2009-05-10 05:18:26 +00:00
parent 84e868b269
commit 8276e4de1a
1 changed files with 16 additions and 17 deletions

View File

@ -1,4 +1,4 @@
/* $NetBSD: nfs_vnops.c,v 1.277 2009/05/10 03:51:43 yamt Exp $ */
/* $NetBSD: nfs_vnops.c,v 1.278 2009/05/10 05:18:26 yamt Exp $ */
/*
* Copyright (c) 1989, 1993
@ -39,7 +39,7 @@
*/
#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: nfs_vnops.c,v 1.277 2009/05/10 03:51:43 yamt Exp $");
__KERNEL_RCSID(0, "$NetBSD: nfs_vnops.c,v 1.278 2009/05/10 05:18:26 yamt Exp $");
#ifdef _KERNEL_OPT
#include "opt_nfs.h"
@ -855,31 +855,30 @@ nfs_lookup(void *v)
* investigate the vnode returned by cache_lookup_raw.
* if it isn't appropriate, do an rpc.
*/
newvp = *vpp;
if ((flags & ISDOTDOT) != 0) {
VOP_UNLOCK(dvp, 0);
}
error = vn_lock(newvp, LK_EXCLUSIVE);
if ((flags & ISDOTDOT) != 0) {
vn_lock(dvp, LK_EXCLUSIVE | LK_RETRY);
}
if (error != 0) {
/* newvp has been reclaimed. */
vrele(newvp);
*vpp = NULLVP;
goto dorpc;
}
if (!VOP_GETATTR(newvp, &vattr, cnp->cn_cred)
&& vattr.va_ctime.tv_sec == VTONFS(newvp)->n_ctime) {
nfsstats.lookupcache_hits++;
if ((flags & ISDOTDOT) != 0) {
VOP_UNLOCK(dvp, 0);
}
error = vn_lock(newvp, LK_EXCLUSIVE);
if ((flags & ISDOTDOT) != 0) {
vn_lock(dvp, LK_EXCLUSIVE | LK_RETRY);
}
if (error != 0) {
/* newvp has been revoked. */
vrele(newvp);
*vpp = NULLVP;
goto dorpc;
}
if (cnp->cn_nameiop != LOOKUP && (flags & ISLASTCN))
cnp->cn_flags |= SAVENAME;
KASSERT(newvp->v_type != VNON);
return (0);
}
cache_purge1(newvp, NULL, PURGE_PARENTS);
vrele(newvp);
vput(newvp);
*vpp = NULLVP;
}
dorpc: