@@ -979,6 +979,9 @@ xfs_writepage_map(
wpc->io_type);
if (error)
goto out;
+ if (wpc->io_type == XFS_IO_DELALLOC &&
+ wpc->imap.br_state == XFS_EXT_UNWRITTEN)
+ wpc->io_type = XFS_IO_UNWRITTEN;
wpc->imap_valid = xfs_imap_valid(inode, &wpc->imap,
offset);
}
@@ -657,6 +657,54 @@ xfs_file_iomap_begin_delay(
return error;
}
+/*
+ * Convert any post-eof blocks that are part of the current allocation to
+ * unwritten extents. This optimizations prevents unnecessary online discards of
+ * speculative prealloc on file removal or truncate. It also prevents zeroing on
+ * file extending writes that might skip over preallocated blocks.
+ *
+ * NOTE: This can be removed once we finally kill off buffer_heads and convert
+ * all delalloc blocks to unwritten by default.
+ */
+static int
+xfs_iomap_convert_eofblocks(
+ struct xfs_trans *tp,
+ struct xfs_inode *ip,
+ struct xfs_bmbt_irec *imap,
+ xfs_fileoff_t end_fsb,
+ xfs_fsblock_t *first_block,
+ struct xfs_defer_ops *dfops,
+ xfs_fileoff_t *conv_fsb)
+{
+ struct xfs_mount *mp = ip->i_mount;
+ struct xfs_bmbt_irec crec = *imap;
+ int nimaps = 1;
+ int error;
+ int64_t nres;
+
+ /* check for post-eof blocks, nothing to do there are none */
+ xfs_trim_extent(&crec, end_fsb, crec.br_blockcount);
+ if (!crec.br_blockcount || crec.br_blockcount == imap->br_blockcount)
+ return 0;
+ ASSERT(crec.br_state == XFS_EXT_NORM);
+
+ /* XXX: transaction hack */
+ nres = XFS_EXTENTADD_SPACE_RES(mp, XFS_DATA_FORK);
+ error = xfs_mod_fdblocks(tp->t_mountp, -nres, false);
+ if (error)
+ return error == -ENOSPC ? 0 : error;
+ tp->t_blk_res += nres;
+
+ *conv_fsb = crec.br_startoff;
+
+ error = xfs_bmapi_write(tp, ip, crec.br_startoff, crec.br_blockcount,
+ XFS_BMAPI_PREALLOC|XFS_BMAPI_CONVERT,
+ first_block, nres, &crec, &nimaps, dfops);
+ if (error)
+ return error;
+ return 0;
+}
+
/*
* Pass in a delayed allocate extent, convert it to real extents;
* return to the caller the extent we create which maps on top of
@@ -677,6 +725,7 @@ xfs_iomap_write_allocate(
xfs_mount_t *mp = ip->i_mount;
xfs_fileoff_t offset_fsb, last_block;
xfs_fileoff_t end_fsb, map_start_fsb;
+ xfs_fileoff_t conv_fsb;
xfs_fsblock_t first_block;
struct xfs_defer_ops dfops;
xfs_filblks_t count_fsb;
@@ -712,6 +761,7 @@ xfs_iomap_write_allocate(
* but before our buffer starts.
*/
nimaps = 0;
+ conv_fsb = NULLFILEOFF;
while (nimaps == 0) {
nres = XFS_EXTENTADD_SPACE_RES(mp, XFS_DATA_FORK);
/*
@@ -785,6 +835,11 @@ xfs_iomap_write_allocate(
count_fsb, flags, &first_block,
nres, imap, &nimaps,
&dfops);
+ /* convert any speculative prealloc in this mapping */
+ if (!error && nimaps)
+ error = xfs_iomap_convert_eofblocks(tp, ip,
+ imap, end_fsb, &first_block, &dfops,
+ &conv_fsb);
if (error)
goto trans_cancel;
@@ -809,6 +864,13 @@ xfs_iomap_write_allocate(
if ((offset_fsb >= imap->br_startoff) &&
(offset_fsb < (imap->br_startoff +
imap->br_blockcount))) {
+ if (conv_fsb != NULLFILEOFF) {
+ xfs_trim_extent(imap, imap->br_startoff,
+ conv_fsb - imap->br_startoff);
+ ASSERT(offset_fsb >= imap->br_startoff &&
+ offset_fsb < (imap->br_startoff +
+ imap->br_blockcount));
+ }
XFS_STATS_INC(mp, xs_xstrat_quick);
return 0;
}