VFS_SYNC() call in dounmount()
Kirk McKusick
mckusick at mckusick.com
Mon Dec 8 18:38:41 UTC 2014
> Date: Mon, 8 Dec 2014 20:19:06 +0200
> From: Konstantin Belousov <kostikbel at gmail.com>
> To: fs at freebsd.org
> Subject: VFS_SYNC() call in dounmount()
>
> 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);
> /*
I agree with your analysis and believe that your proposed change is
functionally correct for at least UFS.
Kirk McKusick
More information about the freebsd-fs
mailing list