svn commit: r321688 - in head/sys: fs/nfs fs/nfsclient nfs sys

Rick Macklem rmacklem at FreeBSD.org
Sat Jul 29 19:52:49 UTC 2017


Author: rmacklem
Date: Sat Jul 29 19:52:47 2017
New Revision: 321688
URL: https://svnweb.freebsd.org/changeset/base/321688

Log:
  Add kernel support for the NFS client forced dismount "umount -N" option.
  
  When an NFS mount is hung against an unresponsive NFS server, the "umount -f"
  option can be used to dismount the mount. Unfortunately, "umount -f" gets
  hung as well if a "umount" without "-f" has already been done. Usually,
  this is because of a vnode lock being held by the "umount" for the mounted-on
  vnode.
  This patch adds kernel code so that a new "-N" option can be added to "umount",
  allowing it to avoid getting hung for this case.
  It adds two flags. One indicates that a forced dismount is about to happen
  and the other is used, along with setting mnt_data == NULL, to handshake
  with the nfs_unmount() VFS call.
  It includes a slight change to the interface used between the client and
  common NFS modules, so I bumped __FreeBSD_version to ensure both modules are
  rebuilt.
  
  Tested by:	pho
  Reviewed by:	kib
  MFC after:	2 weeks
  Differential Revision:	https://reviews.freebsd.org/D11735

Modified:
  head/sys/fs/nfs/nfscl.h
  head/sys/fs/nfsclient/nfs_clport.c
  head/sys/fs/nfsclient/nfs_clvfsops.c
  head/sys/fs/nfsclient/nfsmount.h
  head/sys/nfs/nfs_nfssvc.c
  head/sys/nfs/nfssvc.h
  head/sys/sys/param.h

Modified: head/sys/fs/nfs/nfscl.h
==============================================================================
--- head/sys/fs/nfs/nfscl.h	Sat Jul 29 17:30:25 2017	(r321687)
+++ head/sys/fs/nfs/nfscl.h	Sat Jul 29 19:52:47 2017	(r321688)
@@ -60,7 +60,8 @@ struct nfsv4node {
 #define	NFSCL_LEASE(r)	((r) * 2)
 
 /* This macro checks to see if a forced dismount is about to occur. */
-#define	NFSCL_FORCEDISM(m)	(((m)->mnt_kern_flag & MNTK_UNMOUNTF) != 0)
+#define	NFSCL_FORCEDISM(m)	(((m)->mnt_kern_flag & MNTK_UNMOUNTF) != 0 || \
+    (VFSTONFS(m)->nm_privflag & NFSMNTP_FORCEDISM) != 0)
 
 /*
  * These flag bits are used for the argument to nfscl_fillsattr() to

Modified: head/sys/fs/nfsclient/nfs_clport.c
==============================================================================
--- head/sys/fs/nfsclient/nfs_clport.c	Sat Jul 29 17:30:25 2017	(r321687)
+++ head/sys/fs/nfsclient/nfs_clport.c	Sat Jul 29 19:52:47 2017	(r321688)
@@ -1311,6 +1311,8 @@ nfssvc_nfscl(struct thread *td, struct nfssvc_args *ua
 	cap_rights_t rights;
 	char *buf;
 	int error;
+	struct mount *mp;
+	struct nfsmount *nmp;
 
 	if (uap->flag & NFSSVC_CBADDSOCK) {
 		error = copyin(uap->argp, (caddr_t)&nfscbdarg, sizeof(nfscbdarg));
@@ -1365,6 +1367,56 @@ nfssvc_nfscl(struct thread *td, struct nfssvc_args *ua
 			    dumpmntopts.ndmnt_blen);
 			free(buf, M_TEMP);
 		}
+	} else if (uap->flag & NFSSVC_FORCEDISM) {
+		buf = malloc(MNAMELEN + 1, M_TEMP, M_WAITOK);
+		error = copyinstr(uap->argp, buf, MNAMELEN + 1, NULL);
+		if (error == 0) {
+			nmp = NULL;
+			mtx_lock(&mountlist_mtx);
+			TAILQ_FOREACH(mp, &mountlist, mnt_list) {
+				if (strcmp(mp->mnt_stat.f_mntonname, buf) ==
+				    0 && strcmp(mp->mnt_stat.f_fstypename,
+				    "nfs") == 0 && mp->mnt_data != NULL) {
+					nmp = VFSTONFS(mp);
+					mtx_lock(&nmp->nm_mtx);
+					if ((nmp->nm_privflag &
+					    NFSMNTP_FORCEDISM) == 0) {
+						nmp->nm_privflag |= 
+						   (NFSMNTP_FORCEDISM |
+						    NFSMNTP_CANCELRPCS);
+						mtx_unlock(&nmp->nm_mtx);
+					} else {
+						nmp = NULL;
+						mtx_unlock(&nmp->nm_mtx);
+					}
+					break;
+				}
+			}
+			mtx_unlock(&mountlist_mtx);
+
+			if (nmp != NULL) {
+				/*
+				 * Call newnfs_nmcancelreqs() to cause
+				 * any RPCs in progress on the mount point to
+				 * fail.
+				 * This will cause any process waiting for an
+				 * RPC to complete while holding a vnode lock
+				 * on the mounted-on vnode (such as "df" or
+				 * a non-forced "umount") to fail.
+				 * This will unlock the mounted-on vnode so
+				 * a forced dismount can succeed.
+				 * Then clear NFSMNTP_CANCELRPCS and wakeup(),
+				 * so that nfs_unmount() can complete.
+				 */
+				newnfs_nmcancelreqs(nmp);
+				mtx_lock(&nmp->nm_mtx);
+				nmp->nm_privflag &= ~NFSMNTP_CANCELRPCS;
+				wakeup(nmp);
+				mtx_unlock(&nmp->nm_mtx);
+			} else
+				error = EINVAL;
+		}
+		free(buf, M_TEMP);
 	} else {
 		error = EINVAL;
 	}

