LCOV - code coverage report
Current view: top level - fs/xfs/scrub - health.c (source / functions) Hit Total Coverage
Test: fstests of 6.5.0-rc3-acha @ Mon Jul 31 20:08:06 PDT 2023 Lines: 74 90 82.2 %
Date: 2023-07-31 20:08:07 Functions: 5 5 100.0 %

          Line data    Source code
       1             : // SPDX-License-Identifier: GPL-2.0-or-later
       2             : /*
       3             :  * Copyright (C) 2019-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_btree.h"
      13             : #include "xfs_trans_resv.h"
      14             : #include "xfs_mount.h"
      15             : #include "xfs_ag.h"
      16             : #include "xfs_health.h"
      17             : #include "scrub/scrub.h"
      18             : #include "scrub/health.h"
      19             : #include "scrub/common.h"
      20             : 
      21             : /*
      22             :  * Scrub and In-Core Filesystem Health Assessments
      23             :  * ===============================================
      24             :  *
      25             :  * Online scrub and repair have the time and the ability to perform stronger
      26             :  * checks than we can do from the metadata verifiers, because they can
      27             :  * cross-reference records between data structures.  Therefore, scrub is in a
      28             :  * good position to update the online filesystem health assessments to reflect
      29             :  * the good/bad state of the data structure.
      30             :  *
      31             :  * We therefore extend scrub in the following ways to achieve this:
      32             :  *
      33             :  * 1. Create a "sick_mask" field in the scrub context.  When we're setting up a
      34             :  * scrub call, set this to the default XFS_SICK_* flag(s) for the selected
      35             :  * scrub type (call it A).  Scrub and repair functions can override the default
      36             :  * sick_mask value if they choose.
      37             :  *
      38             :  * 2. If the scrubber returns a runtime error code, we exit making no changes
      39             :  * to the incore sick state.
      40             :  *
      41             :  * 3. If the scrubber finds that A is clean, use sick_mask to clear the incore
      42             :  * sick flags before exiting.
      43             :  *
      44             :  * 4. If the scrubber finds that A is corrupt, use sick_mask to set the incore
      45             :  * sick flags.  If the user didn't want to repair then we exit, leaving the
      46             :  * metadata structure unfixed and the sick flag set.
      47             :  *
      48             :  * 5. Now we know that A is corrupt and the user wants to repair, so run the
      49             :  * repairer.  If the repairer returns an error code, we exit with that error
      50             :  * code, having made no further changes to the incore sick state.
      51             :  *
      52             :  * 6. If repair rebuilds A correctly and the subsequent re-scrub of A is clean,
      53             :  * use sick_mask to clear the incore sick flags.  This should have the effect
      54             :  * that A is no longer marked sick.
      55             :  *
      56             :  * 7. If repair rebuilds A incorrectly, the re-scrub will find it corrupt and
      57             :  * use sick_mask to set the incore sick flags.  This should have no externally
      58             :  * visible effect since we already set them in step (4).
      59             :  *
      60             :  * There are some complications to this story, however.  For certain types of
      61             :  * complementary metadata indices (e.g. inobt/finobt), it is easier to rebuild
      62             :  * both structures at the same time.  The following principles apply to this
      63             :  * type of repair strategy:
      64             :  *
      65             :  * 8. Any repair function that rebuilds multiple structures should update
      66             :  * sick_mask_visible to reflect whatever other structures are rebuilt, and
      67             :  * verify that all the rebuilt structures can pass a scrub check.  The outcomes
      68             :  * of 5-7 still apply, but with a sick_mask that covers everything being
      69             :  * rebuilt.
      70             :  */
      71             : 
      72             : /* Map our scrub type to a sick mask and a set of health update functions. */
      73             : 
      74             : enum xchk_health_group {
      75             :         XHG_FS = 1,
      76             :         XHG_RT,
      77             :         XHG_AG,
      78             :         XHG_INO,
      79             : };
      80             : 
      81             : struct xchk_health_map {
      82             :         enum xchk_health_group  group;
      83             :         unsigned int            sick_mask;
      84             : };
      85             : 
      86             : static const struct xchk_health_map type_to_health_flag[XFS_SCRUB_TYPE_NR] = {
      87             :         [XFS_SCRUB_TYPE_SB]             = { XHG_AG,  XFS_SICK_AG_SB },
      88             :         [XFS_SCRUB_TYPE_AGF]            = { XHG_AG,  XFS_SICK_AG_AGF },
      89             :         [XFS_SCRUB_TYPE_AGFL]           = { XHG_AG,  XFS_SICK_AG_AGFL },
      90             :         [XFS_SCRUB_TYPE_AGI]            = { XHG_AG,  XFS_SICK_AG_AGI },
      91             :         [XFS_SCRUB_TYPE_BNOBT]          = { XHG_AG,  XFS_SICK_AG_BNOBT },
      92             :         [XFS_SCRUB_TYPE_CNTBT]          = { XHG_AG,  XFS_SICK_AG_CNTBT },
      93             :         [XFS_SCRUB_TYPE_INOBT]          = { XHG_AG,  XFS_SICK_AG_INOBT },
      94             :         [XFS_SCRUB_TYPE_FINOBT]         = { XHG_AG,  XFS_SICK_AG_FINOBT },
      95             :         [XFS_SCRUB_TYPE_RMAPBT]         = { XHG_AG,  XFS_SICK_AG_RMAPBT },
      96             :         [XFS_SCRUB_TYPE_REFCNTBT]       = { XHG_AG,  XFS_SICK_AG_REFCNTBT },
      97             :         [XFS_SCRUB_TYPE_INODE]          = { XHG_INO, XFS_SICK_INO_CORE },
      98             :         [XFS_SCRUB_TYPE_BMBTD]          = { XHG_INO, XFS_SICK_INO_BMBTD },
      99             :         [XFS_SCRUB_TYPE_BMBTA]          = { XHG_INO, XFS_SICK_INO_BMBTA },
     100             :         [XFS_SCRUB_TYPE_BMBTC]          = { XHG_INO, XFS_SICK_INO_BMBTC },
     101             :         [XFS_SCRUB_TYPE_DIR]            = { XHG_INO, XFS_SICK_INO_DIR },
     102             :         [XFS_SCRUB_TYPE_XATTR]          = { XHG_INO, XFS_SICK_INO_XATTR },
     103             :         [XFS_SCRUB_TYPE_SYMLINK]        = { XHG_INO, XFS_SICK_INO_SYMLINK },
     104             :         [XFS_SCRUB_TYPE_PARENT]         = { XHG_INO, XFS_SICK_INO_PARENT },
     105             :         [XFS_SCRUB_TYPE_RTBITMAP]       = { XHG_RT,  XFS_SICK_RT_BITMAP },
     106             :         [XFS_SCRUB_TYPE_RTSUM]          = { XHG_RT,  XFS_SICK_RT_SUMMARY },
     107             :         [XFS_SCRUB_TYPE_UQUOTA]         = { XHG_FS,  XFS_SICK_FS_UQUOTA },
     108             :         [XFS_SCRUB_TYPE_GQUOTA]         = { XHG_FS,  XFS_SICK_FS_GQUOTA },
     109             :         [XFS_SCRUB_TYPE_PQUOTA]         = { XHG_FS,  XFS_SICK_FS_PQUOTA },
     110             :         [XFS_SCRUB_TYPE_FSCOUNTERS]     = { XHG_FS,  XFS_SICK_FS_COUNTERS },
     111             :         [XFS_SCRUB_TYPE_QUOTACHECK]     = { XHG_FS,  XFS_SICK_FS_QUOTACHECK },
     112             :         [XFS_SCRUB_TYPE_NLINKS]         = { XHG_FS,  XFS_SICK_FS_NLINKS },
     113             :         [XFS_SCRUB_TYPE_DIRTREE]        = { XHG_INO, XFS_SICK_INO_DIRTREE },
     114             : };
     115             : 
     116             : /* Return the health status mask for this scrub type. */
     117             : unsigned int
     118   645702568 : xchk_health_mask_for_scrub_type(
     119             :         __u32                   scrub_type)
     120             : {
     121   645702568 :         return type_to_health_flag[scrub_type].sick_mask;
     122             : }
     123             : 
     124             : /*
     125             :  * Scrub gave the filesystem a clean bill of health, so clear all the indirect
     126             :  * markers of past problems (at least for the fs and ags) so that we can be
     127             :  * healthy again.
     128             :  */
     129             : STATIC void
     130       12873 : xchk_mark_all_healthy(
     131             :         struct xfs_mount        *mp)
     132             : {
     133       12873 :         struct xfs_perag        *pag;
     134       12873 :         xfs_agnumber_t          agno;
     135             : 
     136       12873 :         xfs_fs_mark_healthy(mp, XFS_SICK_FS_INDIRECT);
     137       12873 :         xfs_rt_mark_healthy(mp, XFS_SICK_RT_INDIRECT);
     138       72384 :         for_each_perag(mp, agno, pag)
     139       59511 :                 xfs_ag_mark_healthy(pag, XFS_SICK_AG_INDIRECT);
     140       12873 : }
     141             : 
     142             : /*
     143             :  * Update filesystem health assessments based on what we found and did.
     144             :  *
     145             :  * If the scrubber finds errors, we mark sick whatever's mentioned in
     146             :  * sick_mask, no matter whether this is a first scan or an
     147             :  * evaluation of repair effectiveness.
     148             :  *
     149             :  * Otherwise, no direct corruption was found, so mark whatever's in
     150             :  * sick_mask as healthy.
     151             :  */
     152             : void
     153   552815972 : xchk_update_health(
     154             :         struct xfs_scrub        *sc)
     155             : {
     156   552815972 :         struct xfs_perag        *pag;
     157   552815972 :         bool                    bad;
     158             : 
     159             :         /*
     160             :          * The HEALTHY scrub type is a request from userspace to clear all the
     161             :          * indirect flags after a clean scan of the entire filesystem.  As such
     162             :          * there's no sick flag defined for it, so we branch here ahead of the
     163             :          * mask check.
     164             :          */
     165   552815972 :         if (sc->sm->sm_type == XFS_SCRUB_TYPE_HEALTHY &&
     166             :             !(sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT)) {
     167       12873 :                 xchk_mark_all_healthy(sc->mp);
     168       12873 :                 return;
     169             :         }
     170             : 
     171   552803099 :         if (!sc->sick_mask)
     172             :                 return;
     173             : 
     174   552769763 :         bad = (sc->sm->sm_flags & (XFS_SCRUB_OFLAG_CORRUPT |
     175             :                                    XFS_SCRUB_OFLAG_XCORRUPT));
     176   552769763 :         switch (type_to_health_flag[sc->sm->sm_type].group) {
     177     2453945 :         case XHG_AG:
     178     2453945 :                 pag = xfs_perag_get(sc->mp, sc->sm->sm_agno);
     179     2453909 :                 if (bad) {
     180          18 :                         xfs_ag_mark_sick(pag, sc->sick_mask);
     181          18 :                         xfs_ag_mark_checked(pag, sc->sick_mask);
     182             :                 } else
     183     2453891 :                         xfs_ag_mark_healthy(pag, sc->sick_mask);
     184     2453942 :                 xfs_perag_put(pag);
     185     2453942 :                 break;
     186   550097801 :         case XHG_INO:
     187   550097801 :                 if (!sc->ip)
     188             :                         return;
     189   550097801 :                 if (bad) {
     190           6 :                         unsigned int    mask = sc->sick_mask;
     191             : 
     192             :                         /*
     193             :                          * If we're coming in for repairs then we don't want
     194             :                          * sickness flags to propagate to the incore health
     195             :                          * status if the inode gets inactivated before we can
     196             :                          * fix it.
     197             :                          */
     198           6 :                         if (sc->sm->sm_flags & XFS_SCRUB_IFLAG_REPAIR)
     199           0 :                                 mask |= XFS_SICK_INO_FORGET;
     200           6 :                         xfs_inode_mark_sick(sc->ip, mask);
     201           6 :                         xfs_inode_mark_checked(sc->ip, sc->sick_mask);
     202             :                 } else
     203   550097795 :                         xfs_inode_mark_healthy(sc->ip, sc->sick_mask);
     204             :                 break;
     205      170017 :         case XHG_FS:
     206      170017 :                 if (bad) {
     207           0 :                         xfs_fs_mark_sick(sc->mp, sc->sick_mask);
     208           0 :                         xfs_fs_mark_checked(sc->mp, sc->sick_mask);
     209             :                 } else
     210      170017 :                         xfs_fs_mark_healthy(sc->mp, sc->sick_mask);
     211             :                 break;
     212       48000 :         case XHG_RT:
     213       48000 :                 if (bad) {
     214           0 :                         xfs_rt_mark_sick(sc->mp, sc->sick_mask);
     215           0 :                         xfs_rt_mark_checked(sc->mp, sc->sick_mask);
     216             :                 } else
     217       48000 :                         xfs_rt_mark_healthy(sc->mp, sc->sick_mask);
     218             :                 break;
     219           0 :         default:
     220           0 :                 ASSERT(0);
     221           0 :                 break;
     222             :         }
     223             : }
     224             : 
     225             : /* Is the given per-AG btree healthy enough for scanning? */
     226             : bool
     227  1553680558 : xchk_ag_btree_healthy_enough(
     228             :         struct xfs_scrub        *sc,
     229             :         struct xfs_perag        *pag,
     230             :         xfs_btnum_t             btnum)
     231             : {
     232  1553680558 :         unsigned int            mask = 0;
     233             : 
     234             :         /*
     235             :          * We always want the cursor if it's the same type as whatever we're
     236             :          * scrubbing, even if we already know the structure is corrupt.
     237             :          *
     238             :          * Otherwise, we're only interested in the btree for cross-referencing.
     239             :          * If we know the btree is bad then don't bother, just set XFAIL.
     240             :          */
     241  1553680558 :         switch (btnum) {
     242   281044945 :         case XFS_BTNUM_BNO:
     243   281044945 :                 if (sc->sm->sm_type == XFS_SCRUB_TYPE_BNOBT)
     244             :                         return true;
     245             :                 mask = XFS_SICK_AG_BNOBT;
     246             :                 break;
     247   281044695 :         case XFS_BTNUM_CNT:
     248   281044695 :                 if (sc->sm->sm_type == XFS_SCRUB_TYPE_CNTBT)
     249             :                         return true;
     250             :                 mask = XFS_SICK_AG_CNTBT;
     251             :                 break;
     252   281044359 :         case XFS_BTNUM_INO:
     253   281044359 :                 if (sc->sm->sm_type == XFS_SCRUB_TYPE_INOBT)
     254             :                         return true;
     255             :                 mask = XFS_SICK_AG_INOBT;
     256             :                 break;
     257   281043923 :         case XFS_BTNUM_FINO:
     258   281043923 :                 if (sc->sm->sm_type == XFS_SCRUB_TYPE_FINOBT)
     259             :                         return true;
     260             :                 mask = XFS_SICK_AG_FINOBT;
     261             :                 break;
     262   214753631 :         case XFS_BTNUM_RMAP:
     263   214753631 :                 if (sc->sm->sm_type == XFS_SCRUB_TYPE_RMAPBT)
     264             :                         return true;
     265             :                 mask = XFS_SICK_AG_RMAPBT;
     266             :                 break;
     267   214749005 :         case XFS_BTNUM_REFC:
     268   214749005 :                 if (sc->sm->sm_type == XFS_SCRUB_TYPE_REFCNTBT)
     269             :                         return true;
     270             :                 mask = XFS_SICK_AG_REFCNTBT;
     271             :                 break;
     272           0 :         default:
     273           0 :                 ASSERT(0);
     274           0 :                 return true;
     275             :         }
     276             : 
     277             :         /*
     278             :          * If we just repaired some AG metadata, sc->sick_mask will reflect all
     279             :          * the per-AG metadata types that were repaired.  Exclude these from
     280             :          * the filesystem health query because we have not yet updated the
     281             :          * health status and we want everything to be scanned.
     282             :          */
     283  1552820809 :         if ((sc->flags & XREP_ALREADY_FIXED) &&
     284    16967843 :             type_to_health_flag[sc->sm->sm_type].group == XHG_AG)
     285     2210334 :                 mask &= ~sc->sick_mask;
     286             : 
     287  1552820809 :         if (xfs_ag_has_sickness(pag, mask)) {
     288           0 :                 sc->sm->sm_flags |= XFS_SCRUB_OFLAG_XFAIL;
     289           0 :                 return false;
     290             :         }
     291             : 
     292             :         return true;
     293             : }
     294             : 
     295             : /*
     296             :  * Quick scan to double-check that there isn't any evidence of lingering
     297             :  * primary health problems.  If we're still clear, then the health update will
     298             :  * take care of clearing the indirect evidence.
     299             :  */
     300             : int
     301       12873 : xchk_health_record(
     302             :         struct xfs_scrub        *sc)
     303             : {
     304       12873 :         struct xfs_mount        *mp = sc->mp;
     305       12873 :         struct xfs_perag        *pag;
     306       12873 :         xfs_agnumber_t          agno;
     307             : 
     308       12873 :         unsigned int            sick;
     309       12873 :         unsigned int            checked;
     310             : 
     311       12873 :         xfs_fs_measure_sickness(mp, &sick, &checked);
     312       12873 :         if (sick & XFS_SICK_FS_PRIMARY)
     313           0 :                 xchk_set_corrupt(sc);
     314             : 
     315       12873 :         xfs_rt_measure_sickness(mp, &sick, &checked);
     316       12873 :         if (sick & XFS_SICK_RT_PRIMARY)
     317           0 :                 xchk_set_corrupt(sc);
     318             : 
     319       72384 :         for_each_perag(mp, agno, pag) {
     320       59511 :                 xfs_ag_measure_sickness(pag, &sick, &checked);
     321       59511 :                 if (sick & XFS_SICK_AG_PRIMARY)
     322           0 :                         xchk_set_corrupt(sc);
     323             :         }
     324             : 
     325       12873 :         return 0;
     326             : }

Generated by: LCOV version 1.14