Add new unmount(2) flag, MNT_NONBUSY, to check whether there are

any open vnodes before proceeding. Make autounmound(8) use this flag.
Without it, even an unsuccessfull unmount causes filesystem flush,
which interferes with normal operation.

Reviewed by:	kib@
Approved by:	re (gjb@)
MFC after:	1 month
Differential Revision:	https://reviews.freebsd.org/D7047
This commit is contained in:
Edward Tomasz Napierala 2016-07-07 09:03:57 +00:00
parent af625dc998
commit debc480e03
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=302388
5 changed files with 68 additions and 12 deletions

View File

@ -28,7 +28,7 @@
.\" @(#)umount.8 8.2 (Berkeley) 5/8/95 .\" @(#)umount.8 8.2 (Berkeley) 5/8/95
.\" $FreeBSD$ .\" $FreeBSD$
.\" .\"
.Dd June 17, 2015 .Dd July 7, 2016
.Dt UMOUNT 8 .Dt UMOUNT 8
.Os .Os
.Sh NAME .Sh NAME
@ -36,12 +36,12 @@
.Nd unmount file systems .Nd unmount file systems
.Sh SYNOPSIS .Sh SYNOPSIS
.Nm .Nm
.Op Fl fv .Op Fl fnv
.Ar special ... | node ... | fsid ... .Ar special ... | node ... | fsid ...
.Nm .Nm
.Fl a | A .Fl a | A
.Op Fl F Ar fstab .Op Fl F Ar fstab
.Op Fl fv .Op Fl fnv
.Op Fl h Ar host .Op Fl h Ar host
.Op Fl t Ar type .Op Fl t Ar type
.Sh DESCRIPTION .Sh DESCRIPTION
@ -94,6 +94,15 @@ option and, unless otherwise specified with the
option, will only unmount option, will only unmount
.Tn NFS .Tn NFS
file systems. file systems.
.It Fl n
Unless the
.Fl f
is used, the
.Nm
will not unmount an active file system.
It will, however, perform a flush.
This flag disables this behaviour, preventing the flush
if there are any files open.
.It Fl t Ar type .It Fl t Ar type
Is used to indicate the actions should only be taken on Is used to indicate the actions should only be taken on
file systems of the specified type. file systems of the specified type.

View File

