diff options
author | rmacklem <rmacklem@FreeBSD.org> | 2011-08-02 11:24:42 +0000 |
---|---|---|
committer | rmacklem <rmacklem@FreeBSD.org> | 2011-08-02 11:24:42 +0000 |
commit | beba62c91b149d28b03b929e9f99d3733ade6d65 (patch) | |
tree | 1cd03efe7ed3f74abdb27e0f6c05d55a63b8e3e4 | |
parent | 863be0d261c477fbf2022f106faf534612d6293a (diff) | |
download | FreeBSD-src-beba62c91b149d28b03b929e9f99d3733ade6d65.zip FreeBSD-src-beba62c91b149d28b03b929e9f99d3733ade6d65.tar.gz |
Fix a LOR in the NFS client which could cause a deadlock.
This was reported to the mailing list freebsd-net@freebsd.org
on July 21, 2011 under the subject "LOR with nfsclient sillyrename".
The LOR occurred when nfs_inactive() called vrele(sp->s_dvp)
while holding the vnode lock on the file in s_dvp. This patch
modifies the client so that it performs the vrele(sp->s_dvp)
as a separate task to avoid the LOR. This fix was discussed
with jhb@ and kib@, who both proposed variations of it.
Tested by: pho, jlott at averesystems.com
Submitted by: jhb (earlier version)
Reviewed by: kib
Approved by: re (kib)
MFC after: 2 weeks
-rw-r--r-- | sys/nfsclient/nfs_node.c | 21 | ||||
-rw-r--r-- | sys/nfsclient/nfsnode.h | 2 |
2 files changed, 21 insertions, 2 deletions
diff --git a/sys/nfsclient/nfs_node.c b/sys/nfsclient/nfs_node.c index 5b43b3d..afe3341 100644 --- a/sys/nfsclient/nfs_node.c +++ b/sys/nfsclient/nfs_node.c @@ -47,6 +47,7 @@ __FBSDID("$FreeBSD$"); #include <sys/proc.h> #include <sys/socket.h> #include <sys/sysctl.h> +#include <sys/taskqueue.h> #include <sys/vnode.h> #include <vm/uma.h> @@ -59,6 +60,8 @@ __FBSDID("$FreeBSD$"); static uma_zone_t nfsnode_zone; +static void nfs_freesillyrename(void *arg, __unused int pending); + #define TRUE 1 #define FALSE 0 @@ -185,6 +188,20 @@ nfs_nget(struct mount *mntp, nfsfh_t *fhp, int fhsize, struct nfsnode **npp, int return (0); } +/* + * Do the vrele(sp->s_dvp) as a separate task in order to avoid a + * deadlock because of a LOR when vrele() locks the directory vnode. + */ +static void +nfs_freesillyrename(void *arg, __unused int pending) +{ + struct sillyrename *sp; + + sp = arg; + vrele(sp->s_dvp); + free(sp, M_NFSREQ); +} + int nfs_inactive(struct vop_inactive_args *ap) { @@ -207,8 +224,8 @@ nfs_inactive(struct vop_inactive_args *ap) */ (sp->s_removeit)(sp); crfree(sp->s_cred); - vrele(sp->s_dvp); - free((caddr_t)sp, M_NFSREQ); + TASK_INIT(&sp->s_task, 0, nfs_freesillyrename, sp); + taskqueue_enqueue(taskqueue_thread, &sp->s_task); mtx_lock(&np->n_mtx); } np->n_flag &= NMODIFIED; diff --git a/sys/nfsclient/nfsnode.h b/sys/nfsclient/nfsnode.h index 19c1c47..8e35fdd 100644 --- a/sys/nfsclient/nfsnode.h +++ b/sys/nfsclient/nfsnode.h @@ -36,6 +36,7 @@ #ifndef _NFSCLIENT_NFSNODE_H_ #define _NFSCLIENT_NFSNODE_H_ +#include <sys/_task.h> #if !defined(_NFSCLIENT_NFS_H_) && !defined(_KERNEL) #include <nfs/nfs.h> #endif @@ -45,6 +46,7 @@ * can be removed by nfs_inactive() */ struct sillyrename { + struct task s_task; struct ucred *s_cred; struct vnode *s_dvp; int (*s_removeit)(struct sillyrename *sp); |