mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-07-22 02:17:36 -04:00
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>
This commit is contained in:
committed by
Carlos Maiolino
parent
9202ee546b
commit
2f4acd0fcd
@@ -440,6 +440,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);
|
||||
unsigned int dblocks = 0, rblocks = 0;
|
||||
int nimaps;
|
||||
int error;
|
||||
@@ -465,6 +466,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;
|
||||
@@ -511,6 +528,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;
|
||||
|
||||
@@ -521,6 +540,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)
|
||||
|
||||
Reference in New Issue
Block a user