summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDarrick J. Wong <djwong@kernel.org>2022-07-14 11:16:04 -0700
committerDarrick J. Wong <djwong@kernel.org>2022-11-09 19:08:10 -0800
commitaa00406b3a9251a3a0f7f622ad54d9c34255cd33 (patch)
tree2ef69a93444c03dd56f8a919c3299c535efd26f6
parentf6e7b8d10e068554b2e62268269d1728e7131dbc (diff)
xfs: refcover CoW leftovers in the realtime volume
Scan the realtime refcount tree at mount time to get rid of leftover CoW staging extents. Signed-off-by: Darrick J. Wong <djwong@kernel.org>
-rw-r--r--fs/xfs/libxfs/xfs_refcount.c63
-rw-r--r--fs/xfs/libxfs/xfs_refcount.h5
-rw-r--r--fs/xfs/xfs_reflink.c14
3 files changed, 65 insertions, 17 deletions
diff --git a/fs/xfs/libxfs/xfs_refcount.c b/fs/xfs/libxfs/xfs_refcount.c
index 75b5a033fb43..b0b671650c68 100644
--- a/fs/xfs/libxfs/xfs_refcount.c
+++ b/fs/xfs/libxfs/xfs_refcount.c
@@ -1923,14 +1923,15 @@ xfs_refcount_recover_extent(
}
/* Find and remove leftover CoW reservations. */
-int
-xfs_refcount_recover_cow_leftovers(
+static int
+xfs_refcount_recover_group_cow_leftovers(
struct xfs_mount *mp,
- struct xfs_perag *pag)
+ struct xfs_perag *pag,
+ struct xfs_rtgroup *rtg)
{
struct xfs_trans *tp;
struct xfs_btree_cur *cur;
- struct xfs_buf *agbp;
+ struct xfs_buf *agbp = NULL;
struct xfs_refcount_recovery *rr, *n;
struct list_head debris;
union xfs_btree_irec low;
@@ -1940,7 +1941,12 @@ xfs_refcount_recover_cow_leftovers(
/* reflink filesystems mustn't have AGs larger than 2^31-1 blocks */
BUILD_BUG_ON(XFS_MAX_CRC_AG_BLOCKS >= XFS_REFC_COWFLAG);
- if (mp->m_sb.sb_agblocks > XFS_MAX_CRC_AG_BLOCKS)
+ if (pag && mp->m_sb.sb_agblocks > XFS_MAX_CRC_AG_BLOCKS)
+ return -EOPNOTSUPP;
+
+ /* rtreflink filesystems can't have rtgroups larger than 2^31-1 blocks */
+ BUILD_BUG_ON(XFS_MAX_RGBLOCKS >= XFS_REFC_COWFLAG);
+ if (rtg && mp->m_sb.sb_rgblocks >= XFS_MAX_RGBLOCKS)
return -EOPNOTSUPP;
INIT_LIST_HEAD(&debris);
@@ -1959,10 +1965,16 @@ xfs_refcount_recover_cow_leftovers(
if (error)
return error;
- error = xfs_alloc_read_agf(pag, tp, 0, &agbp);
- if (error)
- goto out_trans;
- cur = xfs_refcountbt_init_cursor(mp, tp, agbp, pag);
+ if (rtg) {
+ xfs_rtgroup_lock(NULL, rtg, XFS_RTGLOCK_REFCOUNT);
+ cur = xfs_rtrefcountbt_init_cursor(mp, tp, rtg,
+ rtg->rtg_refcountip);
+ } else {
+ error = xfs_alloc_read_agf(pag, tp, 0, &agbp);
+ if (error)
+ goto out_trans;
+ cur = xfs_refcountbt_init_cursor(mp, tp, agbp, pag);
+ }
/* Find all the leftover CoW staging extents. */
memset(&low, 0, sizeof(low));
@@ -1972,7 +1984,10 @@ xfs_refcount_recover_cow_leftovers(
error = xfs_btree_query_range(cur, &low, &high,
xfs_refcount_recover_extent, &debris);
xfs_btree_del_cursor(cur, error);
- xfs_trans_brelse(tp, agbp);
+ if (agbp)
+ xfs_trans_brelse(tp, agbp);
+ else
+ xfs_rtgroup_unlock(rtg, XFS_RTGLOCK_REFCOUNT);
xfs_trans_cancel(tp);
if (error)
goto out_free;
@@ -1985,14 +2000,18 @@ xfs_refcount_recover_cow_leftovers(
goto out_free;
/* Free the orphan record */
- fsb = XFS_AGB_TO_FSB(mp, pag->pag_agno,
- rr->rr_rrec.rc_startblock);
- xfs_refcount_free_cow_extent(tp, false, fsb,
+ if (rtg)
+ fsb = xfs_rgbno_to_rtb(mp, rtg->rtg_rgno,
+ rr->rr_rrec.rc_startblock);
+ else
+ fsb = XFS_AGB_TO_FSB(mp, pag->pag_agno,
+ rr->rr_rrec.rc_startblock);
+ xfs_refcount_free_cow_extent(tp, rtg != NULL, fsb,
rr->rr_rrec.rc_blockcount);
/* Free the block. */
xfs_free_extent_later(tp, fsb, rr->rr_rrec.rc_blockcount, NULL,
- 0);
+ rtg != NULL ? XFS_FREE_EXTENT_REALTIME : 0);
error = xfs_trans_commit(tp);
if (error)
@@ -2014,6 +2033,22 @@ out_free:
return error;
}
+int
+xfs_refcount_recover_cow_leftovers(
+ struct xfs_mount *mp,
+ struct xfs_perag *pag)
+{
+ return xfs_refcount_recover_group_cow_leftovers(mp, pag, NULL);
+}
+
+int
+xfs_refcount_recover_rtcow_leftovers(
+ struct xfs_mount *mp,
+ struct xfs_rtgroup *rtg)
+{
+ return xfs_refcount_recover_group_cow_leftovers(mp, NULL, rtg);
+}
+
/*
* Scan part of the keyspace of the refcount records and tell us if the area
* has no records, is fully mapped by records, or is partially filled.
diff --git a/fs/xfs/libxfs/xfs_refcount.h b/fs/xfs/libxfs/xfs_refcount.h
index 4e725d723e88..c7907119d10c 100644
--- a/fs/xfs/libxfs/xfs_refcount.h
+++ b/fs/xfs/libxfs/xfs_refcount.h
@@ -12,6 +12,7 @@ struct xfs_perag;
struct xfs_btree_cur;
struct xfs_bmbt_irec;
struct xfs_refcount_irec;
+struct xfs_rtgroup;
extern int xfs_refcount_lookup_le(struct xfs_btree_cur *cur,
enum xfs_refc_domain domain, xfs_agblock_t bno, int *stat);
@@ -99,8 +100,10 @@ void xfs_refcount_alloc_cow_extent(struct xfs_trans *tp, bool isrt,
xfs_fsblock_t fsb, xfs_extlen_t len);
void xfs_refcount_free_cow_extent(struct xfs_trans *tp, bool isrt,
xfs_fsblock_t fsb, xfs_extlen_t len);
-extern int xfs_refcount_recover_cow_leftovers(struct xfs_mount *mp,
+int xfs_refcount_recover_cow_leftovers(struct xfs_mount *mp,
struct xfs_perag *pag);
+int xfs_refcount_recover_rtcow_leftovers(struct xfs_mount *mp,
+ struct xfs_rtgroup *rtg);
/*
* While we're adjusting the refcounts records of an extent, we have
diff --git a/fs/xfs/xfs_reflink.c b/fs/xfs/xfs_reflink.c
index b88cabc4faba..80ebd4961df8 100644
--- a/fs/xfs/xfs_reflink.c
+++ b/fs/xfs/xfs_reflink.c
@@ -1004,7 +1004,9 @@ xfs_reflink_recover_cow(
struct xfs_mount *mp)
{
struct xfs_perag *pag;
+ struct xfs_rtgroup *rtg;
xfs_agnumber_t agno;
+ xfs_rgnumber_t rgno;
int error = 0;
if (!xfs_has_reflink(mp))
@@ -1014,11 +1016,19 @@ xfs_reflink_recover_cow(
error = xfs_refcount_recover_cow_leftovers(mp, pag);
if (error) {
xfs_perag_put(pag);
- break;
+ return error;
}
}
- return error;
+ for_each_rtgroup(mp, rgno, rtg) {
+ error = xfs_refcount_recover_rtcow_leftovers(mp, rtg);
+ if (error) {
+ xfs_rtgroup_put(rtg);
+ return error;
+ }
+ }
+
+ return 0;
}
/*