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_ag.h" |
14 | #include "xfs_health.h" |
15 | #include "scrub/scrub.h" |
16 | #include "scrub/health.h" |
17 | #include "scrub/common.h" |
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 }, |
108 | [XFS_SCRUB_TYPE_FSCOUNTERS] = { XHG_FS, XFS_SICK_FS_COUNTERS }, |
109 | [XFS_SCRUB_TYPE_QUOTACHECK] = { XHG_FS, XFS_SICK_FS_QUOTACHECK }, |
110 | [XFS_SCRUB_TYPE_NLINKS] = { XHG_FS, XFS_SICK_FS_NLINKS }, |
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 | |
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 | |
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 | |
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 | |
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 | |
200 | if (!sc->sick_mask) |
201 | return; |
202 | |
203 | bad = (sc->sm->sm_flags & (XFS_SCRUB_OFLAG_CORRUPT | |
204 | XFS_SCRUB_OFLAG_XCORRUPT)); |
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) |
209 | xfs_ag_mark_corrupt(pag, sc->sick_mask); |
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; |
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 |
230 | xfs_inode_mark_healthy(sc->ip, sc->sick_mask); |
231 | break; |
232 | case XHG_FS: |
233 | if (bad) |
234 | xfs_fs_mark_corrupt(sc->mp, sc->sick_mask); |
235 | else |
236 | xfs_fs_mark_healthy(sc->mp, sc->sick_mask); |
237 | break; |
238 | case XHG_RT: |
239 | if (bad) |
240 | xfs_rt_mark_corrupt(sc->mp, sc->sick_mask); |
241 | else |
242 | xfs_rt_mark_healthy(sc->mp, sc->sick_mask); |
243 | break; |
244 | default: |
245 | ASSERT(0); |
246 | break; |
247 | } |
248 | } |
249 | |
250 | /* Is the given per-AG btree healthy enough for scanning? */ |
251 | void |
252 | xchk_ag_btree_del_cursor_if_sick( |
253 | struct xfs_scrub *sc, |
254 | struct xfs_btree_cur **curp, |
255 | unsigned int sm_type) |
256 | { |
257 | unsigned int mask = (*curp)->bc_ops->sick_mask; |
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 | */ |
266 | if (sc->sm->sm_type == sm_type) |
267 | return; |
268 | |
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 | |
279 | if (xfs_ag_has_sickness((*curp)->bc_ag.pag, mask)) { |
280 | sc->sm->sm_flags |= XFS_SCRUB_OFLAG_XFAIL; |
281 | xfs_btree_del_cursor(*curp, XFS_BTREE_NOERROR); |
282 | *curp = NULL; |
283 | } |
284 | } |
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 | } |
318 | |