Module Name:    src
Committed By:   manu
Date:           Wed Jul 15 03:28:55 UTC 2015

Modified Files:
        src/sys/nfs: nfs_bio.c nfs_clntsocket.c nfs_iod.c nfs_socket.c
            nfs_var.h nfs_vfsops.c nfsmount.h

Log Message:
Fix soft NFS force unmount

For many reasons, forcibly unmounting a soft NFS mount could hang forever.
Here are the fixes:
- Introduce decents timeouts in operation that awaited NFS server reply.
- On timeout, fails operations on soft mounts with EIO.
- Introduce NFSMNT_DISMNTFORCE to let the filesystem know that a
  force unmount is ongoing. This causes timeouts to be reduced and
  prevents the NFS client to attempt reconnecting to the NFS server.

Also fix a race condition where some asynchronous I/O could reference
destroyed mount structures. We fix this by awaiting asynchronous I/O
to drain before proceeding.

Reviewed by Chuck Silvers.


To generate a diff of this commit:
cvs rdiff -u -r1.190 -r1.191 src/sys/nfs/nfs_bio.c
cvs rdiff -u -r1.2 -r1.3 src/sys/nfs/nfs_clntsocket.c
cvs rdiff -u -r1.6 -r1.7 src/sys/nfs/nfs_iod.c
cvs rdiff -u -r1.196 -r1.197 src/sys/nfs/nfs_socket.c
cvs rdiff -u -r1.93 -r1.94 src/sys/nfs/nfs_var.h
cvs rdiff -u -r1.229 -r1.230 src/sys/nfs/nfs_vfsops.c
cvs rdiff -u -r1.52 -r1.53 src/sys/nfs/nfsmount.h

Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.

Modified files:

