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/nfs/nfs_commonkrpc.c =================================================================== --- sys/fs/nfs/nfs_commonkrpc.c +++ sys/fs/nfs/nfs_commonkrpc.c @@ -511,7 +511,8 @@ if (xidp != NULL) *xidp = 0; /* Reject requests while attempting a forced unmount. */ - if (nmp != NULL && (nmp->nm_mountp->mnt_kern_flag & MNTK_UNMOUNTF)) { + if (nmp != NULL && ((nmp->nm_mountp->mnt_kern_flag & MNTK_UNMOUNTF) != 0 + || (nmp->nm_privflag & NFSMNTP_FORCEDISM) != 0)) { m_freem(nd->nd_mreq); return (ESTALE); } @@ -1231,7 +1232,8 @@ sigset_t tmpset; /* Terminate all requests while attempting a forced unmount. */ - if (nmp->nm_mountp->mnt_kern_flag & MNTK_UNMOUNTF) + if ((nmp->nm_mountp->mnt_kern_flag & MNTK_UNMOUNTF) != 0 || + (nmp->nm_privflag & NFSMNTP_FORCEDISM) != 0) return (EIO); if (!(nmp->nm_flag & NFSMNT_INT)) return (0); Index: sys/fs/nfs/nfs_commonsubs.c =================================================================== --- sys/fs/nfs/nfs_commonsubs.c +++ sys/fs/nfs/nfs_commonsubs.c @@ -1834,7 +1834,8 @@ lp->nfslock_lock |= NFSV4LOCK_LOCKWANTED; } while (lp->nfslock_lock & (NFSV4LOCK_LOCK | NFSV4LOCK_LOCKWANTED)) { - if (mp != NULL && (mp->mnt_kern_flag & MNTK_UNMOUNTF) != 0) { + if (mp != NULL && ((mp->mnt_kern_flag & MNTK_UNMOUNTF) != 0 || + (VFSTONFS(mp)->nm_privflag & NFSMNTP_FORCEDISM) != 0)) { lp->nfslock_lock &= ~NFSV4LOCK_LOCKWANTED; return (0); } @@ -1888,8 +1889,8 @@ * not wait for threads that want the exclusive lock. If priority needs * to be given to threads that need the exclusive lock, a call to nfsv4_lock() * with the 2nd argument == 0 should be done before calling nfsv4_getref(). - * If the mp argument is not NULL, check for MNTK_UNMOUNTF being set and - * return without getting a refcnt for that case. + * If the mp argument is not NULL, check for MNTK_UNMOUNTF/NFSMNTP_FORCEDISM + * being set and return without getting a refcnt for that case. */ APPLESTATIC void nfsv4_getref(struct nfsv4lock *lp, int *isleptp, void *mutex, @@ -1903,7 +1904,8 @@ * Wait for a lock held. */ while (lp->nfslock_lock & NFSV4LOCK_LOCK) { - if (mp != NULL && (mp->mnt_kern_flag & MNTK_UNMOUNTF) != 0) + if (mp != NULL && ((mp->mnt_kern_flag & MNTK_UNMOUNTF) != 0 || + (VFSTONFS(mp)->nm_privflag & NFSMNTP_FORCEDISM) != 0)) return; lp->nfslock_lock |= NFSV4LOCK_WANTED; if (isleptp) @@ -1911,7 +1913,8 @@ (void) nfsmsleep(&lp->nfslock_lock, mutex, PZERO - 1, "nfsv4gr", NULL); } - if (mp != NULL && (mp->mnt_kern_flag & MNTK_UNMOUNTF) != 0) + if (mp != NULL && ((mp->mnt_kern_flag & MNTK_UNMOUNTF) != 0 || + (VFSTONFS(mp)->nm_privflag & NFSMNTP_FORCEDISM) != 0)) return; lp->nfslock_usecnt++; @@ -4192,9 +4195,9 @@ * This RPC attempt will fail when it calls * newnfs_request(). */ - if (nmp != NULL && - (nmp->nm_mountp->mnt_kern_flag & MNTK_UNMOUNTF) - != 0) { + if (nmp != NULL && ((nmp->nm_mountp->mnt_kern_flag & + MNTK_UNMOUNTF) != 0 || (nmp->nm_privflag & + NFSMNTP_FORCEDISM) != 0)) { mtx_unlock(&sep->nfsess_mtx); return (ESTALE); } Index: sys/fs/nfsclient/nfs_clbio.c =================================================================== --- sys/fs/nfsclient/nfs_clbio.c +++ sys/fs/nfsclient/nfs_clbio.c @@ -1341,7 +1341,8 @@ if ((nmp->nm_flag & NFSMNT_INT) == 0) intrflg = 0; - if ((nmp->nm_mountp->mnt_kern_flag & MNTK_UNMOUNTF)) + if ((nmp->nm_mountp->mnt_kern_flag & MNTK_UNMOUNTF) != 0 || + (nmp->nm_privflag & NFSMNTP_FORCEDISM) != 0) intrflg = 1; if (intrflg) { slpflag = PCATCH; Index: sys/fs/nfsclient/nfs_clport.c =================================================================== --- sys/fs/nfsclient/nfs_clport.c +++ sys/fs/nfsclient/nfs_clport.c @@ -313,7 +313,8 @@ *npp = NULL; /* For forced dismounts, just return error. */ - if ((mntp->mnt_kern_flag & MNTK_UNMOUNTF)) + if ((mntp->mnt_kern_flag & MNTK_UNMOUNTF) != 0 || + (VFSTONFS(mntp)->nm_privflag & NFSMNTP_FORCEDISM) != 0) return (EINTR); MALLOC(nfhp, struct nfsfh *, sizeof (struct nfsfh) + fhsize, M_NFSFH, M_WAITOK); @@ -333,10 +334,12 @@ /* * It is safe so long as a vflush() with * FORCECLOSE has not been done. Since the Renew thread is - * stopped and the MNTK_UNMOUNTF flag is set before doing - * a vflush() with FORCECLOSE, we should be ok here. + * stopped and the MNTK_UNMOUNTF/NFSMNTP_FORCEDISM flag is set + * before doing a vflush() with FORCECLOSE, we should be ok + * here. */ - if ((mntp->mnt_kern_flag & MNTK_UNMOUNTF)) + if ((mntp->mnt_kern_flag & MNTK_UNMOUNTF) != 0 || + (VFSTONFS(mntp)->nm_privflag & NFSMNTP_FORCEDISM) != 0) error = EINTR; else { vfs_hash_ref(mntp, hash, td, &nvp, newnfs_vncmpf, nfhp); @@ -1311,6 +1314,8 @@ 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 +1370,56 @@ 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; } Index: sys/fs/nfsclient/nfs_clstate.c =================================================================== --- sys/fs/nfsclient/nfs_clstate.c +++ sys/fs/nfsclient/nfs_clstate.c @@ -803,7 +803,8 @@ * allocate a new clientid and get out now. For the case where * clp != NULL, this is a harmless optimization. */ - if ((mp->mnt_kern_flag & MNTK_UNMOUNTF) != 0) { + if ((mp->mnt_kern_flag & MNTK_UNMOUNTF) != 0 || + (nmp->nm_privflag & NFSMNTP_FORCEDISM) != 0) { NFSUNLOCKCLSTATE(); if (newclp != NULL) free(newclp, M_NFSCLCLIENT); @@ -843,7 +844,8 @@ } NFSLOCKCLSTATE(); while ((clp->nfsc_flags & NFSCLFLAGS_HASCLIENTID) == 0 && !igotlock && - (mp->mnt_kern_flag & MNTK_UNMOUNTF) == 0) + (mp->mnt_kern_flag & MNTK_UNMOUNTF) == 0 && + (nmp->nm_privflag & NFSMNTP_FORCEDISM) == 0) igotlock = nfsv4_lock(&clp->nfsc_lock, 1, NULL, NFSCLSTATEMUTEXPTR, mp); if (igotlock == 0) { @@ -858,12 +860,13 @@ nfsv4_lock(&clp->nfsc_lock, 0, NULL, NFSCLSTATEMUTEXPTR, mp); nfsv4_getref(&clp->nfsc_lock, NULL, NFSCLSTATEMUTEXPTR, mp); } - if (igotlock == 0 && (mp->mnt_kern_flag & MNTK_UNMOUNTF) != 0) { + if (igotlock == 0 && ((mp->mnt_kern_flag & MNTK_UNMOUNTF) != 0 || + (nmp->nm_privflag & NFSMNTP_FORCEDISM) != 0)) { /* * Both nfsv4_lock() and nfsv4_getref() know to check - * for MNTK_UNMOUNTF and return without sleeping to - * wait for the exclusive lock to be released, since it - * might be held by nfscl_umount() and we need to get out + * for MNTK_UNMOUNTF/NFSMNTP_FORCEDISM and return without + * sleeping to wait for the exclusive lock to be released, since + * it might be held by nfscl_umount() and we need to get out * now for that case and not wait until nfscl_umount() * releases it. */ @@ -4844,7 +4847,8 @@ lyp->nfsly_timestamp = NFSD_MONOSEC + 120; } nfsv4_getref(&lyp->nfsly_lock, NULL, NFSCLSTATEMUTEXPTR, mp); - if ((mp->mnt_kern_flag & MNTK_UNMOUNTF) != 0) { + if ((mp->mnt_kern_flag & MNTK_UNMOUNTF) != 0 || + (nmp->nm_privflag & NFSMNTP_FORCEDISM) != 0) { NFSUNLOCKCLSTATE(); if (tlyp != NULL) free(tlyp, M_NFSLAYOUT); @@ -4904,10 +4908,14 @@ igotlock = nfsv4_lock(&lyp->nfsly_lock, 1, NULL, NFSCLSTATEMUTEXPTR, mp); } while (igotlock == 0 && - (mp->mnt_kern_flag & MNTK_UNMOUNTF) == 0); + (mp->mnt_kern_flag & MNTK_UNMOUNTF) == 0 && + (VFSTONFS(mp)->nm_privflag & + NFSMNTP_FORCEDISM) == 0); *retflpp = NULL; } - if ((mp->mnt_kern_flag & MNTK_UNMOUNTF) != 0) { + if ((mp->mnt_kern_flag & MNTK_UNMOUNTF) != 0 || + (VFSTONFS(mp)->nm_privflag & + NFSMNTP_FORCEDISM) != 0) { lyp = NULL; *recalledp = 1; } Index: sys/fs/nfsclient/nfs_clvfsops.c =================================================================== --- sys/fs/nfsclient/nfs_clvfsops.c +++ sys/fs/nfsclient/nfs_clvfsops.c @@ -1698,6 +1698,11 @@ */ 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 @@ 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, PZERO, "nfsfdism", 0); + mtx_unlock(&nmp->nm_mtx); + newnfs_disconnect(&nmp->nm_sockreq); crfree(nmp->nm_sockreq.nr_cred); FREE(nmp->nm_nam, M_SONAME); @@ -1775,7 +1793,8 @@ * the umount(2) syscall doesn't get stuck in VFS_SYNC() before * calling VFS_UNMOUNT(). */ - if ((mp->mnt_kern_flag & MNTK_UNMOUNTF) != 0) { + if ((mp->mnt_kern_flag & MNTK_UNMOUNTF) != 0 || + (VFSTONFS(mp)->nm_privflag & NFSMNTP_FORCEDISM) != 0) { MNT_IUNLOCK(mp); return (EBADF); } Index: sys/fs/nfsclient/nfs_clvnops.c =================================================================== --- sys/fs/nfsclient/nfs_clvnops.c +++ sys/fs/nfsclient/nfs_clvnops.c @@ -663,7 +663,8 @@ int error = 0, ret, localcred = 0; int fmode = ap->a_fflag; - if ((vp->v_mount->mnt_kern_flag & MNTK_UNMOUNTF)) + if ((vp->v_mount->mnt_kern_flag & MNTK_UNMOUNTF) != 0 || + (VFSTONFS(vp->v_mount)->nm_privflag & NFSMNTP_FORCEDISM) != 0) return (0); /* * During shutdown, a_cred isn't valid, so just use root. Index: sys/fs/nfsclient/nfsmount.h =================================================================== --- sys/fs/nfsclient/nfsmount.h +++ sys/fs/nfsclient/nfsmount.h @@ -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 */ @@ -99,6 +100,10 @@ #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) \ (&((m)->nm_name[(m)->nm_krbnamelen + (m)->nm_dirpathlen + 2])) 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 {