Merge: xfs: resample the data fork mapping after cycling ILOCK

MR: https://gitlab.com/redhat/centos-stream/src/kernel/centos-stream-9/-/merge_requests/8364

JIRA: https://redhat.atlassian.net/browse/RHEL-193940
Upstream Status:  git://git.kernel.org/pub/scm/fs/xfs/xfs-linux.git
commit 2f4acd0fcd862e22eab45690ec2c08c80b6ef2e7
Author: Darrick J. Wong <djwong@kernel.org>

    xfs: resample the data fork mapping after cycling ILOCK

    xfs_reflink_fill_{cow_hole,delalloc} are both presented with an inode,
    a data fork mapping, and a cow fork mapping.  Unfortunately, these two
    helpers cycle the ILOCK to grab a transaction, which means that the
    mappings are stale as soon as we reacquire the ILOCK.  Currently we
    refresh the cow fork mapping by re-calling xfs_find_trim_cow_extent, but
    we don't refresh the data fork mapping beforehand, which means that the
    xfs_bmap_trim_cow in that function queries the refcount btree about the
    wrong physical blocks and returns an inaccurate value in *shared.

    If *shared is now false, the directio write proceeds with a stale data
    fork mapping.  Fix this by querying the data fork mapping if the
    sequence counter changes across the ILOCK cycle.

    Cc: hch@lst.de
    Cc: stable@vger.kernel.org # v4.11
    Fixes: 3c68d44a2b ("xfs: allocate direct I/O COW blocks in iomap_begin")
    Signed-off-by: "Darrick J. Wong" <djwong@kernel.org>
    Reviewed-by: Christoph Hellwig <hch@lst.de>
    Reviewed-by: Carlos Maiolino <cmaiolino@redhat.com>
    Signed-off-by: Carlos Maiolino <cem@kernel.org>

Signed-off-by: Carlos Maiolino <cmaiolino@redhat.com>

Approved-by: Pavel Reichl <preichl@redhat.com>
Approved-by: Jan Stancek <jstancek@redhat.com>
Approved-by: CKI KWF Bot <cki-ci-bot+kwf-gitlab-com@redhat.com>

Merged-by: CKI GitLab Kmaint Pipeline Bot <26919896-cki-kmaint-pipeline-bot@users.noreply.gitlab.com>
This commit is contained in:
CKI KWF Bot
2026-07-29 08:10:54 +00:00
+36
View File
@@ -389,6 +389,7 @@ xfs_reflink_fill_cow_hole(
struct xfs_mount *mp = ip->i_mount;
struct xfs_trans *tp;
xfs_filblks_t resaligned;
unsigned int seq_before = READ_ONCE(ip->i_df.if_seq);
xfs_extlen_t resblks;
int nimaps;
int error;
@@ -408,6 +409,22 @@ xfs_reflink_fill_cow_hole(
*lockmode = XFS_ILOCK_EXCL;
/*
* The data fork mapping may have changed while we dropped the ILOCK
* (a racing O_DIRECT writer under IOLOCK_SHARED can complete a full
* CoW cycle including xfs_reflink_end_cow(), which remaps this offset
* and drops the refcount of the old shared block). Re-read it so the
* shared-status recheck below and the caller's in-place iomap both
* operate on the current mapping rather than a stale physical block.
*/
if (seq_before != READ_ONCE(ip->i_df.if_seq)) {
nimaps = 1;
error = xfs_bmapi_read(ip, imap->br_startoff,
imap->br_blockcount, imap, &nimaps, 0);
if (error)
goto out_trans_cancel;
}
error = xfs_find_trim_cow_extent(ip, imap, cmap, shared, &found);
if (error || !*shared)
goto out_trans_cancel;
@@ -454,6 +471,8 @@ xfs_reflink_fill_delalloc(
bool found;
do {
unsigned int seq_before = READ_ONCE(ip->i_df.if_seq);
xfs_iunlock(ip, *lockmode);
*lockmode = 0;
@@ -464,6 +483,23 @@ xfs_reflink_fill_delalloc(
*lockmode = XFS_ILOCK_EXCL;
/*
* The data fork mapping may have changed while we dropped the
* ILOCK (a racing O_DIRECT writer under IOLOCK_SHARED can
* complete a full CoW cycle including xfs_reflink_end_cow(),
* which remaps this offset and drops the refcount of the old
* shared block). Re-read it so the shared-status recheck
* below and the caller's in-place iomap both operate on the
* current mapping rather than a stale physical block.
*/
if (seq_before != READ_ONCE(ip->i_df.if_seq)) {
nimaps = 1;
error = xfs_bmapi_read(ip, imap->br_startoff,
imap->br_blockcount, imap, &nimaps, 0);
if (error)
goto out_trans_cancel;
}
error = xfs_find_trim_cow_extent(ip, imap, cmap, shared,
&found);
if (error || !*shared)