Modified: head/sys/fs/nfsclient/nfs_clvfsops.c
==============================================================================
--- head/sys/fs/nfsclient/nfs_clvfsops.c	Sat Jul 29 17:30:25 2017	(r321687)
+++ head/sys/fs/nfsclient/nfs_clvfsops.c	Sat Jul 29 19:52:47 2017	(r321688)
@@ -1698,6 +1698,11 @@ nfs_unmount(struct mount *mp, int mntflags)
 	 */
 	if ((mntflags & MNT_FORCE) == 0)
 		nfscl_umount(nmp, td);
+	else {
+		mtx_lock(&nmp->nm_mtx);
+		nmp->nm_privflag |= NFSMNTP_FORCEDISM;
+		mtx_unlock(&nmp->nm_mtx);
+	}
 	/* Make sure no nfsiods are assigned to this mount. */
 	mtx_lock(&ncl_iod_mutex);
 	for (i = 0; i < NFS_MAXASYNCDAEMON; i++)
@@ -1706,6 +1711,19 @@ nfs_unmount(struct mount *mp, int mntflags)
 			ncl_iodmount[i] = NULL;
 		}
 	mtx_unlock(&ncl_iod_mutex);
+
+	/*
+	 * We can now set mnt_data to NULL and wait for
+	 * nfssvc(NFSSVC_FORCEDISM) to complete.
+	 */
+	mtx_lock(&mountlist_mtx);
+	mtx_lock(&nmp->nm_mtx);
+	mp->mnt_data = NULL;
+	mtx_unlock(&mountlist_mtx);
+	while ((nmp->nm_privflag & NFSMNTP_CANCELRPCS) != 0)
+		msleep(nmp, &nmp->nm_mtx, PVFS, "nfsfdism", 0);
+	mtx_unlock(&nmp->nm_mtx);
+
 	newnfs_disconnect(&nmp->nm_sockreq);
 	crfree(nmp->nm_sockreq.nr_cred);
 	FREE(nmp->nm_nam, M_SONAME);

