diff options
author | iedowse <iedowse@FreeBSD.org> | 2001-05-16 18:04:37 +0000 |
---|---|---|
committer | iedowse <iedowse@FreeBSD.org> | 2001-05-16 18:04:37 +0000 |
commit | dafd513732df8c8fa7b8c5069ae3af2203853494 (patch) | |
tree | 57e0aeed936a6069b4a1048d66e40c879871f601 /sys/kern/vfs_subr.c | |
parent | 7faffe08a53e9fd2e664451fb6634f6377a33a46 (diff) | |
download | FreeBSD-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.c | 56 |
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); } |