Index: src/sys/nfs/nfs_bio.c
diff -u src/sys/nfs/nfs_bio.c:1.190 src/sys/nfs/nfs_bio.c:1.191
--- src/sys/nfs/nfs_bio.c:1.190	Fri Sep  5 05:34:57 2014
+++ src/sys/nfs/nfs_bio.c	Wed Jul 15 03:28:55 2015
@@ -1,4 +1,4 @@
-/*	$NetBSD: nfs_bio.c,v 1.190 2014/09/05 05:34:57 matt Exp $	*/
+/*	$NetBSD: nfs_bio.c,v 1.191 2015/07/15 03:28:55 manu Exp $	*/
 
 /*
  * Copyright (c) 1989, 1993
@@ -35,7 +35,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: nfs_bio.c,v 1.190 2014/09/05 05:34:57 matt Exp $");
+__KERNEL_RCSID(0, "$NetBSD: nfs_bio.c,v 1.191 2015/07/15 03:28:55 manu Exp $");
 
 #ifdef _KERNEL_OPT
 #include "opt_nfs.h"
@@ -624,7 +624,10 @@ nfs_vinvalbuf(struct vnode *vp, int flag
 		slptimeo = 2 * hz;
 	} else {
 		catch_p = false;
-		slptimeo = 0;
+		if (nmp->nm_flag & NFSMNT_SOFT)
+			slptimeo = nmp->nm_retry * nmp->nm_timeo;
+		else
+			slptimeo = 0;
 	}
 	/*
 	 * First wait for any other process doing a flush to complete.
@@ -743,6 +746,13 @@ nfs_asyncio(struct buf *bp)
 		return (EIO);
 
 	nmp = VFSTONFS(bp->b_vp->v_mount);
+
+	if (nmp->nm_flag & NFSMNT_SOFT)
+		slptimeo = nmp->nm_retry * nmp->nm_timeo;
+
+	if (nmp->nm_iflag & NFSMNT_DISMNTFORCE)
+		slptimeo = hz;
+
 again:
 	if (nmp->nm_flag & NFSMNT_INT)
 		catch_p = true;
@@ -804,6 +814,13 @@ again:
 				    &nmp->nm_lock, slptimeo);
 			}
 			if (error) {
+				if (error == EWOULDBLOCK &&
+				    nmp->nm_flag & NFSMNT_SOFT) {
+					mutex_exit(&nmp->nm_lock);
+					bp->b_error = EIO;
+					return (EIO);
+				}
+
 				if (nfs_sigintr(nmp, NULL, curlwp)) {
 					mutex_exit(&nmp->nm_lock);
 					return (EINTR);

Index: src/sys/nfs/nfs_clntsocket.c
diff -u src/sys/nfs/nfs_clntsocket.c:1.2 src/sys/nfs/nfs_clntsocket.c:1.3
--- src/sys/nfs/nfs_clntsocket.c:1.2	Fri Sep  5 05:34:57 2014
+++ src/sys/nfs/nfs_clntsocket.c	Wed Jul 15 03:28:55 2015
@@ -1,4 +1,4 @@
-/*	$NetBSD: nfs_clntsocket.c,v 1.2 2014/09/05 05:34:57 matt Exp $	*/
+/*	$NetBSD: nfs_clntsocket.c,v 1.3 2015/07/15 03:28:55 manu Exp $	*/
 
 /*
  * Copyright (c) 1989, 1991, 1993, 1995
@@ -39,7 +39,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: nfs_clntsocket.c,v 1.2 2014/09/05 05:34:57 matt Exp $");
+__KERNEL_RCSID(0, "$NetBSD: nfs_clntsocket.c,v 1.3 2015/07/15 03:28:55 manu Exp $");
 
 #ifdef _KERNEL_OPT
 #include "opt_nfs.h"
@@ -967,6 +967,12 @@ nfs_sndlock(struct nfsmount *nmp, struct
 	bool catch_p = false;
 	int error = 0;
 
+	if (nmp->nm_flag & NFSMNT_SOFT)
+		timeo = nmp->nm_retry * nmp->nm_timeo;
+
+	if (nmp->nm_iflag & NFSMNT_DISMNTFORCE)
+		timeo = hz;
+
 	if (rep) {
 		l = rep->r_lwp;
 		if (rep->r_nmp->nm_flag & NFSMNT_INT)
@@ -980,9 +986,20 @@ nfs_sndlock(struct nfsmount *nmp, struct
 			goto quit;
 		}
 		if (catch_p) {
-			cv_timedwait_sig(&nmp->nm_sndcv, &nmp->nm_lock, timeo);
+			error = cv_timedwait_sig(&nmp->nm_sndcv,
+						 &nmp->nm_lock, timeo);
 		} else {
-			cv_timedwait(&nmp->nm_sndcv, &nmp->nm_lock, timeo);
+			error = cv_timedwait(&nmp->nm_sndcv,
+					     &nmp->nm_lock, timeo);
+		}
+
+		if (error) {
+			if ((error == EWOULDBLOCK) &&
+			    (nmp->nm_flag & NFSMNT_SOFT)) {
+				error = EIO;
+				goto quit;
+			}
+			error = 0;
 		}
 		if (catch_p) {
 			catch_p = false;

Index: src/sys/nfs/nfs_iod.c
diff -u src/sys/nfs/nfs_iod.c:1.6 src/sys/nfs/nfs_iod.c:1.7
--- src/sys/nfs/nfs_iod.c:1.6	Fri Oct 25 16:01:56 2013
+++ src/sys/nfs/nfs_iod.c	Wed Jul 15 03:28:55 2015
@@ -1,4 +1,4 @@
-/*	$NetBSD: nfs_iod.c,v 1.6 2013/10/25 16:01:56 martin Exp $	*/
+/*	$NetBSD: nfs_iod.c,v 1.7 2015/07/15 03:28:55 manu Exp $	*/
 
 /*
  * Copyright (c) 1989, 1993
@@ -35,7 +35,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: nfs_iod.c,v 1.6 2013/10/25 16:01:56 martin Exp $");
+__KERNEL_RCSID(0, "$NetBSD: nfs_iod.c,v 1.7 2015/07/15 03:28:55 manu Exp $");
 
 #include <sys/param.h>
 #include <sys/systm.h>
@@ -202,6 +202,22 @@ nfs_iodfini(void)
 }
 
 int
+nfs_iodbusy(struct nfsmount *nmp)
+{
+	struct nfs_iod *iod;
+	int ret = 0;
+
+	mutex_enter(&nfs_iodlist_lock);
+	LIST_FOREACH(iod, &nfs_iodlist_all, nid_all) {
+		if (iod->nid_mount == nmp)
+			ret++;
+	}
+	mutex_exit(&nfs_iodlist_lock);
+
+	return ret;
+}
+
+int
 nfs_set_niothreads(int newval)
 {
 	struct nfs_iod *nid;

Index: src/sys/nfs/nfs_socket.c
diff -u src/sys/nfs/nfs_socket.c:1.196 src/sys/nfs/nfs_socket.c:1.197
--- src/sys/nfs/nfs_socket.c:1.196	Sat May  9 15:22:47 2015
+++ src/sys/nfs/nfs_socket.c	Wed Jul 15 03:28:55 2015
@@ -1,4 +1,4 @@
-/*	$NetBSD: nfs_socket.c,v 1.196 2015/05/09 15:22:47 rtr Exp $	*/
+/*	$NetBSD: nfs_socket.c,v 1.197 2015/07/15 03:28:55 manu Exp $	*/
 
 /*
  * Copyright (c) 1989, 1991, 1993, 1995
@@ -39,7 +39,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: nfs_socket.c,v 1.196 2015/05/09 15:22:47 rtr Exp $");
+__KERNEL_RCSID(0, "$NetBSD: nfs_socket.c,v 1.197 2015/07/15 03:28:55 manu Exp $");
 
 #ifdef _KERNEL_OPT
 #include "opt_nfs.h"
@@ -66,6 +66,7 @@ __KERNEL_RCSID(0, "$NetBSD: nfs_socket.c
 #include <sys/signal.h>
 #include <sys/signalvar.h>
 #include <sys/kauth.h>
+#include <sys/time.h>
 
 #include <netinet/in.h>
 #include <netinet/tcp.h>
@@ -349,11 +350,33 @@ nfs_reconnect(struct nfsreq *rep)
 	struct nfsreq *rp;
 	struct nfsmount *nmp = rep->r_nmp;
 	int error;
+	time_t before_ts;
 
 	nfs_disconnect(nmp);
+
+	/*
+	 * Force unmount: do not try to reconnect
+	 */
+	if (nmp->nm_iflag & NFSMNT_DISMNTFORCE)
+		return EIO;
+
+	before_ts = time_uptime;
 	while ((error = nfs_connect(nmp, rep, &lwp0)) != 0) {
 		if (error == EINTR || error == ERESTART)
 			return (EINTR);
+
+		if (rep->r_flags & R_SOFTTERM)
+			return (EIO);
+
+		/*
+		 * Soft mount can fail here, but not too fast: 
+		 * we want to make sure we at least honoured 
+		 * NFS timeout.
+		 */
+		if ((nmp->nm_flag & NFSMNT_SOFT) &&
+		    (time_uptime - before_ts > nmp->nm_timeo / NFS_HZ))
+			return (EIO);
+
 		kpause("nfscn2", false, hz, NULL);
 	}
 
