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-djwa @ Mon Jul 31 20:08:17 PDT 2023 Lines: 145 166 87.3 %
Date: 2023-07-31 20:08:17 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      258962 : xchk_probe(
     136             :         struct xfs_scrub        *sc)
     137             : {
     138      258962 :         int                     error = 0;
     139             : 
     140      258962 :         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  1873483851 : xchk_fsgates_disable(
     150             :         struct xfs_scrub        *sc)
     151             : {
     152  1873483851 :         if (!(sc->flags & XCHK_FSGATES_ALL))
     153             :                 return;
     154             : 
     155       49232 :         trace_xchk_fsgates_disable(sc, sc->flags & XCHK_FSGATES_ALL);
     156             : 
     157       49232 :         if (sc->flags & XCHK_FSGATES_DRAIN)
     158       49232 :                 xfs_drain_wait_disable();
     159             : 
     160       49232 :         sc->flags &= ~XCHK_FSGATES_ALL;
     161             : }
     162             : 
     163             : /* Free all the resources and finish the transactions. */
     164             : STATIC int
     165  1872638276 : xchk_teardown(
     166             :         struct xfs_scrub        *sc,
     167             :         int                     error)
     168             : {
     169  1872638276 :         struct xfs_inode        *ip_in = XFS_I(file_inode(sc->file));
     170             : 
     171  1872638276 :         xchk_ag_free(sc, &sc->sa);
     172  1874390315 :         if (sc->tp) {
     173  1865351257 :                 if (error == 0 && (sc->sm->sm_flags & XFS_SCRUB_IFLAG_REPAIR))
     174     1846069 :                         error = xfs_trans_commit(sc->tp);
     175             :                 else
     176  1863505188 :                         xfs_trans_cancel(sc->tp);
     177  1865645923 :                 sc->tp = NULL;
     178             :         }
     179  1874684981 :         if (sc->ip) {
     180  1855898729 :                 if (sc->ilock_flags)
     181  1855722867 :                         xfs_iunlock(sc->ip, sc->ilock_flags);
     182  3258551082 :                 if (sc->ip != ip_in &&
     183  1402753707 :                     !xfs_internal_inum(sc->mp, sc->ip->i_ino))
     184  1401762009 :                         xchk_irele(sc, sc->ip);
     185  1855748050 :                 sc->ip = NULL;
     186             :         }
     187  1874534302 :         if (sc->flags & XCHK_HAVE_FREEZE_PROT) {
     188   278960305 :                 sc->flags &= ~XCHK_HAVE_FREEZE_PROT;
     189   278960305 :                 mnt_drop_write_file(sc->file);
     190             :         }
     191  1873979736 :         if (sc->buf) {
     192    52467062 :                 if (sc->buf_cleanup)
     193    13672998 :                         sc->buf_cleanup(sc->buf);
     194    52467191 :                 kvfree(sc->buf);
     195    52467658 :                 sc->buf_cleanup = NULL;
     196    52467658 :                 sc->buf = NULL;
     197             :         }
     198             : 
     199  1873980332 :         xchk_fsgates_disable(sc);
     200  1873534977 :         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  1873809168 : xchk_validate_inputs(
     365             :         struct xfs_mount                *mp,
     366             :         struct xfs_scrub_metadata       *sm)
     367             : {
     368  1873809168 :         int                             error;
     369  1873809168 :         const struct xchk_meta_ops      *ops;
     370             : 
     371  1873809168 :         error = -EINVAL;
     372             :         /* Check our inputs. */
     373  1873809168 :         sm->sm_flags &= ~XFS_SCRUB_FLAGS_OUT;
     374  1873809168 :         if (sm->sm_flags & ~XFS_SCRUB_FLAGS_IN)
     375           0 :                 goto out;
     376             :         /* sm_reserved[] must be zero */
     377  3747713493 :         if (memchr_inv(sm->sm_reserved, 0, sizeof(sm->sm_reserved)))
     378           0 :                 goto out;
     379             : 
     380  1873904325 :         error = -ENOENT;
     381             :         /* Do we know about this type of metadata? */
     382  1873904325 :         if (sm->sm_type >= XFS_SCRUB_TYPE_NR)
     383           0 :                 goto out;
     384  1873904325 :         ops = &meta_scrub_ops[sm->sm_type];
     385  1873794184 :         if (ops->setup == NULL || ops->scrub == NULL)
     386           0 :                 goto out;
     387             :         /* Does this fs even support this type of metadata? */
     388  1873794184 :         if (ops->has && !ops->has(mp))
     389      531706 :                 goto out;
     390             : 
     391  1873303034 :         error = -EINVAL;
     392             :         /* restricting fields must be appropriate for type */
     393  1873303034 :         switch (ops->type) {
     394     1668428 :         case ST_NONE:
     395             :         case ST_FS:
     396     1668428 :                 if (sm->sm_ino || sm->sm_gen || sm->sm_agno)
     397           0 :                         goto out;
     398             :                 break;
     399     8237255 :         case ST_PERAG:
     400     8237255 :                 if (sm->sm_ino || sm->sm_gen ||
     401     8237255 :                     sm->sm_agno >= mp->m_sb.sb_agcount)
     402           0 :                         goto out;
     403             :                 break;
     404  1863397351 :         case ST_INODE:
     405  1863397351 :                 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  1873303034 :         if (sm->sm_flags & XFS_SCRUB_IFLAG_REPAIR) {
     419   278063170 :                 error = -EOPNOTSUPP;
     420   278063170 :                 if (!xfs_has_crc(mp))
     421           0 :                         goto out;
     422             : 
     423   278063170 :                 error = -EROFS;
     424   556126340 :                 if (xfs_is_readonly(mp))
     425       12369 :                         goto out;
     426             :         }
     427             : 
     428             :         error = 0;
     429  1873834740 : out:
     430  1873834740 :         return error;
     431             : }
     432             : 
     433             : #ifdef CONFIG_XFS_ONLINE_REPAIR
     434  1195953308 : 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  1195953308 :         if ((sc->sm->sm_flags & XFS_SCRUB_IFLAG_REPAIR) &&
     442   277861135 :             (sc->sm->sm_flags & (XFS_SCRUB_OFLAG_CORRUPT |
     443             :                                  XFS_SCRUB_OFLAG_XCORRUPT)))
     444   276982504 :                 xrep_failure(sc->mp);
     445  1195974369 : }
     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  1873584984 : xfs_scrub_metadata(
     463             :         struct file                     *file,
     464             :         struct xfs_scrub_metadata       *sm)
     465             : {
     466  1873584984 :         struct xfs_scrub                *sc;
     467  1873584984 :         struct xfs_mount                *mp = XFS_I(file_inode(file))->i_mount;
     468  1873584984 :         int                             error = 0;
     469             : 
     470  1873584984 :         BUILD_BUG_ON(sizeof(meta_scrub_ops) !=
     471             :                 (sizeof(struct xchk_meta_ops) * XFS_SCRUB_TYPE_NR));
     472             : 
     473  1873584984 :         trace_xchk_start(XFS_I(file_inode(file)), sm, error);
     474             : 
     475             :         /* Forbidden if we are shut down or mounted norecovery. */
     476  1873867854 :         error = -ESHUTDOWN;
     477  3747735708 :         if (xfs_is_shutdown(mp))
     478           0 :                 goto out;
     479  1873867854 :         error = -ENOTRECOVERABLE;
     480  1873867854 :         if (xfs_has_norecovery(mp))
     481           2 :                 goto out;
     482             : 
     483  1873867852 :         error = xchk_validate_inputs(mp, sm);
     484  1874014899 :         if (error)
     485      544075 :                 goto out;
     486             : 
     487  1873470824 :         xfs_warn_mount(mp, XFS_OPSTATE_WARNED_SCRUB,
     488             :  "EXPERIMENTAL online scrub feature in use. Use at your own risk!");
     489             : 
     490  1873625479 :         sc = kzalloc(sizeof(struct xfs_scrub), XCHK_GFP_FLAGS);
     491  1873694259 :         if (!sc) {
     492           0 :                 error = -ENOMEM;
     493           0 :                 goto out;
     494             :         }
     495             : 
     496  1873694259 :         sc->mp = mp;
     497  1873694259 :         sc->file = file;
     498  1873694259 :         sc->sm = sm;
     499  1873694259 :         sc->ops = &meta_scrub_ops[sm->sm_type];
     500  1873685274 :         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  1874671178 :         if (sm->sm_flags & XFS_SCRUB_IFLAG_REPAIR) {
     507   278970544 :                 error = mnt_want_write_file(sc->file);
     508   278977062 :                 if (error)
     509         251 :                         goto out_sc;
     510             : 
     511   278976811 :                 sc->flags |= XCHK_HAVE_FREEZE_PROT;
     512             :         }
     513             : 
     514             :         /* Set up for the operation. */
     515  1874677445 :         error = sc->ops->setup(sc);
     516  1874309435 :         if (error == -EDEADLOCK && !(sc->flags & XCHK_TRY_HARDER))
     517           0 :                 goto try_harder;
     518  1874309435 :         if (error == -ECHRNG && !(sc->flags & XCHK_NEED_DRAIN))
     519       18275 :                 goto need_drain;
     520  1874291160 :         if (error)
     521     9039269 :                 goto out_teardown;
     522             : 
     523             :         /* Scrub for errors. */
     524  1865251891 :         error = sc->ops->scrub(sc);
     525  1865151483 :         if (error == -EDEADLOCK && !(sc->flags & XCHK_TRY_HARDER))
     526       26515 :                 goto try_harder;
     527  1865124968 :         if (error == -ECHRNG && !(sc->flags & XCHK_NEED_DRAIN))
     528       28600 :                 goto need_drain;
     529  1865096368 :         if (error || (sm->sm_flags & XFS_SCRUB_OFLAG_INCOMPLETE))
     530   668367170 :                 goto out_teardown;
     531             : 
     532  1196729198 :         xchk_update_health(sc);
     533             : 
     534  1196885748 :         if ((sc->sm->sm_flags & XFS_SCRUB_IFLAG_REPAIR) &&
     535   278803175 :             !(sc->flags & XREP_ALREADY_FIXED)) {
     536   277906510 :                 bool needs_fix;
     537             : 
     538             :                 /* Let debug users force us into the repair routines. */
     539   277906510 :                 if (XFS_TEST_ERROR(false, mp, XFS_ERRTAG_FORCE_SCRUB_REPAIR))
     540   277860799 :                         sc->sm->sm_flags |= XFS_SCRUB_OFLAG_CORRUPT;
     541             : 
     542   277897524 :                 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   277897524 :                 if (!needs_fix) {
     550         136 :                         sc->sm->sm_flags |= XFS_SCRUB_OFLAG_NO_REPAIR_NEEDED;
     551         136 :                         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   277897388 :                 error = xrep_attempt(sc);
     559   277877581 :                 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      896608 :                         error = xchk_teardown(sc, 0);
     566      896655 :                         if (error) {
     567           0 :                                 xrep_failure(mp);
     568           0 :                                 goto out_sc;
     569             :                         }
     570      896655 :                         goto retry_op;
     571             :                 }
     572             :         }
     573             : 
     574  1195960211 : out_nofix:
     575  1195960347 :         xchk_postmortem(sc);
     576  1873330584 : out_teardown:
     577  1873330584 :         error = xchk_teardown(sc, error);
     578  1872762849 : out_sc:
     579  1872762849 :         kfree(sc);
     580  1874199814 : out:
     581  1874199814 :         trace_xchk_done(XFS_I(file_inode(file)), sm, error);
     582  1874234542 :         if (error == -EFSCORRUPTED || error == -EFSBADCRC) {
     583           0 :                 sm->sm_flags |= XFS_SCRUB_OFLAG_CORRUPT;
     584           0 :                 error = 0;
     585             :         }
     586  1874234542 :         return error;
     587       46875 : need_drain:
     588       46875 :         error = xchk_teardown(sc, 0);
     589       46875 :         if (error)
     590           0 :                 goto out_sc;
     591       46875 :         sc->flags |= XCHK_NEED_DRAIN;
     592       46875 :         goto retry_op;
     593       26515 : 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       26515 :         error = xchk_teardown(sc, 0);
     600       26515 :         if (error)
     601           0 :                 goto out_sc;
     602       26515 :         sc->flags |= XCHK_TRY_HARDER;
     603       26515 :         goto retry_op;
     604             : }

Generated by: LCOV version 1.14