Author: rmacklem
Date: Sun Sep  1 23:02:59 2013
New Revision: 255136
URL: http://svnweb.freebsd.org/changeset/base/255136

Log:
  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

Modified:
  head/sys/fs/nfsclient/nfs_clvfsops.c
  head/sys/kern/vfs_mount.c
  head/sys/sys/mount.h

Modified: head/sys/fs/nfsclient/nfs_clvfsops.c
==============================================================================
--- head/sys/fs/nfsclient/nfs_clvfsops.c        Sun Sep  1 22:30:24 2013        
(r255135)
+++ head/sys/fs/nfsclient/nfs_clvfsops.c        Sun Sep  1 23:02:59 2013        
(r255136)
@@ -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);
 
@@ -1676,6 +1678,19 @@ nfs_sysctl(struct mount *mp, fsctlop_t o
 }
 
 /*
+ * 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.
  */
 static void

Modified: head/sys/kern/vfs_mount.c
==============================================================================
--- head/sys/kern/vfs_mount.c   Sun Sep  1 22:30:24 2013        (r255135)
+++ head/sys/kern/vfs_mount.c   Sun Sep  1 23:02:59 2013        (r255136)
@@ -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;

Modified: head/sys/sys/mount.h
==============================================================================
--- head/sys/sys/mount.h        Sun Sep  1 22:30:24 2013        (r255135)
+++ head/sys/sys/mount.h        Sun Sep  1 23:02:59 2013        (r255136)
@@ -609,6 +609,7 @@ typedef int vfs_sysctl_t(struct mount *m
                    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)                          \
_______________________________________________
svn-src-head@freebsd.org mailing list
http://lists.freebsd.org/mailman/listinfo/svn-src-head
To unsubscribe, send any mail to "svn-src-head-unsubscr...@freebsd.org"

Reply via email to