mirror of
https://github.com/linux-msm/laptops-kernel.git
synced 2026-08-13 14:19:53 -07:00
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-counts Signed-off-by: Darrick J. Wong <djwong@kernel.org> Reviewed-by: Dave Chinner <dchinner@redhat.com> Reviewed-by: Christoph Hellwig <hch@lst.de>
This commit is contained in:
@@ -186,6 +186,7 @@ xfs-y += $(addprefix scrub/, \
|
||||
ialloc_repair.o \
|
||||
newbt.o \
|
||||
reap.o \
|
||||
refcount_repair.o \
|
||||
repair.o \
|
||||
)
|
||||
endif
|
||||
|
||||
@@ -87,6 +87,7 @@ struct xfs_perag {
|
||||
* verifiers while rebuilding the AG btrees.
|
||||
*/
|
||||
uint8_t pagf_repair_levels[XFS_BTNUM_AGF];
|
||||
uint8_t pagf_repair_refcount_level;
|
||||
#endif
|
||||
|
||||
spinlock_t pag_state_lock;
|
||||
|
||||
@@ -5212,3 +5212,29 @@ xfs_btree_destroy_cur_caches(void)
|
||||
xfs_rmapbt_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(
|
||||
int __init xfs_btree_init_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__ */
|
||||
|
||||
@@ -123,11 +123,9 @@ xfs_refcount_btrec_to_irec(
|
||||
/* Simple checks for refcount records. */
|
||||
xfs_failaddr_t
|
||||
xfs_refcount_check_irec(
|
||||
struct xfs_btree_cur *cur,
|
||||
struct xfs_perag *pag,
|
||||
const struct xfs_refcount_irec *irec)
|
||||
{
|
||||
struct xfs_perag *pag = cur->bc_ag.pag;
|
||||
|
||||
if (irec->rc_blockcount == 0 || irec->rc_blockcount > MAXREFCEXTLEN)
|
||||
return __this_address;
|
||||
|
||||
@@ -179,7 +177,7 @@ xfs_refcount_get_rec(
|
||||
return error;
|
||||
|
||||
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)
|
||||
return xfs_refcount_complain_bad_rec(cur, fa, irec);
|
||||
|
||||
@@ -1899,7 +1897,7 @@ xfs_refcount_recover_extent(
|
||||
INIT_LIST_HEAD(&rr->rr_list);
|
||||
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,
|
||||
rr->rr_rrec.rc_domain != XFS_REFC_DOMAIN_COW)) {
|
||||
kfree(rr);
|
||||
|
||||
@@ -117,7 +117,7 @@ extern int xfs_refcount_has_records(struct xfs_btree_cur *cur,
|
||||
union xfs_btree_rec;
|
||||
extern void xfs_refcount_btrec_to_irec(const union xfs_btree_rec *rec,
|
||||
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);
|
||||
extern int xfs_refcount_insert(struct xfs_btree_cur *cur,
|
||||
struct xfs_refcount_irec *irec, int *stat);
|
||||
|
||||
@@ -226,7 +226,18 @@ xfs_refcountbt_verify(
|
||||
|
||||
level = be16_to_cpu(block->bb_level);
|
||||
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;
|
||||
} else if (level >= mp->m_refc_maxlevels)
|
||||
return __this_address;
|
||||
|
||||
@@ -441,7 +441,7 @@ xchk_refcountbt_rec(
|
||||
struct xchk_refcbt_records *rrc = bs->private;
|
||||
|
||||
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);
|
||||
return 0;
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -80,6 +80,7 @@ int xrep_agfl(struct xfs_scrub *sc);
|
||||
int xrep_agi(struct xfs_scrub *sc);
|
||||
int xrep_allocbt(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_pagi(struct xfs_scrub *sc);
|
||||
@@ -133,6 +134,7 @@ xrep_setup_nothing(
|
||||
#define xrep_agi xrep_notsupported
|
||||
#define xrep_allocbt xrep_notsupported
|
||||
#define xrep_iallocbt xrep_notsupported
|
||||
#define xrep_refcountbt xrep_notsupported
|
||||
|
||||
#endif /* CONFIG_XFS_ONLINE_REPAIR */
|
||||
|
||||
|
||||
@@ -276,7 +276,7 @@ static const struct xchk_meta_ops meta_scrub_ops[] = {
|
||||
.setup = xchk_setup_ag_refcountbt,
|
||||
.scrub = xchk_refcountbt,
|
||||
.has = xfs_has_reflink,
|
||||
.repair = xrep_notsupported,
|
||||
.repair = xrep_refcountbt,
|
||||
},
|
||||
[XFS_SCRUB_TYPE_INODE] = { /* inode record */
|
||||
.type = ST_INODE,
|
||||
|
||||
+12
-10
@@ -1232,27 +1232,29 @@ TRACE_EVENT(xrep_ibt_found,
|
||||
__entry->freemask)
|
||||
)
|
||||
|
||||
TRACE_EVENT(xrep_refcount_extent_fn,
|
||||
TP_PROTO(struct xfs_mount *mp, xfs_agnumber_t agno,
|
||||
struct xfs_refcount_irec *irec),
|
||||
TP_ARGS(mp, agno, irec),
|
||||
TRACE_EVENT(xrep_refc_found,
|
||||
TP_PROTO(struct xfs_perag *pag, const struct xfs_refcount_irec *rec),
|
||||
TP_ARGS(pag, rec),
|
||||
TP_STRUCT__entry(
|
||||
__field(dev_t, dev)
|
||||
__field(xfs_agnumber_t, agno)
|
||||
__field(enum xfs_refc_domain, domain)
|
||||
__field(xfs_agblock_t, startblock)
|
||||
__field(xfs_extlen_t, blockcount)
|
||||
__field(xfs_nlink_t, refcount)
|
||||
),
|
||||
TP_fast_assign(
|
||||
__entry->dev = mp->m_super->s_dev;
|
||||
__entry->agno = agno;
|
||||
__entry->startblock = irec->rc_startblock;
|
||||
__entry->blockcount = irec->rc_blockcount;
|
||||
__entry->refcount = irec->rc_refcount;
|
||||
__entry->dev = pag->pag_mount->m_super->s_dev;
|
||||
__entry->agno = pag->pag_agno;
|
||||
__entry->domain = rec->rc_domain;
|
||||
__entry->startblock = rec->rc_startblock;
|
||||
__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),
|
||||
__entry->agno,
|
||||
__print_symbolic(__entry->domain, XFS_REFC_DOMAIN_STRINGS),
|
||||
__entry->startblock,
|
||||
__entry->blockcount,
|
||||
__entry->refcount)
|
||||
|
||||
Reference in New Issue
Block a user