Skip site navigation (1)Skip section navigation (2)
Date:      Mon, 8 Dec 2014 20:19:06 +0200
From:      Konstantin Belousov <kostikbel@gmail.com>
To:        fs@freebsd.org
Subject:   VFS_SYNC() call in dounmount()
Message-ID:  <20141208181906.GW97072@kib.kiev.ua>

next in thread | raw e-mail | index | archive | help
Right now, dounmount() has the following code:
	if (((mp->mnt_flag & MNT_RDONLY) ||
	     (error = VFS_SYNC(mp, MNT_WAIT)) == 0) || (flags & MNT_FORCE) != 0)
		error = VFS_UNMOUNT(mp, flags);
In other words, if the filesystem is mounted rw, we try VFS_SYNC().
If the unmount request if forced, VFS_UNMOUNT() is called unconditionally,
otherwise, VFS_UNMOUNT() is only performed when VFS_SYNC() succeeded.

Apparently, the sync call is problematic, both for UFS and NFS. It
was demonstrated by Peter Holm that sufficient fsx load prevents sync
from finishing for unbounded amount of time. The ffs_unmount() makes
neccessary measures to stop writers and to sync filesystem to the stable
state before destroying the mount structures, so removal of VFS_SYNC()
above fixed the test.

More, NFS client just ignores the VFS_SYNC() call for forced unmount,
to work around the hung nfs requests.

Andrey Gapon assured me that ZFS handles unmount correctly and does
not need help in the form of sync before unmount.  The only major
writeable filesystem which apparently did not correctly synced on
unmount is msdosfs.

Note that relying on VFS_SYNC() before VFS_UNMOUNT() to flush all caches
to permanent storage is racy, since VFS does not (and cannot) stop
other threads from writing to fs meantime.  UFS and TMPFS suspend
filesystem in VFS_UNMOUNT(), handling the race in VFS_UNMOUNT().

I propose to only call VFS_SYNC() before VFS_UNMOUNT() for non-forced
unmount.  As I explained, the call for forced case is mostly pointless.
For non-forced unmount, this is needed for KBI compatibility for NFS
(not important), and to increase the chances of unmount succeedeing
(again not important).  I still prefer to keep the call around for
non-forced case for some time.

diff --git a/sys/fs/msdosfs/msdosfs_vfsops.c b/sys/fs/msdosfs/msdosfs_vfsops.c
index 213dd00..d14cdef 100644
--- a/sys/fs/msdosfs/msdosfs_vfsops.c
+++ b/sys/fs/msdosfs/msdosfs_vfsops.c
@@ -797,11 +797,15 @@ msdosfs_unmount(struct mount *mp, int mntflags)
 	int error, flags;
 
 	flags = 0;
-	if (mntflags & MNT_FORCE)
+	error = msdosfs_sync(mp, MNT_WAIT);
+	if ((mntflags & MNT_FORCE) != 0) {
 		flags |= FORCECLOSE;
+	} else if (error != 0) {
+		return (error);
+	}
 	error = vflush(mp, 0, flags, curthread);
-	if (error && error != ENXIO)
-		return error;
+	if (error != 0 && error != ENXIO)
+		return (error);
 	pmp = VFSTOMSDOSFS(mp);
 	if ((pmp->pm_flags & MSDOSFSMNT_RONLY) == 0) {
 		error = markvoldirty(pmp, 0);
diff --git a/sys/kern/vfs_mount.c b/sys/kern/vfs_mount.c
index c407699..b2b4969 100644
--- a/sys/kern/vfs_mount.c
+++ b/sys/kern/vfs_mount.c
@@ -1305,8 +1305,8 @@ dounmount(mp, flags, td)
 		}
 		vput(fsrootvp);
 	}
-	if (((mp->mnt_flag & MNT_RDONLY) ||
-	     (error = VFS_SYNC(mp, MNT_WAIT)) == 0) || (flags & MNT_FORCE) != 0)
+	if ((mp->mnt_flag & MNT_RDONLY) != 0 || (flags & MNT_FORCE) != 0 ||
+	    (error = VFS_SYNC(mp, MNT_WAIT)) == 0)
 		error = VFS_UNMOUNT(mp, flags);
 	vn_finished_write(mp);
 	/*



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?20141208181906.GW97072>