@ -91,7 +91,7 @@ main(int argc, char *argv[])
struct addrinfo hints; struct addrinfo hints;
all = errs = 0; all = errs = 0;
while ((ch = getopt(argc, argv, "AaF:fh:t:v")) != -1) while ((ch = getopt(argc, argv, "AaF:fh:nt:v")) != -1)
switch (ch) { switch (ch) {
case 'A': case 'A':
all = 2; all = 2;
@ -103,12 +103,15 @@ main(int argc, char *argv[])
setfstab(optarg); setfstab(optarg);
break; break;
case 'f': case 'f':
fflag = MNT_FORCE; fflag |= MNT_FORCE;
break; break;
case 'h': /* -h implies -A. */ case 'h': /* -h implies -A. */
all = 2; all = 2;
nfshost = optarg; nfshost = optarg;
break; break;
case 'n':
fflag |= MNT_NONBUSY;
break;
case 't': case 't':
if (typelist != NULL) if (typelist != NULL)
err(1, "only one -t option may be specified"); err(1, "only one -t option may be specified");
@ -124,8 +127,11 @@ main(int argc, char *argv[])
argc -= optind; argc -= optind;
argv += optind; argv += optind;
if ((fflag & MNT_FORCE) != 0 && (fflag & MNT_NONBUSY) != 0)
err(1, "-f and -n are mutually exclusive");
/* Start disks transferring immediately. */ /* Start disks transferring immediately. */
if ((fflag & MNT_FORCE) == 0) if ((fflag & (MNT_FORCE | MNT_NONBUSY)) == 0)
sync(); sync();
if ((argc == 0 && !all) || (argc != 0 && all)) if ((argc == 0 && !all) || (argc != 0 && all))
@ -609,7 +615,7 @@ usage(void)
{ {
(void)fprintf(stderr, "%s\n%s\n", (void)fprintf(stderr, "%s\n%s\n",
"usage: umount [-fv] special ... | node ... | fsid ...", "usage: umount [-fnv] special ... | node ... | fsid ...",
" umount -a | -A [-F fstab] [-fv] [-h host] [-t type]"); " umount -a | -A [-F fstab] [-fnv] [-h host] [-t type]");
exit(1); exit(1);
} }

View File

@ -1204,6 +1204,28 @@ sys_unmount(struct thread *td, struct unmount_args *uap)
return (error); return (error);
} }
/*
* Return error if any of the vnodes, ignoring the root vnode
* and the syncer vnode, have non-zero usecount.
*/
static int
vfs_check_usecounts(struct mount *mp)
{
struct vnode *vp, *mvp;
MNT_VNODE_FOREACH_ALL(vp, mp, mvp) {
if ((vp->v_vflag & VV_ROOT) == 0 && vp->v_type != VNON &&
vp->v_usecount != 0) {
VI_UNLOCK(vp);
MNT_VNODE_FOREACH_ALL_ABORT(mp, mvp);
return (EBUSY);
}
VI_UNLOCK(vp);
}
return (0);
}
/* /*
* Do the actual filesystem unmount. * Do the actual filesystem unmount.
*/ */
@ -1260,6 +1282,21 @@ dounmount(struct mount *mp, int flags, struct thread *td)
return (EBUSY); return (EBUSY);
} }
mp->mnt_kern_flag |= MNTK_UNMOUNT | MNTK_NOINSMNTQ; mp->mnt_kern_flag |= MNTK_UNMOUNT | MNTK_NOINSMNTQ;
if (flags & MNT_NONBUSY) {
MNT_IUNLOCK(mp);
error = vfs_check_usecounts(mp);
MNT_ILOCK(mp);
if (error != 0) {
mp->mnt_kern_flag &= ~(MNTK_UNMOUNT | MNTK_NOINSMNTQ);
MNT_IUNLOCK(mp);
if (coveredvp != NULL) {
VOP_UNLOCK(coveredvp, 0);
vdrop(coveredvp);
}
vn_finished_write(mp);
return (error);
}
}
/* Allow filesystems to detect that a forced unmount is in progress. */ /* 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; mp->mnt_kern_flag |= MNTK_UNMOUNTF;

View File

@ -312,17 +312,21 @@ void __mnt_vnode_markerfree_active(struct vnode **mvp, struct mount *);
* External filesystem command modifier flags. * External filesystem command modifier flags.
* Unmount can use the MNT_FORCE flag. * Unmount can use the MNT_FORCE flag.
* XXX: These are not STATES and really should be somewhere else. * XXX: These are not STATES and really should be somewhere else.
* XXX: MNT_BYFSID collides with MNT_ACLS, but because MNT_ACLS is only used for * XXX: MNT_BYFSID and MNT_NONBUSY collide with MNT_ACLS and MNT_MULTILABEL,
* mount(2) and MNT_BYFSID is only used for unmount(2) it's harmless. * but because MNT_ACLS and MNT_MULTILABEL are only used for mount(2),
* and MNT_BYFSID and MNT_NONBUSY are only used for unmount(2),
* it's harmless.
*/ */
#define MNT_UPDATE 0x0000000000010000ULL /* not real mount, just update */ #define MNT_UPDATE 0x0000000000010000ULL /* not real mount, just update */
#define MNT_DELEXPORT 0x0000000000020000ULL /* delete export host lists */ #define MNT_DELEXPORT 0x0000000000020000ULL /* delete export host lists */
#define MNT_RELOAD 0x0000000000040000ULL /* reload filesystem data */ #define MNT_RELOAD 0x0000000000040000ULL /* reload filesystem data */
#define MNT_FORCE 0x0000000000080000ULL /* force unmount or readonly */ #define MNT_FORCE 0x0000000000080000ULL /* force unmount or readonly */
#define MNT_SNAPSHOT 0x0000000001000000ULL /* snapshot the filesystem */ #define MNT_SNAPSHOT 0x0000000001000000ULL /* snapshot the filesystem */
#define MNT_NONBUSY 0x0000000004000000ULL /* check vnode use counts. */
#define MNT_BYFSID 0x0000000008000000ULL /* specify filesystem by ID. */ #define MNT_BYFSID 0x0000000008000000ULL /* specify filesystem by ID. */
#define MNT_CMDFLAGS (MNT_UPDATE | MNT_DELEXPORT | MNT_RELOAD | \ #define MNT_CMDFLAGS (MNT_UPDATE | MNT_DELEXPORT | MNT_RELOAD | \
MNT_FORCE | MNT_SNAPSHOT | MNT_BYFSID) MNT_FORCE | MNT_SNAPSHOT | MNT_NONBUSY | \
MNT_BYFSID)
/* /*
* Internal filesystem control flags stored in mnt_kern_flag. * Internal filesystem control flags stored in mnt_kern_flag.
* *

View File

@ -161,7 +161,7 @@ unmount_by_fsid(const fsid_t fsid, const char *mountpoint)
if (ret < 0) if (ret < 0)
log_err(1, "asprintf"); log_err(1, "asprintf");
error = unmount(fsid_str, MNT_BYFSID); error = unmount(fsid_str, MNT_NONBUSY | MNT_BYFSID);
if (error != 0) { if (error != 0) {
if (errno == EBUSY) { if (errno == EBUSY) {
log_debugx("cannot unmount %s (%s): %s", log_debugx("cannot unmount %s (%s): %s",