Commit 9099cd38 authored by Darrick J. Wong's avatar Darrick J. Wong

xfs: repair refcount btrees

Reconstruct the refcount data from the rmap btree.

Link: https://docs.kernel.org/filesystems/xfs-online-fsck-design.html#case-study-rebuilding-the-space-reference-countsSigned-off-by: default avatarDarrick J. Wong <djwong@kernel.org>
Reviewed-by: default avatarDave Chinner <dchinner@redhat.com>
Reviewed-by: default avatarChristoph Hellwig <hch@lst.de>
parent dbfbf3bd
...@@ -186,6 +186,7 @@ xfs-y += $(addprefix scrub/, \ ...@@ -186,6 +186,7 @@ xfs-y += $(addprefix scrub/, \
ialloc_repair.o \ ialloc_repair.o \
newbt.o \ newbt.o \
reap.o \ reap.o \
refcount_repair.o \
repair.o \ repair.o \
) )
endif endif
......
...@@ -87,6 +87,7 @@ struct xfs_perag { ...@@ -87,6 +87,7 @@ struct xfs_perag {
* verifiers while rebuilding the AG btrees. * verifiers while rebuilding the AG btrees.
*/ */
uint8_t pagf_repair_levels[XFS_BTNUM_AGF]; uint8_t pagf_repair_levels[XFS_BTNUM_AGF];
uint8_t pagf_repair_refcount_level;
#endif #endif
spinlock_t pag_state_lock; spinlock_t pag_state_lock;
......
...@@ -5212,3 +5212,29 @@ xfs_btree_destroy_cur_caches(void) ...@@ -5212,3 +5212,29 @@ xfs_btree_destroy_cur_caches(void)
xfs_rmapbt_destroy_cur_cache(); xfs_rmapbt_destroy_cur_cache();
xfs_refcountbt_destroy_cur_cache(); xfs_refcountbt_destroy_cur_cache();
} }
/* Move the btree cursor before the first record. */
int
xfs_btree_goto_left_edge(
struct xfs_btree_cur *cur)
{
int stat = 0;
int error;
memset(&cur->bc_rec, 0, sizeof(cur->bc_rec));
error = xfs_btree_lookup(cur, XFS_LOOKUP_LE, &stat);
if (error)
return error;
if (!stat)
return 0;
error = xfs_btree_decrement(cur, 0, &stat);
if (error)
return error;
if (stat != 0) {
ASSERT(0);
return -EFSCORRUPTED;
}
return 0;
}
...@@ -738,4 +738,6 @@ xfs_btree_alloc_cursor( ...@@ -738,4 +738,6 @@ xfs_btree_alloc_cursor(
int __init xfs_btree_init_cur_caches(void); int __init xfs_btree_init_cur_caches(void);
void xfs_btree_destroy_cur_caches(void); void xfs_btree_destroy_cur_caches(void);
int xfs_btree_goto_left_edge(struct xfs_btree_cur *cur);
#endif /* __XFS_BTREE_H__ */ #endif /* __XFS_BTREE_H__ */
...@@ -123,11 +123,9 @@ xfs_refcount_btrec_to_irec( ...@@ -123,11 +123,9 @@ xfs_refcount_btrec_to_irec(
/* Simple checks for refcount records. */ /* Simple checks for refcount records. */
xfs_failaddr_t xfs_failaddr_t
xfs_refcount_check_irec( xfs_refcount_check_irec(
struct xfs_btree_cur *cur, struct xfs_perag *pag,
const struct xfs_refcount_irec *irec) const struct xfs_refcount_irec *irec)
{ {
struct xfs_perag *pag = cur->bc_ag.pag;
if (irec->rc_blockcount == 0 || irec->rc_blockcount > MAXREFCEXTLEN) if (irec->rc_blockcount == 0 || irec->rc_blockcount > MAXREFCEXTLEN)
return __this_address; return __this_address;
...@@ -179,7 +177,7 @@ xfs_refcount_get_rec( ...@@ -179,7 +177,7 @@ xfs_refcount_get_rec(
return error; return error;
xfs_refcount_btrec_to_irec(rec, irec); xfs_refcount_btrec_to_irec(rec, irec);
fa = xfs_refcount_check_irec(cur, irec); fa = xfs_refcount_check_irec(cur->bc_ag.pag, irec);
if (fa) if (fa)
return xfs_refcount_complain_bad_rec(cur, fa, irec); return xfs_refcount_complain_bad_rec(cur, fa, irec);
...@@ -1899,7 +1897,7 @@ xfs_refcount_recover_extent( ...@@ -1899,7 +1897,7 @@ xfs_refcount_recover_extent(
INIT_LIST_HEAD(&rr->rr_list); INIT_LIST_HEAD(&rr->rr_list);
xfs_refcount_btrec_to_irec(rec, &rr->rr_rrec); xfs_refcount_btrec_to_irec(rec, &rr->rr_rrec);
if (xfs_refcount_check_irec(cur, &rr->rr_rrec) != NULL || if (xfs_refcount_check_irec(cur->bc_ag.pag, &rr->rr_rrec) != NULL ||
XFS_IS_CORRUPT(cur->bc_mp, XFS_IS_CORRUPT(cur->bc_mp,
rr->rr_rrec.rc_domain != XFS_REFC_DOMAIN_COW)) { rr->rr_rrec.rc_domain != XFS_REFC_DOMAIN_COW)) {
kfree(rr); kfree(rr);
......
...@@ -117,7 +117,7 @@ extern int xfs_refcount_has_records(struct xfs_btree_cur *cur, ...@@ -117,7 +117,7 @@ extern int xfs_refcount_has_records(struct xfs_btree_cur *cur,
union xfs_btree_rec; union xfs_btree_rec;
extern void xfs_refcount_btrec_to_irec(const union xfs_btree_rec *rec, extern void xfs_refcount_btrec_to_irec(const union xfs_btree_rec *rec,
struct xfs_refcount_irec *irec); struct xfs_refcount_irec *irec);
xfs_failaddr_t xfs_refcount_check_irec(struct xfs_btree_cur *cur, xfs_failaddr_t xfs_refcount_check_irec(struct xfs_perag *pag,
const struct xfs_refcount_irec *irec); const struct xfs_refcount_irec *irec);
extern int xfs_refcount_insert(struct xfs_btree_cur *cur, extern int xfs_refcount_insert(struct xfs_btree_cur *cur,
struct xfs_refcount_irec *irec, int *stat); struct xfs_refcount_irec *irec, int *stat);
......
...@@ -226,7 +226,18 @@ xfs_refcountbt_verify( ...@@ -226,7 +226,18 @@ xfs_refcountbt_verify(
level = be16_to_cpu(block->bb_level); level = be16_to_cpu(block->bb_level);
if (pag && xfs_perag_initialised_agf(pag)) { if (pag && xfs_perag_initialised_agf(pag)) {
if (level >= pag->pagf_refcount_level) unsigned int maxlevel = pag->pagf_refcount_level;
#ifdef CONFIG_XFS_ONLINE_REPAIR
/*
* Online repair could be rewriting the refcount btree, so
* we'll validate against the larger of either tree while this
* is going on.
*/
maxlevel = max_t(unsigned int, maxlevel,
pag->pagf_repair_refcount_level);
#endif
if (level >= maxlevel)
return __this_address; return __this_address;
} else if (level >= mp->m_refc_maxlevels) } else if (level >= mp->m_refc_maxlevels)
return __this_address; return __this_address;
......
...@@ -441,7 +441,7 @@ xchk_refcountbt_rec( ...@@ -441,7 +441,7 @@ xchk_refcountbt_rec(
struct xchk_refcbt_records *rrc = bs->private; struct xchk_refcbt_records *rrc = bs->private;
xfs_refcount_btrec_to_irec(rec, &irec); xfs_refcount_btrec_to_irec(rec, &irec);
if (xfs_refcount_check_irec(bs->cur, &irec) != NULL) { if (xfs_refcount_check_irec(bs->cur->bc_ag.pag, &irec) != NULL) {
xchk_btree_set_corrupt(bs->sc, bs->cur, 0); xchk_btree_set_corrupt(bs->sc, bs->cur, 0);
return 0; return 0;
} }
......
This diff is collapsed.
...@@ -80,6 +80,7 @@ int xrep_agfl(struct xfs_scrub *sc); ...@@ -80,6 +80,7 @@ int xrep_agfl(struct xfs_scrub *sc);
int xrep_agi(struct xfs_scrub *sc); int xrep_agi(struct xfs_scrub *sc);
int xrep_allocbt(struct xfs_scrub *sc); int xrep_allocbt(struct xfs_scrub *sc);
int xrep_iallocbt(struct xfs_scrub *sc); int xrep_iallocbt(struct xfs_scrub *sc);
int xrep_refcountbt(struct xfs_scrub *sc);
int xrep_reinit_pagf(struct xfs_scrub *sc); int xrep_reinit_pagf(struct xfs_scrub *sc);
int xrep_reinit_pagi(struct xfs_scrub *sc); int xrep_reinit_pagi(struct xfs_scrub *sc);
...@@ -133,6 +134,7 @@ xrep_setup_nothing( ...@@ -133,6 +134,7 @@ xrep_setup_nothing(
#define xrep_agi xrep_notsupported #define xrep_agi xrep_notsupported
#define xrep_allocbt xrep_notsupported #define xrep_allocbt xrep_notsupported
#define xrep_iallocbt xrep_notsupported #define xrep_iallocbt xrep_notsupported
#define xrep_refcountbt xrep_notsupported
#endif /* CONFIG_XFS_ONLINE_REPAIR */ #endif /* CONFIG_XFS_ONLINE_REPAIR */
......
...@@ -276,7 +276,7 @@ static const struct xchk_meta_ops meta_scrub_ops[] = { ...@@ -276,7 +276,7 @@ static const struct xchk_meta_ops meta_scrub_ops[] = {
.setup = xchk_setup_ag_refcountbt, .setup = xchk_setup_ag_refcountbt,
.scrub = xchk_refcountbt, .scrub = xchk_refcountbt,
.has = xfs_has_reflink, .has = xfs_has_reflink,
.repair = xrep_notsupported, .repair = xrep_refcountbt,
}, },
[XFS_SCRUB_TYPE_INODE] = { /* inode record */ [XFS_SCRUB_TYPE_INODE] = { /* inode record */
.type = ST_INODE, .type = ST_INODE,
......
...@@ -1232,27 +1232,29 @@ TRACE_EVENT(xrep_ibt_found, ...@@ -1232,27 +1232,29 @@ TRACE_EVENT(xrep_ibt_found,
__entry->freemask) __entry->freemask)
) )
TRACE_EVENT(xrep_refcount_extent_fn, TRACE_EVENT(xrep_refc_found,
TP_PROTO(struct xfs_mount *mp, xfs_agnumber_t agno, TP_PROTO(struct xfs_perag *pag, const struct xfs_refcount_irec *rec),
struct xfs_refcount_irec *irec), TP_ARGS(pag, rec),
TP_ARGS(mp, agno, irec),
TP_STRUCT__entry( TP_STRUCT__entry(
__field(dev_t, dev) __field(dev_t, dev)
__field(xfs_agnumber_t, agno) __field(xfs_agnumber_t, agno)
__field(enum xfs_refc_domain, domain)
__field(xfs_agblock_t, startblock) __field(xfs_agblock_t, startblock)
__field(xfs_extlen_t, blockcount) __field(xfs_extlen_t, blockcount)
__field(xfs_nlink_t, refcount) __field(xfs_nlink_t, refcount)
), ),
TP_fast_assign( TP_fast_assign(
__entry->dev = mp->m_super->s_dev; __entry->dev = pag->pag_mount->m_super->s_dev;
__entry->agno = agno; __entry->agno = pag->pag_agno;
__entry->startblock = irec->rc_startblock; __entry->domain = rec->rc_domain;
__entry->blockcount = irec->rc_blockcount; __entry->startblock = rec->rc_startblock;
__entry->refcount = irec->rc_refcount; __entry->blockcount = rec->rc_blockcount;
__entry->refcount = rec->rc_refcount;
), ),
TP_printk("dev %d:%d agno 0x%x agbno 0x%x fsbcount 0x%x refcount %u", TP_printk("dev %d:%d agno 0x%x dom %s agbno 0x%x fsbcount 0x%x refcount %u",
MAJOR(__entry->dev), MINOR(__entry->dev), MAJOR(__entry->dev), MINOR(__entry->dev),
__entry->agno, __entry->agno,
__print_symbolic(__entry->domain, XFS_REFC_DOMAIN_STRINGS),
__entry->startblock, __entry->startblock,
__entry->blockcount, __entry->blockcount,
__entry->refcount) __entry->refcount)
......
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