* [PATCH 0/2] xfs: skip redundant fsync cache flushes
@ 2026-11-05 2:49 Daejun Park via B4 Relay
2026-11-05 2:49 ` [PATCH 1/2] xfs: number the cache flushes submitted to each buftarg Daejun Park via B4 Relay
2026-11-05 2:49 ` [PATCH 2/2] xfs: skip the fsync flush when a later flush has completed Daejun Park via B4 Relay
0 siblings, 2 replies; 5+ messages in thread
From: Daejun Park via B4 Relay @ 2026-11-05 2:49 UTC (permalink / raw)
To: Carlos Maiolino, linux-xfs
Cc: Christoph Hellwig, Dave Chinner, Darrick J. Wong, Amir Goldstein,
linux-kernel, Daejun Park
Concurrent fsyncs often wait for an iclog that another task wrote out,
and then send their own cache flush. Patch 1 numbers the cache flushes
on each buftarg. Patch 2 uses the numbers to skip that flush when a
later flush has completed.
16 fio jobs of 4k append + fsync in a QEMU guest: 809 -> 4185
fsyncs/s. fstests -g quick shows no change, and a dm-log-writes crash
test lost no data (patch 2).
---
Daejun Park (2):
xfs: number the cache flushes submitted to each buftarg
xfs: skip the fsync flush when a later flush has completed
fs/xfs/xfs_buf.c | 39 +++++++++++++++++++++++++++++++++++++++
fs/xfs/xfs_buf.h | 40 ++++++++++++++++++++++++++++++++++++++++
fs/xfs/xfs_file.c | 21 +++++++++++++++------
fs/xfs/xfs_log.c | 25 +++++++++++++++++++------
fs/xfs/xfs_log_priv.h | 1 +
5 files changed, 114 insertions(+), 12 deletions(-)
---
base-commit: 1e17fc56469d62d1c1ac45c137a2456d14456f1d
change-id: 20261105-xfs-fsync-flush-seq-83ae2cff6225
Best regards,
--
Daejun Park <daejun7.park@samsung.com>
^ permalink raw reply [flat|nested] 5+ messages in thread
* [PATCH 1/2] xfs: number the cache flushes submitted to each buftarg
2026-11-05 2:49 [PATCH 0/2] xfs: skip redundant fsync cache flushes Daejun Park via B4 Relay
@ 2026-11-05 2:49 ` Daejun Park via B4 Relay
2026-11-09 17:44 ` Brian Foster
2026-11-05 2:49 ` [PATCH 2/2] xfs: skip the fsync flush when a later flush has completed Daejun Park via B4 Relay
1 sibling, 1 reply; 5+ messages in thread
From: Daejun Park via B4 Relay @ 2026-11-05 2:49 UTC (permalink / raw)
To: Carlos Maiolino, linux-xfs
Cc: Christoph Hellwig, Dave Chinner, Darrick J. Wong, Amir Goldstein,
linux-kernel, Daejun Park
From: Daejun Park <daejun7.park@samsung.com>
fsync can only skip its own cache flush when its log force wrote out
the iclog. It cannot tell whether a flush that another task submitted
covers its data.
Number the cache flushes that the log code submits to each buftarg,
and record the highest number that completed without error. Nothing
reads the numbers yet.
Signed-off-by: Daejun Park <daejun7.park@samsung.com>
---
fs/xfs/xfs_buf.c | 13 +++++++++++++
fs/xfs/xfs_buf.h | 21 +++++++++++++++++++++
fs/xfs/xfs_log.c | 25 +++++++++++++++++++------
fs/xfs/xfs_log_priv.h | 1 +
4 files changed, 54 insertions(+), 6 deletions(-)
diff --git a/fs/xfs/xfs_buf.c b/fs/xfs/xfs_buf.c
index 6c93b4f5629..9025b177931 100644
--- a/fs/xfs/xfs_buf.c
+++ b/fs/xfs/xfs_buf.c
@@ -1772,6 +1772,19 @@ xfs_init_buftarg(
return -ENOMEM;
}
+/* Record that the flush numbered @seq completed successfully. */
+void
+xfs_buftarg_flush_done(
+ struct xfs_buftarg *btp,
+ s64 seq)
+{
+ s64 old = atomic64_read(&btp->bt_flush_completed);
+
+ while (seq > old &&
+ !atomic64_try_cmpxchg(&btp->bt_flush_completed, &old, seq))
+ ;
+}
+
struct xfs_buftarg *
xfs_alloc_buftarg(
struct xfs_mount *mp,
diff --git a/fs/xfs/xfs_buf.h b/fs/xfs/xfs_buf.h
index a4729253b56..e8ca4452025 100644
--- a/fs/xfs/xfs_buf.h
+++ b/fs/xfs/xfs_buf.h
@@ -103,6 +103,17 @@ struct xfs_buftarg {
unsigned int bt_awu_max;
struct rhashtable bt_hash;
+
+ /*
+ * Cache flush tracking: a flush that takes the next number from
+ * xfs_buftarg_flush_start() before it is submitted records it here
+ * once it completes without error, keeping the highest number. A
+ * completed flush with a number above a snapshot taken after some
+ * writes completed covers those writes. Flushes without a number
+ * are not counted.
+ */
+ atomic64_t bt_flush_submitted;
+ atomic64_t bt_flush_completed;
};
struct xfs_buf_map {
@@ -356,6 +367,16 @@ xfs_buf_update_cksum(struct xfs_buf *bp, unsigned long cksum_offset)
struct xfs_buftarg *xfs_alloc_buftarg(struct xfs_mount *mp,
struct file *bdev_file);
extern void xfs_free_buftarg(struct xfs_buftarg *);
+
+/* Call right before submitting a cache flush to @btp. */
+static inline s64
+xfs_buftarg_flush_start(
+ struct xfs_buftarg *btp)
+{
+ return atomic64_inc_return(&btp->bt_flush_submitted);
+}
+
+void xfs_buftarg_flush_done(struct xfs_buftarg *btp, s64 seq);
extern void xfs_buftarg_wait(struct xfs_buftarg *);
extern void xfs_buftarg_drain(struct xfs_buftarg *);
int xfs_configure_buftarg(struct xfs_buftarg *btp, unsigned int sectorsize,
diff --git a/fs/xfs/xfs_log.c b/fs/xfs/xfs_log.c
index 74b324f915a..734daa4e99b 100644
--- a/fs/xfs/xfs_log.c
+++ b/fs/xfs/xfs_log.c
@@ -1211,6 +1211,8 @@ xlog_ioend_work(
xlog_force_shutdown(log, SHUTDOWN_LOG_IO_ERROR);
}
+ if (!error && iclog->ic_flush_seq)
+ xfs_buftarg_flush_done(log->l_targ, iclog->ic_flush_seq);
xlog_state_done_syncing(iclog);
bio_uninit(&iclog->ic_bio);
@@ -1570,19 +1572,21 @@ xlog_flush_end_io(
bio_put(bio);
}
-static void
+static s64
xlog_flush_async(
struct xlog_flush_done *done,
- struct block_device *bdev)
+ struct xfs_buftarg *btp)
{
struct bio *bio;
+ s64 seq = xfs_buftarg_flush_start(btp);
- bio = bio_alloc(bdev, 0, REQ_OP_WRITE | REQ_PREFLUSH | REQ_SYNC,
+ bio = bio_alloc(btp->bt_bdev, 0, REQ_OP_WRITE | REQ_PREFLUSH | REQ_SYNC,
GFP_NOFS);
bio->bi_private = done;
bio->bi_end_io = xlog_flush_end_io;
atomic_inc(&done->pending);
submit_bio(bio);
+ return seq;
}
/*
@@ -1605,16 +1609,17 @@ xlog_flush_data_caches(
.pending = ATOMIC_INIT(1),
.done = COMPLETION_INITIALIZER_ONSTACK(done.done),
};
+ s64 dseq = 0, rseq = 0, lseq;
*did_flush = false;
if (mp->m_ddev_targp != mp->m_logdev_targp &&
bdev_write_cache(mp->m_ddev_targp->bt_bdev)) {
- xlog_flush_async(&done, mp->m_ddev_targp->bt_bdev);
+ dseq = xlog_flush_async(&done, mp->m_ddev_targp);
*did_flush = true;
}
if (mp->m_rtdev_targp && mp->m_rtdev_targp != mp->m_ddev_targp &&
bdev_write_cache(mp->m_rtdev_targp->bt_bdev)) {
- xlog_flush_async(&done, mp->m_rtdev_targp->bt_bdev);
+ rseq = xlog_flush_async(&done, mp->m_rtdev_targp);
*did_flush = true;
}
@@ -1623,12 +1628,17 @@ xlog_flush_data_caches(
* If we flushed any other device, also use an async flush for
* the log device so that all flushes happen in parallel.
*/
- xlog_flush_async(&done, mp->m_logdev_targp->bt_bdev);
+ lseq = xlog_flush_async(&done, mp->m_logdev_targp);
xlog_flush_done(&done);
wait_for_completion(&done.done);
if (done.status)
return -EIO;
+ if (dseq)
+ xfs_buftarg_flush_done(mp->m_ddev_targp, dseq);
+ if (rseq)
+ xfs_buftarg_flush_done(mp->m_rtdev_targp, rseq);
+ xfs_buftarg_flush_done(mp->m_logdev_targp, lseq);
}
return 0;
@@ -1688,6 +1698,9 @@ xlog_write_iclog(
iclog->ic_bio.bi_opf |= REQ_PREFLUSH;
if (iclog->ic_flags & XLOG_ICL_NEED_FUA)
iclog->ic_bio.bi_opf |= REQ_FUA;
+ iclog->ic_flush_seq = 0;
+ if (iclog->ic_bio.bi_opf & REQ_PREFLUSH)
+ iclog->ic_flush_seq = xfs_buftarg_flush_start(log->l_targ);
iclog->ic_flags &= ~(XLOG_ICL_NEED_FLUSH | XLOG_ICL_NEED_FLUSH_LOG |
XLOG_ICL_NEED_FUA);
diff --git a/fs/xfs/xfs_log_priv.h b/fs/xfs/xfs_log_priv.h
index 529720a4984..df0f13c4e04 100644
--- a/fs/xfs/xfs_log_priv.h
+++ b/fs/xfs/xfs_log_priv.h
@@ -222,6 +222,7 @@ struct xlog_in_core {
#endif
struct semaphore ic_sema;
struct work_struct ic_end_io_work;
+ s64 ic_flush_seq; /* buftarg flush number */
struct bio ic_bio;
struct bio_vec ic_bvec[];
};
--
2.43.0
^ permalink raw reply [flat|nested] 5+ messages in thread
* [PATCH 2/2] xfs: skip the fsync flush when a later flush has completed
2026-11-05 2:49 [PATCH 0/2] xfs: skip redundant fsync cache flushes Daejun Park via B4 Relay
2026-11-05 2:49 ` [PATCH 1/2] xfs: number the cache flushes submitted to each buftarg Daejun Park via B4 Relay
@ 2026-11-05 2:49 ` Daejun Park via B4 Relay
2026-11-09 17:44 ` Brian Foster
1 sibling, 1 reply; 5+ messages in thread
From: Daejun Park via B4 Relay @ 2026-11-05 2:49 UTC (permalink / raw)
To: Carlos Maiolino, linux-xfs
Cc: Christoph Hellwig, Dave Chinner, Darrick J. Wong, Amir Goldstein,
linux-kernel, Daejun Park
From: Daejun Park <daejun7.park@samsung.com>
Since commit 47c7d0b19502 ("xfs: fix incorrect log_flushed on
fsync"), fsync sends its own cache flush whenever another task wrote
out the iclog it waited for. With concurrent fsyncs, these flushes can
limit throughput.
Skip the flush when a flush submitted after the data writes completed
has finished. Number the fsync's own flush too, so that concurrent
fsyncs can share it. DAX does not use the new skip, because DAX writes
do not go through block I/O.
Suggested-by: Christoph Hellwig <hch@lst.de>
Signed-off-by: Daejun Park <daejun7.park@samsung.com>
---
16 fio jobs of 4k append + fsync, QEMU guest on a 990 PRO without
power loss protection (virtio-blk, cache=none): 809 -> 4185 fsyncs/s,
mean fsync latency 19.8 -> 3.8 ms.
dm-log-writes with 8 fsync writers: no fsync-ed block was lost in 528
replays, while at least 33257 of 48048 fsyncs skipped their flush
through the new check. Taking the snapshot before the writeback loses
15 of 528. The test needs "dm-log-writes: log earlier flushes' writes
when a later flush completes", sent to dm-devel.
---
fs/xfs/xfs_buf.c | 26 ++++++++++++++++++++++++++
fs/xfs/xfs_buf.h | 19 +++++++++++++++++++
fs/xfs/xfs_file.c | 21 +++++++++++++++------
3 files changed, 60 insertions(+), 6 deletions(-)
diff --git a/fs/xfs/xfs_buf.c b/fs/xfs/xfs_buf.c
index 9025b177931..6b545d79a41 100644
--- a/fs/xfs/xfs_buf.c
+++ b/fs/xfs/xfs_buf.c
@@ -1785,6 +1785,32 @@ xfs_buftarg_flush_done(
;
}
+/*
+ * Return true if a flush numbered above @seq has completed. All writes that
+ * completed before @seq was read are then on stable storage.
+ */
+bool
+xfs_buftarg_flushed_since(
+ struct xfs_buftarg *btp,
+ s64 seq)
+{
+ return atomic64_read(&btp->bt_flush_completed) > seq;
+}
+
+/* Flush the device cache and record it. */
+int
+xfs_buftarg_flush(
+ struct xfs_buftarg *btp)
+{
+ s64 seq = xfs_buftarg_flush_start(btp);
+ int error;
+
+ error = blkdev_issue_flush(btp->bt_bdev);
+ if (!error)
+ xfs_buftarg_flush_done(btp, seq);
+ return error;
+}
+
struct xfs_buftarg *
xfs_alloc_buftarg(
struct xfs_mount *mp,
diff --git a/fs/xfs/xfs_buf.h b/fs/xfs/xfs_buf.h
index e8ca4452025..0f48ee6d3f2 100644
--- a/fs/xfs/xfs_buf.h
+++ b/fs/xfs/xfs_buf.h
@@ -368,6 +368,23 @@ struct xfs_buftarg *xfs_alloc_buftarg(struct xfs_mount *mp,
struct file *bdev_file);
extern void xfs_free_buftarg(struct xfs_buftarg *);
+/*
+ * Call after the writes that a flush must cover have completed, and pass the
+ * result to xfs_buftarg_flushed_since().
+ */
+static inline s64
+xfs_buftarg_flush_seq(
+ struct xfs_buftarg *btp)
+{
+ /*
+ * Order the read after the write completions the caller waited for.
+ * Pairs with the fully ordered atomic64_inc_return() that
+ * xfs_buftarg_flush_start() does before the flush is submitted.
+ */
+ smp_mb();
+ return atomic64_read(&btp->bt_flush_submitted);
+}
+
/* Call right before submitting a cache flush to @btp. */
static inline s64
xfs_buftarg_flush_start(
@@ -377,6 +394,8 @@ xfs_buftarg_flush_start(
}
void xfs_buftarg_flush_done(struct xfs_buftarg *btp, s64 seq);
+bool xfs_buftarg_flushed_since(struct xfs_buftarg *btp, s64 seq);
+int xfs_buftarg_flush(struct xfs_buftarg *btp);
extern void xfs_buftarg_wait(struct xfs_buftarg *);
extern void xfs_buftarg_drain(struct xfs_buftarg *);
int xfs_configure_buftarg(struct xfs_buftarg *btp, unsigned int sectorsize,
diff --git a/fs/xfs/xfs_file.c b/fs/xfs/xfs_file.c
index 396aca8c66a..a98361e9ead 100644
--- a/fs/xfs/xfs_file.c
+++ b/fs/xfs/xfs_file.c
@@ -87,7 +87,9 @@ xfs_file_fsync(
int datasync)
{
struct xfs_inode *ip = XFS_I(file->f_mapping->host);
+ struct xfs_buftarg *btp = xfs_inode_buftarg(ip);
int log_flushed = 0;
+ s64 flush_seq;
int error;
trace_xfs_file_fsync(ip);
@@ -95,6 +97,7 @@ xfs_file_fsync(
error = file_write_and_wait_range(file, start, end);
if (error)
return error;
+ flush_seq = xfs_buftarg_flush_seq(btp);
if (xfs_is_shutdown(ip->i_mount))
return -EIO;
@@ -115,16 +118,22 @@ xfs_file_fsync(
}
/*
- * If the log force was a no-op, we may still need to flush the
- * file data target cache here. This can happen for fdatasync/O_DSYNC
- * when no metadata needed to be committed.
+ * Flush the file data target unless a flush submitted to it after the
+ * data writes above completed has already finished. That is usually
+ * the flush before an iclog written out by this log force or by
+ * another task, or another fsync's flush.
+ *
+ * DAX writes are CPU stores, not block I/O, and the check above
+ * relies on REQ_PREFLUSH covering completed block I/O. So DAX only
+ * skips the flush when this log force flushed, as before.
*
* Use the inode's actual file data target rather than assuming the
* main data device.
*/
- if (!log_flushed)
- return blkdev_issue_flush(xfs_inode_buftarg(ip)->bt_bdev);
- return 0;
+ if (log_flushed ||
+ (!IS_DAX(VFS_I(ip)) && xfs_buftarg_flushed_since(btp, flush_seq)))
+ return 0;
+ return xfs_buftarg_flush(btp);
}
static int
--
2.43.0
^ permalink raw reply [flat|nested] 5+ messages in thread
* Re: [PATCH 1/2] xfs: number the cache flushes submitted to each buftarg
2026-11-05 2:49 ` [PATCH 1/2] xfs: number the cache flushes submitted to each buftarg Daejun Park via B4 Relay
@ 2026-11-09 17:44 ` Brian Foster
0 siblings, 0 replies; 5+ messages in thread
From: Brian Foster @ 2026-11-09 17:44 UTC (permalink / raw)
To: daejun7.park
Cc: Carlos Maiolino, linux-xfs, Christoph Hellwig, Dave Chinner,
Darrick J. Wong, Amir Goldstein, linux-kernel
On Thu, Nov 05, 2026 at 11:49:12AM +0900, Daejun Park via B4 Relay wrote:
> From: Daejun Park <daejun7.park@samsung.com>
>
> fsync can only skip its own cache flush when its log force wrote out
> the iclog. It cannot tell whether a flush that another task submitted
> covers its data.
>
> Number the cache flushes that the log code submits to each buftarg,
> and record the highest number that completed without error. Nothing
> reads the numbers yet.
>
> Signed-off-by: Daejun Park <daejun7.park@samsung.com>
> ---
> fs/xfs/xfs_buf.c | 13 +++++++++++++
> fs/xfs/xfs_buf.h | 21 +++++++++++++++++++++
> fs/xfs/xfs_log.c | 25 +++++++++++++++++++------
> fs/xfs/xfs_log_priv.h | 1 +
> 4 files changed, 54 insertions(+), 6 deletions(-)
>
...
> diff --git a/fs/xfs/xfs_log.c b/fs/xfs/xfs_log.c
> index 74b324f915a..734daa4e99b 100644
> --- a/fs/xfs/xfs_log.c
> +++ b/fs/xfs/xfs_log.c
...
> @@ -1688,6 +1698,9 @@ xlog_write_iclog(
> iclog->ic_bio.bi_opf |= REQ_PREFLUSH;
> if (iclog->ic_flags & XLOG_ICL_NEED_FUA)
> iclog->ic_bio.bi_opf |= REQ_FUA;
> + iclog->ic_flush_seq = 0;
> + if (iclog->ic_bio.bi_opf & REQ_PREFLUSH)
> + iclog->ic_flush_seq = xfs_buftarg_flush_start(log->l_targ);
Bit of a nit, but it looks like this would be cleaner if we moved both
of these a bit further up. I.e., zero ->ic_flush_seq right after where
we init the iclog->ic_bio fields, and then move the assignment under the
same block where we set the PREFLUSH flag. Hm?
Brian
>
> iclog->ic_flags &= ~(XLOG_ICL_NEED_FLUSH | XLOG_ICL_NEED_FLUSH_LOG |
> XLOG_ICL_NEED_FUA);
> diff --git a/fs/xfs/xfs_log_priv.h b/fs/xfs/xfs_log_priv.h
> index 529720a4984..df0f13c4e04 100644
> --- a/fs/xfs/xfs_log_priv.h
> +++ b/fs/xfs/xfs_log_priv.h
> @@ -222,6 +222,7 @@ struct xlog_in_core {
> #endif
> struct semaphore ic_sema;
> struct work_struct ic_end_io_work;
> + s64 ic_flush_seq; /* buftarg flush number */
> struct bio ic_bio;
> struct bio_vec ic_bvec[];
> };
>
> --
> 2.43.0
>
>
>
^ permalink raw reply [flat|nested] 5+ messages in thread
* Re: [PATCH 2/2] xfs: skip the fsync flush when a later flush has completed
2026-11-05 2:49 ` [PATCH 2/2] xfs: skip the fsync flush when a later flush has completed Daejun Park via B4 Relay
@ 2026-11-09 17:44 ` Brian Foster
0 siblings, 0 replies; 5+ messages in thread
From: Brian Foster @ 2026-11-09 17:44 UTC (permalink / raw)
To: daejun7.park
Cc: Carlos Maiolino, linux-xfs, Christoph Hellwig, Dave Chinner,
Darrick J. Wong, Amir Goldstein, linux-kernel
On Thu, Nov 05, 2026 at 11:49:13AM +0900, Daejun Park via B4 Relay wrote:
> From: Daejun Park <daejun7.park@samsung.com>
>
> Since commit 47c7d0b19502 ("xfs: fix incorrect log_flushed on
> fsync"), fsync sends its own cache flush whenever another task wrote
> out the iclog it waited for. With concurrent fsyncs, these flushes can
> limit throughput.
>
> Skip the flush when a flush submitted after the data writes completed
> has finished. Number the fsync's own flush too, so that concurrent
> fsyncs can share it. DAX does not use the new skip, because DAX writes
> do not go through block I/O.
>
> Suggested-by: Christoph Hellwig <hch@lst.de>
> Signed-off-by: Daejun Park <daejun7.park@samsung.com>
> ---
> 16 fio jobs of 4k append + fsync, QEMU guest on a 990 PRO without
> power loss protection (virtio-blk, cache=none): 809 -> 4185 fsyncs/s,
> mean fsync latency 19.8 -> 3.8 ms.
>
Can you include this in the commit log so we have a record of the perf
impact?
> dm-log-writes with 8 fsync writers: no fsync-ed block was lost in 528
> replays, while at least 33257 of 48048 fsyncs skipped their flush
> through the new check. Taking the snapshot before the writeback loses
> 15 of 528. The test needs "dm-log-writes: log earlier flushes' writes
> when a later flush completes", sent to dm-devel.
> ---
> fs/xfs/xfs_buf.c | 26 ++++++++++++++++++++++++++
> fs/xfs/xfs_buf.h | 19 +++++++++++++++++++
> fs/xfs/xfs_file.c | 21 +++++++++++++++------
> 3 files changed, 60 insertions(+), 6 deletions(-)
>
> diff --git a/fs/xfs/xfs_buf.c b/fs/xfs/xfs_buf.c
> index 9025b177931..6b545d79a41 100644
> --- a/fs/xfs/xfs_buf.c
> +++ b/fs/xfs/xfs_buf.c
> @@ -1785,6 +1785,32 @@ xfs_buftarg_flush_done(
> ;
> }
>
> +/*
> + * Return true if a flush numbered above @seq has completed. All writes that
> + * completed before @seq was read are then on stable storage.
> + */
I think it would be helpful if the comment here and perhaps above
xfs_buftarg_flush_seq() referred to the submit/completion ordering logic
being applied. Maybe even rename seq -> submit_seq as well to make it
more clear. For example, something like:
"Return true if a flush has been submitted and completed since
@submit_seq was sampled. If so, this guarantees writes completed before
@submit_seq are flushed to disk."
But feel free to change that.
> +bool
> +xfs_buftarg_flushed_since(
> + struct xfs_buftarg *btp,
> + s64 seq)
> +{
> + return atomic64_read(&btp->bt_flush_completed) > seq;
> +}
> +
> +/* Flush the device cache and record it. */
> +int
> +xfs_buftarg_flush(
> + struct xfs_buftarg *btp)
> +{
> + s64 seq = xfs_buftarg_flush_start(btp);
> + int error;
> +
> + error = blkdev_issue_flush(btp->bt_bdev);
> + if (!error)
> + xfs_buftarg_flush_done(btp, seq);
> + return error;
> +}
> +
> struct xfs_buftarg *
> xfs_alloc_buftarg(
> struct xfs_mount *mp,
...
> diff --git a/fs/xfs/xfs_file.c b/fs/xfs/xfs_file.c
> index 396aca8c66a..a98361e9ead 100644
> --- a/fs/xfs/xfs_file.c
> +++ b/fs/xfs/xfs_file.c
> @@ -87,7 +87,9 @@ xfs_file_fsync(
> int datasync)
> {
> struct xfs_inode *ip = XFS_I(file->f_mapping->host);
> + struct xfs_buftarg *btp = xfs_inode_buftarg(ip);
> int log_flushed = 0;
> + s64 flush_seq;
> int error;
>
> trace_xfs_file_fsync(ip);
> @@ -95,6 +97,7 @@ xfs_file_fsync(
> error = file_write_and_wait_range(file, start, end);
> if (error)
> return error;
> + flush_seq = xfs_buftarg_flush_seq(btp);
>
Ok, so I think I follow the logic. We write the file, sample the submit
sequence (after write completes), then can optimize away our own flush
if we can confirm that a flush submitted after write completion has
already completed. Right?
That kind of makes me wonder whether we could do something like "flush
or wait on any flush submitted after this seq," but maybe that's
something for another day. ;)
> if (xfs_is_shutdown(ip->i_mount))
> return -EIO;
> @@ -115,16 +118,22 @@ xfs_file_fsync(
> }
>
> /*
> - * If the log force was a no-op, we may still need to flush the
> - * file data target cache here. This can happen for fdatasync/O_DSYNC
> - * when no metadata needed to be committed.
Did we intend to remove the comment on why we flush in the first place?
Brian
> + * Flush the file data target unless a flush submitted to it after the
> + * data writes above completed has already finished. That is usually
> + * the flush before an iclog written out by this log force or by
> + * another task, or another fsync's flush.
> + *
> + * DAX writes are CPU stores, not block I/O, and the check above
> + * relies on REQ_PREFLUSH covering completed block I/O. So DAX only
> + * skips the flush when this log force flushed, as before.
> *
> * Use the inode's actual file data target rather than assuming the
> * main data device.
> */
> - if (!log_flushed)
> - return blkdev_issue_flush(xfs_inode_buftarg(ip)->bt_bdev);
> - return 0;
> + if (log_flushed ||
> + (!IS_DAX(VFS_I(ip)) && xfs_buftarg_flushed_since(btp, flush_seq)))
> + return 0;
> + return xfs_buftarg_flush(btp);
> }
>
> static int
>
> --
> 2.43.0
>
>
>
^ permalink raw reply [flat|nested] 5+ messages in thread
end of thread, other threads:[~2026-11-09 17:45 UTC | newest]
Thread overview: 5+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-11-05 2:49 [PATCH 0/2] xfs: skip redundant fsync cache flushes Daejun Park via B4 Relay
2026-11-05 2:49 ` [PATCH 1/2] xfs: number the cache flushes submitted to each buftarg Daejun Park via B4 Relay
2026-11-09 17:44 ` Brian Foster
2026-11-05 2:49 ` [PATCH 2/2] xfs: skip the fsync flush when a later flush has completed Daejun Park via B4 Relay
2026-11-09 17:44 ` Brian Foster
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®