Skip to content

Commit b25d198

Browse files
author
Darrick J. Wong
committed
xfs: estimate post-merge refcounts correctly
Upon enabling fsdax + reflink for XFS, xfs/179 began to report refcount metadata corruptions after being run. Specifically, xfs_repair noticed single-block refcount records that could be combined but had not been. The root cause of this is improper MAXREFCOUNT edge case handling in xfs_refcount_merge_extents. When we're trying to find candidates for a refcount btree record merge, we compute the refcount attribute of the merged record, but we fail to account for the fact that once a record hits rc_refcount == MAXREFCOUNT, it is pinned that way forever. Hence the computed refcount is wrong, and we fail to merge the extents. Fix this by adjusting the merge predicates to compute the adjusted refcount correctly. Fixes: 3172725 ("xfs: adjust refcount of an extent of blocks in refcount btree") Signed-off-by: Darrick J. Wong <[email protected]> Reviewed-by: Dave Chinner <[email protected]> Reviewed-by: Xiao Yang <[email protected]>
1 parent 9d720a5 commit b25d198

File tree

1 file changed

+21
-4
lines changed

1 file changed

+21
-4
lines changed

fs/xfs/libxfs/xfs_refcount.c

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -820,6 +820,17 @@ xfs_refc_valid(
820820
return rc->rc_startblock != NULLAGBLOCK;
821821
}
822822

823+
static inline xfs_nlink_t
824+
xfs_refc_merge_refcount(
825+
const struct xfs_refcount_irec *irec,
826+
enum xfs_refc_adjust_op adjust)
827+
{
828+
/* Once a record hits MAXREFCOUNT, it is pinned there forever */
829+
if (irec->rc_refcount == MAXREFCOUNT)
830+
return MAXREFCOUNT;
831+
return irec->rc_refcount + adjust;
832+
}
833+
823834
static inline bool
824835
xfs_refc_want_merge_center(
825836
const struct xfs_refcount_irec *left,
@@ -831,6 +842,7 @@ xfs_refc_want_merge_center(
831842
unsigned long long *ulenp)
832843
{
833844
unsigned long long ulen = left->rc_blockcount;
845+
xfs_nlink_t new_refcount;
834846

835847
/*
836848
* To merge with a center record, both shoulder records must be
@@ -846,9 +858,10 @@ xfs_refc_want_merge_center(
846858
return false;
847859

848860
/* The shoulder record refcounts must match the new refcount. */
849-
if (left->rc_refcount != cleft->rc_refcount + adjust)
861+
new_refcount = xfs_refc_merge_refcount(cleft, adjust);
862+
if (left->rc_refcount != new_refcount)
850863
return false;
851-
if (right->rc_refcount != cleft->rc_refcount + adjust)
864+
if (right->rc_refcount != new_refcount)
852865
return false;
853866

854867
/*
@@ -871,6 +884,7 @@ xfs_refc_want_merge_left(
871884
enum xfs_refc_adjust_op adjust)
872885
{
873886
unsigned long long ulen = left->rc_blockcount;
887+
xfs_nlink_t new_refcount;
874888

875889
/*
876890
* For a left merge, the left shoulder record must be adjacent to the
@@ -881,7 +895,8 @@ xfs_refc_want_merge_left(
881895
return false;
882896

883897
/* Left shoulder record refcount must match the new refcount. */
884-
if (left->rc_refcount != cleft->rc_refcount + adjust)
898+
new_refcount = xfs_refc_merge_refcount(cleft, adjust);
899+
if (left->rc_refcount != new_refcount)
885900
return false;
886901

887902
/*
@@ -903,6 +918,7 @@ xfs_refc_want_merge_right(
903918
enum xfs_refc_adjust_op adjust)
904919
{
905920
unsigned long long ulen = right->rc_blockcount;
921+
xfs_nlink_t new_refcount;
906922

907923
/*
908924
* For a right merge, the right shoulder record must be adjacent to the
@@ -913,7 +929,8 @@ xfs_refc_want_merge_right(
913929
return false;
914930

915931
/* Right shoulder record refcount must match the new refcount. */
916-
if (right->rc_refcount != cright->rc_refcount + adjust)
932+
new_refcount = xfs_refc_merge_refcount(cright, adjust);
933+
if (right->rc_refcount != new_refcount)
917934
return false;
918935

919936
/*

0 commit comments

Comments
 (0)