Line data Source code
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_inode.h"
15 : #include "xfs_quota.h"
16 : #include "xfs_qm.h"
17 : #include "xfs_errortag.h"
18 : #include "xfs_error.h"
19 : #include "xfs_scrub.h"
20 : #include "scrub/scrub.h"
21 : #include "scrub/common.h"
22 : #include "scrub/trace.h"
23 : #include "scrub/repair.h"
24 : #include "scrub/health.h"
25 :
26 : /*
27 : * Online Scrub and Repair
28 : *
29 : * Traditionally, XFS (the kernel driver) did not know how to check or
30 : * repair on-disk data structures. That task was left to the xfs_check
31 : * and xfs_repair tools, both of which require taking the filesystem
32 : * offline for a thorough but time consuming examination. Online
33 : * scrub & repair, on the other hand, enables us to check the metadata
34 : * for obvious errors while carefully stepping around the filesystem's
35 : * ongoing operations, locking rules, etc.
36 : *
37 : * Given that most XFS metadata consist of records stored in a btree,
38 : * most of the checking functions iterate the btree blocks themselves
39 : * looking for irregularities. When a record block is encountered, each
40 : * record can be checked for obviously bad values. Record values can
41 : * also be cross-referenced against other btrees to look for potential
42 : * misunderstandings between pieces of metadata.
43 : *
44 : * It is expected that the checkers responsible for per-AG metadata
45 : * structures will lock the AG headers (AGI, AGF, AGFL), iterate the
46 : * metadata structure, and perform any relevant cross-referencing before
47 : * unlocking the AG and returning the results to userspace. These
48 : * scrubbers must not keep an AG locked for too long to avoid tying up
49 : * the block and inode allocators.
50 : *
51 : * Block maps and b-trees rooted in an inode present a special challenge
52 : * because they can involve extents from any AG. The general scrubber
53 : * structure of lock -> check -> xref -> unlock still holds, but AG
54 : * locking order rules /must/ be obeyed to avoid deadlocks. The
55 : * ordering rule, of course, is that we must lock in increasing AG
56 : * order. Helper functions are provided to track which AG headers we've
57 : * already locked. If we detect an imminent locking order violation, we
58 : * can signal a potential deadlock, in which case the scrubber can jump
59 : * out to the top level, lock all the AGs in order, and retry the scrub.
60 : *
61 : * For file data (directories, extended attributes, symlinks) scrub, we
62 : * can simply lock the inode and walk the data. For btree data
63 : * (directories and attributes) we follow the same btree-scrubbing
64 : * strategy outlined previously to check the records.
65 : *
66 : * We use a bit of trickery with transactions to avoid buffer deadlocks
67 : * if there is a cycle in the metadata. The basic problem is that
68 : * travelling down a btree involves locking the current buffer at each
69 : * tree level. If a pointer should somehow point back to a buffer that
70 : * we've already examined, we will deadlock due to the second buffer
71 : * locking attempt. Note however that grabbing a buffer in transaction
72 : * context links the locked buffer to the transaction. If we try to
73 : * re-grab the buffer in the context of the same transaction, we avoid
74 : * the second lock attempt and continue. Between the verifier and the
75 : * scrubber, something will notice that something is amiss and report
76 : * the corruption. Therefore, each scrubber will allocate an empty
77 : * transaction, attach buffers to it, and cancel the transaction at the
78 : * end of the scrub run. Cancelling a non-dirty transaction simply
79 : * unlocks the buffers.
80 : *
81 : * There are four pieces of data that scrub can communicate to
82 : * userspace. The first is the error code (errno), which can be used to
83 : * communicate operational errors in performing the scrub. There are
84 : * also three flags that can be set in the scrub context. If the data
85 : * structure itself is corrupt, the CORRUPT flag will be set. If
86 : * the metadata is correct but otherwise suboptimal, the PREEN flag
87 : * will be set.
88 : *
89 : * We perform secondary validation of filesystem metadata by
90 : * cross-referencing every record with all other available metadata.
91 : * For example, for block mapping extents, we verify that there are no
92 : * records in the free space and inode btrees corresponding to that
93 : * space extent and that there is a corresponding entry in the reverse
94 : * mapping btree. Inconsistent metadata is noted by setting the
95 : * XCORRUPT flag; btree query function errors are noted by setting the
96 : * XFAIL flag and deleting the cursor to prevent further attempts to
97 : * cross-reference with a defective btree.
98 : *
99 : * If a piece of metadata proves corrupt or suboptimal, the userspace
100 : * program can ask the kernel to apply some tender loving care (TLC) to
101 : * the metadata object by setting the REPAIR flag and re-calling the
102 : * scrub ioctl. "Corruption" is defined by metadata violating the
103 : * on-disk specification; operations cannot continue if the violation is
104 : * left untreated. It is possible for XFS to continue if an object is
105 : * "suboptimal", however performance may be degraded. Repairs are
106 : * usually performed by rebuilding the metadata entirely out of
107 : * redundant metadata. Optimizing, on the other hand, can sometimes be
108 : * done without rebuilding entire structures.
109 : *
110 : * Generally speaking, the repair code has the following code structure:
111 : * Lock -> scrub -> repair -> commit -> re-lock -> re-scrub -> unlock.
112 : * The first check helps us figure out if we need to rebuild or simply
113 : * optimize the structure so that the rebuild knows what to do. The
114 : * second check evaluates the completeness of the repair; that is what
115 : * is reported to userspace.
116 : *
117 : * A quick note on symbol prefixes:
118 : * - "xfs_" are general XFS symbols.
119 : * - "xchk_" are symbols related to metadata checking.
120 : * - "xrep_" are symbols related to metadata repair.
121 : * - "xfs_scrub_" are symbols that tie online fsck to the rest of XFS.
122 : */
123 :
124 : /*
125 : * Scrub probe -- userspace uses this to probe if we're willing to scrub
126 : * or repair a given mountpoint. This will be used by xfs_scrub to
127 : * probe the kernel's abilities to scrub (and repair) the metadata. We
128 : * do this by validating the ioctl inputs from userspace, preparing the
129 : * filesystem for a scrub (or a repair) operation, and immediately
130 : * returning to userspace. Userspace can use the returned errno and
131 : * structure state to decide (in broad terms) if scrub/repair are
132 : * supported by the running kernel.
133 : */
134 : static int
135 264828 : xchk_probe(
136 : struct xfs_scrub *sc)
137 : {
138 264828 : int error = 0;
139 :
140 264828 : if (xchk_should_terminate(sc, &error))
141 0 : return error;
142 :
143 : return 0;
144 : }
145 :
146 : /* Scrub setup and teardown */
147 :
148 : static inline void
149 2189862473 : xchk_fsgates_disable(
150 : struct xfs_scrub *sc)
151 : {
152 2189862473 : if (!(sc->flags & XCHK_FSGATES_ALL))
153 : return;
154 :
155 103677 : trace_xchk_fsgates_disable(sc, sc->flags & XCHK_FSGATES_ALL);
156 :
157 103676 : if (sc->flags & XCHK_FSGATES_DRAIN)
158 103676 : xfs_drain_wait_disable();
159 :
160 103677 : sc->flags &= ~XCHK_FSGATES_ALL;
161 : }
162 :
163 : /* Free all the resources and finish the transactions. */
164 : STATIC int
165 2189164550 : xchk_teardown(
166 : struct xfs_scrub *sc,
167 : int error)
168 : {
169 2189164550 : struct xfs_inode *ip_in = XFS_I(file_inode(sc->file));
170 :
171 2189164550 : xchk_ag_free(sc, &sc->sa);
172 2191274103 : if (sc->tp) {
173 2180363736 : if (error == 0 && (sc->sm->sm_flags & XFS_SCRUB_IFLAG_REPAIR))
174 2142299 : error = xfs_trans_commit(sc->tp);
175 : else
176 2178221437 : xfs_trans_cancel(sc->tp);
177 2183587857 : sc->tp = NULL;
178 : }
179 2194498224 : if (sc->ip) {
180 2172656793 : if (sc->ilock_flags)
181 2170120035 : xfs_iunlock(sc->ip, sc->ilock_flags);
182 3638994846 : if (sc->ip != ip_in &&
183 1469457147 : !xfs_internal_inum(sc->mp, sc->ip->i_ino))
184 1466878998 : xchk_irele(sc, sc->ip);
185 2169714351 : sc->ip = NULL;
186 : }
187 2191555782 : if (sc->flags & XCHK_HAVE_FREEZE_PROT) {
188 317683686 : sc->flags &= ~XCHK_HAVE_FREEZE_PROT;
189 317683686 : mnt_drop_write_file(sc->file);
190 : }
191 2191182756 : if (sc->buf) {
192 60831479 : if (sc->buf_cleanup)
193 19015785 : sc->buf_cleanup(sc->buf);
194 60831605 : kvfree(sc->buf);
195 60825108 : sc->buf_cleanup = NULL;
196 60825108 : sc->buf = NULL;
197 : }
198 :
199 2191176385 : xchk_fsgates_disable(sc);
200 2188982175 : return error;
201 : }
202 :
203 : /* Scrubbing dispatch. */
204 :
205 : static const struct xchk_meta_ops meta_scrub_ops[] = {
206 : [XFS_SCRUB_TYPE_PROBE] = { /* ioctl presence test */
207 : .type = ST_NONE,
208 : .setup = xchk_setup_fs,
209 : .scrub = xchk_probe,
210 : .repair = xrep_probe,
211 : },
212 : [XFS_SCRUB_TYPE_SB] = { /* superblock */
213 : .type = ST_PERAG,
214 : .setup = xchk_setup_agheader,
215 : .scrub = xchk_superblock,
216 : .repair = xrep_superblock,
217 : },
218 : [XFS_SCRUB_TYPE_AGF] = { /* agf */
219 : .type = ST_PERAG,
220 : .setup = xchk_setup_agheader,
221 : .scrub = xchk_agf,
222 : .repair = xrep_agf,
223 : },
224 : [XFS_SCRUB_TYPE_AGFL]= { /* agfl */
225 : .type = ST_PERAG,
226 : .setup = xchk_setup_agheader,
227 : .scrub = xchk_agfl,
228 : .repair = xrep_agfl,
229 : },
230 : [XFS_SCRUB_TYPE_AGI] = { /* agi */
231 : .type = ST_PERAG,
232 : .setup = xchk_setup_agheader,
233 : .scrub = xchk_agi,
234 : .repair = xrep_agi,
235 : },
236 : [XFS_SCRUB_TYPE_BNOBT] = { /* bnobt */
237 : .type = ST_PERAG,
238 : .setup = xchk_setup_ag_allocbt,
239 : .scrub = xchk_bnobt,
240 : .repair = xrep_notsupported,
241 : },
242 : [XFS_SCRUB_TYPE_CNTBT] = { /* cntbt */
243 : .type = ST_PERAG,
244 : .setup = xchk_setup_ag_allocbt,
245 : .scrub = xchk_cntbt,
246 : .repair = xrep_notsupported,
247 : },
248 : [XFS_SCRUB_TYPE_INOBT] = { /* inobt */
249 : .type = ST_PERAG,
250 : .setup = xchk_setup_ag_iallocbt,
251 : .scrub = xchk_inobt,
252 : .repair = xrep_notsupported,
253 : },
254 : [XFS_SCRUB_TYPE_FINOBT] = { /* finobt */
255 : .type = ST_PERAG,
256 : .setup = xchk_setup_ag_iallocbt,
257 : .scrub = xchk_finobt,
258 : .has = xfs_has_finobt,
259 : .repair = xrep_notsupported,
260 : },
261 : [XFS_SCRUB_TYPE_RMAPBT] = { /* rmapbt */
262 : .type = ST_PERAG,
263 : .setup = xchk_setup_ag_rmapbt,
264 : .scrub = xchk_rmapbt,
265 : .has = xfs_has_rmapbt,
266 : .repair = xrep_notsupported,
267 : },
268 : [XFS_SCRUB_TYPE_REFCNTBT] = { /* refcountbt */
269 : .type = ST_PERAG,
270 : .setup = xchk_setup_ag_refcountbt,
271 : .scrub = xchk_refcountbt,
272 : .has = xfs_has_reflink,
273 : .repair = xrep_notsupported,
274 : },
275 : [XFS_SCRUB_TYPE_INODE] = { /* inode record */
276 : .type = ST_INODE,
277 : .setup = xchk_setup_inode,
278 : .scrub = xchk_inode,
279 : .repair = xrep_notsupported,
280 : },
281 : [XFS_SCRUB_TYPE_BMBTD] = { /* inode data fork */
282 : .type = ST_INODE,
283 : .setup = xchk_setup_inode_bmap,
284 : .scrub = xchk_bmap_data,
285 : .repair = xrep_notsupported,
286 : },
287 : [XFS_SCRUB_TYPE_BMBTA] = { /* inode attr fork */
288 : .type = ST_INODE,
289 : .setup = xchk_setup_inode_bmap,
290 : .scrub = xchk_bmap_attr,
291 : .repair = xrep_notsupported,
292 : },
293 : [XFS_SCRUB_TYPE_BMBTC] = { /* inode CoW fork */
294 : .type = ST_INODE,
295 : .setup = xchk_setup_inode_bmap,
296 : .scrub = xchk_bmap_cow,
297 : .repair = xrep_notsupported,
298 : },
299 : [XFS_SCRUB_TYPE_DIR] = { /* directory */
300 : .type = ST_INODE,
301 : .setup = xchk_setup_directory,
302 : .scrub = xchk_directory,
303 : .repair = xrep_notsupported,
304 : },
305 : [XFS_SCRUB_TYPE_XATTR] = { /* extended attributes */
306 : .type = ST_INODE,
307 : .setup = xchk_setup_xattr,
308 : .scrub = xchk_xattr,
309 : .repair = xrep_notsupported,
310 : },
311 : [XFS_SCRUB_TYPE_SYMLINK] = { /* symbolic link */
312 : .type = ST_INODE,
313 : .setup = xchk_setup_symlink,
314 : .scrub = xchk_symlink,
315 : .repair = xrep_notsupported,
316 : },
317 : [XFS_SCRUB_TYPE_PARENT] = { /* parent pointers */
318 : .type = ST_INODE,
319 : .setup = xchk_setup_parent,
320 : .scrub = xchk_parent,
321 : .repair = xrep_notsupported,
322 : },
323 : [XFS_SCRUB_TYPE_RTBITMAP] = { /* realtime bitmap */
324 : .type = ST_FS,
325 : .setup = xchk_setup_rt,
326 : .scrub = xchk_rtbitmap,
327 : .has = xfs_has_realtime,
328 : .repair = xrep_notsupported,
329 : },
330 : [XFS_SCRUB_TYPE_RTSUM] = { /* realtime summary */
331 : .type = ST_FS,
332 : .setup = xchk_setup_rt,
333 : .scrub = xchk_rtsummary,
334 : .has = xfs_has_realtime,
335 : .repair = xrep_notsupported,
336 : },
337 : [XFS_SCRUB_TYPE_UQUOTA] = { /* user quota */
338 : .type = ST_FS,
339 : .setup = xchk_setup_quota,
340 : .scrub = xchk_quota,
341 : .repair = xrep_notsupported,
342 : },
343 : [XFS_SCRUB_TYPE_GQUOTA] = { /* group quota */
344 : .type = ST_FS,
345 : .setup = xchk_setup_quota,
346 : .scrub = xchk_quota,
347 : .repair = xrep_notsupported,
348 : },
349 : [XFS_SCRUB_TYPE_PQUOTA] = { /* project quota */
350 : .type = ST_FS,
351 : .setup = xchk_setup_quota,
352 : .scrub = xchk_quota,
353 : .repair = xrep_notsupported,
354 : },
355 : [XFS_SCRUB_TYPE_FSCOUNTERS] = { /* fs summary counters */
356 : .type = ST_FS,
357 : .setup = xchk_setup_fscounters,
358 : .scrub = xchk_fscounters,
359 : .repair = xrep_notsupported,
360 : },
361 : };
362 :
363 : static int
364 2188825825 : xchk_validate_inputs(
365 : struct xfs_mount *mp,
366 : struct xfs_scrub_metadata *sm)
367 : {
368 2188825825 : int error;
369 2188825825 : const struct xchk_meta_ops *ops;
370 :
371 2188825825 : error = -EINVAL;
372 : /* Check our inputs. */
373 2188825825 : sm->sm_flags &= ~XFS_SCRUB_FLAGS_OUT;
374 2188825825 : if (sm->sm_flags & ~XFS_SCRUB_FLAGS_IN)
375 0 : goto out;
376 : /* sm_reserved[] must be zero */
377 4378532428 : if (memchr_inv(sm->sm_reserved, 0, sizeof(sm->sm_reserved)))
378 0 : goto out;
379 :
380 2189706603 : error = -ENOENT;
381 : /* Do we know about this type of metadata? */
382 2189706603 : if (sm->sm_type >= XFS_SCRUB_TYPE_NR)
383 0 : goto out;
384 2189706603 : ops = &meta_scrub_ops[sm->sm_type];
385 2190956398 : if (ops->setup == NULL || ops->scrub == NULL)
386 0 : goto out;
387 : /* Does this fs even support this type of metadata? */
388 2190956398 : if (ops->has && !ops->has(mp))
389 538191 : goto out;
390 :
391 2189651657 : error = -EINVAL;
392 : /* restricting fields must be appropriate for type */
393 2189651657 : switch (ops->type) {
394 1702438 : case ST_NONE:
395 : case ST_FS:
396 1702438 : if (sm->sm_ino || sm->sm_gen || sm->sm_agno)
397 0 : goto out;
398 : break;
399 9208567 : case ST_PERAG:
400 9208567 : if (sm->sm_ino || sm->sm_gen ||
401 9208567 : sm->sm_agno >= mp->m_sb.sb_agcount)
402 0 : goto out;
403 : break;
404 2178740652 : case ST_INODE:
405 2178740652 : if (sm->sm_agno || (sm->sm_gen && !sm->sm_ino))
406 0 : goto out;
407 : break;
408 0 : default:
409 0 : goto out;
410 : }
411 :
412 : /*
413 : * We only want to repair read-write v5+ filesystems. Defer the check
414 : * for ops->repair until after our scrub confirms that we need to
415 : * perform repairs so that we avoid failing due to not supporting
416 : * repairing an object that doesn't need repairs.
417 : */
418 2189651657 : if (sm->sm_flags & XFS_SCRUB_IFLAG_REPAIR) {
419 317009305 : error = -EOPNOTSUPP;
420 317009305 : if (!xfs_has_crc(mp))
421 0 : goto out;
422 :
423 317009305 : error = -EROFS;
424 634018610 : if (xfs_is_readonly(mp))
425 16893 : goto out;
426 : }
427 :
428 : error = 0;
429 2190189848 : out:
430 2190189848 : return error;
431 : }
432 :
433 : #ifdef CONFIG_XFS_ONLINE_REPAIR
434 1390053522 : static inline void xchk_postmortem(struct xfs_scrub *sc)
435 : {
436 : /*
437 : * Userspace asked us to repair something, we repaired it, rescanned
438 : * it, and the rescan says it's still broken. Scream about this in
439 : * the system logs.
440 : */
441 1390053522 : if ((sc->sm->sm_flags & XFS_SCRUB_IFLAG_REPAIR) &&
442 316551077 : (sc->sm->sm_flags & (XFS_SCRUB_OFLAG_CORRUPT |
443 : XFS_SCRUB_OFLAG_XCORRUPT)))
444 315526301 : xrep_failure(sc->mp);
445 1390126478 : }
446 : #else
447 : static inline void xchk_postmortem(struct xfs_scrub *sc)
448 : {
449 : /*
450 : * Userspace asked us to scrub something, it's broken, and we have no
451 : * way of fixing it. Scream in the logs.
452 : */
453 : if (sc->sm->sm_flags & (XFS_SCRUB_OFLAG_CORRUPT |
454 : XFS_SCRUB_OFLAG_XCORRUPT))
455 : xfs_alert_ratelimited(sc->mp,
456 : "Corruption detected during scrub.");
457 : }
458 : #endif /* CONFIG_XFS_ONLINE_REPAIR */
459 :
460 : /* Dispatch metadata scrubbing. */
461 : int
462 2187454274 : xfs_scrub_metadata(
463 : struct file *file,
464 : struct xfs_scrub_metadata *sm)
465 : {
466 2187454274 : struct xfs_scrub *sc;
467 2187454274 : struct xfs_mount *mp = XFS_I(file_inode(file))->i_mount;
468 2187454274 : int error = 0;
469 :
470 2187454274 : BUILD_BUG_ON(sizeof(meta_scrub_ops) !=
471 : (sizeof(struct xchk_meta_ops) * XFS_SCRUB_TYPE_NR));
472 :
473 2187454274 : trace_xchk_start(XFS_I(file_inode(file)), sm, error);
474 :
475 : /* Forbidden if we are shut down or mounted norecovery. */
476 2189151862 : error = -ESHUTDOWN;
477 4378303724 : if (xfs_is_shutdown(mp))
478 0 : goto out;
479 2189151862 : error = -ENOTRECOVERABLE;
480 2189151862 : if (xfs_has_norecovery(mp))
481 11 : goto out;
482 :
483 2189151851 : error = xchk_validate_inputs(mp, sm);
484 2189745968 : if (error)
485 554951 : goto out;
486 :
487 2189191017 : xfs_warn_mount(mp, XFS_OPSTATE_WARNED_SCRUB,
488 : "EXPERIMENTAL online scrub feature in use. Use at your own risk!");
489 :
490 2191114826 : sc = kzalloc(sizeof(struct xfs_scrub), XCHK_GFP_FLAGS);
491 2190958216 : if (!sc) {
492 0 : error = -ENOMEM;
493 0 : goto out;
494 : }
495 :
496 2190958216 : sc->mp = mp;
497 2190958216 : sc->file = file;
498 2190958216 : sc->sm = sm;
499 2190958216 : sc->ops = &meta_scrub_ops[sm->sm_type];
500 2190307949 : sc->sick_mask = xchk_health_mask_for_scrub_type(sm->sm_type);
501 : retry_op:
502 : /*
503 : * When repairs are allowed, prevent freezing or readonly remount while
504 : * scrub is running with a real transaction.
505 : */
506 2189897013 : if (sm->sm_flags & XFS_SCRUB_IFLAG_REPAIR) {
507 318073594 : error = mnt_want_write_file(sc->file);
508 318079426 : if (error)
509 252 : goto out_sc;
510 :
511 318079174 : sc->flags |= XCHK_HAVE_FREEZE_PROT;
512 : }
513 :
514 : /* Set up for the operation. */
515 2189902593 : error = sc->ops->setup(sc);
516 2189606383 : if (error == -EDEADLOCK && !(sc->flags & XCHK_TRY_HARDER))
517 0 : goto try_harder;
518 2189606383 : if (error == -ECHRNG && !(sc->flags & XCHK_NEED_DRAIN))
519 26273 : goto need_drain;
520 2189580110 : if (error)
521 10910385 : goto out_teardown;
522 :
523 : /* Scrub for errors. */
524 2178669725 : error = sc->ops->scrub(sc);
525 2182137051 : if (error == -EDEADLOCK && !(sc->flags & XCHK_TRY_HARDER))
526 34775 : goto try_harder;
527 2182102276 : if (error == -ECHRNG && !(sc->flags & XCHK_NEED_DRAIN))
528 71597 : goto need_drain;
529 2182030679 : if (error || (sm->sm_flags & XFS_SCRUB_OFLAG_INCOMPLETE))
530 789632206 : goto out_teardown;
531 :
532 1392398473 : xchk_update_health(sc);
533 :
534 1391470531 : if ((sc->sm->sm_flags & XFS_SCRUB_IFLAG_REPAIR) &&
535 317535584 : !(sc->flags & XREP_ALREADY_FIXED)) {
536 316512649 : bool needs_fix;
537 :
538 : /* Let debug users force us into the repair routines. */
539 316512649 : if (XFS_TEST_ERROR(false, mp, XFS_ERRTAG_FORCE_SCRUB_REPAIR))
540 316770346 : sc->sm->sm_flags |= XFS_SCRUB_OFLAG_CORRUPT;
541 :
542 316780173 : needs_fix = (sc->sm->sm_flags & (XFS_SCRUB_OFLAG_CORRUPT |
543 : XFS_SCRUB_OFLAG_XCORRUPT |
544 : XFS_SCRUB_OFLAG_PREEN));
545 : /*
546 : * If userspace asked for a repair but it wasn't necessary,
547 : * report that back to userspace.
548 : */
549 316780173 : if (!needs_fix) {
550 477 : sc->sm->sm_flags |= XFS_SCRUB_OFLAG_NO_REPAIR_NEEDED;
551 477 : goto out_nofix;
552 : }
553 :
554 : /*
555 : * If it's broken, userspace wants us to fix it, and we haven't
556 : * already tried to fix it, then attempt a repair.
557 : */
558 316779696 : error = xrep_attempt(sc);
559 316530931 : if (error == -EAGAIN) {
560 : /*
561 : * Either the repair function succeeded or it couldn't
562 : * get all the resources it needs; either way, we go
563 : * back to the beginning and call the scrub function.
564 : */
565 1022101 : error = xchk_teardown(sc, 0);
566 1022028 : if (error) {
567 0 : xrep_failure(mp);
568 0 : goto out_sc;
569 : }
570 1022028 : goto retry_op;
571 : }
572 : }
573 :
574 1390466712 : out_nofix:
575 1390467189 : xchk_postmortem(sc);
576 2190332102 : out_teardown:
577 2190332102 : error = xchk_teardown(sc, error);
578 2187153431 : out_sc:
579 2187153431 : kfree(sc);
580 2191035269 : out:
581 2191035269 : trace_xchk_done(XFS_I(file_inode(file)), sm, error);
582 2185861996 : if (error == -EFSCORRUPTED || error == -EFSBADCRC) {
583 0 : sm->sm_flags |= XFS_SCRUB_OFLAG_CORRUPT;
584 0 : error = 0;
585 : }
586 2185861996 : return error;
587 97870 : need_drain:
588 97870 : error = xchk_teardown(sc, 0);
589 97866 : if (error)
590 0 : goto out_sc;
591 97866 : sc->flags |= XCHK_NEED_DRAIN;
592 97866 : goto retry_op;
593 34775 : try_harder:
594 : /*
595 : * Scrubbers return -EDEADLOCK to mean 'try harder'. Tear down
596 : * everything we hold, then set up again with preparation for
597 : * worst-case scenarios.
598 : */
599 34775 : error = xchk_teardown(sc, 0);
600 34775 : if (error)
601 0 : goto out_sc;
602 34775 : sc->flags |= XCHK_TRY_HARDER;
603 34775 : goto retry_op;
604 : }
|