File Index
Files and directories
Sign in
FILE

health.c

File preview with syntax highlighting.

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