Author: rmacklem
Date: Fri Aug 25 22:52:40 2017
New Revision: 322909
URL: https://svnweb.freebsd.org/changeset/base/322909

Log:
  MFC: r321688
  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.

Modified:
  stable/11/sys/fs/nfs/nfscl.h
  stable/11/sys/fs/nfsclient/nfs_clport.c
  stable/11/sys/fs/nfsclient/nfs_clvfsops.c
  stable/11/sys/fs/nfsclient/nfsmount.h
  stable/11/sys/nfs/nfs_nfssvc.c
  stable/11/sys/nfs/nfssvc.h
  stable/11/sys/sys/param.h
Directory Properties:
  stable/11/   (props changed)

Modified: stable/11/sys/fs/nfs/nfscl.h
==============================================================================
--- stable/11/sys/fs/nfs/nfscl.h        Fri Aug 25 22:44:55 2017        
(r322908)
+++ stable/11/sys/fs/nfs/nfscl.h        Fri Aug 25 22:52:40 2017        
(r322909)
@@ -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: stable/11/sys/fs/nfsclient/nfs_clport.c
==============================================================================
--- stable/11/sys/fs/nfsclient/nfs_clport.c     Fri Aug 25 22:44:55 2017        
(r322908)
+++ stable/11/sys/fs/nfsclient/nfs_clport.c     Fri Aug 25 22:52:40 2017        
(r322909)
@@ -1313,6 +1313,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));
@@ -1367,6 +1369,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: stable/11/sys/fs/nfsclient/nfs_clvfsops.c
==============================================================================
--- stable/11/sys/fs/nfsclient/nfs_clvfsops.c   Fri Aug 25 22:44:55 2017        
(r322908)
+++ stable/11/sys/fs/nfsclient/nfs_clvfsops.c   Fri Aug 25 22:52:40 2017        
(r322909)
@@ -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: stable/11/sys/fs/nfsclient/nfsmount.h
==============================================================================
--- stable/11/sys/fs/nfsclient/nfsmount.h       Fri Aug 25 22:44:55 2017        
(r322908)
+++ stable/11/sys/fs/nfsclient/nfsmount.h       Fri Aug 25 22:52:40 2017        
(r322909)
@@ -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: stable/11/sys/nfs/nfs_nfssvc.c
==============================================================================
--- stable/11/sys/nfs/nfs_nfssvc.c      Fri Aug 25 22:44:55 2017        
(r322908)
+++ stable/11/sys/nfs/nfs_nfssvc.c      Fri Aug 25 22:52:40 2017        
(r322909)
@@ -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: stable/11/sys/nfs/nfssvc.h
==============================================================================
--- stable/11/sys/nfs/nfssvc.h  Fri Aug 25 22:44:55 2017        (r322908)
+++ stable/11/sys/nfs/nfssvc.h  Fri Aug 25 22:52:40 2017        (r322909)
@@ -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: stable/11/sys/sys/param.h
==============================================================================
--- stable/11/sys/sys/param.h   Fri Aug 25 22:44:55 2017        (r322908)
+++ stable/11/sys/sys/param.h   Fri Aug 25 22:52:40 2017        (r322909)
@@ -58,7 +58,7 @@
  *             in the range 5 to 9.
  */
 #undef __FreeBSD_version
-#define __FreeBSD_version 1101501      /* Master, propagated to newvers */
+#define __FreeBSD_version 1101502      /* Master, propagated to newvers */
 
 /*
  * __FreeBSD_kernel__ indicates that this system uses the kernel of FreeBSD,
_______________________________________________
svn-src-all@freebsd.org mailing list
https://lists.freebsd.org/mailman/listinfo/svn-src-all
To unsubscribe, send any mail to "svn-src-all-unsubscr...@freebsd.org"

Reply via email to