Index: sbin/umount/umount.8 =================================================================== --- sbin/umount/umount.8 +++ sbin/umount/umount.8 @@ -28,7 +28,7 @@ .\" @(#)umount.8 8.2 (Berkeley) 5/8/95 .\" $FreeBSD: head/sbin/umount/umount.8 314436 2017-02-28 23:42:47Z imp $ .\" -.Dd September 10, 2016 +.Dd July 25, 2017 .Dt UMOUNT 8 .Os .Sh NAME @@ -36,7 +36,7 @@ .Nd unmount file systems .Sh SYNOPSIS .Nm -.Op Fl fnv +.Op Fl fNnv .Ar special ... | node ... | fsid ... .Nm .Fl a | A @@ -81,6 +81,17 @@ For NFS, a forced dismount can take up to 1 minute or more to complete against an unresponsive server and may throw away data not yet written to the server for this case. +If a process, such as +.Xr df 1 +or +.Nm +without the +.Fl f +flag is hung on an +.Tn NFS +mount point, use the +.Fl N +flag instead. Also, doing a forced dismount of an NFSv3 mount when .Xr rpc.lockd 8 is running is unsafe and can result in a crash. @@ -94,6 +105,20 @@ option, will only unmount .Tn NFS file systems. +.It Fl N +Do a forced dismount of an +.Tn NFS +mount point without checking the mount path. +This option can only be used with the path to the mount point +.Ar node +and the path must be specified exactly as it was at mount time. +This option is useful when a process is hung waiting for an unresponsive +.Tn NFS +server while holding a vnode lock on the mounted-on vnode, such that +.Nm +with the +.Fl f +flag can't complete. .It Fl n Unless the .Fl f Index: sbin/umount/umount.c =================================================================== --- sbin/umount/umount.c +++ sbin/umount/umount.c @@ -86,13 +86,13 @@ int main(int argc, char *argv[]) { - int all, errs, ch, mntsize, error; + int all, errs, ch, mntsize, error, nfsforce, ret; char **typelist = NULL; struct statfs *mntbuf, *sfs; struct addrinfo hints; - all = errs = 0; - while ((ch = getopt(argc, argv, "AaF:fh:nt:v")) != -1) + nfsforce = all = errs = 0; + while ((ch = getopt(argc, argv, "AaF:fh:Nnt:v")) != -1) switch (ch) { case 'A': all = 2; @@ -110,6 +110,9 @@ all = 2; nfshost = optarg; break; + case 'N': + nfsforce = 1; + break; case 'n': fflag |= MNT_NONBUSY; break; @@ -138,6 +141,9 @@ if ((argc == 0 && !all) || (argc != 0 && all)) usage(); + if (argc == 0 && nfsforce != 0) + usage(); + /* -h implies "-t nfs" if no -t flag. */ if ((nfshost != NULL) && (typelist == NULL)) typelist = makevfslist("nfs"); @@ -175,7 +181,20 @@ break; case 0: for (errs = 0; *argv != NULL; ++argv) - if (checkname(*argv, typelist) != 0) + if (nfsforce != 0) { + /* + * First do the nfssvc() syscall to shut down + * the mount point and then do the forced + * dismount. + */ + ret = nfssvc(NFSSVC_FORCEDISM, *argv); + if (ret >= 0) + ret = unmount(*argv, MNT_FORCE); + if (ret < 0) { + warn("%s", *argv); + errs = 1; + } + } else if (checkname(*argv, typelist) != 0) errs = 1; break; } @@ -635,7 +654,7 @@ { (void)fprintf(stderr, "%s\n%s\n", - "usage: umount [-fnv] special ... | node ... | fsid ...", + "usage: umount [-fNnv] special ... | node ... | fsid ...", " umount -a | -A [-F fstab] [-fnv] [-h host] [-t type]"); exit(1); } Index: sys/fs/nfsclient/nfs_clport.c =================================================================== --- sys/fs/nfsclient/nfs_clport.c +++ sys/fs/nfsclient/nfs_clport.c @@ -1311,6 +1311,7 @@ cap_rights_t rights; char *buf; int error; + struct mount *mp; if (uap->flag & NFSSVC_CBADDSOCK) { error = copyin(uap->argp, (caddr_t)&nfscbdarg, sizeof(nfscbdarg)); @@ -1365,6 +1366,44 @@ 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) { + 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) { + vfs_ref(mp); + break; + } + } + mtx_unlock(&mountlist_mtx); + + if (mp != NULL) { + /* + * First, set MNTK_UNMOUNTF so that no new + * RPCs will fail. + * Then, 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. + */ + MNT_ILOCK(mp); + mp->mnt_kern_flag |= MNTK_UNMOUNTF; + MNT_IUNLOCK(mp); + newnfs_nmcancelreqs(VFSTONFS(mp)); + vfs_rel(mp); + } else + error = EINVAL; + } + free(buf, M_TEMP); } else { error = EINVAL; } Index: sys/nfs/nfs_nfssvc.c =================================================================== --- sys/nfs/nfs_nfssvc.c +++ sys/nfs/nfs_nfssvc.c @@ -92,7 +92,7 @@ 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 | Index: sys/nfs/nfssvc.h =================================================================== --- sys/nfs/nfssvc.h +++ sys/nfs/nfssvc.h @@ -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 {