summaryrefslogtreecommitdiffstats
path: root/sys/kern/vfs_subr.c
diff options
context:
space:
mode:
authoriedowse <iedowse@FreeBSD.org>2001-05-16 18:04:37 +0000
committeriedowse <iedowse@FreeBSD.org>2001-05-16 18:04:37 +0000
commitdafd513732df8c8fa7b8c5069ae3af2203853494 (patch)
tree57e0aeed936a6069b4a1048d66e40c879871f601 /sys/kern/vfs_subr.c
parent7faffe08a53e9fd2e664451fb6634f6377a33a46 (diff)
downloadFreeBSD-src-dafd513732df8c8fa7b8c5069ae3af2203853494.zip
FreeBSD-src-dafd513732df8c8fa7b8c5069ae3af2203853494.tar.gz
Change the second argument of vflush() to an integer that specifies
the number of references on the filesystem root vnode to be both expected and released. Many filesystems hold an extra reference on the filesystem root vnode, which must be accounted for when determining if the filesystem is busy and then released if it isn't busy. The old `skipvp' approach required individual filesystem xxx_unmount functions to re-implement much of vflush()'s logic to deal with the root vnode. All 9 filesystems that hold an extra reference on the root vnode got the logic wrong in the case of forced unmounts, so `umount -f' would always fail if there were any extra root vnode references. Fix this issue centrally in vflush(), now that we can. This commit also fixes a vnode reference leak in devfs, which could result in idle devfs filesystems that refuse to unmount. Reviewed by: phk, bp
Diffstat (limited to 'sys/kern/vfs_subr.c')
-rw-r--r--sys/kern/vfs_subr.c56
1 files changed, 45 insertions, 11 deletions
diff --git a/sys/kern/vfs_subr.c b/sys/kern/vfs_subr.c
index cf2ea29..2f4dc8d 100644
--- a/sys/kern/vfs_subr.c
+++ b/sys/kern/vfs_subr.c
@@ -1627,10 +1627,22 @@ vdrop(vp)
/*
* Remove any vnodes in the vnode table belonging to mount point mp.
*
- * If MNT_NOFORCE is specified, there should not be any active ones,
+ * If FORCECLOSE is not specified, there should not be any active ones,
* return error if any are found (nb: this is a user error, not a
- * system error). If MNT_FORCE is specified, detach any active vnodes
+ * system error). If FORCECLOSE is specified, detach any active vnodes
* that are found.
+ *
+ * If WRITECLOSE is set, only flush out regular file vnodes open for
+ * writing.
+ *
+ * SKIPSYSTEM causes any vnodes marked VSYSTEM to be skipped.
+ *
+ * `rootrefs' specifies the base reference count for the root vnode
+ * of this filesystem. The root vnode is considered busy if its
+ * v_usecount exceeds this value. On a successful return, vflush()
+ * will call vrele() on the root vnode exactly rootrefs times.
+ * If the SKIPSYSTEM or WRITECLOSE flags are specified, rootrefs must
+ * be zero.
*/
#ifdef DIAGNOSTIC
static int busyprt = 0; /* print out busy vnodes */
@@ -1638,15 +1650,26 @@ SYSCTL_INT(_debug, OID_AUTO, busyprt, CTLFLAG_RW, &busyprt, 0, "");
#endif
int
-vflush(mp, skipvp, flags)
+vflush(mp, rootrefs, flags)
struct mount *mp;
- struct vnode *skipvp;
+ int rootrefs;
int flags;
{
struct proc *p = curproc; /* XXX */
- struct vnode *vp, *nvp;
- int busy = 0;
+ struct vnode *vp, *nvp, *rootvp = NULL;
+ int busy = 0, error;
+ if (rootrefs > 0) {
+ KASSERT((flags & (SKIPSYSTEM | WRITECLOSE)) == 0,
+ ("vflush: bad args"));
+ /*
+ * Get the filesystem root vnode. We can vput() it
+ * immediately, since with rootrefs > 0, it won't go away.
+ */
+ if ((error = VFS_ROOT(mp, &rootvp)) != 0)
+ return (error);
+ vput(rootvp);
+ }
mtx_lock(&mntvnode_mtx);
loop:
for (vp = LIST_FIRST(&mp->mnt_vnodelist); vp; vp = nvp) {
@@ -1657,11 +1680,6 @@ loop:
if (vp->v_mount != mp)
goto loop;
nvp = LIST_NEXT(vp, v_mntvnodes);
- /*
- * Skip over a selected vnode.
- */
- if (vp == skipvp)
- continue;
mtx_lock(&vp->v_interlock);
/*
@@ -1717,8 +1735,24 @@ loop:
busy++;
}
mtx_unlock(&mntvnode_mtx);
+ if (rootrefs > 0 && (flags & FORCECLOSE) == 0) {
+ /*
+ * If just the root vnode is busy, and if its refcount
+ * is equal to `rootrefs', then go ahead and kill it.
+ */
+ mtx_lock(&rootvp->v_interlock);
+ KASSERT(busy > 0, ("vflush: not busy"));
+ KASSERT(rootvp->v_usecount >= rootrefs, ("vflush: rootrefs"));
+ if (busy == 1 && rootvp->v_usecount == rootrefs) {
+ vgonel(rootvp, p);
+ busy = 0;
+ } else
+ mtx_unlock(&rootvp->v_interlock);
+ }
if (busy)
return (EBUSY);
+ for (; rootrefs > 0; rootrefs--)
+ vrele(rootvp);
return (0);
}
OpenPOWER on IntegriCloud