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