Jeremiah Lott wrote:
> We're seeing nfsclient deadlocks with what looks like lock order
> reversal after removing a "silly rename". It is fairly rare, but we've
> seen it happen a few times. I included relevant back traces from an
> occurrence. From what I can see, nfs_inactive() is called with the
> vnode locked. If there is a silly-rename, it will call vrele() on its
> parent directory, which can potentially try to lock the parent
> directory. Since this is the opposite order of the lock acquisition in
> lookup, it can deadlock. This happened in a FreeBSD7 build, but I
> looked through freebsd head and didn't see any change that addressed
> this. Anyone seen this before?
> 
> Jeremiah Lott
> Avere Systems
> 
Please try the attached patch (which is also at):
  http://people.freebsd.org/~rmacklem/oldsilly.patch
  http://people.freebsd.org/~rmacklem/newsilly.patch
(for the old and new clients in -current, respectively)

- I think oldsilly.patch should apply to the 7.n kernel
  sources, although you might have to do the edit by hand?

The patch is based on what jhb@ posted, with changes as recommended
by kib@.

Please let me know how testing goes with it, rick
ps: Kostik, could you please review this, thanks.

--- nfsclient/nfsnode.h.sav	2011-07-22 15:31:11.000000000 -0400
+++ nfsclient/nfsnode.h	2011-07-22 15:32:54.000000000 -0400
@@ -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);
--- nfsclient/nfs_node.c.sav	2011-07-22 15:33:04.000000000 -0400
+++ nfsclient/nfs_node.c	2011-07-22 16:31:45.000000000 -0400
@@ -47,6 +47,7 @@ __FBSDID("$FreeBSD: head/sys/nfsclient/n
 #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: head/sys/nfsclient/n
 
 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 *fh
 	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 *a
 		 */
 		(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;
--- fs/nfsclient/nfsnode.h.sav2	2011-07-22 15:42:14.000000000 -0400
+++ fs/nfsclient/nfsnode.h	2011-07-22 15:43:25.000000000 -0400
@@ -35,11 +35,14 @@
 #ifndef _NFSCLIENT_NFSNODE_H_
 #define	_NFSCLIENT_NFSNODE_H_
 
+#include <sys/_task.h>
+
 /*
  * Silly rename structure that hangs off the nfsnode until the name
  * can be removed by nfs_inactive()
  */
 struct sillyrename {
+	struct	task s_task;
 	struct	ucred *s_cred;
 	struct	vnode *s_dvp;
 	long	s_namlen;
--- fs/nfsclient/nfs_clnode.c.sav2	2011-07-22 15:43:40.000000000 -0400
+++ fs/nfsclient/nfs_clnode.c	2011-07-22 16:32:53.000000000 -0400
@@ -47,6 +47,7 @@ __FBSDID("$FreeBSD: head/sys/fs/nfsclien
 #include <sys/proc.h>
 #include <sys/socket.h>
 #include <sys/sysctl.h>
+#include <sys/taskqueue.h>
 #include <sys/vnode.h>
 
 #include <vm/uma.h>
@@ -65,6 +66,8 @@ MALLOC_DECLARE(M_NEWNFSREQ);
 
 uma_zone_t newnfsnode_zone;
 
+static void	nfs_freesillyrename(void *arg, __unused int pending);
+
 void
 ncl_nhinit(void)
 {
@@ -186,6 +189,20 @@ ncl_nget(struct mount *mntp, u_int8_t *f
 	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_NEWNFSREQ);
+}
+
 int
 ncl_inactive(struct vop_inactive_args *ap)
 {
@@ -220,8 +237,8 @@ ncl_inactive(struct vop_inactive_args *a
 		 */
 		ncl_removeit(sp, vp);
 		crfree(sp->s_cred);
-		vrele(sp->s_dvp);
-		FREE((caddr_t)sp, M_NEWNFSREQ);
+		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;
_______________________________________________
freebsd-net@freebsd.org mailing list
http://lists.freebsd.org/mailman/listinfo/freebsd-net
To unsubscribe, send any mail to "freebsd-net-unsubscr...@freebsd.org"

Reply via email to