Index: share/man/man7/ffs.7 =================================================================== --- share/man/man7/ffs.7 +++ share/man/man7/ffs.7 @@ -287,6 +287,14 @@ Enable support for the rearrangement of blocks to be contiguous. .Pq Default: 1 . +.It Va vfs.ffs.nocacheflush +Disables device write cache flushes. +Without cache flushes, data may be lost on power loss even after +.Xr fsync 2 +or +.Xr fdatasync 2 +completes, on storage with a volatile write cache. +.Pq Default: 1 . .El .Sh HISTORY The Index: sys/ufs/ffs/ffs_vnops.c =================================================================== --- sys/ufs/ffs/ffs_vnops.c +++ sys/ufs/ffs/ffs_vnops.c @@ -87,6 +87,8 @@ #include #include +#include + #include #include #include @@ -253,6 +255,46 @@ return (0); } +static int +ffs_flushwritecache(struct vnode *vp) +{ + struct ufsmount *ump; + struct bio *bp; + int error; + + ump = VFSTOUFS(vp->v_mount); + + /* + * XXX How can we find out if ump has write caching enabled, especially + * volatile write caching, and has DISKFLAG_CANFLUSHCACHE, and skip all + * this if not? + */ + + bp = g_alloc_bio(); + + bp->bio_cmd = BIO_FLUSH; + bp->bio_flags = 0; /* don't need BIO_ORDERED, caller waited */ + bp->bio_data = NULL; + bp->bio_offset = 0; + bp->bio_length = ump->um_cp->provider->mediasize; + bp->bio_done = NULL; + g_io_request(bp, ump->um_cp); + error = biowait(bp, "ffs_fc"); + + /* silently ignore errors due to lack of DISKFLAG_CANFLUSHCACHE */ + if (error != 0 && bp->bio_error == EOPNOTSUPP) + error = 0; + + g_destroy_bio(bp); + + return (error); +} + +SYSCTL_DECL(_vfs_ffs); +static int nocacheflush = 1; +SYSCTL_INT(_vfs_ffs, OID_AUTO, nocacheflush, CTLFLAG_RWTUN, &nocacheflush, 0, + "Disable write cache flushes"); + int ffs_syncvnode(struct vnode *vp, int waitfor, int flags) { @@ -460,6 +502,9 @@ error = ERELOOKUP; if (error == 0) ip->i_flag &= ~IN_NEEDSYNC; + if (error == 0 && waitfor == MNT_WAIT && nocacheflush == 0) + error = ffs_flushwritecache(vp); + return (error); } @@ -1036,6 +1081,12 @@ if (ffs_fsfail_cleanup(VFSTOUFS(vp->v_mount), error)) error = ENXIO; } + /* + * For O_SYNC and O_DSYNC writes, we could potentially use FUA, but for + * now just flush like fsync/fdatasync. + */ + if (error == 0 && (ioflag & IO_SYNC) && nocacheflush == 0) + error = ffs_flushwritecache(vp); return (error); }