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;
*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;
bool found;
do {
+ unsigned int seq_before = READ_ONCE(ip->i_df.if_seq);
+
xfs_iunlock(ip, *lockmode);
*lockmode = 0;
*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)