Skip to content

Commit df2fd88

Browse files
author
Darrick J. Wong
committed
xfs: rewrite xfs_reflink_end_cow to use intents
Currently, the code that performs CoW remapping after a write has this odd behavior where it walks /backwards/ through the data fork to remap extents in reverse order. Earlier, we rewrote the reflink remap function to use deferred bmap log items instead of trying to cram as much into the first transaction that we could. Now do the same for the CoW remap code. There doesn't seem to be any performance impact; we're just making better use of code that we added for the benefit of reflink. Signed-off-by: Darrick J. Wong <[email protected]> Reviewed-by: Dave Chinner <[email protected]> Reviewed-by: Christoph Hellwig <[email protected]>
1 parent b037c4e commit df2fd88

File tree

2 files changed

+58
-33
lines changed

2 files changed

+58
-33
lines changed

fs/xfs/xfs_reflink.c

Lines changed: 56 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -586,21 +586,21 @@ xfs_reflink_cancel_cow_range(
586586
STATIC int
587587
xfs_reflink_end_cow_extent(
588588
struct xfs_inode *ip,
589-
xfs_fileoff_t offset_fsb,
590-
xfs_fileoff_t *end_fsb)
589+
xfs_fileoff_t *offset_fsb,
590+
xfs_fileoff_t end_fsb)
591591
{
592-
struct xfs_bmbt_irec got, del;
593592
struct xfs_iext_cursor icur;
593+
struct xfs_bmbt_irec got, del, data;
594594
struct xfs_mount *mp = ip->i_mount;
595595
struct xfs_trans *tp;
596596
struct xfs_ifork *ifp = XFS_IFORK_PTR(ip, XFS_COW_FORK);
597-
xfs_filblks_t rlen;
598597
unsigned int resblks;
598+
int nmaps;
599599
int error;
600600

601601
/* No COW extents? That's easy! */
602602
if (ifp->if_bytes == 0) {
603-
*end_fsb = offset_fsb;
603+
*offset_fsb = end_fsb;
604604
return 0;
605605
}
606606

@@ -631,42 +631,66 @@ xfs_reflink_end_cow_extent(
631631
* left by the time I/O completes for the loser of the race. In that
632632
* case we are done.
633633
*/
634-
if (!xfs_iext_lookup_extent_before(ip, ifp, end_fsb, &icur, &got) ||
635-
got.br_startoff + got.br_blockcount <= offset_fsb) {
636-
*end_fsb = offset_fsb;
634+
if (!xfs_iext_lookup_extent(ip, ifp, *offset_fsb, &icur, &got) ||
635+
got.br_startoff >= end_fsb) {
636+
*offset_fsb = end_fsb;
637637
goto out_cancel;
638638
}
639639

640-
/*
641-
* Structure copy @got into @del, then trim @del to the range that we
642-
* were asked to remap. We preserve @got for the eventual CoW fork
643-
* deletion; from now on @del represents the mapping that we're
644-
* actually remapping.
645-
*/
646-
del = got;
647-
xfs_trim_extent(&del, offset_fsb, *end_fsb - offset_fsb);
648-
649-
ASSERT(del.br_blockcount > 0);
650-
651640
/*
652641
* Only remap real extents that contain data. With AIO, speculative
653642
* preallocations can leak into the range we are called upon, and we
654-
* need to skip them.
643+
* need to skip them. Preserve @got for the eventual CoW fork
644+
* deletion; from now on @del represents the mapping that we're
645+
* actually remapping.
655646
*/
656-
if (!xfs_bmap_is_written_extent(&got)) {
657-
*end_fsb = del.br_startoff;
658-
goto out_cancel;
647+
while (!xfs_bmap_is_written_extent(&got)) {
648+
if (!xfs_iext_next_extent(ifp, &icur, &got) ||
649+
got.br_startoff >= end_fsb) {
650+
*offset_fsb = end_fsb;
651+
goto out_cancel;
652+
}
659653
}
654+
del = got;
660655

661-
/* Unmap the old blocks in the data fork. */
662-
rlen = del.br_blockcount;
663-
error = __xfs_bunmapi(tp, ip, del.br_startoff, &rlen, 0, 1);
656+
/* Grab the corresponding mapping in the data fork. */
657+
nmaps = 1;
658+
error = xfs_bmapi_read(ip, del.br_startoff, del.br_blockcount, &data,
659+
&nmaps, 0);
664660
if (error)
665661
goto out_cancel;
666662

667-
/* Trim the extent to whatever got unmapped. */
668-
xfs_trim_extent(&del, del.br_startoff + rlen, del.br_blockcount - rlen);
669-
trace_xfs_reflink_cow_remap(ip, &del);
663+
/* We can only remap the smaller of the two extent sizes. */
664+
data.br_blockcount = min(data.br_blockcount, del.br_blockcount);
665+
del.br_blockcount = data.br_blockcount;
666+
667+
trace_xfs_reflink_cow_remap_from(ip, &del);
668+
trace_xfs_reflink_cow_remap_to(ip, &data);
669+
670+
if (xfs_bmap_is_real_extent(&data)) {
671+
/*
672+
* If the extent we're remapping is backed by storage (written
673+
* or not), unmap the extent and drop its refcount.
674+
*/
675+
xfs_bmap_unmap_extent(tp, ip, &data);
676+
xfs_refcount_decrease_extent(tp, &data);
677+
xfs_trans_mod_dquot_byino(tp, ip, XFS_TRANS_DQ_BCOUNT,
678+
-data.br_blockcount);
679+
} else if (data.br_startblock == DELAYSTARTBLOCK) {
680+
int done;
681+
682+
/*
683+
* If the extent we're remapping is a delalloc reservation,
684+
* we can use the regular bunmapi function to release the
685+
* incore state. Dropping the delalloc reservation takes care
686+
* of the quota reservation for us.
687+
*/
688+
error = xfs_bunmapi(NULL, ip, data.br_startoff,
689+
data.br_blockcount, 0, 1, &done);
690+
if (error)
691+
goto out_cancel;
692+
ASSERT(done);
693+
}
670694

671695
/* Free the CoW orphan record. */
672696
xfs_refcount_free_cow_extent(tp, del.br_startblock, del.br_blockcount);
@@ -687,7 +711,7 @@ xfs_reflink_end_cow_extent(
687711
return error;
688712

689713
/* Update the caller about how much progress we made. */
690-
*end_fsb = del.br_startoff;
714+
*offset_fsb = del.br_startoff + del.br_blockcount;
691715
return 0;
692716

693717
out_cancel:
@@ -715,7 +739,7 @@ xfs_reflink_end_cow(
715739
end_fsb = XFS_B_TO_FSB(ip->i_mount, offset + count);
716740

717741
/*
718-
* Walk backwards until we're out of the I/O range. The loop function
742+
* Walk forwards until we've remapped the I/O range. The loop function
719743
* repeatedly cycles the ILOCK to allocate one transaction per remapped
720744
* extent.
721745
*
@@ -747,7 +771,7 @@ xfs_reflink_end_cow(
747771
* blocks will be remapped.
748772
*/
749773
while (end_fsb > offset_fsb && !error)
750-
error = xfs_reflink_end_cow_extent(ip, offset_fsb, &end_fsb);
774+
error = xfs_reflink_end_cow_extent(ip, &offset_fsb, end_fsb);
751775

752776
if (error)
753777
trace_xfs_reflink_end_cow_error(ip, error, _RET_IP_);

fs/xfs/xfs_trace.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3405,7 +3405,8 @@ DEFINE_INODE_IREC_EVENT(xfs_reflink_convert_cow);
34053405

34063406
DEFINE_SIMPLE_IO_EVENT(xfs_reflink_cancel_cow_range);
34073407
DEFINE_SIMPLE_IO_EVENT(xfs_reflink_end_cow);
3408-
DEFINE_INODE_IREC_EVENT(xfs_reflink_cow_remap);
3408+
DEFINE_INODE_IREC_EVENT(xfs_reflink_cow_remap_from);
3409+
DEFINE_INODE_IREC_EVENT(xfs_reflink_cow_remap_to);
34093410

34103411
DEFINE_INODE_ERROR_EVENT(xfs_reflink_cancel_cow_range_error);
34113412
DEFINE_INODE_ERROR_EVENT(xfs_reflink_end_cow_error);

0 commit comments

Comments
 (0)