162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (C) 2017-2023 Oracle. All Rights Reserved. 462306a36Sopenharmony_ci * Author: Darrick J. Wong <djwong@kernel.org> 562306a36Sopenharmony_ci */ 662306a36Sopenharmony_ci#include "xfs.h" 762306a36Sopenharmony_ci#include "xfs_fs.h" 862306a36Sopenharmony_ci#include "xfs_shared.h" 962306a36Sopenharmony_ci#include "xfs_format.h" 1062306a36Sopenharmony_ci#include "xfs_trans_resv.h" 1162306a36Sopenharmony_ci#include "xfs_mount.h" 1262306a36Sopenharmony_ci#include "xfs_btree.h" 1362306a36Sopenharmony_ci#include "xfs_alloc.h" 1462306a36Sopenharmony_ci#include "xfs_rmap.h" 1562306a36Sopenharmony_ci#include "scrub/scrub.h" 1662306a36Sopenharmony_ci#include "scrub/common.h" 1762306a36Sopenharmony_ci#include "scrub/btree.h" 1862306a36Sopenharmony_ci#include "xfs_ag.h" 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_ci/* 2162306a36Sopenharmony_ci * Set us up to scrub free space btrees. 2262306a36Sopenharmony_ci */ 2362306a36Sopenharmony_ciint 2462306a36Sopenharmony_cixchk_setup_ag_allocbt( 2562306a36Sopenharmony_ci struct xfs_scrub *sc) 2662306a36Sopenharmony_ci{ 2762306a36Sopenharmony_ci if (xchk_need_intent_drain(sc)) 2862306a36Sopenharmony_ci xchk_fsgates_enable(sc, XCHK_FSGATES_DRAIN); 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_ci return xchk_setup_ag_btree(sc, false); 3162306a36Sopenharmony_ci} 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci/* Free space btree scrubber. */ 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_cistruct xchk_alloc { 3662306a36Sopenharmony_ci /* Previous free space extent. */ 3762306a36Sopenharmony_ci struct xfs_alloc_rec_incore prev; 3862306a36Sopenharmony_ci}; 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_ci/* 4162306a36Sopenharmony_ci * Ensure there's a corresponding cntbt/bnobt record matching this 4262306a36Sopenharmony_ci * bnobt/cntbt record, respectively. 4362306a36Sopenharmony_ci */ 4462306a36Sopenharmony_ciSTATIC void 4562306a36Sopenharmony_cixchk_allocbt_xref_other( 4662306a36Sopenharmony_ci struct xfs_scrub *sc, 4762306a36Sopenharmony_ci xfs_agblock_t agbno, 4862306a36Sopenharmony_ci xfs_extlen_t len) 4962306a36Sopenharmony_ci{ 5062306a36Sopenharmony_ci struct xfs_btree_cur **pcur; 5162306a36Sopenharmony_ci xfs_agblock_t fbno; 5262306a36Sopenharmony_ci xfs_extlen_t flen; 5362306a36Sopenharmony_ci int has_otherrec; 5462306a36Sopenharmony_ci int error; 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_ci if (sc->sm->sm_type == XFS_SCRUB_TYPE_BNOBT) 5762306a36Sopenharmony_ci pcur = &sc->sa.cnt_cur; 5862306a36Sopenharmony_ci else 5962306a36Sopenharmony_ci pcur = &sc->sa.bno_cur; 6062306a36Sopenharmony_ci if (!*pcur || xchk_skip_xref(sc->sm)) 6162306a36Sopenharmony_ci return; 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_ci error = xfs_alloc_lookup_le(*pcur, agbno, len, &has_otherrec); 6462306a36Sopenharmony_ci if (!xchk_should_check_xref(sc, &error, pcur)) 6562306a36Sopenharmony_ci return; 6662306a36Sopenharmony_ci if (!has_otherrec) { 6762306a36Sopenharmony_ci xchk_btree_xref_set_corrupt(sc, *pcur, 0); 6862306a36Sopenharmony_ci return; 6962306a36Sopenharmony_ci } 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_ci error = xfs_alloc_get_rec(*pcur, &fbno, &flen, &has_otherrec); 7262306a36Sopenharmony_ci if (!xchk_should_check_xref(sc, &error, pcur)) 7362306a36Sopenharmony_ci return; 7462306a36Sopenharmony_ci if (!has_otherrec) { 7562306a36Sopenharmony_ci xchk_btree_xref_set_corrupt(sc, *pcur, 0); 7662306a36Sopenharmony_ci return; 7762306a36Sopenharmony_ci } 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci if (fbno != agbno || flen != len) 8062306a36Sopenharmony_ci xchk_btree_xref_set_corrupt(sc, *pcur, 0); 8162306a36Sopenharmony_ci} 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci/* Cross-reference with the other btrees. */ 8462306a36Sopenharmony_ciSTATIC void 8562306a36Sopenharmony_cixchk_allocbt_xref( 8662306a36Sopenharmony_ci struct xfs_scrub *sc, 8762306a36Sopenharmony_ci const struct xfs_alloc_rec_incore *irec) 8862306a36Sopenharmony_ci{ 8962306a36Sopenharmony_ci xfs_agblock_t agbno = irec->ar_startblock; 9062306a36Sopenharmony_ci xfs_extlen_t len = irec->ar_blockcount; 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci if (sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT) 9362306a36Sopenharmony_ci return; 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci xchk_allocbt_xref_other(sc, agbno, len); 9662306a36Sopenharmony_ci xchk_xref_is_not_inode_chunk(sc, agbno, len); 9762306a36Sopenharmony_ci xchk_xref_has_no_owner(sc, agbno, len); 9862306a36Sopenharmony_ci xchk_xref_is_not_shared(sc, agbno, len); 9962306a36Sopenharmony_ci xchk_xref_is_not_cow_staging(sc, agbno, len); 10062306a36Sopenharmony_ci} 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ci/* Flag failures for records that could be merged. */ 10362306a36Sopenharmony_ciSTATIC void 10462306a36Sopenharmony_cixchk_allocbt_mergeable( 10562306a36Sopenharmony_ci struct xchk_btree *bs, 10662306a36Sopenharmony_ci struct xchk_alloc *ca, 10762306a36Sopenharmony_ci const struct xfs_alloc_rec_incore *irec) 10862306a36Sopenharmony_ci{ 10962306a36Sopenharmony_ci if (bs->sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT) 11062306a36Sopenharmony_ci return; 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci if (ca->prev.ar_blockcount > 0 && 11362306a36Sopenharmony_ci ca->prev.ar_startblock + ca->prev.ar_blockcount == irec->ar_startblock && 11462306a36Sopenharmony_ci ca->prev.ar_blockcount + irec->ar_blockcount < (uint32_t)~0U) 11562306a36Sopenharmony_ci xchk_btree_set_corrupt(bs->sc, bs->cur, 0); 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci memcpy(&ca->prev, irec, sizeof(*irec)); 11862306a36Sopenharmony_ci} 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci/* Scrub a bnobt/cntbt record. */ 12162306a36Sopenharmony_ciSTATIC int 12262306a36Sopenharmony_cixchk_allocbt_rec( 12362306a36Sopenharmony_ci struct xchk_btree *bs, 12462306a36Sopenharmony_ci const union xfs_btree_rec *rec) 12562306a36Sopenharmony_ci{ 12662306a36Sopenharmony_ci struct xfs_alloc_rec_incore irec; 12762306a36Sopenharmony_ci struct xchk_alloc *ca = bs->private; 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci xfs_alloc_btrec_to_irec(rec, &irec); 13062306a36Sopenharmony_ci if (xfs_alloc_check_irec(bs->cur, &irec) != NULL) { 13162306a36Sopenharmony_ci xchk_btree_set_corrupt(bs->sc, bs->cur, 0); 13262306a36Sopenharmony_ci return 0; 13362306a36Sopenharmony_ci } 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci xchk_allocbt_mergeable(bs, ca, &irec); 13662306a36Sopenharmony_ci xchk_allocbt_xref(bs->sc, &irec); 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_ci return 0; 13962306a36Sopenharmony_ci} 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ci/* Scrub the freespace btrees for some AG. */ 14262306a36Sopenharmony_ciSTATIC int 14362306a36Sopenharmony_cixchk_allocbt( 14462306a36Sopenharmony_ci struct xfs_scrub *sc, 14562306a36Sopenharmony_ci xfs_btnum_t which) 14662306a36Sopenharmony_ci{ 14762306a36Sopenharmony_ci struct xchk_alloc ca = { }; 14862306a36Sopenharmony_ci struct xfs_btree_cur *cur; 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_ci cur = which == XFS_BTNUM_BNO ? sc->sa.bno_cur : sc->sa.cnt_cur; 15162306a36Sopenharmony_ci return xchk_btree(sc, cur, xchk_allocbt_rec, &XFS_RMAP_OINFO_AG, &ca); 15262306a36Sopenharmony_ci} 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_ciint 15562306a36Sopenharmony_cixchk_bnobt( 15662306a36Sopenharmony_ci struct xfs_scrub *sc) 15762306a36Sopenharmony_ci{ 15862306a36Sopenharmony_ci return xchk_allocbt(sc, XFS_BTNUM_BNO); 15962306a36Sopenharmony_ci} 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_ciint 16262306a36Sopenharmony_cixchk_cntbt( 16362306a36Sopenharmony_ci struct xfs_scrub *sc) 16462306a36Sopenharmony_ci{ 16562306a36Sopenharmony_ci return xchk_allocbt(sc, XFS_BTNUM_CNT); 16662306a36Sopenharmony_ci} 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ci/* xref check that the extent is not free */ 16962306a36Sopenharmony_civoid 17062306a36Sopenharmony_cixchk_xref_is_used_space( 17162306a36Sopenharmony_ci struct xfs_scrub *sc, 17262306a36Sopenharmony_ci xfs_agblock_t agbno, 17362306a36Sopenharmony_ci xfs_extlen_t len) 17462306a36Sopenharmony_ci{ 17562306a36Sopenharmony_ci enum xbtree_recpacking outcome; 17662306a36Sopenharmony_ci int error; 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_ci if (!sc->sa.bno_cur || xchk_skip_xref(sc->sm)) 17962306a36Sopenharmony_ci return; 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_ci error = xfs_alloc_has_records(sc->sa.bno_cur, agbno, len, &outcome); 18262306a36Sopenharmony_ci if (!xchk_should_check_xref(sc, &error, &sc->sa.bno_cur)) 18362306a36Sopenharmony_ci return; 18462306a36Sopenharmony_ci if (outcome != XBTREE_RECPACKING_EMPTY) 18562306a36Sopenharmony_ci xchk_btree_xref_set_corrupt(sc, sc->sa.bno_cur, 0); 18662306a36Sopenharmony_ci} 187