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
This commit is contained in:
parent
d35f6548e6
commit
47cbff34fa
Notes:
svn2git
2020-12-20 02:59:44 +00:00
svn path=/head/; revision=321688
@ -60,7 +60,8 @@ struct nfsv4node {
|
|||||||
#define NFSCL_LEASE(r) ((r) * 2)
|
#define NFSCL_LEASE(r) ((r) * 2)
|
||||||
|
|
||||||
/* This macro checks to see if a forced dismount is about to occur. */
|
/* 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
|
* These flag bits are used for the argument to nfscl_fillsattr() to
|
||||||
|
@ -1311,6 +1311,8 @@ nfssvc_nfscl(struct thread *td, struct nfssvc_args *uap)
|
|||||||
cap_rights_t rights;
|
cap_rights_t rights;
|
||||||
char *buf;
|
char *buf;
|
||||||
int error;
|
int error;
|
||||||
|
struct mount *mp;
|
||||||
|
struct nfsmount *nmp;
|
||||||
|
|
||||||
if (uap->flag & NFSSVC_CBADDSOCK) {
|
if (uap->flag & NFSSVC_CBADDSOCK) {
|
||||||
error = copyin(uap->argp, (caddr_t)&nfscbdarg, sizeof(nfscbdarg));
|
error = copyin(uap->argp, (caddr_t)&nfscbdarg, sizeof(nfscbdarg));
|
||||||
@ -1365,6 +1367,56 @@ nfssvc_nfscl(struct thread *td, struct nfssvc_args *uap)
|
|||||||
dumpmntopts.ndmnt_blen);
|
dumpmntopts.ndmnt_blen);
|
||||||
free(buf, M_TEMP);
|
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 {
|
} else {
|
||||||
error = EINVAL;
|
error = EINVAL;
|
||||||
}
|
}
|
||||||
|
@ -1698,6 +1698,11 @@ nfs_unmount(struct mount *mp, int mntflags)
|
|||||||
*/
|
*/
|
||||||
if ((mntflags & MNT_FORCE) == 0)
|
if ((mntflags & MNT_FORCE) == 0)
|
||||||
nfscl_umount(nmp, td);
|
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. */
|
/* Make sure no nfsiods are assigned to this mount. */
|
||||||
mtx_lock(&ncl_iod_mutex);
|
mtx_lock(&ncl_iod_mutex);
|
||||||
for (i = 0; i < NFS_MAXASYNCDAEMON; i++)
|
for (i = 0; i < NFS_MAXASYNCDAEMON; i++)
|
||||||
@ -1706,6 +1711,19 @@ nfs_unmount(struct mount *mp, int mntflags)
|
|||||||
ncl_iodmount[i] = NULL;
|
ncl_iodmount[i] = NULL;
|
||||||
}
|
}
|
||||||
mtx_unlock(&ncl_iod_mutex);
|
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);
|
newnfs_disconnect(&nmp->nm_sockreq);
|
||||||
crfree(nmp->nm_sockreq.nr_cred);
|
crfree(nmp->nm_sockreq.nr_cred);
|
||||||
FREE(nmp->nm_nam, M_SONAME);
|
FREE(nmp->nm_nam, M_SONAME);
|
||||||
|
@ -44,6 +44,7 @@
|
|||||||
*/
|
*/
|
||||||
struct nfsmount {
|
struct nfsmount {
|
||||||
struct nfsmount_common nm_com; /* Common fields for nlm */
|
struct nfsmount_common nm_com; /* Common fields for nlm */
|
||||||
|
uint32_t nm_privflag; /* Private flags */
|
||||||
int nm_numgrps; /* Max. size of groupslist */
|
int nm_numgrps; /* Max. size of groupslist */
|
||||||
u_char nm_fh[NFSX_FHMAX]; /* File handle of root dir */
|
u_char nm_fh[NFSX_FHMAX]; /* File handle of root dir */
|
||||||
int nm_fhsize; /* Size of root file handle */
|
int nm_fhsize; /* Size of root file handle */
|
||||||
@ -99,6 +100,10 @@ struct nfsmount {
|
|||||||
#define nm_getinfo nm_com.nmcom_getinfo
|
#define nm_getinfo nm_com.nmcom_getinfo
|
||||||
#define nm_vinvalbuf nm_com.nmcom_vinvalbuf
|
#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_DIRPATH(m) (&((m)->nm_name[(m)->nm_krbnamelen + 1]))
|
||||||
#define NFSMNT_SRVKRBNAME(m) \
|
#define NFSMNT_SRVKRBNAME(m) \
|
||||||
(&((m)->nm_name[(m)->nm_krbnamelen + (m)->nm_dirpathlen + 2]))
|
(&((m)->nm_name[(m)->nm_krbnamelen + (m)->nm_dirpathlen + 2]))
|
||||||
|
@ -92,7 +92,7 @@ sys_nfssvc(struct thread *td, struct nfssvc_args *uap)
|
|||||||
nfsd_call_nfsserver != NULL)
|
nfsd_call_nfsserver != NULL)
|
||||||
error = (*nfsd_call_nfsserver)(td, uap);
|
error = (*nfsd_call_nfsserver)(td, uap);
|
||||||
else if ((uap->flag & (NFSSVC_CBADDSOCK | NFSSVC_NFSCBD |
|
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);
|
error = (*nfsd_call_nfscl)(td, uap);
|
||||||
else if ((uap->flag & (NFSSVC_IDNAME | NFSSVC_GETSTATS |
|
else if ((uap->flag & (NFSSVC_IDNAME | NFSSVC_GETSTATS |
|
||||||
NFSSVC_GSSDADDPORT | NFSSVC_GSSDADDFIRST | NFSSVC_GSSDDELETEALL |
|
NFSSVC_GSSDADDPORT | NFSSVC_GSSDADDFIRST | NFSSVC_GSSDDELETEALL |
|
||||||
|
@ -70,6 +70,7 @@
|
|||||||
#define NFSSVC_RESUMENFSD 0x08000000
|
#define NFSSVC_RESUMENFSD 0x08000000
|
||||||
#define NFSSVC_DUMPMNTOPTS 0x10000000
|
#define NFSSVC_DUMPMNTOPTS 0x10000000
|
||||||
#define NFSSVC_NEWSTRUCT 0x20000000
|
#define NFSSVC_NEWSTRUCT 0x20000000
|
||||||
|
#define NFSSVC_FORCEDISM 0x40000000
|
||||||
|
|
||||||
/* Argument structure for NFSSVC_DUMPMNTOPTS. */
|
/* Argument structure for NFSSVC_DUMPMNTOPTS. */
|
||||||
struct nfscl_dumpmntopts {
|
struct nfscl_dumpmntopts {
|
||||||
|
@ -58,7 +58,7 @@
|
|||||||
* in the range 5 to 9.
|
* in the range 5 to 9.
|
||||||
*/
|
*/
|
||||||
#undef __FreeBSD_version
|
#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,
|
* __FreeBSD_kernel__ indicates that this system uses the kernel of FreeBSD,
|
||||||
|
Loading…
Reference in New Issue
Block a user