Modified: head/sys/fs/nfsclient/nfsmount.h
==============================================================================
--- head/sys/fs/nfsclient/nfsmount.h	Sat Jul 29 17:30:25 2017	(r321687)
+++ head/sys/fs/nfsclient/nfsmount.h	Sat Jul 29 19:52:47 2017	(r321688)
@@ -44,6 +44,7 @@
  */
 struct	nfsmount {
 	struct	nfsmount_common nm_com;	/* Common fields for nlm */
+	uint32_t nm_privflag;		/* Private flags */
 	int	nm_numgrps;		/* Max. size of groupslist */
 	u_char	nm_fh[NFSX_FHMAX];	/* File handle of root dir */
 	int	nm_fhsize;		/* Size of root file handle */
@@ -98,6 +99,10 @@ struct	nfsmount {
 #define	nm_hostname	nm_com.nmcom_hostname
 #define	nm_getinfo	nm_com.nmcom_getinfo
 #define	nm_vinvalbuf	nm_com.nmcom_vinvalbuf
+
+/* Private flags. */
+#define	NFSMNTP_FORCEDISM	0x00000001
+#define	NFSMNTP_CANCELRPCS	0x00000002
 
 #define	NFSMNT_DIRPATH(m)	(&((m)->nm_name[(m)->nm_krbnamelen + 1]))
 #define	NFSMNT_SRVKRBNAME(m)						\

Modified: head/sys/nfs/nfs_nfssvc.c
==============================================================================
--- head/sys/nfs/nfs_nfssvc.c	Sat Jul 29 17:30:25 2017	(r321687)
+++ head/sys/nfs/nfs_nfssvc.c	Sat Jul 29 19:52:47 2017	(r321688)
@@ -92,7 +92,7 @@ sys_nfssvc(struct thread *td, struct nfssvc_args *uap)
 	    nfsd_call_nfsserver != NULL)
 		error = (*nfsd_call_nfsserver)(td, uap);
 	else if ((uap->flag & (NFSSVC_CBADDSOCK | NFSSVC_NFSCBD |
-	    NFSSVC_DUMPMNTOPTS)) && nfsd_call_nfscl != NULL)
+	    NFSSVC_DUMPMNTOPTS | NFSSVC_FORCEDISM)) && nfsd_call_nfscl != NULL)
 		error = (*nfsd_call_nfscl)(td, uap);
 	else if ((uap->flag & (NFSSVC_IDNAME | NFSSVC_GETSTATS |
 	    NFSSVC_GSSDADDPORT | NFSSVC_GSSDADDFIRST | NFSSVC_GSSDDELETEALL |

Modified: head/sys/nfs/nfssvc.h
==============================================================================
--- head/sys/nfs/nfssvc.h	Sat Jul 29 17:30:25 2017	(r321687)
+++ head/sys/nfs/nfssvc.h	Sat Jul 29 19:52:47 2017	(r321688)
@@ -70,6 +70,7 @@
 #define	NFSSVC_RESUMENFSD	0x08000000
 #define	NFSSVC_DUMPMNTOPTS	0x10000000
 #define	NFSSVC_NEWSTRUCT	0x20000000
+#define	NFSSVC_FORCEDISM	0x40000000
 
 /* Argument structure for NFSSVC_DUMPMNTOPTS. */
 struct nfscl_dumpmntopts {

Modified: head/sys/sys/param.h
==============================================================================
--- head/sys/sys/param.h	Sat Jul 29 17:30:25 2017	(r321687)
+++ head/sys/sys/param.h	Sat Jul 29 19:52:47 2017	(r321688)
@@ -58,7 +58,7 @@
  *		in the range 5 to 9.
  */
 #undef __FreeBSD_version
-#define __FreeBSD_version 1200039	/* Master, propagated to newvers */
+#define __FreeBSD_version 1200040	/* Master, propagated to newvers */
 
 /*
  * __FreeBSD_kernel__ indicates that this system uses the kernel of FreeBSD,


More information about the svn-src-all mailing list