xfs: make pNFS LAYOUTCOMMIT stable the way fdatasync does

Every LAYOUTCOMMIT commits a synchronous transaction and forces the
log. fdatasync after the same overwrite does not, because timestamps
alone do not set ili_datasync_seq.

Do what fdatasync does. Commit without XFS_TRANS_SYNC, force the log up
to ili_datasync_seq, and flush the data device cache if that wrote no
iclog. Conversions and size changes still force the log. As with
fdatasync, a crash before the next log write can lose the new
timestamps and the change attribute that nfsd derives from the ctime.

Signed-off-by: Daejun Park <daejun7.park@samsung.com>
Reviewed-by: Christoph Hellwig <hch@lst.de>
Signed-off-by: Carlos Maiolino <cem@kernel.org>
This commit is contained in:
Daejun Park 2026-10-29 11:23:06 +09:00 committed by Carlos Maiolino
parent 199eee7c67
commit 9cc3b0eda9

View File

@ -295,10 +295,12 @@ xfs_pnfs_validate_isize(
* converting any unwritten extents, flushing the disk cache and updating the
* time stamps.
*
* Note that we rely on the caller to always send us a timestamp update so that
* we always commit a transaction here. If that stops being true we will have
* to manually flush the cache here similar to what the fsync code path does
* for datasyncs on files that have no dirty metadata.
* Like fdatasync, a commit that only updates the timestamps does not force the
* log: they are logged as XFS_ILOG_TIMESTAMP and reach the disk with a later
* log write. The log is forced up to the last change that a datasync needs,
* such as the unwritten extent conversions or a new size, and the cache of the
* device holding the file data is flushed if that log force did not write an
* iclog itself.
*/
static int
xfs_fs_commit_blocks(
@ -312,6 +314,7 @@ xfs_fs_commit_blocks(
struct xfs_trans *tp;
struct timespec64 now;
bool update_isize = false;
int log_flushed = 0;
int error, i;
loff_t size;
@ -368,7 +371,8 @@ xfs_fs_commit_blocks(
xfs_ilock(ip, XFS_ILOCK_EXCL);
xfs_trans_ijoin(tp, ip, XFS_ILOCK_EXCL);
xfs_trans_log_inode(tp, ip, XFS_ILOG_CORE);
xfs_trans_log_inode(tp, ip,
update_isize ? XFS_ILOG_CORE : XFS_ILOG_TIMESTAMP);
now = inode_set_ctime_current(inode);
inode_set_atime_to_ts(inode, now);
@ -379,8 +383,13 @@ xfs_fs_commit_blocks(
ip->i_disk_size = new_size;
}
xfs_trans_set_sync(tp);
error = xfs_trans_commit(tp);
if (error)
goto out_drop_iolock;
error = xfs_log_force_inode(ip, true, &log_flushed);
if (!error && !log_flushed)
error = blkdev_issue_flush(xfs_inode_buftarg(ip)->bt_bdev);
out_drop_iolock:
xfs_iunlock(ip, XFS_IOLOCK_EXCL);