git: 1498efc4243e - main - ufs: make fdatasync(2) write dirty indirect blocks
- Go to: [ bottom of page ] [ top of archives ] [ this month ]
Date: Tue, 13 Oct 2026 04:29:53 UTC
The branch main has been updated by sobomax:
URL: https://cgit.FreeBSD.org/src/commit/?id=1498efc4243e963503179547221335940e2aeded
commit 1498efc4243e963503179547221335940e2aeded
Author: Maxim Sobolev <sobomax@FreeBSD.org>
AuthorDate: 2026-10-13 02:41:27 +0000
Commit: Maxim Sobolev <sobomax@FreeBSD.org>
CommitDate: 2026-10-13 04:28:32 +0000
ufs: make fdatasync(2) write dirty indirect blocks
A data-only ffs_syncvnode() skips dirty indirect blocks on file systems
without soft updates, and with soft updates stops making passes once
only indirect blocks are left dirty. Indirect blocks only get dirty
when the block pointers in them change, and those pointers are what
makes a newly allocated block reachable. So when fdatasync() follows
a write that extended the file or filled a hole past the direct blocks,
the data and the inode size reached the disk but the pointer to the new
block did not, and a crash lost the "synced" data. With soft updates
the indirect block was usually written once, with the new pointer
rolled back, and never rewritten.
POSIX requires fdatasync() to write the metadata needed to retrieve the
data, which 52488b51489 already acknowledged for the inode, so treat
dirty indirect blocks the same way for a data-only sync as for a full
one. This restores the loop to what it was before 2f514f92cf0. Plain
overwrites, the case data-only sync is meant to speed up, do not dirty
indirect blocks and still write neither them nor the inode.
Fixes: 2f514f92cf0 ("Implement VOP_FDATASYNC() for UFS.")
Sponsored by: Sippy Software, Inc.
Reviewed by: kib
Tested by: pho
Differential Revision: https://reviews.freebsd.org/D60134
MFC after: 1 month
---
sys/ufs/ffs/ffs_vnops.c | 50 +++++++++++++++----------------------------------
1 file changed, 15 insertions(+), 35 deletions(-)
diff --git a/sys/ufs/ffs/ffs_vnops.c b/sys/ufs/ffs/ffs_vnops.c
index be708d07a2ca..4044ecf20f92 100644
--- a/sys/ufs/ffs/ffs_vnops.c
+++ b/sys/ufs/ffs/ffs_vnops.c
@@ -267,7 +267,7 @@ ffs_syncvnode(struct vnode *vp, int waitfor, int flags)
struct buf *bp, *nbp;
ufs_lbn_t lbn;
int error, passes, wflag;
- bool still_dirty, unlocked, wait;
+ bool unlocked, wait;
ip = VTOI(vp);
bo = &vp->v_bufobj;
@@ -314,14 +314,13 @@ loop:
/*
* Flush indirects in order, if requested.
*
- * Note that if only datasync is requested, we can
- * skip indirect blocks when softupdates are not
- * active. Otherwise we must flush them with data,
- * since dependencies prevent data block writes.
+ * Indirect blocks are only dirtied by changes to the
+ * block pointers they hold, so even a data-only sync
+ * must write them: they are needed to retrieve newly
+ * allocated blocks.
*/
if (waitfor == MNT_WAIT && bp->b_lblkno <= -UFS_NDADDR &&
- (lbn_level(bp->b_lblkno) >= passes ||
- ((flags & DATA_ONLY) != 0 && !DOINGSOFTDEP(vp))))
+ lbn_level(bp->b_lblkno) >= passes)
continue;
if (bp->b_lblkno > lbn)
panic("ffs_syncvnode: syncing truncated data.");
@@ -421,35 +420,16 @@ next_locked:
* these will be done with one sync and one async pass.
*/
if (bo->bo_dirty.bv_cnt > 0) {
- if ((flags & DATA_ONLY) == 0) {
- still_dirty = true;
- } else {
- /*
- * For data-only sync, dirty indirect buffers
- * are ignored.
- */
- still_dirty = false;
- TAILQ_FOREACH(bp, &bo->bo_dirty.bv_hd, b_bobufs) {
- if (bp->b_lblkno > -UFS_NDADDR) {
- still_dirty = true;
- break;
- }
- }
- }
-
- if (still_dirty) {
- /* Write the inode after sync passes to flush deps. */
- if (wait && DOINGSOFTDEP(vp) &&
- (flags & NO_INO_UPDT) == 0) {
- BO_UNLOCK(bo);
- ffs_update(vp, 1);
- BO_LOCK(bo);
- }
- /* switch between sync/async. */
- wait = !wait;
- if (wait || ++passes < UFS_NIADDR + 2)
- goto loop;
+ /* Write the inode after sync passes to flush deps. */
+ if (wait && DOINGSOFTDEP(vp) && (flags & NO_INO_UPDT) == 0) {
+ BO_UNLOCK(bo);
+ ffs_update(vp, 1);
+ BO_LOCK(bo);
}
+ /* switch between sync/async. */
+ wait = !wait;
+ if (wait || ++passes < UFS_NIADDR + 2)
+ goto loop;
}
BO_UNLOCK(bo);
error = 0;