@@ -886,6 +909,12 @@ nfs_rcvlock(struct nfsmount *nmp, struct
 
 	KASSERT(nmp == rep->r_nmp);
 
+	if (nmp->nm_flag & NFSMNT_SOFT)
+		slptimeo = nmp->nm_retry * nmp->nm_timeo;
+
+	if (nmp->nm_iflag & NFSMNT_DISMNTFORCE)
+		slptimeo = hz;
+
 	catch_p = (nmp->nm_flag & NFSMNT_INT) != 0;
 	mutex_enter(&nmp->nm_lock);
 	while (/* CONSTCOND */ true) {
@@ -914,12 +943,20 @@ nfs_rcvlock(struct nfsmount *nmp, struct
 			break;
 		}
 		if (catch_p) {
-			cv_timedwait_sig(&nmp->nm_rcvcv, &nmp->nm_lock,
+			error = cv_timedwait_sig(&nmp->nm_rcvcv, &nmp->nm_lock,
 			    slptimeo);
 		} else {
-			cv_timedwait(&nmp->nm_rcvcv, &nmp->nm_lock,
+			error = cv_timedwait(&nmp->nm_rcvcv, &nmp->nm_lock,
 			    slptimeo);
 		}
+		if (error) {
+			if ((error == EWOULDBLOCK) &&
+			    (nmp->nm_flag & NFSMNT_SOFT)) {
+				error = EIO;
+				break;
+			}
+			error = 0;
+		}
 		if (catch_p) {
 			catch_p = false;
 			slptimeo = 2 * hz;

Index: src/sys/nfs/nfs_var.h
diff -u src/sys/nfs/nfs_var.h:1.93 src/sys/nfs/nfs_var.h:1.94
--- src/sys/nfs/nfs_var.h:1.93	Mon Jun 22 10:35:00 2015
+++ src/sys/nfs/nfs_var.h	Wed Jul 15 03:28:55 2015
@@ -1,4 +1,4 @@
-/*	$NetBSD: nfs_var.h,v 1.93 2015/06/22 10:35:00 mrg Exp $	*/
+/*	$NetBSD: nfs_var.h,v 1.94 2015/07/15 03:28:55 manu Exp $	*/
 
 /*-
  * Copyright (c) 1996 The NetBSD Foundation, Inc.
@@ -329,6 +329,7 @@ void nfsrv_init(int);
 void nfsrv_fini(void);
 void nfs_iodinit(void);
 void nfs_iodfini(void);
+int nfs_iodbusy(struct nfsmount *);
 int nfs_set_niothreads(int);
 int nfs_getauth(struct nfsmount *, struct nfsreq *, kauth_cred_t, char **,
 	int *, char *, int *, NFSKERBKEY_T);

Index: src/sys/nfs/nfs_vfsops.c
diff -u src/sys/nfs/nfs_vfsops.c:1.229 src/sys/nfs/nfs_vfsops.c:1.230
--- src/sys/nfs/nfs_vfsops.c:1.229	Fri May 30 08:47:45 2014
+++ src/sys/nfs/nfs_vfsops.c	Wed Jul 15 03:28:55 2015
@@ -1,4 +1,4 @@
-/*	$NetBSD: nfs_vfsops.c,v 1.229 2014/05/30 08:47:45 hannken Exp $	*/
+/*	$NetBSD: nfs_vfsops.c,v 1.230 2015/07/15 03:28:55 manu Exp $	*/
 
 /*
  * Copyright (c) 1989, 1993, 1995
@@ -35,7 +35,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: nfs_vfsops.c,v 1.229 2014/05/30 08:47:45 hannken Exp $");
+__KERNEL_RCSID(0, "$NetBSD: nfs_vfsops.c,v 1.230 2015/07/15 03:28:55 manu Exp $");
 
 #if defined(_KERNEL_OPT)
 #include "opt_nfs.h"
@@ -841,13 +841,18 @@ bad:
 int
 nfs_unmount(struct mount *mp, int mntflags)
 {
-	struct nfsmount *nmp;
+	struct nfsmount *nmp = VFSTONFS(mp);
 	struct vnode *vp;
 	int error, flags = 0;
 
-	if (mntflags & MNT_FORCE)
+	if (mntflags & MNT_FORCE) {
+		mutex_enter(&nmp->nm_lock);
 		flags |= FORCECLOSE;
-	nmp = VFSTONFS(mp);
+		nmp->nm_iflag |= NFSMNT_DISMNTFORCE;
+		mutex_exit(&nmp->nm_lock);
+
+	}
+
 	/*
 	 * Goes something like this..
 	 * - Check for activity on the root vnode (other than ourselves).
@@ -864,17 +869,18 @@ nfs_unmount(struct mount *mp, int mntfla
 	vp = nmp->nm_vnode;
 	error = vn_lock(vp, LK_EXCLUSIVE | LK_RETRY);
 	if (error != 0)
-		return error;
+		goto err;
 
 	if ((mntflags & MNT_FORCE) == 0 && vp->v_usecount > 1) {
 		VOP_UNLOCK(vp);
-		return (EBUSY);
+		error = EBUSY;
+		goto err;
 	}
 
 	error = vflush(mp, vp, flags);
 	if (error) {
 		VOP_UNLOCK(vp);
-		return (error);
+		goto err;
 	}
 
 	/*
@@ -885,6 +891,13 @@ nfs_unmount(struct mount *mp, int mntfla
 	nmp->nm_iflag |= NFSMNT_DISMNT;
 
 	/*
+	 * No new async I/O will be added, but await for pending
+	 * ones to drain.
+	 */
+	while (nfs_iodbusy(nmp))
+		kpause("nfsumnt", false, hz, NULL);
+
+	/*
 	 * Clean up the stats... note that we carefully avoid decrementing
 	 * nfs_mount_count here for good reason - we may not be unmounting
 	 * the last thing mounted.
@@ -908,6 +921,15 @@ nfs_unmount(struct mount *mp, int mntfla
 	cv_destroy(&nmp->nm_disconcv);
 	kmem_free(nmp, sizeof(*nmp));
 	return (0);
+
+err:
+	if (mntflags & MNT_FORCE) {
+		mutex_enter(&nmp->nm_lock);
+		nmp->nm_iflag &= ~NFSMNT_DISMNTFORCE;	
+		mutex_exit(&nmp->nm_lock);
+	}
+
+	return error;
 }
 
 /*

Index: src/sys/nfs/nfsmount.h
diff -u src/sys/nfs/nfsmount.h:1.52 src/sys/nfs/nfsmount.h:1.53
--- src/sys/nfs/nfsmount.h:1.52	Fri May 30 08:47:45 2014
+++ src/sys/nfs/nfsmount.h	Wed Jul 15 03:28:55 2015
@@ -1,4 +1,4 @@
-/*	$NetBSD: nfsmount.h,v 1.52 2014/05/30 08:47:45 hannken Exp $	*/
+/*	$NetBSD: nfsmount.h,v 1.53 2015/07/15 03:28:55 manu Exp $	*/
 
 /*
  * Copyright (c) 1989, 1993
@@ -120,6 +120,7 @@ struct nfs_args {
 #define NFSMNT_SWAPCOOKIE	0x00004000  /* XDR encode dir cookies */
 #define NFSMNT_STALEWRITEVERF	0x00008000  /* Write verifier is changing */
 #define NFSMNT_WCCKLUDGE	0x00010000  /* see nfs_check_wccdata() */
+#define NFSMNT_DISMNTFORCE	0x00020000  /* force unmount requested */
 
 #if defined(_KERNEL) && !defined(NFS_ARGS_ONLY)
 /*

Reply via email to