1739a2fe0SDarrick J. Wong // SPDX-License-Identifier: GPL-2.0-or-later
2edc09b52SDarrick J. Wong /*
3ecc73f8aSDarrick J. Wong * Copyright (C) 2017-2023 Oracle. All Rights Reserved.
4739a2fe0SDarrick J. Wong * Author: Darrick J. Wong <djwong@kernel.org>
5edc09b52SDarrick J. Wong */
6edc09b52SDarrick J. Wong #include "xfs.h"
7edc09b52SDarrick J. Wong #include "xfs_fs.h"
8edc09b52SDarrick J. Wong #include "xfs_shared.h"
9edc09b52SDarrick J. Wong #include "xfs_format.h"
10d5c88131SDarrick J. Wong #include "xfs_trans_resv.h"
11d5c88131SDarrick J. Wong #include "xfs_mount.h"
12*422d5653SDave Chinner #include "xfs_ag.h"
13edc09b52SDarrick J. Wong #include "xfs_btree.h"
14edc09b52SDarrick J. Wong #include "xfs_rmap.h"
15f6d5fc21SDarrick J. Wong #include "xfs_refcount.h"
16edc09b52SDarrick J. Wong #include "scrub/scrub.h"
17edc09b52SDarrick J. Wong #include "scrub/common.h"
18edc09b52SDarrick J. Wong #include "scrub/btree.h"
1990148903SDarrick J. Wong #include "scrub/trace.h"
20edc09b52SDarrick J. Wong
21edc09b52SDarrick J. Wong /*
22edc09b52SDarrick J. Wong * Set us up to scrub reference count btrees.
23edc09b52SDarrick J. Wong */
24edc09b52SDarrick J. Wong int
xchk_setup_ag_refcountbt(struct xfs_scrub * sc)25c517b3aaSDarrick J. Wong xchk_setup_ag_refcountbt(
26026f57ebSDarrick J. Wong struct xfs_scrub *sc)
27edc09b52SDarrick J. Wong {
28466c525dSDarrick J. Wong if (xchk_need_intent_drain(sc))
29466c525dSDarrick J. Wong xchk_fsgates_enable(sc, XCHK_FSGATES_DRAIN);
30026f57ebSDarrick J. Wong return xchk_setup_ag_btree(sc, false);
31edc09b52SDarrick J. Wong }
32edc09b52SDarrick J. Wong
33edc09b52SDarrick J. Wong /* Reference count btree scrubber. */
34edc09b52SDarrick J. Wong
35dbde19daSDarrick J. Wong /*
36dbde19daSDarrick J. Wong * Confirming Reference Counts via Reverse Mappings
37dbde19daSDarrick J. Wong *
38dbde19daSDarrick J. Wong * We want to count the reverse mappings overlapping a refcount record
39dbde19daSDarrick J. Wong * (bno, len, refcount), allowing for the possibility that some of the
40dbde19daSDarrick J. Wong * overlap may come from smaller adjoining reverse mappings, while some
41dbde19daSDarrick J. Wong * comes from single extents which overlap the range entirely. The
42dbde19daSDarrick J. Wong * outer loop is as follows:
43dbde19daSDarrick J. Wong *
44dbde19daSDarrick J. Wong * 1. For all reverse mappings overlapping the refcount extent,
45dbde19daSDarrick J. Wong * a. If a given rmap completely overlaps, mark it as seen.
46dbde19daSDarrick J. Wong * b. Otherwise, record the fragment (in agbno order) for later
47dbde19daSDarrick J. Wong * processing.
48dbde19daSDarrick J. Wong *
49dbde19daSDarrick J. Wong * Once we've seen all the rmaps, we know that for all blocks in the
50dbde19daSDarrick J. Wong * refcount record we want to find $refcount owners and we've already
51dbde19daSDarrick J. Wong * visited $seen extents that overlap all the blocks. Therefore, we
52dbde19daSDarrick J. Wong * need to find ($refcount - $seen) owners for every block in the
53dbde19daSDarrick J. Wong * extent; call that quantity $target_nr. Proceed as follows:
54dbde19daSDarrick J. Wong *
55dbde19daSDarrick J. Wong * 2. Pull the first $target_nr fragments from the list; all of them
56dbde19daSDarrick J. Wong * should start at or before the start of the extent.
57dbde19daSDarrick J. Wong * Call this subset of fragments the working set.
58dbde19daSDarrick J. Wong * 3. Until there are no more unprocessed fragments,
59dbde19daSDarrick J. Wong * a. Find the shortest fragments in the set and remove them.
60dbde19daSDarrick J. Wong * b. Note the block number of the end of these fragments.
61dbde19daSDarrick J. Wong * c. Pull the same number of fragments from the list. All of these
62dbde19daSDarrick J. Wong * fragments should start at the block number recorded in the
63dbde19daSDarrick J. Wong * previous step.
64dbde19daSDarrick J. Wong * d. Put those fragments in the set.
65dbde19daSDarrick J. Wong * 4. Check that there are $target_nr fragments remaining in the list,
66dbde19daSDarrick J. Wong * and that they all end at or beyond the end of the refcount extent.
67dbde19daSDarrick J. Wong *
68dbde19daSDarrick J. Wong * If the refcount is correct, all the check conditions in the algorithm
69dbde19daSDarrick J. Wong * should always hold true. If not, the refcount is incorrect.
70dbde19daSDarrick J. Wong */
71c517b3aaSDarrick J. Wong struct xchk_refcnt_frag {
72dbde19daSDarrick J. Wong struct list_head list;
73dbde19daSDarrick J. Wong struct xfs_rmap_irec rm;
74dbde19daSDarrick J. Wong };
75dbde19daSDarrick J. Wong
76c517b3aaSDarrick J. Wong struct xchk_refcnt_check {
771d8a748aSDarrick J. Wong struct xfs_scrub *sc;
78dbde19daSDarrick J. Wong struct list_head fragments;
79dbde19daSDarrick J. Wong
80dbde19daSDarrick J. Wong /* refcount extent we're examining */
81dbde19daSDarrick J. Wong xfs_agblock_t bno;
82dbde19daSDarrick J. Wong xfs_extlen_t len;
83dbde19daSDarrick J. Wong xfs_nlink_t refcount;
84dbde19daSDarrick J. Wong
85dbde19daSDarrick J. Wong /* number of owners seen */
86dbde19daSDarrick J. Wong xfs_nlink_t seen;
87dbde19daSDarrick J. Wong };
88dbde19daSDarrick J. Wong
89dbde19daSDarrick J. Wong /*
90dbde19daSDarrick J. Wong * Decide if the given rmap is large enough that we can redeem it
91dbde19daSDarrick J. Wong * towards refcount verification now, or if it's a fragment, in
92dbde19daSDarrick J. Wong * which case we'll hang onto it in the hopes that we'll later
93dbde19daSDarrick J. Wong * discover that we've collected exactly the correct number of
94dbde19daSDarrick J. Wong * fragments as the refcountbt says we should have.
95dbde19daSDarrick J. Wong */
96dbde19daSDarrick J. Wong STATIC int
xchk_refcountbt_rmap_check(struct xfs_btree_cur * cur,const struct xfs_rmap_irec * rec,void * priv)97c517b3aaSDarrick J. Wong xchk_refcountbt_rmap_check(
98dbde19daSDarrick J. Wong struct xfs_btree_cur *cur,
99159eb69dSDarrick J. Wong const struct xfs_rmap_irec *rec,
100dbde19daSDarrick J. Wong void *priv)
101dbde19daSDarrick J. Wong {
102c517b3aaSDarrick J. Wong struct xchk_refcnt_check *refchk = priv;
103c517b3aaSDarrick J. Wong struct xchk_refcnt_frag *frag;
104dbde19daSDarrick J. Wong xfs_agblock_t rm_last;
105dbde19daSDarrick J. Wong xfs_agblock_t rc_last;
106dbde19daSDarrick J. Wong int error = 0;
107dbde19daSDarrick J. Wong
108c517b3aaSDarrick J. Wong if (xchk_should_terminate(refchk->sc, &error))
109dbde19daSDarrick J. Wong return error;
110dbde19daSDarrick J. Wong
111dbde19daSDarrick J. Wong rm_last = rec->rm_startblock + rec->rm_blockcount - 1;
112dbde19daSDarrick J. Wong rc_last = refchk->bno + refchk->len - 1;
113dbde19daSDarrick J. Wong
114dbde19daSDarrick J. Wong /* Confirm that a single-owner refc extent is a CoW stage. */
115dbde19daSDarrick J. Wong if (refchk->refcount == 1 && rec->rm_owner != XFS_RMAP_OWN_COW) {
116c517b3aaSDarrick J. Wong xchk_btree_xref_set_corrupt(refchk->sc, cur, 0);
117dbde19daSDarrick J. Wong return 0;
118dbde19daSDarrick J. Wong }
119dbde19daSDarrick J. Wong
120dbde19daSDarrick J. Wong if (rec->rm_startblock <= refchk->bno && rm_last >= rc_last) {
121dbde19daSDarrick J. Wong /*
122dbde19daSDarrick J. Wong * The rmap overlaps the refcount record, so we can confirm
123dbde19daSDarrick J. Wong * one refcount owner seen.
124dbde19daSDarrick J. Wong */
125dbde19daSDarrick J. Wong refchk->seen++;
126dbde19daSDarrick J. Wong } else {
127dbde19daSDarrick J. Wong /*
128dbde19daSDarrick J. Wong * This rmap covers only part of the refcount record, so
129dbde19daSDarrick J. Wong * save the fragment for later processing. If the rmapbt
130dbde19daSDarrick J. Wong * is healthy each rmap_irec we see will be in agbno order
131dbde19daSDarrick J. Wong * so we don't need insertion sort here.
132dbde19daSDarrick J. Wong */
133306195f3SDarrick J. Wong frag = kmalloc(sizeof(struct xchk_refcnt_frag),
134306195f3SDarrick J. Wong XCHK_GFP_FLAGS);
135dbde19daSDarrick J. Wong if (!frag)
136dbde19daSDarrick J. Wong return -ENOMEM;
137dbde19daSDarrick J. Wong memcpy(&frag->rm, rec, sizeof(frag->rm));
138dbde19daSDarrick J. Wong list_add_tail(&frag->list, &refchk->fragments);
139dbde19daSDarrick J. Wong }
140dbde19daSDarrick J. Wong
141dbde19daSDarrick J. Wong return 0;
142dbde19daSDarrick J. Wong }
143dbde19daSDarrick J. Wong
144dbde19daSDarrick J. Wong /*
145dbde19daSDarrick J. Wong * Given a bunch of rmap fragments, iterate through them, keeping
146dbde19daSDarrick J. Wong * a running tally of the refcount. If this ever deviates from
147dbde19daSDarrick J. Wong * what we expect (which is the refcountbt's refcount minus the
148dbde19daSDarrick J. Wong * number of extents that totally covered the refcountbt extent),
149dbde19daSDarrick J. Wong * we have a refcountbt error.
150dbde19daSDarrick J. Wong */
151dbde19daSDarrick J. Wong STATIC void
xchk_refcountbt_process_rmap_fragments(struct xchk_refcnt_check * refchk)152c517b3aaSDarrick J. Wong xchk_refcountbt_process_rmap_fragments(
153c517b3aaSDarrick J. Wong struct xchk_refcnt_check *refchk)
154dbde19daSDarrick J. Wong {
155dbde19daSDarrick J. Wong struct list_head worklist;
156c517b3aaSDarrick J. Wong struct xchk_refcnt_frag *frag;
157c517b3aaSDarrick J. Wong struct xchk_refcnt_frag *n;
158dbde19daSDarrick J. Wong xfs_agblock_t bno;
159dbde19daSDarrick J. Wong xfs_agblock_t rbno;
160dbde19daSDarrick J. Wong xfs_agblock_t next_rbno;
161dbde19daSDarrick J. Wong xfs_nlink_t nr;
162dbde19daSDarrick J. Wong xfs_nlink_t target_nr;
163dbde19daSDarrick J. Wong
164dbde19daSDarrick J. Wong target_nr = refchk->refcount - refchk->seen;
165dbde19daSDarrick J. Wong if (target_nr == 0)
166dbde19daSDarrick J. Wong return;
167dbde19daSDarrick J. Wong
168dbde19daSDarrick J. Wong /*
169dbde19daSDarrick J. Wong * There are (refchk->rc.rc_refcount - refchk->nr refcount)
170dbde19daSDarrick J. Wong * references we haven't found yet. Pull that many off the
171dbde19daSDarrick J. Wong * fragment list and figure out where the smallest rmap ends
172dbde19daSDarrick J. Wong * (and therefore the next rmap should start). All the rmaps
173dbde19daSDarrick J. Wong * we pull off should start at or before the beginning of the
174dbde19daSDarrick J. Wong * refcount record's range.
175dbde19daSDarrick J. Wong */
176dbde19daSDarrick J. Wong INIT_LIST_HEAD(&worklist);
177dbde19daSDarrick J. Wong rbno = NULLAGBLOCK;
178dbde19daSDarrick J. Wong
179dbde19daSDarrick J. Wong /* Make sure the fragments actually /are/ in agbno order. */
180dbde19daSDarrick J. Wong bno = 0;
181dbde19daSDarrick J. Wong list_for_each_entry(frag, &refchk->fragments, list) {
182dbde19daSDarrick J. Wong if (frag->rm.rm_startblock < bno)
183dbde19daSDarrick J. Wong goto done;
184dbde19daSDarrick J. Wong bno = frag->rm.rm_startblock;
185dbde19daSDarrick J. Wong }
186dbde19daSDarrick J. Wong
187dbde19daSDarrick J. Wong /*
188dbde19daSDarrick J. Wong * Find all the rmaps that start at or before the refc extent,
189dbde19daSDarrick J. Wong * and put them on the worklist.
190dbde19daSDarrick J. Wong */
19154e9b09eSDarrick J. Wong nr = 0;
192dbde19daSDarrick J. Wong list_for_each_entry_safe(frag, n, &refchk->fragments, list) {
19354e9b09eSDarrick J. Wong if (frag->rm.rm_startblock > refchk->bno || nr > target_nr)
19454e9b09eSDarrick J. Wong break;
195dbde19daSDarrick J. Wong bno = frag->rm.rm_startblock + frag->rm.rm_blockcount;
196dbde19daSDarrick J. Wong if (bno < rbno)
197dbde19daSDarrick J. Wong rbno = bno;
198dbde19daSDarrick J. Wong list_move_tail(&frag->list, &worklist);
199dbde19daSDarrick J. Wong nr++;
200dbde19daSDarrick J. Wong }
201dbde19daSDarrick J. Wong
202dbde19daSDarrick J. Wong /*
203dbde19daSDarrick J. Wong * We should have found exactly $target_nr rmap fragments starting
204dbde19daSDarrick J. Wong * at or before the refcount extent.
205dbde19daSDarrick J. Wong */
206dbde19daSDarrick J. Wong if (nr != target_nr)
207dbde19daSDarrick J. Wong goto done;
208dbde19daSDarrick J. Wong
209dbde19daSDarrick J. Wong while (!list_empty(&refchk->fragments)) {
210dbde19daSDarrick J. Wong /* Discard any fragments ending at rbno from the worklist. */
211dbde19daSDarrick J. Wong nr = 0;
212dbde19daSDarrick J. Wong next_rbno = NULLAGBLOCK;
213dbde19daSDarrick J. Wong list_for_each_entry_safe(frag, n, &worklist, list) {
214dbde19daSDarrick J. Wong bno = frag->rm.rm_startblock + frag->rm.rm_blockcount;
215dbde19daSDarrick J. Wong if (bno != rbno) {
216dbde19daSDarrick J. Wong if (bno < next_rbno)
217dbde19daSDarrick J. Wong next_rbno = bno;
218dbde19daSDarrick J. Wong continue;
219dbde19daSDarrick J. Wong }
220dbde19daSDarrick J. Wong list_del(&frag->list);
221306195f3SDarrick J. Wong kfree(frag);
222dbde19daSDarrick J. Wong nr++;
223dbde19daSDarrick J. Wong }
224dbde19daSDarrick J. Wong
225dbde19daSDarrick J. Wong /* Try to add nr rmaps starting at rbno to the worklist. */
226dbde19daSDarrick J. Wong list_for_each_entry_safe(frag, n, &refchk->fragments, list) {
227dbde19daSDarrick J. Wong bno = frag->rm.rm_startblock + frag->rm.rm_blockcount;
228dbde19daSDarrick J. Wong if (frag->rm.rm_startblock != rbno)
229dbde19daSDarrick J. Wong goto done;
230dbde19daSDarrick J. Wong list_move_tail(&frag->list, &worklist);
231dbde19daSDarrick J. Wong if (next_rbno > bno)
232dbde19daSDarrick J. Wong next_rbno = bno;
233dbde19daSDarrick J. Wong nr--;
234dbde19daSDarrick J. Wong if (nr == 0)
235dbde19daSDarrick J. Wong break;
236dbde19daSDarrick J. Wong }
237dbde19daSDarrick J. Wong
238dbde19daSDarrick J. Wong /*
239dbde19daSDarrick J. Wong * If we get here and nr > 0, this means that we added fewer
240dbde19daSDarrick J. Wong * items to the worklist than we discarded because the fragment
241dbde19daSDarrick J. Wong * list ran out of items. Therefore, we cannot maintain the
242dbde19daSDarrick J. Wong * required refcount. Something is wrong, so we're done.
243dbde19daSDarrick J. Wong */
244dbde19daSDarrick J. Wong if (nr)
245dbde19daSDarrick J. Wong goto done;
246dbde19daSDarrick J. Wong
247dbde19daSDarrick J. Wong rbno = next_rbno;
248dbde19daSDarrick J. Wong }
249dbde19daSDarrick J. Wong
250dbde19daSDarrick J. Wong /*
251dbde19daSDarrick J. Wong * Make sure the last extent we processed ends at or beyond
252dbde19daSDarrick J. Wong * the end of the refcount extent.
253dbde19daSDarrick J. Wong */
254dbde19daSDarrick J. Wong if (rbno < refchk->bno + refchk->len)
255dbde19daSDarrick J. Wong goto done;
256dbde19daSDarrick J. Wong
257dbde19daSDarrick J. Wong /* Actually record us having seen the remaining refcount. */
258dbde19daSDarrick J. Wong refchk->seen = refchk->refcount;
259dbde19daSDarrick J. Wong done:
260dbde19daSDarrick J. Wong /* Delete fragments and work list. */
261dbde19daSDarrick J. Wong list_for_each_entry_safe(frag, n, &worklist, list) {
262dbde19daSDarrick J. Wong list_del(&frag->list);
263306195f3SDarrick J. Wong kfree(frag);
264dbde19daSDarrick J. Wong }
265dbde19daSDarrick J. Wong list_for_each_entry_safe(frag, n, &refchk->fragments, list) {
266dbde19daSDarrick J. Wong list_del(&frag->list);
267306195f3SDarrick J. Wong kfree(frag);
268dbde19daSDarrick J. Wong }
269dbde19daSDarrick J. Wong }
270dbde19daSDarrick J. Wong
271dbde19daSDarrick J. Wong /* Use the rmap entries covering this extent to verify the refcount. */
272dbde19daSDarrick J. Wong STATIC void
xchk_refcountbt_xref_rmap(struct xfs_scrub * sc,const struct xfs_refcount_irec * irec)273c517b3aaSDarrick J. Wong xchk_refcountbt_xref_rmap(
2741d8a748aSDarrick J. Wong struct xfs_scrub *sc,
2755a8c345cSDarrick J. Wong const struct xfs_refcount_irec *irec)
276dbde19daSDarrick J. Wong {
277c517b3aaSDarrick J. Wong struct xchk_refcnt_check refchk = {
278dbde19daSDarrick J. Wong .sc = sc,
2795a8c345cSDarrick J. Wong .bno = irec->rc_startblock,
2805a8c345cSDarrick J. Wong .len = irec->rc_blockcount,
2815a8c345cSDarrick J. Wong .refcount = irec->rc_refcount,
282dbde19daSDarrick J. Wong .seen = 0,
283dbde19daSDarrick J. Wong };
284dbde19daSDarrick J. Wong struct xfs_rmap_irec low;
285dbde19daSDarrick J. Wong struct xfs_rmap_irec high;
286c517b3aaSDarrick J. Wong struct xchk_refcnt_frag *frag;
287c517b3aaSDarrick J. Wong struct xchk_refcnt_frag *n;
288dbde19daSDarrick J. Wong int error;
289dbde19daSDarrick J. Wong
290c517b3aaSDarrick J. Wong if (!sc->sa.rmap_cur || xchk_skip_xref(sc->sm))
291dbde19daSDarrick J. Wong return;
292dbde19daSDarrick J. Wong
293dbde19daSDarrick J. Wong /* Cross-reference with the rmapbt to confirm the refcount. */
294dbde19daSDarrick J. Wong memset(&low, 0, sizeof(low));
2955a8c345cSDarrick J. Wong low.rm_startblock = irec->rc_startblock;
296dbde19daSDarrick J. Wong memset(&high, 0xFF, sizeof(high));
2975a8c345cSDarrick J. Wong high.rm_startblock = irec->rc_startblock + irec->rc_blockcount - 1;
298dbde19daSDarrick J. Wong
299dbde19daSDarrick J. Wong INIT_LIST_HEAD(&refchk.fragments);
300dbde19daSDarrick J. Wong error = xfs_rmap_query_range(sc->sa.rmap_cur, &low, &high,
301c517b3aaSDarrick J. Wong &xchk_refcountbt_rmap_check, &refchk);
302c517b3aaSDarrick J. Wong if (!xchk_should_check_xref(sc, &error, &sc->sa.rmap_cur))
303dbde19daSDarrick J. Wong goto out_free;
304dbde19daSDarrick J. Wong
305c517b3aaSDarrick J. Wong xchk_refcountbt_process_rmap_fragments(&refchk);
30690148903SDarrick J. Wong if (irec->rc_refcount != refchk.seen) {
30790148903SDarrick J. Wong trace_xchk_refcount_incorrect(sc->sa.pag, irec, refchk.seen);
308c517b3aaSDarrick J. Wong xchk_btree_xref_set_corrupt(sc, sc->sa.rmap_cur, 0);
30990148903SDarrick J. Wong }
310dbde19daSDarrick J. Wong
311dbde19daSDarrick J. Wong out_free:
312dbde19daSDarrick J. Wong list_for_each_entry_safe(frag, n, &refchk.fragments, list) {
313dbde19daSDarrick J. Wong list_del(&frag->list);
314306195f3SDarrick J. Wong kfree(frag);
315dbde19daSDarrick J. Wong }
316dbde19daSDarrick J. Wong }
317dbde19daSDarrick J. Wong
318166d7641SDarrick J. Wong /* Cross-reference with the other btrees. */
319166d7641SDarrick J. Wong STATIC void
xchk_refcountbt_xref(struct xfs_scrub * sc,const struct xfs_refcount_irec * irec)320c517b3aaSDarrick J. Wong xchk_refcountbt_xref(
3211d8a748aSDarrick J. Wong struct xfs_scrub *sc,
3225a8c345cSDarrick J. Wong const struct xfs_refcount_irec *irec)
323166d7641SDarrick J. Wong {
324166d7641SDarrick J. Wong if (sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT)
325166d7641SDarrick J. Wong return;
32652dc4b44SDarrick J. Wong
3275a8c345cSDarrick J. Wong xchk_xref_is_used_space(sc, irec->rc_startblock, irec->rc_blockcount);
3285a8c345cSDarrick J. Wong xchk_xref_is_not_inode_chunk(sc, irec->rc_startblock,
3295a8c345cSDarrick J. Wong irec->rc_blockcount);
3305a8c345cSDarrick J. Wong xchk_refcountbt_xref_rmap(sc, irec);
331166d7641SDarrick J. Wong }
332166d7641SDarrick J. Wong
3337ad9ea63SDarrick J. Wong struct xchk_refcbt_records {
334db0502b3SDarrick J. Wong /* Previous refcount record. */
335db0502b3SDarrick J. Wong struct xfs_refcount_irec prev_rec;
336db0502b3SDarrick J. Wong
3377ad9ea63SDarrick J. Wong /* The next AG block where we aren't expecting shared extents. */
3387ad9ea63SDarrick J. Wong xfs_agblock_t next_unshared_agbno;
3397ad9ea63SDarrick J. Wong
3407ad9ea63SDarrick J. Wong /* Number of CoW blocks we expect. */
3417ad9ea63SDarrick J. Wong xfs_agblock_t cow_blocks;
3427ad9ea63SDarrick J. Wong
3437ad9ea63SDarrick J. Wong /* Was the last record a shared or CoW staging extent? */
3447ad9ea63SDarrick J. Wong enum xfs_refc_domain prev_domain;
3457ad9ea63SDarrick J. Wong };
3467ad9ea63SDarrick J. Wong
3477ad9ea63SDarrick J. Wong STATIC int
xchk_refcountbt_rmap_check_gap(struct xfs_btree_cur * cur,const struct xfs_rmap_irec * rec,void * priv)3487ad9ea63SDarrick J. Wong xchk_refcountbt_rmap_check_gap(
3497ad9ea63SDarrick J. Wong struct xfs_btree_cur *cur,
3507ad9ea63SDarrick J. Wong const struct xfs_rmap_irec *rec,
3517ad9ea63SDarrick J. Wong void *priv)
3527ad9ea63SDarrick J. Wong {
3537ad9ea63SDarrick J. Wong xfs_agblock_t *next_bno = priv;
3547ad9ea63SDarrick J. Wong
3557ad9ea63SDarrick J. Wong if (*next_bno != NULLAGBLOCK && rec->rm_startblock < *next_bno)
3567ad9ea63SDarrick J. Wong return -ECANCELED;
3577ad9ea63SDarrick J. Wong
3587ad9ea63SDarrick J. Wong *next_bno = rec->rm_startblock + rec->rm_blockcount;
3597ad9ea63SDarrick J. Wong return 0;
3607ad9ea63SDarrick J. Wong }
3617ad9ea63SDarrick J. Wong
3627ad9ea63SDarrick J. Wong /*
3637ad9ea63SDarrick J. Wong * Make sure that a gap in the reference count records does not correspond to
3647ad9ea63SDarrick J. Wong * overlapping records (i.e. shared extents) in the reverse mappings.
3657ad9ea63SDarrick J. Wong */
3667ad9ea63SDarrick J. Wong static inline void
xchk_refcountbt_xref_gaps(struct xfs_scrub * sc,struct xchk_refcbt_records * rrc,xfs_agblock_t bno)3677ad9ea63SDarrick J. Wong xchk_refcountbt_xref_gaps(
3687ad9ea63SDarrick J. Wong struct xfs_scrub *sc,
3697ad9ea63SDarrick J. Wong struct xchk_refcbt_records *rrc,
3707ad9ea63SDarrick J. Wong xfs_agblock_t bno)
3717ad9ea63SDarrick J. Wong {
3727ad9ea63SDarrick J. Wong struct xfs_rmap_irec low;
3737ad9ea63SDarrick J. Wong struct xfs_rmap_irec high;
3747ad9ea63SDarrick J. Wong xfs_agblock_t next_bno = NULLAGBLOCK;
3757ad9ea63SDarrick J. Wong int error;
3767ad9ea63SDarrick J. Wong
3777ad9ea63SDarrick J. Wong if (bno <= rrc->next_unshared_agbno || !sc->sa.rmap_cur ||
3787ad9ea63SDarrick J. Wong xchk_skip_xref(sc->sm))
3797ad9ea63SDarrick J. Wong return;
3807ad9ea63SDarrick J. Wong
3817ad9ea63SDarrick J. Wong memset(&low, 0, sizeof(low));
3827ad9ea63SDarrick J. Wong low.rm_startblock = rrc->next_unshared_agbno;
3837ad9ea63SDarrick J. Wong memset(&high, 0xFF, sizeof(high));
3847ad9ea63SDarrick J. Wong high.rm_startblock = bno - 1;
3857ad9ea63SDarrick J. Wong
3867ad9ea63SDarrick J. Wong error = xfs_rmap_query_range(sc->sa.rmap_cur, &low, &high,
3877ad9ea63SDarrick J. Wong xchk_refcountbt_rmap_check_gap, &next_bno);
3887ad9ea63SDarrick J. Wong if (error == -ECANCELED)
3897ad9ea63SDarrick J. Wong xchk_btree_xref_set_corrupt(sc, sc->sa.rmap_cur, 0);
3907ad9ea63SDarrick J. Wong else
3917ad9ea63SDarrick J. Wong xchk_should_check_xref(sc, &error, &sc->sa.rmap_cur);
3927ad9ea63SDarrick J. Wong }
3937ad9ea63SDarrick J. Wong
394db0502b3SDarrick J. Wong static inline bool
xchk_refcount_mergeable(struct xchk_refcbt_records * rrc,const struct xfs_refcount_irec * r2)395db0502b3SDarrick J. Wong xchk_refcount_mergeable(
396db0502b3SDarrick J. Wong struct xchk_refcbt_records *rrc,
397db0502b3SDarrick J. Wong const struct xfs_refcount_irec *r2)
398db0502b3SDarrick J. Wong {
399db0502b3SDarrick J. Wong const struct xfs_refcount_irec *r1 = &rrc->prev_rec;
400db0502b3SDarrick J. Wong
401db0502b3SDarrick J. Wong /* Ignore if prev_rec is not yet initialized. */
402db0502b3SDarrick J. Wong if (r1->rc_blockcount > 0)
403db0502b3SDarrick J. Wong return false;
404db0502b3SDarrick J. Wong
405db0502b3SDarrick J. Wong if (r1->rc_domain != r2->rc_domain)
406db0502b3SDarrick J. Wong return false;
407db0502b3SDarrick J. Wong if (r1->rc_startblock + r1->rc_blockcount != r2->rc_startblock)
408db0502b3SDarrick J. Wong return false;
409db0502b3SDarrick J. Wong if (r1->rc_refcount != r2->rc_refcount)
410db0502b3SDarrick J. Wong return false;
411db0502b3SDarrick J. Wong if ((unsigned long long)r1->rc_blockcount + r2->rc_blockcount >
412db0502b3SDarrick J. Wong MAXREFCEXTLEN)
413db0502b3SDarrick J. Wong return false;
414db0502b3SDarrick J. Wong
415db0502b3SDarrick J. Wong return true;
416db0502b3SDarrick J. Wong }
417db0502b3SDarrick J. Wong
418db0502b3SDarrick J. Wong /* Flag failures for records that could be merged. */
419db0502b3SDarrick J. Wong STATIC void
xchk_refcountbt_check_mergeable(struct xchk_btree * bs,struct xchk_refcbt_records * rrc,const struct xfs_refcount_irec * irec)420db0502b3SDarrick J. Wong xchk_refcountbt_check_mergeable(
421db0502b3SDarrick J. Wong struct xchk_btree *bs,
422db0502b3SDarrick J. Wong struct xchk_refcbt_records *rrc,
423db0502b3SDarrick J. Wong const struct xfs_refcount_irec *irec)
424db0502b3SDarrick J. Wong {
425db0502b3SDarrick J. Wong if (bs->sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT)
426db0502b3SDarrick J. Wong return;
427db0502b3SDarrick J. Wong
428db0502b3SDarrick J. Wong if (xchk_refcount_mergeable(rrc, irec))
429db0502b3SDarrick J. Wong xchk_btree_set_corrupt(bs->sc, bs->cur, 0);
430db0502b3SDarrick J. Wong
431db0502b3SDarrick J. Wong memcpy(&rrc->prev_rec, irec, sizeof(struct xfs_refcount_irec));
432db0502b3SDarrick J. Wong }
433db0502b3SDarrick J. Wong
434edc09b52SDarrick J. Wong /* Scrub a refcountbt record. */
435edc09b52SDarrick J. Wong STATIC int
xchk_refcountbt_rec(struct xchk_btree * bs,const union xfs_btree_rec * rec)436c517b3aaSDarrick J. Wong xchk_refcountbt_rec(
437c517b3aaSDarrick J. Wong struct xchk_btree *bs,
43822ece4e8SDarrick J. Wong const union xfs_btree_rec *rec)
439edc09b52SDarrick J. Wong {
4405a8c345cSDarrick J. Wong struct xfs_refcount_irec irec;
4417ad9ea63SDarrick J. Wong struct xchk_refcbt_records *rrc = bs->private;
442edc09b52SDarrick J. Wong
4435a8c345cSDarrick J. Wong xfs_refcount_btrec_to_irec(rec, &irec);
4442b30cc0bSDarrick J. Wong if (xfs_refcount_check_irec(bs->cur, &irec) != NULL) {
445c517b3aaSDarrick J. Wong xchk_btree_set_corrupt(bs->sc, bs->cur, 0);
4462b30cc0bSDarrick J. Wong return 0;
4472b30cc0bSDarrick J. Wong }
448f492135dSDarrick J. Wong
449f492135dSDarrick J. Wong if (irec.rc_domain == XFS_REFC_DOMAIN_COW)
4507ad9ea63SDarrick J. Wong rrc->cow_blocks += irec.rc_blockcount;
4517ad9ea63SDarrick J. Wong
4527ad9ea63SDarrick J. Wong /* Shared records always come before CoW records. */
4537ad9ea63SDarrick J. Wong if (irec.rc_domain == XFS_REFC_DOMAIN_SHARED &&
4547ad9ea63SDarrick J. Wong rrc->prev_domain == XFS_REFC_DOMAIN_COW)
4557ad9ea63SDarrick J. Wong xchk_btree_set_corrupt(bs->sc, bs->cur, 0);
4567ad9ea63SDarrick J. Wong rrc->prev_domain = irec.rc_domain;
457edc09b52SDarrick J. Wong
458db0502b3SDarrick J. Wong xchk_refcountbt_check_mergeable(bs, rrc, &irec);
4595a8c345cSDarrick J. Wong xchk_refcountbt_xref(bs->sc, &irec);
460166d7641SDarrick J. Wong
4617ad9ea63SDarrick J. Wong /*
4627ad9ea63SDarrick J. Wong * If this is a record for a shared extent, check that all blocks
4637ad9ea63SDarrick J. Wong * between the previous record and this one have at most one reverse
4647ad9ea63SDarrick J. Wong * mapping.
4657ad9ea63SDarrick J. Wong */
4667ad9ea63SDarrick J. Wong if (irec.rc_domain == XFS_REFC_DOMAIN_SHARED) {
4677ad9ea63SDarrick J. Wong xchk_refcountbt_xref_gaps(bs->sc, rrc, irec.rc_startblock);
4687ad9ea63SDarrick J. Wong rrc->next_unshared_agbno = irec.rc_startblock +
4697ad9ea63SDarrick J. Wong irec.rc_blockcount;
4707ad9ea63SDarrick J. Wong }
4717ad9ea63SDarrick J. Wong
472d5cc14d9SAliasgar Surti return 0;
473edc09b52SDarrick J. Wong }
474edc09b52SDarrick J. Wong
475dbde19daSDarrick J. Wong /* Make sure we have as many refc blocks as the rmap says. */
476dbde19daSDarrick J. Wong STATIC void
xchk_refcount_xref_rmap(struct xfs_scrub * sc,xfs_filblks_t cow_blocks)477c517b3aaSDarrick J. Wong xchk_refcount_xref_rmap(
4781d8a748aSDarrick J. Wong struct xfs_scrub *sc,
479dbde19daSDarrick J. Wong xfs_filblks_t cow_blocks)
480dbde19daSDarrick J. Wong {
481dbde19daSDarrick J. Wong xfs_extlen_t refcbt_blocks = 0;
482dbde19daSDarrick J. Wong xfs_filblks_t blocks;
483dbde19daSDarrick J. Wong int error;
484dbde19daSDarrick J. Wong
485c517b3aaSDarrick J. Wong if (!sc->sa.rmap_cur || xchk_skip_xref(sc->sm))
486dbde19daSDarrick J. Wong return;
487dbde19daSDarrick J. Wong
488dbde19daSDarrick J. Wong /* Check that we saw as many refcbt blocks as the rmap knows about. */
489dbde19daSDarrick J. Wong error = xfs_btree_count_blocks(sc->sa.refc_cur, &refcbt_blocks);
490c517b3aaSDarrick J. Wong if (!xchk_btree_process_error(sc, sc->sa.refc_cur, 0, &error))
491dbde19daSDarrick J. Wong return;
4927280fedaSDarrick J. Wong error = xchk_count_rmap_ownedby_ag(sc, sc->sa.rmap_cur,
4937280fedaSDarrick J. Wong &XFS_RMAP_OINFO_REFC, &blocks);
494c517b3aaSDarrick J. Wong if (!xchk_should_check_xref(sc, &error, &sc->sa.rmap_cur))
495dbde19daSDarrick J. Wong return;
496dbde19daSDarrick J. Wong if (blocks != refcbt_blocks)
497c517b3aaSDarrick J. Wong xchk_btree_xref_set_corrupt(sc, sc->sa.rmap_cur, 0);
498dbde19daSDarrick J. Wong
499dbde19daSDarrick J. Wong /* Check that we saw as many cow blocks as the rmap knows about. */
5007280fedaSDarrick J. Wong error = xchk_count_rmap_ownedby_ag(sc, sc->sa.rmap_cur,
5017280fedaSDarrick J. Wong &XFS_RMAP_OINFO_COW, &blocks);
502c517b3aaSDarrick J. Wong if (!xchk_should_check_xref(sc, &error, &sc->sa.rmap_cur))
503dbde19daSDarrick J. Wong return;
504dbde19daSDarrick J. Wong if (blocks != cow_blocks)
505c517b3aaSDarrick J. Wong xchk_btree_xref_set_corrupt(sc, sc->sa.rmap_cur, 0);
506dbde19daSDarrick J. Wong }
507dbde19daSDarrick J. Wong
508edc09b52SDarrick J. Wong /* Scrub the refcount btree for some AG. */
509edc09b52SDarrick J. Wong int
xchk_refcountbt(struct xfs_scrub * sc)510c517b3aaSDarrick J. Wong xchk_refcountbt(
5111d8a748aSDarrick J. Wong struct xfs_scrub *sc)
512edc09b52SDarrick J. Wong {
5137ad9ea63SDarrick J. Wong struct xchk_refcbt_records rrc = {
5147ad9ea63SDarrick J. Wong .cow_blocks = 0,
5157ad9ea63SDarrick J. Wong .next_unshared_agbno = 0,
5167ad9ea63SDarrick J. Wong .prev_domain = XFS_REFC_DOMAIN_SHARED,
5177ad9ea63SDarrick J. Wong };
518dbde19daSDarrick J. Wong int error;
519edc09b52SDarrick J. Wong
520c517b3aaSDarrick J. Wong error = xchk_btree(sc, sc->sa.refc_cur, xchk_refcountbt_rec,
5217ad9ea63SDarrick J. Wong &XFS_RMAP_OINFO_REFC, &rrc);
522dbde19daSDarrick J. Wong if (error)
523dbde19daSDarrick J. Wong return error;
524dbde19daSDarrick J. Wong
5257ad9ea63SDarrick J. Wong /*
5267ad9ea63SDarrick J. Wong * Check that all blocks between the last refcount > 1 record and the
5277ad9ea63SDarrick J. Wong * end of the AG have at most one reverse mapping.
5287ad9ea63SDarrick J. Wong */
5297ad9ea63SDarrick J. Wong xchk_refcountbt_xref_gaps(sc, &rrc, sc->mp->m_sb.sb_agblocks);
5307ad9ea63SDarrick J. Wong
5317ad9ea63SDarrick J. Wong xchk_refcount_xref_rmap(sc, rrc.cow_blocks);
532dbde19daSDarrick J. Wong
533dbde19daSDarrick J. Wong return 0;
534edc09b52SDarrick J. Wong }
535f6d5fc21SDarrick J. Wong
536f6d5fc21SDarrick J. Wong /* xref check that a cow staging extent is marked in the refcountbt. */
537f6d5fc21SDarrick J. Wong void
xchk_xref_is_cow_staging(struct xfs_scrub * sc,xfs_agblock_t agbno,xfs_extlen_t len)538c517b3aaSDarrick J. Wong xchk_xref_is_cow_staging(
5391d8a748aSDarrick J. Wong struct xfs_scrub *sc,
540f6d5fc21SDarrick J. Wong xfs_agblock_t agbno,
541f6d5fc21SDarrick J. Wong xfs_extlen_t len)
542f6d5fc21SDarrick J. Wong {
543f6d5fc21SDarrick J. Wong struct xfs_refcount_irec rc;
544f6d5fc21SDarrick J. Wong int has_refcount;
545f6d5fc21SDarrick J. Wong int error;
546f6d5fc21SDarrick J. Wong
547c517b3aaSDarrick J. Wong if (!sc->sa.refc_cur || xchk_skip_xref(sc->sm))
548f6d5fc21SDarrick J. Wong return;
549f6d5fc21SDarrick J. Wong
550f6d5fc21SDarrick J. Wong /* Find the CoW staging extent. */
5519a50ee4fSDarrick J. Wong error = xfs_refcount_lookup_le(sc->sa.refc_cur, XFS_REFC_DOMAIN_COW,
5529a50ee4fSDarrick J. Wong agbno, &has_refcount);
553c517b3aaSDarrick J. Wong if (!xchk_should_check_xref(sc, &error, &sc->sa.refc_cur))
554f6d5fc21SDarrick J. Wong return;
555f6d5fc21SDarrick J. Wong if (!has_refcount) {
556c517b3aaSDarrick J. Wong xchk_btree_xref_set_corrupt(sc, sc->sa.refc_cur, 0);
557f6d5fc21SDarrick J. Wong return;
558f6d5fc21SDarrick J. Wong }
559f6d5fc21SDarrick J. Wong
560f6d5fc21SDarrick J. Wong error = xfs_refcount_get_rec(sc->sa.refc_cur, &rc, &has_refcount);
561c517b3aaSDarrick J. Wong if (!xchk_should_check_xref(sc, &error, &sc->sa.refc_cur))
562f6d5fc21SDarrick J. Wong return;
563f6d5fc21SDarrick J. Wong if (!has_refcount) {
564c517b3aaSDarrick J. Wong xchk_btree_xref_set_corrupt(sc, sc->sa.refc_cur, 0);
565f6d5fc21SDarrick J. Wong return;
566f6d5fc21SDarrick J. Wong }
567f6d5fc21SDarrick J. Wong
568f62ac3e0SDarrick J. Wong /* CoW lookup returned a shared extent record? */
569f62ac3e0SDarrick J. Wong if (rc.rc_domain != XFS_REFC_DOMAIN_COW)
570c517b3aaSDarrick J. Wong xchk_btree_xref_set_corrupt(sc, sc->sa.refc_cur, 0);
571f6d5fc21SDarrick J. Wong
572f6d5fc21SDarrick J. Wong /* Must be at least as long as what was passed in */
573f6d5fc21SDarrick J. Wong if (rc.rc_blockcount < len)
574c517b3aaSDarrick J. Wong xchk_btree_xref_set_corrupt(sc, sc->sa.refc_cur, 0);
575f6d5fc21SDarrick J. Wong }
576f6d5fc21SDarrick J. Wong
577f6d5fc21SDarrick J. Wong /*
578f6d5fc21SDarrick J. Wong * xref check that the extent is not shared. Only file data blocks
579f6d5fc21SDarrick J. Wong * can have multiple owners.
580f6d5fc21SDarrick J. Wong */
581f6d5fc21SDarrick J. Wong void
xchk_xref_is_not_shared(struct xfs_scrub * sc,xfs_agblock_t agbno,xfs_extlen_t len)582c517b3aaSDarrick J. Wong xchk_xref_is_not_shared(
5831d8a748aSDarrick J. Wong struct xfs_scrub *sc,
584f6d5fc21SDarrick J. Wong xfs_agblock_t agbno,
585f6d5fc21SDarrick J. Wong xfs_extlen_t len)
586f6d5fc21SDarrick J. Wong {
5876abc7aefSDarrick J. Wong enum xbtree_recpacking outcome;
588f6d5fc21SDarrick J. Wong int error;
589f6d5fc21SDarrick J. Wong
590c517b3aaSDarrick J. Wong if (!sc->sa.refc_cur || xchk_skip_xref(sc->sm))
591f6d5fc21SDarrick J. Wong return;
592f6d5fc21SDarrick J. Wong
5936abc7aefSDarrick J. Wong error = xfs_refcount_has_records(sc->sa.refc_cur,
5946abc7aefSDarrick J. Wong XFS_REFC_DOMAIN_SHARED, agbno, len, &outcome);
595c517b3aaSDarrick J. Wong if (!xchk_should_check_xref(sc, &error, &sc->sa.refc_cur))
596f6d5fc21SDarrick J. Wong return;
5976abc7aefSDarrick J. Wong if (outcome != XBTREE_RECPACKING_EMPTY)
598c517b3aaSDarrick J. Wong xchk_btree_xref_set_corrupt(sc, sc->sa.refc_cur, 0);
599f6d5fc21SDarrick J. Wong }
6007ac14fa2SDarrick J. Wong
6017ac14fa2SDarrick J. Wong /* xref check that the extent is not being used for CoW staging. */
6027ac14fa2SDarrick J. Wong void
xchk_xref_is_not_cow_staging(struct xfs_scrub * sc,xfs_agblock_t agbno,xfs_extlen_t len)6037ac14fa2SDarrick J. Wong xchk_xref_is_not_cow_staging(
6047ac14fa2SDarrick J. Wong struct xfs_scrub *sc,
6057ac14fa2SDarrick J. Wong xfs_agblock_t agbno,
6067ac14fa2SDarrick J. Wong xfs_extlen_t len)
6077ac14fa2SDarrick J. Wong {
6087ac14fa2SDarrick J. Wong enum xbtree_recpacking outcome;
6097ac14fa2SDarrick J. Wong int error;
6107ac14fa2SDarrick J. Wong
6117ac14fa2SDarrick J. Wong if (!sc->sa.refc_cur || xchk_skip_xref(sc->sm))
6127ac14fa2SDarrick J. Wong return;
6137ac14fa2SDarrick J. Wong
6147ac14fa2SDarrick J. Wong error = xfs_refcount_has_records(sc->sa.refc_cur, XFS_REFC_DOMAIN_COW,
6157ac14fa2SDarrick J. Wong agbno, len, &outcome);
6167ac14fa2SDarrick J. Wong if (!xchk_should_check_xref(sc, &error, &sc->sa.refc_cur))
6177ac14fa2SDarrick J. Wong return;
6187ac14fa2SDarrick J. Wong if (outcome != XBTREE_RECPACKING_EMPTY)
6197ac14fa2SDarrick J. Wong xchk_btree_xref_set_corrupt(sc, sc->sa.refc_cur, 0);
6207ac14fa2SDarrick J. Wong }
621