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