epoll: convert internal api to timespec64
[linux-2.6-microblaze.git] / fs / xfs / scrub / health.c
1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * Copyright (C) 2019 Oracle.  All Rights Reserved.
4  * Author: Darrick J. Wong <darrick.wong@oracle.com>
5  */
6 #include "xfs.h"
7 #include "xfs_fs.h"
8 #include "xfs_shared.h"
9 #include "xfs_format.h"
10 #include "xfs_btree.h"
11 #include "xfs_sb.h"
12 #include "xfs_health.h"
13 #include "scrub/scrub.h"
14 #include "scrub/health.h"
15
16 /*
17  * Scrub and In-Core Filesystem Health Assessments
18  * ===============================================
19  *
20  * Online scrub and repair have the time and the ability to perform stronger
21  * checks than we can do from the metadata verifiers, because they can
22  * cross-reference records between data structures.  Therefore, scrub is in a
23  * good position to update the online filesystem health assessments to reflect
24  * the good/bad state of the data structure.
25  *
26  * We therefore extend scrub in the following ways to achieve this:
27  *
28  * 1. Create a "sick_mask" field in the scrub context.  When we're setting up a
29  * scrub call, set this to the default XFS_SICK_* flag(s) for the selected
30  * scrub type (call it A).  Scrub and repair functions can override the default
31  * sick_mask value if they choose.
32  *
33  * 2. If the scrubber returns a runtime error code, we exit making no changes
34  * to the incore sick state.
35  *
36  * 3. If the scrubber finds that A is clean, use sick_mask to clear the incore
37  * sick flags before exiting.
38  *
39  * 4. If the scrubber finds that A is corrupt, use sick_mask to set the incore
40  * sick flags.  If the user didn't want to repair then we exit, leaving the
41  * metadata structure unfixed and the sick flag set.
42  *
43  * 5. Now we know that A is corrupt and the user wants to repair, so run the
44  * repairer.  If the repairer returns an error code, we exit with that error
45  * code, having made no further changes to the incore sick state.
46  *
47  * 6. If repair rebuilds A correctly and the subsequent re-scrub of A is clean,
48  * use sick_mask to clear the incore sick flags.  This should have the effect
49  * that A is no longer marked sick.
50  *
51  * 7. If repair rebuilds A incorrectly, the re-scrub will find it corrupt and
52  * use sick_mask to set the incore sick flags.  This should have no externally
53  * visible effect since we already set them in step (4).
54  *
55  * There are some complications to this story, however.  For certain types of
56  * complementary metadata indices (e.g. inobt/finobt), it is easier to rebuild
57  * both structures at the same time.  The following principles apply to this
58  * type of repair strategy:
59  *
60  * 8. Any repair function that rebuilds multiple structures should update
61  * sick_mask_visible to reflect whatever other structures are rebuilt, and
62  * verify that all the rebuilt structures can pass a scrub check.  The outcomes
63  * of 5-7 still apply, but with a sick_mask that covers everything being
64  * rebuilt.
65  */
66
67 /* Map our scrub type to a sick mask and a set of health update functions. */
68
69 enum xchk_health_group {
70         XHG_FS = 1,
71         XHG_RT,
72         XHG_AG,
73         XHG_INO,
74 };
75
76 struct xchk_health_map {
77         enum xchk_health_group  group;
78         unsigned int            sick_mask;
79 };
80
81 static const struct xchk_health_map type_to_health_flag[XFS_SCRUB_TYPE_NR] = {
82         [XFS_SCRUB_TYPE_SB]             = { XHG_AG,  XFS_SICK_AG_SB },
83         [XFS_SCRUB_TYPE_AGF]            = { XHG_AG,  XFS_SICK_AG_AGF },
84         [XFS_SCRUB_TYPE_AGFL]           = { XHG_AG,  XFS_SICK_AG_AGFL },
85         [XFS_SCRUB_TYPE_AGI]            = { XHG_AG,  XFS_SICK_AG_AGI },
86         [XFS_SCRUB_TYPE_BNOBT]          = { XHG_AG,  XFS_SICK_AG_BNOBT },
87         [XFS_SCRUB_TYPE_CNTBT]          = { XHG_AG,  XFS_SICK_AG_CNTBT },
88         [XFS_SCRUB_TYPE_INOBT]          = { XHG_AG,  XFS_SICK_AG_INOBT },
89         [XFS_SCRUB_TYPE_FINOBT]         = { XHG_AG,  XFS_SICK_AG_FINOBT },
90         [XFS_SCRUB_TYPE_RMAPBT]         = { XHG_AG,  XFS_SICK_AG_RMAPBT },
91         [XFS_SCRUB_TYPE_REFCNTBT]       = { XHG_AG,  XFS_SICK_AG_REFCNTBT },
92         [XFS_SCRUB_TYPE_INODE]          = { XHG_INO, XFS_SICK_INO_CORE },
93         [XFS_SCRUB_TYPE_BMBTD]          = { XHG_INO, XFS_SICK_INO_BMBTD },
94         [XFS_SCRUB_TYPE_BMBTA]          = { XHG_INO, XFS_SICK_INO_BMBTA },
95         [XFS_SCRUB_TYPE_BMBTC]          = { XHG_INO, XFS_SICK_INO_BMBTC },
96         [XFS_SCRUB_TYPE_DIR]            = { XHG_INO, XFS_SICK_INO_DIR },
97         [XFS_SCRUB_TYPE_XATTR]          = { XHG_INO, XFS_SICK_INO_XATTR },
98         [XFS_SCRUB_TYPE_SYMLINK]        = { XHG_INO, XFS_SICK_INO_SYMLINK },
99         [XFS_SCRUB_TYPE_PARENT]         = { XHG_INO, XFS_SICK_INO_PARENT },
100         [XFS_SCRUB_TYPE_RTBITMAP]       = { XHG_RT,  XFS_SICK_RT_BITMAP },
101         [XFS_SCRUB_TYPE_RTSUM]          = { XHG_RT,  XFS_SICK_RT_SUMMARY },
102         [XFS_SCRUB_TYPE_UQUOTA]         = { XHG_FS,  XFS_SICK_FS_UQUOTA },
103         [XFS_SCRUB_TYPE_GQUOTA]         = { XHG_FS,  XFS_SICK_FS_GQUOTA },
104         [XFS_SCRUB_TYPE_PQUOTA]         = { XHG_FS,  XFS_SICK_FS_PQUOTA },
105         [XFS_SCRUB_TYPE_FSCOUNTERS]     = { XHG_FS,  XFS_SICK_FS_COUNTERS },
106 };
107
108 /* Return the health status mask for this scrub type. */
109 unsigned int
110 xchk_health_mask_for_scrub_type(
111         __u32                   scrub_type)
112 {
113         return type_to_health_flag[scrub_type].sick_mask;
114 }
115
116 /*
117  * Update filesystem health assessments based on what we found and did.
118  *
119  * If the scrubber finds errors, we mark sick whatever's mentioned in
120  * sick_mask, no matter whether this is a first scan or an
121  * evaluation of repair effectiveness.
122  *
123  * Otherwise, no direct corruption was found, so mark whatever's in
124  * sick_mask as healthy.
125  */
126 void
127 xchk_update_health(
128         struct xfs_scrub        *sc)
129 {
130         struct xfs_perag        *pag;
131         bool                    bad;
132
133         if (!sc->sick_mask)
134                 return;
135
136         bad = (sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT);
137         switch (type_to_health_flag[sc->sm->sm_type].group) {
138         case XHG_AG:
139                 pag = xfs_perag_get(sc->mp, sc->sm->sm_agno);
140                 if (bad)
141                         xfs_ag_mark_sick(pag, sc->sick_mask);
142                 else
143                         xfs_ag_mark_healthy(pag, sc->sick_mask);
144                 xfs_perag_put(pag);
145                 break;
146         case XHG_INO:
147                 if (!sc->ip)
148                         return;
149                 if (bad)
150                         xfs_inode_mark_sick(sc->ip, sc->sick_mask);
151                 else
152                         xfs_inode_mark_healthy(sc->ip, sc->sick_mask);
153                 break;
154         case XHG_FS:
155                 if (bad)
156                         xfs_fs_mark_sick(sc->mp, sc->sick_mask);
157                 else
158                         xfs_fs_mark_healthy(sc->mp, sc->sick_mask);
159                 break;
160         case XHG_RT:
161                 if (bad)
162                         xfs_rt_mark_sick(sc->mp, sc->sick_mask);
163                 else
164                         xfs_rt_mark_healthy(sc->mp, sc->sick_mask);
165                 break;
166         default:
167                 ASSERT(0);
168                 break;
169         }
170 }
171
172 /* Is the given per-AG btree healthy enough for scanning? */
173 bool
174 xchk_ag_btree_healthy_enough(
175         struct xfs_scrub        *sc,
176         struct xfs_perag        *pag,
177         xfs_btnum_t             btnum)
178 {
179         unsigned int            mask = 0;
180
181         /*
182          * We always want the cursor if it's the same type as whatever we're
183          * scrubbing, even if we already know the structure is corrupt.
184          *
185          * Otherwise, we're only interested in the btree for cross-referencing.
186          * If we know the btree is bad then don't bother, just set XFAIL.
187          */
188         switch (btnum) {
189         case XFS_BTNUM_BNO:
190                 if (sc->sm->sm_type == XFS_SCRUB_TYPE_BNOBT)
191                         return true;
192                 mask = XFS_SICK_AG_BNOBT;
193                 break;
194         case XFS_BTNUM_CNT:
195                 if (sc->sm->sm_type == XFS_SCRUB_TYPE_CNTBT)
196                         return true;
197                 mask = XFS_SICK_AG_CNTBT;
198                 break;
199         case XFS_BTNUM_INO:
200                 if (sc->sm->sm_type == XFS_SCRUB_TYPE_INOBT)
201                         return true;
202                 mask = XFS_SICK_AG_INOBT;
203                 break;
204         case XFS_BTNUM_FINO:
205                 if (sc->sm->sm_type == XFS_SCRUB_TYPE_FINOBT)
206                         return true;
207                 mask = XFS_SICK_AG_FINOBT;
208                 break;
209         case XFS_BTNUM_RMAP:
210                 if (sc->sm->sm_type == XFS_SCRUB_TYPE_RMAPBT)
211                         return true;
212                 mask = XFS_SICK_AG_RMAPBT;
213                 break;
214         case XFS_BTNUM_REFC:
215                 if (sc->sm->sm_type == XFS_SCRUB_TYPE_REFCNTBT)
216                         return true;
217                 mask = XFS_SICK_AG_REFCNTBT;
218                 break;
219         default:
220                 ASSERT(0);
221                 return true;
222         }
223
224         if (xfs_ag_has_sickness(pag, mask)) {
225                 sc->sm->sm_flags |= XFS_SCRUB_OFLAG_XFAIL;
226                 return false;
227         }
228
229         return true;
230 }