Skip to content

Commit ef7ec65

Browse files
committed
xfs: resample the data fork mapping after cycling ILOCK
commit-author Darrick J. Wong <djwong@kernel.org> commit 2f4acd0 upstream-diff | Between this kernel and the recent change xfs_reflink_allocate_cow() was refactored into two separate functions in d621133 xfs: Fix false ENOSPC when performing direct write on a delalloc extent in cow fork In addition after fixing the bug the offset_fsb and count_fsb need to be refreshed as a part of the above commit it creates xfs_reflink_convert_unwritten() which refreshes prior to returns. 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: 3c68d44 ("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: Jonathan Maple <jmaple@ciq.com>
1 parent b3b5d34 commit ef7ec65

1 file changed

Lines changed: 19 additions & 0 deletions

File tree

fs/xfs/xfs_reflink.c

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -357,6 +357,7 @@ xfs_reflink_allocate_cow(
357357
int nimaps, error = 0;
358358
bool found;
359359
xfs_filblks_t resaligned;
360+
unsigned int seq_before = READ_ONCE(ip->i_df.if_seq);
360361
xfs_extlen_t resblks = 0;
361362

362363
ASSERT(xfs_isilocked(ip, XFS_ILOCK_EXCL));
@@ -385,6 +386,24 @@ xfs_reflink_allocate_cow(
385386

386387
*lockmode = XFS_ILOCK_EXCL;
387388

389+
/*
390+
* The data fork mapping may have changed while we dropped the ILOCK
391+
* (a racing O_DIRECT writer under IOLOCK_SHARED can complete a full
392+
* CoW cycle including xfs_reflink_end_cow(), which remaps this offset
393+
* and drops the refcount of the old shared block). Re-read it so the
394+
* shared-status recheck below and the caller's in-place iomap both
395+
* operate on the current mapping rather than a stale physical block.
396+
*/
397+
if (seq_before != READ_ONCE(ip->i_df.if_seq)) {
398+
nimaps = 1;
399+
error = xfs_bmapi_read(ip, imap->br_startoff,
400+
imap->br_blockcount, imap, &nimaps, 0);
401+
if (error)
402+
goto out_trans_cancel;
403+
offset_fsb = imap->br_startoff;
404+
count_fsb = imap->br_blockcount;
405+
}
406+
388407
/*
389408
* Check for an overlapping extent again now that we dropped the ilock.
390409
*/

0 commit comments

Comments
 (0)