aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDave Chinner <dchinner@redhat.com>2018-11-19 16:31:10 -0500
committerDarrick J. Wong <darrick.wong@oracle.com>2018-11-21 13:10:53 -0500
commit2c307174ab77e34645e75e12827646e044d273c3 (patch)
tree6daad4c4a35de98f1c27b54254880d9c49707049
parent7f9f71be84bcab368e58020a42f6d0dd97adf0ce (diff)
xfs: flush removing page cache in xfs_reflink_remap_prep
On a sub-page block size filesystem, fsx is failing with a data corruption after a series of operations involving copying a file with the destination offset beyond EOF of the destination of the file: 8093(157 mod 256): TRUNCATE DOWN from 0x7a120 to 0x50000 ******WWWW 8094(158 mod 256): INSERT 0x25000 thru 0x25fff (0x1000 bytes) 8095(159 mod 256): COPY 0x18000 thru 0x1afff (0x3000 bytes) to 0x2f400 8096(160 mod 256): WRITE 0x5da00 thru 0x651ff (0x7800 bytes) HOLE 8097(161 mod 256): COPY 0x2000 thru 0x5fff (0x4000 bytes) to 0x6fc00 The second copy here is beyond EOF, and it is to sub-page (4k) but block aligned (1k) offset. The clone runs the EOF zeroing, landing in a pre-existing post-eof delalloc extent. This zeroes the post-eof extents in the page cache just fine, dirtying the pages correctly. The problem is that xfs_reflink_remap_prep() now truncates the page cache over the range that it is copying it to, and rounds that down to cover the entire start page. This removes the dirty page over the delalloc extent from the page cache without having written it back. Hence later, when the page cache is flushed, the page at offset 0x6f000 has not been written back and hence exposes stale data, which fsx trips over less than 10 operations later. Fix this by changing xfs_reflink_remap_prep() to use xfs_flush_unmap_range(). Signed-off-by: Dave Chinner <dchinner@redhat.com> Reviewed-by: Christoph Hellwig <hch@lst.de> Reviewed-by: Darrick J. Wong <darrick.wong@oracle.com> Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
-rw-r--r--fs/xfs/xfs_bmap_util.c2
-rw-r--r--fs/xfs/xfs_bmap_util.h3
-rw-r--r--fs/xfs/xfs_reflink.c17
3 files changed, 17 insertions, 5 deletions
diff --git a/fs/xfs/xfs_bmap_util.c b/fs/xfs/xfs_bmap_util.c
index 167ff4297e5c..404e581f1ea1 100644
--- a/fs/xfs/xfs_bmap_util.c
+++ b/fs/xfs/xfs_bmap_util.c
@@ -1042,7 +1042,7 @@ out_trans_cancel:
1042 goto out_unlock; 1042 goto out_unlock;
1043} 1043}
1044 1044
1045static int 1045int
1046xfs_flush_unmap_range( 1046xfs_flush_unmap_range(
1047 struct xfs_inode *ip, 1047 struct xfs_inode *ip,
1048 xfs_off_t offset, 1048 xfs_off_t offset,
diff --git a/fs/xfs/xfs_bmap_util.h b/fs/xfs/xfs_bmap_util.h
index 87363d136bb6..7a78229cf1a7 100644
--- a/fs/xfs/xfs_bmap_util.h
+++ b/fs/xfs/xfs_bmap_util.h
@@ -80,4 +80,7 @@ int xfs_bmap_count_blocks(struct xfs_trans *tp, struct xfs_inode *ip,
80 int whichfork, xfs_extnum_t *nextents, 80 int whichfork, xfs_extnum_t *nextents,
81 xfs_filblks_t *count); 81 xfs_filblks_t *count);
82 82
83int xfs_flush_unmap_range(struct xfs_inode *ip, xfs_off_t offset,
84 xfs_off_t len);
85
83#endif /* __XFS_BMAP_UTIL_H__ */ 86#endif /* __XFS_BMAP_UTIL_H__ */
diff --git a/fs/xfs/xfs_reflink.c b/fs/xfs/xfs_reflink.c
index c56bdbfcf7ae..322a852ce284 100644
--- a/fs/xfs/xfs_reflink.c
+++ b/fs/xfs/xfs_reflink.c
@@ -1352,10 +1352,19 @@ xfs_reflink_remap_prep(
1352 if (ret) 1352 if (ret)
1353 goto out_unlock; 1353 goto out_unlock;
1354 1354
1355 /* Zap any page cache for the destination file's range. */ 1355 /*
1356 truncate_inode_pages_range(&inode_out->i_data, 1356 * If pos_out > EOF, we may have dirtied blocks between EOF and
1357 round_down(pos_out, PAGE_SIZE), 1357 * pos_out. In that case, we need to extend the flush and unmap to cover
1358 round_up(pos_out + *len, PAGE_SIZE) - 1); 1358 * from EOF to the end of the copy length.
1359 */
1360 if (pos_out > XFS_ISIZE(dest)) {
1361 loff_t flen = *len + (pos_out - XFS_ISIZE(dest));
1362 ret = xfs_flush_unmap_range(dest, XFS_ISIZE(dest), flen);
1363 } else {
1364 ret = xfs_flush_unmap_range(dest, pos_out, *len);
1365 }
1366 if (ret)
1367 goto out_unlock;
1359 1368
1360 return 1; 1369 return 1;
1361out_unlock: 1370out_unlock: