Commit a14234c7 authored by Christoph Hellwig's avatar Christoph Hellwig Committed by Darrick J. Wong

xfs: go straight to real allocations for direct I/O COW writes

When we allocate COW fork blocks for direct I/O writes we currently first
create a delayed allocation, and then convert it to a real allocation
once we've got the delayed one.

As there is no good reason for that this patch instead makes use call
xfs_bmapi_write from the COW allocation path.  The only interesting bits
are a few tweaks the low-level allocator to allow for this, most notably
the need to remove the call to xfs_bmap_extsize_align for the cowextsize
in xfs_bmap_btalloc - for the existing convert case it's a no-op, but
for the direct allocation case it would blow up our block reservation
way beyond what we reserved for the transaction.
Signed-off-by: default avatarChristoph Hellwig <hch@lst.de>
Reviewed-by: default avatarDarrick J. Wong <darrick.wong@oracle.com>
Signed-off-by: default avatarDarrick J. Wong <darrick.wong@oracle.com>
parent dcf9585a
...@@ -2895,13 +2895,14 @@ xfs_bmap_add_extent_hole_real( ...@@ -2895,13 +2895,14 @@ xfs_bmap_add_extent_hole_real(
ASSERT(!isnullstartblock(new->br_startblock)); ASSERT(!isnullstartblock(new->br_startblock));
ASSERT(!bma->cur || ASSERT(!bma->cur ||
!(bma->cur->bc_private.b.flags & XFS_BTCUR_BPRV_WASDEL)); !(bma->cur->bc_private.b.flags & XFS_BTCUR_BPRV_WASDEL));
ASSERT(whichfork != XFS_COW_FORK);
XFS_STATS_INC(mp, xs_add_exlist); XFS_STATS_INC(mp, xs_add_exlist);
state = 0; state = 0;
if (whichfork == XFS_ATTR_FORK) if (whichfork == XFS_ATTR_FORK)
state |= BMAP_ATTRFORK; state |= BMAP_ATTRFORK;
if (whichfork == XFS_COW_FORK)
state |= BMAP_COWFORK;
/* /*
* Check and set flags if this segment has a left neighbor. * Check and set flags if this segment has a left neighbor.
......
...@@ -389,62 +389,100 @@ __xfs_reflink_allocate_cow( ...@@ -389,62 +389,100 @@ __xfs_reflink_allocate_cow(
xfs_fileoff_t end_fsb) xfs_fileoff_t end_fsb)
{ {
struct xfs_mount *mp = ip->i_mount; struct xfs_mount *mp = ip->i_mount;
struct xfs_bmbt_irec imap; struct xfs_bmbt_irec imap, got;
struct xfs_defer_ops dfops; struct xfs_defer_ops dfops;
struct xfs_trans *tp; struct xfs_trans *tp;
xfs_fsblock_t first_block; xfs_fsblock_t first_block;
int nimaps = 1, error; int nimaps, error, lockmode;
bool shared; bool shared, trimmed;
xfs_filblks_t resaligned;
xfs_extlen_t resblks;
xfs_extnum_t idx;
xfs_defer_init(&dfops, &first_block); resaligned = xfs_aligned_fsb_count(*offset_fsb, end_fsb - *offset_fsb,
xfs_get_cowextsz_hint(ip));
resblks = XFS_DIOSTRAT_SPACE_RES(mp, resaligned);
error = xfs_trans_alloc(mp, &M_RES(mp)->tr_write, 0, 0, error = xfs_trans_alloc(mp, &M_RES(mp)->tr_write, resblks, 0, 0, &tp);
XFS_TRANS_RESERVE, &tp);
if (error) if (error)
return error; return error;
xfs_ilock(ip, XFS_ILOCK_EXCL); lockmode = XFS_ILOCK_EXCL;
xfs_ilock(ip, lockmode);
/* Read extent from the source file. */ /*
nimaps = 1; * Even if the extent is not shared we might have a preallocation for
error = xfs_bmapi_read(ip, *offset_fsb, end_fsb - *offset_fsb, * it in the COW fork. If so use it.
&imap, &nimaps, 0); */
if (error) if (xfs_iext_lookup_extent(ip, ip->i_cowfp, *offset_fsb, &idx, &got) &&
goto out_unlock; got.br_startoff <= *offset_fsb) {
ASSERT(nimaps == 1); /* If we have a real allocation in the COW fork we're done. */
if (!isnullstartblock(got.br_startblock)) {
xfs_trim_extent(&got, *offset_fsb,
end_fsb - *offset_fsb);
*offset_fsb = got.br_startoff + got.br_blockcount;
goto out_trans_cancel;
}
} else {
nimaps = 1;
error = xfs_bmapi_read(ip, *offset_fsb, end_fsb - *offset_fsb,
&imap, &nimaps, 0);
if (error)
goto out_trans_cancel;
ASSERT(nimaps == 1);
/* Trim the mapping to the nearest shared extent boundary. */
error = xfs_reflink_trim_around_shared(ip, &imap, &shared,
&trimmed);
if (error)
goto out_trans_cancel;
if (!shared) {
*offset_fsb = imap.br_startoff + imap.br_blockcount;
goto out_trans_cancel;
}
/* Make sure there's a CoW reservation for it. */ *offset_fsb = imap.br_startoff;
error = xfs_reflink_reserve_cow(ip, &imap, &shared); end_fsb = imap.br_startoff + imap.br_blockcount;
}
error = xfs_trans_reserve_quota_nblks(tp, ip, resblks, 0,
XFS_QMOPT_RES_REGBLKS);
if (error) if (error)
goto out_trans_cancel; goto out_trans_cancel;
if (!shared) { xfs_trans_ijoin(tp, ip, 0);
*offset_fsb = imap.br_startoff + imap.br_blockcount;
goto out_trans_cancel; xfs_defer_init(&dfops, &first_block);
} nimaps = 1;
/* Allocate the entire reservation as unwritten blocks. */ /* Allocate the entire reservation as unwritten blocks. */
xfs_trans_ijoin(tp, ip, 0); error = xfs_bmapi_write(tp, ip, *offset_fsb, end_fsb - *offset_fsb,
error = xfs_bmapi_write(tp, ip, imap.br_startoff, imap.br_blockcount,
XFS_BMAPI_COWFORK | XFS_BMAPI_PREALLOC, &first_block, XFS_BMAPI_COWFORK | XFS_BMAPI_PREALLOC, &first_block,
XFS_EXTENTADD_SPACE_RES(mp, XFS_DATA_FORK), resblks, &imap, &nimaps, &dfops);
&imap, &nimaps, &dfops);
if (error) if (error)
goto out_trans_cancel; goto out_bmap_cancel;
/* Finish up. */ /* Finish up. */
error = xfs_defer_finish(&tp, &dfops, NULL); error = xfs_defer_finish(&tp, &dfops, NULL);
if (error) if (error)
goto out_trans_cancel; goto out_bmap_cancel;
error = xfs_trans_commit(tp); error = xfs_trans_commit(tp);
if (error)
goto out_unlock;
*offset_fsb = imap.br_startoff + imap.br_blockcount; *offset_fsb = imap.br_startoff + imap.br_blockcount;
out_unlock: out_unlock:
xfs_iunlock(ip, XFS_ILOCK_EXCL); xfs_iunlock(ip, lockmode);
return error; return error;
out_trans_cancel:
out_bmap_cancel:
xfs_defer_cancel(&dfops); xfs_defer_cancel(&dfops);
xfs_trans_unreserve_quota_nblks(tp, ip, (long)resblks, 0,
XFS_QMOPT_RES_REGBLKS);
out_trans_cancel:
xfs_trans_cancel(tp); xfs_trans_cancel(tp);
goto out_unlock; goto out_unlock;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment