From 94f2e6f24f393d773800d0f9c448969bc5b16cf0 Mon Sep 17 00:00:00 2001 From: Matthew Dillon Date: Thu, 11 Feb 2010 12:44:21 -0800 Subject: [PATCH] kernel NFS - Fix another deadlock in the readdirplus code * Add vn_islocked_unlock() and vn_islocked_relock() to help NFS temporarily unlock a possibly-locked directory vnode when trying to instantiate readdirplus children under that directory. * Fixes a deadlock in NFS. --- sys/kern/vfs_vnops.c | 39 +++++++++++++++++++++++++++++++++++++++ sys/sys/vnode.h | 2 ++ sys/vfs/nfs/nfs_vnops.c | 13 +++++++++++++ 3 files changed, 54 insertions(+) diff --git a/sys/kern/vfs_vnops.c b/sys/kern/vfs_vnops.c index 899eb71292..abe40630a4 100644 --- a/sys/kern/vfs_vnops.c +++ b/sys/kern/vfs_vnops.c @@ -1018,6 +1018,45 @@ vn_islocked(struct vnode *vp) } /* + * Return the lock status of a vnode and unlock the vnode + * if we owned the lock. This is not a boolean, if the + * caller cares what the lock status is the caller must + * check the various possible values. + * + * This only unlocks exclusive locks held by the caller, + * it will NOT unlock shared locks (there is no way to + * tell who the shared lock belongs to). + * + * MPSAFE + */ +int +vn_islocked_unlock(struct vnode *vp) +{ + int vpls; + + vpls = lockstatus(&vp->v_lock, curthread); + if (vpls == LK_EXCLUSIVE) + lockmgr(&vp->v_lock, LK_RELEASE); + return(vpls); +} + +/* + * Restore a vnode lock that we previously released via + * vn_islocked_unlock(). This is a NOP if we did not + * own the original lock. + * + * MPSAFE + */ +void +vn_islocked_relock(struct vnode *vp, int vpls) +{ + int error; + + if (vpls == LK_EXCLUSIVE) + error = lockmgr(&vp->v_lock, vpls); +} + +/* * MPSAFE */ static int diff --git a/sys/sys/vnode.h b/sys/sys/vnode.h index 45778870a8..52b4a517c3 100644 --- a/sys/sys/vnode.h +++ b/sys/sys/vnode.h @@ -525,6 +525,8 @@ int vn_close (struct vnode *vp, int flags); int vn_isdisk (struct vnode *vp, int *errp); int vn_lock (struct vnode *vp, int flags); int vn_islocked (struct vnode *vp); +int vn_islocked_unlock (struct vnode *vp); +void vn_islocked_relock (struct vnode *vp, int vpls); void vn_unlock (struct vnode *vp); #ifdef DEBUG_LOCKS int debug_vn_lock (struct vnode *vp, int flags, diff --git a/sys/vfs/nfs/nfs_vnops.c b/sys/vfs/nfs/nfs_vnops.c index 0cf7dea268..dba0935db7 100644 --- a/sys/vfs/nfs/nfs_vnops.c +++ b/sys/vfs/nfs/nfs_vnops.c @@ -2505,6 +2505,7 @@ nfs_readdirplusrpc_uio(struct vnode *vp, struct uio *uiop) u_quad_t fileno; int error = 0, tlen, more_dirs = 1, blksiz = 0, doit, bigenough = 1, i; int attrflag, fhsize; + int vpls; struct nchandle nch; struct nchandle dnch; struct nlcomponent nlc; @@ -2528,6 +2529,10 @@ nfs_readdirplusrpc_uio(struct vnode *vp, struct uio *uiop) * from the tree and cannot be used for upward traversals, and the * ncp may be unnamed. Note that other unrelated operations may * cause the ncp to be named at any time. + * + * We have to lock the ncp to prevent a lock order reversal when + * rdirplus does nlookups of the children, because the vnode is + * locked and has to stay that way. */ cache_fromdvp(vp, NULL, 0, &dnch); bzero(&nlc, sizeof(nlc)); @@ -2655,10 +2660,18 @@ nfs_readdirplusrpc_uio(struct vnode *vp, struct uio *uiop) nlc.nlc_namelen, nlc.nlc_namelen, nlc.nlc_nameptr); #endif + /* + * This is a bit hokey but there isn't + * much we can do about it. We can't + * hold the directory vp locked while + * doing lookups and gets. + */ + vpls = vn_islocked_unlock(vp); nch = cache_nlookup(&dnch, &nlc); cache_setunresolved(&nch); error = nfs_nget(vp->v_mount, fhp, fhsize, &np); + vn_islocked_relock(vp, vpls); if (error == 0) { newvp = NFSTOV(np); dpossav2 = info.dpos; -- 2.11.4.GIT