Forced dismounts of NFS mounts can fail when thread(s) are stuck
waiting for an RPC reply from the server while holding the mount point busy (mnt_lockref incremented). This happens because dounmount() msleep()s waiting for mnt_lockref to become 0, before calling VFS_UNMOUNT(). This patch adds a new VFS operation called VFS_PURGE(), which the NFS client implements as purging RPCs in progress. Making this call before checking mnt_lockref fixes the problem, by ensuring that the VOP_xxx() calls will fail and unbusy the mount point. Reported by: sbruno Reviewed by: kib MFC after: 2 weeks
This commit is contained in:
parent
17df25c41c
commit
8fe6bddff7
@ -120,6 +120,7 @@ static vfs_root_t nfs_root;
|
||||
static vfs_statfs_t nfs_statfs;
|
||||
static vfs_sync_t nfs_sync;
|
||||
static vfs_sysctl_t nfs_sysctl;
|
||||
static vfs_purge_t nfs_purge;
|
||||
|
||||
/*
|
||||
* nfs vfs operations.
|
||||
@ -134,6 +135,7 @@ static struct vfsops nfs_vfsops = {
|
||||
.vfs_uninit = ncl_uninit,
|
||||
.vfs_unmount = nfs_unmount,
|
||||
.vfs_sysctl = nfs_sysctl,
|
||||
.vfs_purge = nfs_purge,
|
||||
};
|
||||
VFS_SET(nfs_vfsops, nfs, VFCF_NETWORK | VFCF_SBDRY);
|
||||
|
||||
@ -1675,6 +1677,19 @@ nfs_sysctl(struct mount *mp, fsctlop_t op, struct sysctl_req *req)
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Purge any RPCs in progress, so that they will all return errors.
|
||||
* This allows dounmount() to continue as far as VFS_UNMOUNT() for a
|
||||
* forced dismount.
|
||||
*/
|
||||
static void
|
||||
nfs_purge(struct mount *mp)
|
||||
{
|
||||
struct nfsmount *nmp = VFSTONFS(mp);
|
||||
|
||||
newnfs_nmcancelreqs(nmp);
|
||||
}
|
||||
|
||||
/*
|
||||
* Extract the information needed by the nlm from the nfs vnode.
|
||||
*/
|
||||
|
@ -1269,8 +1269,16 @@ dounmount(mp, flags, td)
|
||||
}
|
||||
mp->mnt_kern_flag |= MNTK_UNMOUNT | MNTK_NOINSMNTQ;
|
||||
/* Allow filesystems to detect that a forced unmount is in progress. */
|
||||
if (flags & MNT_FORCE)
|
||||
if (flags & MNT_FORCE) {
|
||||
mp->mnt_kern_flag |= MNTK_UNMOUNTF;
|
||||
MNT_IUNLOCK(mp);
|
||||
/*
|
||||
* Must be done after setting MNTK_UNMOUNTF and before
|
||||
* waiting for mnt_lockref to become 0.
|
||||
*/
|
||||
VFS_PURGE(mp);
|
||||
MNT_ILOCK(mp);
|
||||
}
|
||||
error = 0;
|
||||
if (mp->mnt_lockref) {
|
||||
mp->mnt_kern_flag |= MNTK_DRAINING;
|
||||
|
@ -609,6 +609,7 @@ typedef int vfs_sysctl_t(struct mount *mp, fsctlop_t op,
|
||||
struct sysctl_req *req);
|
||||
typedef void vfs_susp_clean_t(struct mount *mp);
|
||||
typedef void vfs_notify_lowervp_t(struct mount *mp, struct vnode *lowervp);
|
||||
typedef void vfs_purge_t(struct mount *mp);
|
||||
|
||||
struct vfsops {
|
||||
vfs_mount_t *vfs_mount;
|
||||
@ -628,6 +629,7 @@ struct vfsops {
|
||||
vfs_susp_clean_t *vfs_susp_clean;
|
||||
vfs_notify_lowervp_t *vfs_reclaim_lowervp;
|
||||
vfs_notify_lowervp_t *vfs_unlink_lowervp;
|
||||
vfs_purge_t *vfs_purge;
|
||||
vfs_mount_t *vfs_spare[6]; /* spares for ABI compat */
|
||||
};
|
||||
|
||||
@ -757,6 +759,14 @@ vfs_statfs_t __vfs_statfs;
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define VFS_PURGE(MP) do { \
|
||||
if (*(MP)->mnt_op->vfs_purge != NULL) { \
|
||||
VFS_PROLOGUE(MP); \
|
||||
(*(MP)->mnt_op->vfs_purge)(MP); \
|
||||
VFS_EPILOGUE(MP); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define VFS_KNOTE_LOCKED(vp, hint) do \
|
||||
{ \
|
||||
if (((vp)->v_vflag & VV_NOKNOTE) == 0) \
|
||||
|
Loading…
x
Reference in New Issue
Block a user