Skip to content

Commit 5ca5916

Browse files
Brian FosterDarrick J. Wong
authored andcommitted
xfs: punch out data fork delalloc blocks on COW writeback failure
If writeback I/O to a COW extent fails, the COW fork blocks are punched out and the data fork blocks left alone. It is possible for COW fork blocks to overlap non-shared data fork blocks (due to cowextsz hint prealloc), however, and writeback unconditionally maps to the COW fork whenever blocks exist at the corresponding offset of the page undergoing writeback. This means it's quite possible for a COW fork extent to overlap delalloc data fork blocks, writeback to convert and map to the COW fork blocks, writeback to fail, and finally for ioend completion to cancel the COW fork blocks and leave stale data fork delalloc blocks around in the inode. The blocks are effectively stale because writeback failure also discards dirty page state. If this occurs, it is likely to trigger assert failures, free space accounting corruption and failures in unrelated file operations. For example, a subsequent reflink attempt of the affected file to a new target file will trip over the stale delalloc in the source file and fail. Several of these issues are occasionally reproduced by generic/648, but are reproducible on demand with the right sequence of operations and timely I/O error injection. To fix this problem, update the ioend failure path to also punch out underlying data fork delalloc blocks on I/O error. This is analogous to the writeback submission failure path in xfs_discard_page() where we might fail to map data fork delalloc blocks and consistent with the successful COW writeback completion path, which is responsible for unmapping from the data fork and remapping in COW fork blocks. Fixes: 787eb48 ("xfs: fix and streamline error handling in xfs_end_io") Signed-off-by: Brian Foster <[email protected]> Reviewed-by: Darrick J. Wong <[email protected]> Signed-off-by: Darrick J. Wong <[email protected]>
1 parent c04c51c commit 5ca5916

File tree

1 file changed

+12
-3
lines changed

1 file changed

+12
-3
lines changed

fs/xfs/xfs_aops.c

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,7 @@ xfs_end_ioend(
8282
struct iomap_ioend *ioend)
8383
{
8484
struct xfs_inode *ip = XFS_I(ioend->io_inode);
85+
struct xfs_mount *mp = ip->i_mount;
8586
xfs_off_t offset = ioend->io_offset;
8687
size_t size = ioend->io_size;
8788
unsigned int nofs_flag;
@@ -97,18 +98,26 @@ xfs_end_ioend(
9798
/*
9899
* Just clean up the in-memory structures if the fs has been shut down.
99100
*/
100-
if (xfs_is_shutdown(ip->i_mount)) {
101+
if (xfs_is_shutdown(mp)) {
101102
error = -EIO;
102103
goto done;
103104
}
104105

105106
/*
106-
* Clean up any COW blocks on an I/O error.
107+
* Clean up all COW blocks and underlying data fork delalloc blocks on
108+
* I/O error. The delalloc punch is required because this ioend was
109+
* mapped to blocks in the COW fork and the associated pages are no
110+
* longer dirty. If we don't remove delalloc blocks here, they become
111+
* stale and can corrupt free space accounting on unmount.
107112
*/
108113
error = blk_status_to_errno(ioend->io_bio->bi_status);
109114
if (unlikely(error)) {
110-
if (ioend->io_flags & IOMAP_F_SHARED)
115+
if (ioend->io_flags & IOMAP_F_SHARED) {
111116
xfs_reflink_cancel_cow_range(ip, offset, size, true);
117+
xfs_bmap_punch_delalloc_range(ip,
118+
XFS_B_TO_FSBT(mp, offset),
119+
XFS_B_TO_FSB(mp, size));
120+
}
112121
goto done;
113122
}
114123

0 commit comments

Comments
 (0)