LCOV - code coverage report
Current view: top level - fs/xfs/scrub - scrub.c (source / functions) Hit Total Coverage
Test: fstests of 6.5.0-rc3-djwx @ Mon Jul 31 20:08:22 PDT 2023 Lines: 145 166 87.3 %
Date: 2023-07-31 20:08:22 Functions: 6 6 100.0 %

          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             : }

Generated by: LCOV version 1.14