1 // SPDX-License-Identifier: GPL-2.0-or-later 2 /* 3 * Copyright (C) 2017-2023 Oracle. All Rights Reserved. 4 * Author: Darrick J. Wong <djwong@kernel.org> 5 */ 6 #include "xfs.h" 7 #include "xfs_fs.h" 8 #include "xfs_shared.h" 9 #include "xfs_format.h" 10 #include "xfs_trans_resv.h" 11 #include "xfs_mount.h" 12 #include "xfs_btree.h" 13 #include "xfs_alloc.h" 14 #include "xfs_rmap.h" 15 #include "scrub/scrub.h" 16 #include "scrub/common.h" 17 #include "scrub/btree.h" 18 #include "xfs_ag.h" 19 20 /* 21 * Set us up to scrub free space btrees. 22 */ 23 int 24 xchk_setup_ag_allocbt( 25 struct xfs_scrub *sc) 26 { 27 if (xchk_need_intent_drain(sc)) 28 xchk_fsgates_enable(sc, XCHK_FSGATES_DRAIN); 29 30 return xchk_setup_ag_btree(sc, false); 31 } 32 33 /* Free space btree scrubber. */ 34 35 struct xchk_alloc { 36 /* Previous free space extent. */ 37 struct xfs_alloc_rec_incore prev; 38 }; 39 40 /* 41 * Ensure there's a corresponding cntbt/bnobt record matching this 42 * bnobt/cntbt record, respectively. 43 */ 44 STATIC void 45 xchk_allocbt_xref_other( 46 struct xfs_scrub *sc, 47 xfs_agblock_t agbno, 48 xfs_extlen_t len) 49 { 50 struct xfs_btree_cur **pcur; 51 xfs_agblock_t fbno; 52 xfs_extlen_t flen; 53 int has_otherrec; 54 int error; 55 56 if (sc->sm->sm_type == XFS_SCRUB_TYPE_BNOBT) 57 pcur = &sc->sa.cnt_cur; 58 else 59 pcur = &sc->sa.bno_cur; 60 if (!*pcur || xchk_skip_xref(sc->sm)) 61 return; 62 63 error = xfs_alloc_lookup_le(*pcur, agbno, len, &has_otherrec); 64 if (!xchk_should_check_xref(sc, &error, pcur)) 65 return; 66 if (!has_otherrec) { 67 xchk_btree_xref_set_corrupt(sc, *pcur, 0); 68 return; 69 } 70 71 error = xfs_alloc_get_rec(*pcur, &fbno, &flen, &has_otherrec); 72 if (!xchk_should_check_xref(sc, &error, pcur)) 73 return; 74 if (!has_otherrec) { 75 xchk_btree_xref_set_corrupt(sc, *pcur, 0); 76 return; 77 } 78 79 if (fbno != agbno || flen != len) 80 xchk_btree_xref_set_corrupt(sc, *pcur, 0); 81 } 82 83 /* Cross-reference with the other btrees. */ 84 STATIC void 85 xchk_allocbt_xref( 86 struct xfs_scrub *sc, 87 const struct xfs_alloc_rec_incore *irec) 88 { 89 xfs_agblock_t agbno = irec->ar_startblock; 90 xfs_extlen_t len = irec->ar_blockcount; 91 92 if (sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT) 93 return; 94 95 xchk_allocbt_xref_other(sc, agbno, len); 96 xchk_xref_is_not_inode_chunk(sc, agbno, len); 97 xchk_xref_has_no_owner(sc, agbno, len); 98 xchk_xref_is_not_shared(sc, agbno, len); 99 xchk_xref_is_not_cow_staging(sc, agbno, len); 100 } 101 102 /* Flag failures for records that could be merged. */ 103 STATIC void 104 xchk_allocbt_mergeable( 105 struct xchk_btree *bs, 106 struct xchk_alloc *ca, 107 const struct xfs_alloc_rec_incore *irec) 108 { 109 if (bs->sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT) 110 return; 111 112 if (ca->prev.ar_blockcount > 0 && 113 ca->prev.ar_startblock + ca->prev.ar_blockcount == irec->ar_startblock && 114 ca->prev.ar_blockcount + irec->ar_blockcount < (uint32_t)~0U) 115 xchk_btree_set_corrupt(bs->sc, bs->cur, 0); 116 117 memcpy(&ca->prev, irec, sizeof(*irec)); 118 } 119 120 /* Scrub a bnobt/cntbt record. */ 121 STATIC int 122 xchk_allocbt_rec( 123 struct xchk_btree *bs, 124 const union xfs_btree_rec *rec) 125 { 126 struct xfs_alloc_rec_incore irec; 127 struct xchk_alloc *ca = bs->private; 128 129 xfs_alloc_btrec_to_irec(rec, &irec); 130 if (xfs_alloc_check_irec(bs->cur, &irec) != NULL) { 131 xchk_btree_set_corrupt(bs->sc, bs->cur, 0); 132 return 0; 133 } 134 135 xchk_allocbt_mergeable(bs, ca, &irec); 136 xchk_allocbt_xref(bs->sc, &irec); 137 138 return 0; 139 } 140 141 /* Scrub the freespace btrees for some AG. */ 142 STATIC int 143 xchk_allocbt( 144 struct xfs_scrub *sc, 145 xfs_btnum_t which) 146 { 147 struct xchk_alloc ca = { }; 148 struct xfs_btree_cur *cur; 149 150 cur = which == XFS_BTNUM_BNO ? sc->sa.bno_cur : sc->sa.cnt_cur; 151 return xchk_btree(sc, cur, xchk_allocbt_rec, &XFS_RMAP_OINFO_AG, &ca); 152 } 153 154 int 155 xchk_bnobt( 156 struct xfs_scrub *sc) 157 { 158 return xchk_allocbt(sc, XFS_BTNUM_BNO); 159 } 160 161 int 162 xchk_cntbt( 163 struct xfs_scrub *sc) 164 { 165 return xchk_allocbt(sc, XFS_BTNUM_CNT); 166 } 167 168 /* xref check that the extent is not free */ 169 void 170 xchk_xref_is_used_space( 171 struct xfs_scrub *sc, 172 xfs_agblock_t agbno, 173 xfs_extlen_t len) 174 { 175 enum xbtree_recpacking outcome; 176 int error; 177 178 if (!sc->sa.bno_cur || xchk_skip_xref(sc->sm)) 179 return; 180 181 error = xfs_alloc_has_records(sc->sa.bno_cur, agbno, len, &outcome); 182 if (!xchk_should_check_xref(sc, &error, &sc->sa.bno_cur)) 183 return; 184 if (outcome != XBTREE_RECPACKING_EMPTY) 185 xchk_btree_xref_set_corrupt(sc, sc->sa.bno_cur, 0); 186 } 187