diff --git a/fs/xfs/xfs_iomap.c b/fs/xfs/xfs_iomap.c index df1eab646cae..6229a0bf793b 100644 --- a/fs/xfs/xfs_iomap.c +++ b/fs/xfs/xfs_iomap.c @@ -1781,10 +1781,12 @@ xfs_buffered_write_iomap_begin( { struct iomap_iter *iter = container_of(iomap, struct iomap_iter, iomap); + struct address_space *mapping = inode->i_mapping; struct xfs_inode *ip = XFS_I(inode); struct xfs_mount *mp = ip->i_mount; xfs_fileoff_t offset_fsb = XFS_B_TO_FSBT(mp, offset); xfs_fileoff_t end_fsb = xfs_iomap_end_fsb(mp, offset, count); + xfs_fileoff_t cow_fsb = NULLFILEOFF; struct xfs_bmbt_irec imap, cmap; struct xfs_iext_cursor icur, ccur; xfs_fsblock_t prealloc_blocks = 0; @@ -1852,6 +1854,8 @@ restart: } cow_eof = !xfs_iext_lookup_extent(ip, ip->i_cowfp, offset_fsb, &ccur, &cmap); + if (!cow_eof) + cow_fsb = cmap.br_startoff; } /* We never need to allocate blocks for unsharing a hole. */ @@ -1866,17 +1870,37 @@ restart: * writeback to remap pending blocks and restart the lookup. */ if ((flags & IOMAP_ZERO) && imap.br_startoff > offset_fsb) { - if (filemap_range_needs_writeback(inode->i_mapping, offset, - offset + count - 1)) { + loff_t start, end; + + imap.br_blockcount = imap.br_startoff - offset_fsb; + imap.br_startoff = offset_fsb; + imap.br_startblock = HOLESTARTBLOCK; + imap.br_state = XFS_EXT_NORM; + + if (cow_fsb == NULLFILEOFF) + goto found_imap; + if (cow_fsb > offset_fsb) { + xfs_trim_extent(&imap, offset_fsb, + cow_fsb - offset_fsb); + goto found_imap; + } + + /* COW fork blocks overlap the hole */ + xfs_trim_extent(&imap, offset_fsb, + cmap.br_startoff + cmap.br_blockcount - offset_fsb); + start = XFS_FSB_TO_B(mp, imap.br_startoff); + end = XFS_FSB_TO_B(mp, + imap.br_startoff + imap.br_blockcount) - 1; + if (filemap_range_needs_writeback(mapping, start, end)) { xfs_iunlock(ip, lockmode); - error = filemap_write_and_wait_range(inode->i_mapping, - offset, offset + count - 1); + error = filemap_write_and_wait_range(mapping, start, + end); if (error) return error; goto restart; } - xfs_hole_to_iomap(ip, iomap, offset_fsb, imap.br_startoff); - goto out_unlock; + + goto found_imap; } /*