xref: /openbmc/linux/fs/xfs/scrub/common.c (revision 8bb04028)
1739a2fe0SDarrick J. Wong // SPDX-License-Identifier: GPL-2.0-or-later
2dcb660f9SDarrick J. Wong /*
3ecc73f8aSDarrick J. Wong  * Copyright (C) 2017-2023 Oracle.  All Rights Reserved.
4739a2fe0SDarrick J. Wong  * Author: Darrick J. Wong <djwong@kernel.org>
5dcb660f9SDarrick J. Wong  */
6dcb660f9SDarrick J. Wong #include "xfs.h"
7dcb660f9SDarrick J. Wong #include "xfs_fs.h"
8dcb660f9SDarrick J. Wong #include "xfs_shared.h"
9dcb660f9SDarrick J. Wong #include "xfs_format.h"
10dcb660f9SDarrick J. Wong #include "xfs_trans_resv.h"
11dcb660f9SDarrick J. Wong #include "xfs_mount.h"
12dcb660f9SDarrick J. Wong #include "xfs_btree.h"
13dcb660f9SDarrick J. Wong #include "xfs_log_format.h"
14dcb660f9SDarrick J. Wong #include "xfs_trans.h"
15dcb660f9SDarrick J. Wong #include "xfs_inode.h"
1680e4e126SDarrick J. Wong #include "xfs_icache.h"
17dcb660f9SDarrick J. Wong #include "xfs_alloc.h"
18dcb660f9SDarrick J. Wong #include "xfs_alloc_btree.h"
19dcb660f9SDarrick J. Wong #include "xfs_ialloc.h"
20dcb660f9SDarrick J. Wong #include "xfs_ialloc_btree.h"
21dcb660f9SDarrick J. Wong #include "xfs_refcount_btree.h"
22dcb660f9SDarrick J. Wong #include "xfs_rmap.h"
23dcb660f9SDarrick J. Wong #include "xfs_rmap_btree.h"
243daa6641SDarrick J. Wong #include "xfs_log.h"
253daa6641SDarrick J. Wong #include "xfs_trans_priv.h"
26fd920008SAllison Henderson #include "xfs_da_format.h"
27fd920008SAllison Henderson #include "xfs_da_btree.h"
2887d9d609SDarrick J. Wong #include "xfs_attr.h"
2987d9d609SDarrick J. Wong #include "xfs_reflink.h"
309bbafc71SDave Chinner #include "xfs_ag.h"
31dcb660f9SDarrick J. Wong #include "scrub/scrub.h"
32dcb660f9SDarrick J. Wong #include "scrub/common.h"
33dcb660f9SDarrick J. Wong #include "scrub/trace.h"
340a9633faSDarrick J. Wong #include "scrub/repair.h"
354fb7951fSDarrick J. Wong #include "scrub/health.h"
36dcb660f9SDarrick J. Wong 
37dcb660f9SDarrick J. Wong /* Common code for the metadata scrubbers. */
38dcb660f9SDarrick J. Wong 
394700d229SDarrick J. Wong /*
404700d229SDarrick J. Wong  * Handling operational errors.
414700d229SDarrick J. Wong  *
424700d229SDarrick J. Wong  * The *_process_error() family of functions are used to process error return
434700d229SDarrick J. Wong  * codes from functions called as part of a scrub operation.
444700d229SDarrick J. Wong  *
454700d229SDarrick J. Wong  * If there's no error, we return true to tell the caller that it's ok
464700d229SDarrick J. Wong  * to move on to the next check in its list.
474700d229SDarrick J. Wong  *
484700d229SDarrick J. Wong  * For non-verifier errors (e.g. ENOMEM) we return false to tell the
494700d229SDarrick J. Wong  * caller that something bad happened, and we preserve *error so that
504700d229SDarrick J. Wong  * the caller can return the *error up the stack to userspace.
514700d229SDarrick J. Wong  *
524700d229SDarrick J. Wong  * Verifier errors (EFSBADCRC/EFSCORRUPTED) are recorded by setting
534700d229SDarrick J. Wong  * OFLAG_CORRUPT in sm_flags and the *error is cleared.  In other words,
544700d229SDarrick J. Wong  * we track verifier errors (and failed scrub checks) via OFLAG_CORRUPT,
554700d229SDarrick J. Wong  * not via return codes.  We return false to tell the caller that
564700d229SDarrick J. Wong  * something bad happened.  Since the error has been cleared, the caller
574700d229SDarrick J. Wong  * will (presumably) return that zero and scrubbing will move on to
584700d229SDarrick J. Wong  * whatever's next.
594700d229SDarrick J. Wong  *
604700d229SDarrick J. Wong  * ftrace can be used to record the precise metadata location and the
614700d229SDarrick J. Wong  * approximate code location of the failed operation.
624700d229SDarrick J. Wong  */
634700d229SDarrick J. Wong 
644700d229SDarrick J. Wong /* Check for operational errors. */
6564b12563SDarrick J. Wong static bool
__xchk_process_error(struct xfs_scrub * sc,xfs_agnumber_t agno,xfs_agblock_t bno,int * error,__u32 errflag,void * ret_ip)66c517b3aaSDarrick J. Wong __xchk_process_error(
671d8a748aSDarrick J. Wong 	struct xfs_scrub	*sc,
6864b12563SDarrick J. Wong 	xfs_agnumber_t		agno,
6964b12563SDarrick J. Wong 	xfs_agblock_t		bno,
7064b12563SDarrick J. Wong 	int			*error,
7164b12563SDarrick J. Wong 	__u32			errflag,
7264b12563SDarrick J. Wong 	void			*ret_ip)
7364b12563SDarrick J. Wong {
7464b12563SDarrick J. Wong 	switch (*error) {
7564b12563SDarrick J. Wong 	case 0:
7664b12563SDarrick J. Wong 		return true;
7764b12563SDarrick J. Wong 	case -EDEADLOCK:
7888accf17SDarrick J. Wong 	case -ECHRNG:
7964b12563SDarrick J. Wong 		/* Used to restart an op with deadlock avoidance. */
8016c9de54SDarrick J. Wong 		trace_xchk_deadlock_retry(
8116c9de54SDarrick J. Wong 				sc->ip ? sc->ip : XFS_I(file_inode(sc->file)),
8216c9de54SDarrick J. Wong 				sc->sm, *error);
8364b12563SDarrick J. Wong 		break;
8464b12563SDarrick J. Wong 	case -EFSBADCRC:
8564b12563SDarrick J. Wong 	case -EFSCORRUPTED:
8664b12563SDarrick J. Wong 		/* Note the badness but don't abort. */
8764b12563SDarrick J. Wong 		sc->sm->sm_flags |= errflag;
8864b12563SDarrick J. Wong 		*error = 0;
8953004ee7SGustavo A. R. Silva 		fallthrough;
9064b12563SDarrick J. Wong 	default:
91c517b3aaSDarrick J. Wong 		trace_xchk_op_error(sc, agno, bno, *error,
9264b12563SDarrick J. Wong 				ret_ip);
9364b12563SDarrick J. Wong 		break;
9464b12563SDarrick J. Wong 	}
9564b12563SDarrick J. Wong 	return false;
9664b12563SDarrick J. Wong }
9764b12563SDarrick J. Wong 
984700d229SDarrick J. Wong bool
xchk_process_error(struct xfs_scrub * sc,xfs_agnumber_t agno,xfs_agblock_t bno,int * error)99c517b3aaSDarrick J. Wong xchk_process_error(
1001d8a748aSDarrick J. Wong 	struct xfs_scrub	*sc,
1014700d229SDarrick J. Wong 	xfs_agnumber_t		agno,
1024700d229SDarrick J. Wong 	xfs_agblock_t		bno,
1034700d229SDarrick J. Wong 	int			*error)
1044700d229SDarrick J. Wong {
105c517b3aaSDarrick J. Wong 	return __xchk_process_error(sc, agno, bno, error,
10664b12563SDarrick J. Wong 			XFS_SCRUB_OFLAG_CORRUPT, __return_address);
1074700d229SDarrick J. Wong }
10864b12563SDarrick J. Wong 
10964b12563SDarrick J. Wong bool
xchk_xref_process_error(struct xfs_scrub * sc,xfs_agnumber_t agno,xfs_agblock_t bno,int * error)110c517b3aaSDarrick J. Wong xchk_xref_process_error(
1111d8a748aSDarrick J. Wong 	struct xfs_scrub	*sc,
11264b12563SDarrick J. Wong 	xfs_agnumber_t		agno,
11364b12563SDarrick J. Wong 	xfs_agblock_t		bno,
11464b12563SDarrick J. Wong 	int			*error)
11564b12563SDarrick J. Wong {
116c517b3aaSDarrick J. Wong 	return __xchk_process_error(sc, agno, bno, error,
11764b12563SDarrick J. Wong 			XFS_SCRUB_OFLAG_XFAIL, __return_address);
1184700d229SDarrick J. Wong }
1194700d229SDarrick J. Wong 
1204700d229SDarrick J. Wong /* Check for operational errors for a file offset. */
12164b12563SDarrick J. Wong static bool
__xchk_fblock_process_error(struct xfs_scrub * sc,int whichfork,xfs_fileoff_t offset,int * error,__u32 errflag,void * ret_ip)122c517b3aaSDarrick J. Wong __xchk_fblock_process_error(
1231d8a748aSDarrick J. Wong 	struct xfs_scrub	*sc,
1244700d229SDarrick J. Wong 	int			whichfork,
1254700d229SDarrick J. Wong 	xfs_fileoff_t		offset,
12664b12563SDarrick J. Wong 	int			*error,
12764b12563SDarrick J. Wong 	__u32			errflag,
12864b12563SDarrick J. Wong 	void			*ret_ip)
1294700d229SDarrick J. Wong {
1304700d229SDarrick J. Wong 	switch (*error) {
1314700d229SDarrick J. Wong 	case 0:
1324700d229SDarrick J. Wong 		return true;
1334700d229SDarrick J. Wong 	case -EDEADLOCK:
13488accf17SDarrick J. Wong 	case -ECHRNG:
1354700d229SDarrick J. Wong 		/* Used to restart an op with deadlock avoidance. */
136c517b3aaSDarrick J. Wong 		trace_xchk_deadlock_retry(sc->ip, sc->sm, *error);
1374700d229SDarrick J. Wong 		break;
1384700d229SDarrick J. Wong 	case -EFSBADCRC:
1394700d229SDarrick J. Wong 	case -EFSCORRUPTED:
1404700d229SDarrick J. Wong 		/* Note the badness but don't abort. */
14164b12563SDarrick J. Wong 		sc->sm->sm_flags |= errflag;
1424700d229SDarrick J. Wong 		*error = 0;
14353004ee7SGustavo A. R. Silva 		fallthrough;
1444700d229SDarrick J. Wong 	default:
145c517b3aaSDarrick J. Wong 		trace_xchk_file_op_error(sc, whichfork, offset, *error,
14664b12563SDarrick J. Wong 				ret_ip);
1474700d229SDarrick J. Wong 		break;
1484700d229SDarrick J. Wong 	}
1494700d229SDarrick J. Wong 	return false;
1504700d229SDarrick J. Wong }
1514700d229SDarrick J. Wong 
15264b12563SDarrick J. Wong bool
xchk_fblock_process_error(struct xfs_scrub * sc,int whichfork,xfs_fileoff_t offset,int * error)153c517b3aaSDarrick J. Wong xchk_fblock_process_error(
1541d8a748aSDarrick J. Wong 	struct xfs_scrub	*sc,
15564b12563SDarrick J. Wong 	int			whichfork,
15664b12563SDarrick J. Wong 	xfs_fileoff_t		offset,
15764b12563SDarrick J. Wong 	int			*error)
15864b12563SDarrick J. Wong {
159c517b3aaSDarrick J. Wong 	return __xchk_fblock_process_error(sc, whichfork, offset, error,
16064b12563SDarrick J. Wong 			XFS_SCRUB_OFLAG_CORRUPT, __return_address);
16164b12563SDarrick J. Wong }
16264b12563SDarrick J. Wong 
16364b12563SDarrick J. Wong bool
xchk_fblock_xref_process_error(struct xfs_scrub * sc,int whichfork,xfs_fileoff_t offset,int * error)164c517b3aaSDarrick J. Wong xchk_fblock_xref_process_error(
1651d8a748aSDarrick J. Wong 	struct xfs_scrub	*sc,
16664b12563SDarrick J. Wong 	int			whichfork,
16764b12563SDarrick J. Wong 	xfs_fileoff_t		offset,
16864b12563SDarrick J. Wong 	int			*error)
16964b12563SDarrick J. Wong {
170c517b3aaSDarrick J. Wong 	return __xchk_fblock_process_error(sc, whichfork, offset, error,
17164b12563SDarrick J. Wong 			XFS_SCRUB_OFLAG_XFAIL, __return_address);
17264b12563SDarrick J. Wong }
17364b12563SDarrick J. Wong 
1744700d229SDarrick J. Wong /*
1754700d229SDarrick J. Wong  * Handling scrub corruption/optimization/warning checks.
1764700d229SDarrick J. Wong  *
1774700d229SDarrick J. Wong  * The *_set_{corrupt,preen,warning}() family of functions are used to
1784700d229SDarrick J. Wong  * record the presence of metadata that is incorrect (corrupt), could be
1794700d229SDarrick J. Wong  * optimized somehow (preen), or should be flagged for administrative
1804700d229SDarrick J. Wong  * review but is not incorrect (warn).
1814700d229SDarrick J. Wong  *
1824700d229SDarrick J. Wong  * ftrace can be used to record the precise metadata location and
1834700d229SDarrick J. Wong  * approximate code location of the failed check.
1844700d229SDarrick J. Wong  */
1854700d229SDarrick J. Wong 
1864700d229SDarrick J. Wong /* Record a block which could be optimized. */
1874700d229SDarrick J. Wong void
xchk_block_set_preen(struct xfs_scrub * sc,struct xfs_buf * bp)188c517b3aaSDarrick J. Wong xchk_block_set_preen(
1891d8a748aSDarrick J. Wong 	struct xfs_scrub	*sc,
1904700d229SDarrick J. Wong 	struct xfs_buf		*bp)
1914700d229SDarrick J. Wong {
1924700d229SDarrick J. Wong 	sc->sm->sm_flags |= XFS_SCRUB_OFLAG_PREEN;
1939343ee76SDave Chinner 	trace_xchk_block_preen(sc, xfs_buf_daddr(bp), __return_address);
1944700d229SDarrick J. Wong }
1954700d229SDarrick J. Wong 
1964700d229SDarrick J. Wong /*
1974700d229SDarrick J. Wong  * Record an inode which could be optimized.  The trace data will
1984700d229SDarrick J. Wong  * include the block given by bp if bp is given; otherwise it will use
1994700d229SDarrick J. Wong  * the block location of the inode record itself.
2004700d229SDarrick J. Wong  */
2014700d229SDarrick J. Wong void
xchk_ino_set_preen(struct xfs_scrub * sc,xfs_ino_t ino)202c517b3aaSDarrick J. Wong xchk_ino_set_preen(
2031d8a748aSDarrick J. Wong 	struct xfs_scrub	*sc,
2047e56d9eaSDarrick J. Wong 	xfs_ino_t		ino)
2054700d229SDarrick J. Wong {
2064700d229SDarrick J. Wong 	sc->sm->sm_flags |= XFS_SCRUB_OFLAG_PREEN;
207c517b3aaSDarrick J. Wong 	trace_xchk_ino_preen(sc, ino, __return_address);
2084700d229SDarrick J. Wong }
2094700d229SDarrick J. Wong 
21075efa57dSDarrick J. Wong /* Record something being wrong with the filesystem primary superblock. */
21175efa57dSDarrick J. Wong void
xchk_set_corrupt(struct xfs_scrub * sc)21275efa57dSDarrick J. Wong xchk_set_corrupt(
21375efa57dSDarrick J. Wong 	struct xfs_scrub	*sc)
21475efa57dSDarrick J. Wong {
21575efa57dSDarrick J. Wong 	sc->sm->sm_flags |= XFS_SCRUB_OFLAG_CORRUPT;
21675efa57dSDarrick J. Wong 	trace_xchk_fs_error(sc, 0, __return_address);
21775efa57dSDarrick J. Wong }
21875efa57dSDarrick J. Wong 
2194700d229SDarrick J. Wong /* Record a corrupt block. */
2204700d229SDarrick J. Wong void
xchk_block_set_corrupt(struct xfs_scrub * sc,struct xfs_buf * bp)221c517b3aaSDarrick J. Wong xchk_block_set_corrupt(
2221d8a748aSDarrick J. Wong 	struct xfs_scrub	*sc,
2234700d229SDarrick J. Wong 	struct xfs_buf		*bp)
2244700d229SDarrick J. Wong {
2254700d229SDarrick J. Wong 	sc->sm->sm_flags |= XFS_SCRUB_OFLAG_CORRUPT;
2269343ee76SDave Chinner 	trace_xchk_block_error(sc, xfs_buf_daddr(bp), __return_address);
2274700d229SDarrick J. Wong }
2284700d229SDarrick J. Wong 
22964b12563SDarrick J. Wong /* Record a corruption while cross-referencing. */
23064b12563SDarrick J. Wong void
xchk_block_xref_set_corrupt(struct xfs_scrub * sc,struct xfs_buf * bp)231c517b3aaSDarrick J. Wong xchk_block_xref_set_corrupt(
2321d8a748aSDarrick J. Wong 	struct xfs_scrub	*sc,
23364b12563SDarrick J. Wong 	struct xfs_buf		*bp)
23464b12563SDarrick J. Wong {
23564b12563SDarrick J. Wong 	sc->sm->sm_flags |= XFS_SCRUB_OFLAG_XCORRUPT;
2369343ee76SDave Chinner 	trace_xchk_block_error(sc, xfs_buf_daddr(bp), __return_address);
23764b12563SDarrick J. Wong }
23864b12563SDarrick J. Wong 
2394700d229SDarrick J. Wong /*
2404700d229SDarrick J. Wong  * Record a corrupt inode.  The trace data will include the block given
2414700d229SDarrick J. Wong  * by bp if bp is given; otherwise it will use the block location of the
2424700d229SDarrick J. Wong  * inode record itself.
2434700d229SDarrick J. Wong  */
2444700d229SDarrick J. Wong void
xchk_ino_set_corrupt(struct xfs_scrub * sc,xfs_ino_t ino)245c517b3aaSDarrick J. Wong xchk_ino_set_corrupt(
2461d8a748aSDarrick J. Wong 	struct xfs_scrub	*sc,
2477e56d9eaSDarrick J. Wong 	xfs_ino_t		ino)
2484700d229SDarrick J. Wong {
2494700d229SDarrick J. Wong 	sc->sm->sm_flags |= XFS_SCRUB_OFLAG_CORRUPT;
250c517b3aaSDarrick J. Wong 	trace_xchk_ino_error(sc, ino, __return_address);
2514700d229SDarrick J. Wong }
2524700d229SDarrick J. Wong 
25364b12563SDarrick J. Wong /* Record a corruption while cross-referencing with an inode. */
25464b12563SDarrick J. Wong void
xchk_ino_xref_set_corrupt(struct xfs_scrub * sc,xfs_ino_t ino)255c517b3aaSDarrick J. Wong xchk_ino_xref_set_corrupt(
2561d8a748aSDarrick J. Wong 	struct xfs_scrub	*sc,
2577e56d9eaSDarrick J. Wong 	xfs_ino_t		ino)
25864b12563SDarrick J. Wong {
25964b12563SDarrick J. Wong 	sc->sm->sm_flags |= XFS_SCRUB_OFLAG_XCORRUPT;
260c517b3aaSDarrick J. Wong 	trace_xchk_ino_error(sc, ino, __return_address);
26164b12563SDarrick J. Wong }
26264b12563SDarrick J. Wong 
2634700d229SDarrick J. Wong /* Record corruption in a block indexed by a file fork. */
2644700d229SDarrick J. Wong void
xchk_fblock_set_corrupt(struct xfs_scrub * sc,int whichfork,xfs_fileoff_t offset)265c517b3aaSDarrick J. Wong xchk_fblock_set_corrupt(
2661d8a748aSDarrick J. Wong 	struct xfs_scrub	*sc,
2674700d229SDarrick J. Wong 	int			whichfork,
2684700d229SDarrick J. Wong 	xfs_fileoff_t		offset)
2694700d229SDarrick J. Wong {
2704700d229SDarrick J. Wong 	sc->sm->sm_flags |= XFS_SCRUB_OFLAG_CORRUPT;
271c517b3aaSDarrick J. Wong 	trace_xchk_fblock_error(sc, whichfork, offset, __return_address);
2724700d229SDarrick J. Wong }
2734700d229SDarrick J. Wong 
27464b12563SDarrick J. Wong /* Record a corruption while cross-referencing a fork block. */
27564b12563SDarrick J. Wong void
xchk_fblock_xref_set_corrupt(struct xfs_scrub * sc,int whichfork,xfs_fileoff_t offset)276c517b3aaSDarrick J. Wong xchk_fblock_xref_set_corrupt(
2771d8a748aSDarrick J. Wong 	struct xfs_scrub	*sc,
27864b12563SDarrick J. Wong 	int			whichfork,
27964b12563SDarrick J. Wong 	xfs_fileoff_t		offset)
28064b12563SDarrick J. Wong {
28164b12563SDarrick J. Wong 	sc->sm->sm_flags |= XFS_SCRUB_OFLAG_XCORRUPT;
282c517b3aaSDarrick J. Wong 	trace_xchk_fblock_error(sc, whichfork, offset, __return_address);
28364b12563SDarrick J. Wong }
28464b12563SDarrick J. Wong 
2854700d229SDarrick J. Wong /*
2864700d229SDarrick J. Wong  * Warn about inodes that need administrative review but is not
2874700d229SDarrick J. Wong  * incorrect.
2884700d229SDarrick J. Wong  */
2894700d229SDarrick J. Wong void
xchk_ino_set_warning(struct xfs_scrub * sc,xfs_ino_t ino)290c517b3aaSDarrick J. Wong xchk_ino_set_warning(
2911d8a748aSDarrick J. Wong 	struct xfs_scrub	*sc,
2927e56d9eaSDarrick J. Wong 	xfs_ino_t		ino)
2934700d229SDarrick J. Wong {
2944700d229SDarrick J. Wong 	sc->sm->sm_flags |= XFS_SCRUB_OFLAG_WARNING;
295c517b3aaSDarrick J. Wong 	trace_xchk_ino_warning(sc, ino, __return_address);
2964700d229SDarrick J. Wong }
2974700d229SDarrick J. Wong 
2984700d229SDarrick J. Wong /* Warn about a block indexed by a file fork that needs review. */
2994700d229SDarrick J. Wong void
xchk_fblock_set_warning(struct xfs_scrub * sc,int whichfork,xfs_fileoff_t offset)300c517b3aaSDarrick J. Wong xchk_fblock_set_warning(
3011d8a748aSDarrick J. Wong 	struct xfs_scrub	*sc,
3024700d229SDarrick J. Wong 	int			whichfork,
3034700d229SDarrick J. Wong 	xfs_fileoff_t		offset)
3044700d229SDarrick J. Wong {
3054700d229SDarrick J. Wong 	sc->sm->sm_flags |= XFS_SCRUB_OFLAG_WARNING;
306c517b3aaSDarrick J. Wong 	trace_xchk_fblock_warning(sc, whichfork, offset, __return_address);
3074700d229SDarrick J. Wong }
3084700d229SDarrick J. Wong 
3094700d229SDarrick J. Wong /* Signal an incomplete scrub. */
3104700d229SDarrick J. Wong void
xchk_set_incomplete(struct xfs_scrub * sc)311c517b3aaSDarrick J. Wong xchk_set_incomplete(
3121d8a748aSDarrick J. Wong 	struct xfs_scrub	*sc)
3134700d229SDarrick J. Wong {
3144700d229SDarrick J. Wong 	sc->sm->sm_flags |= XFS_SCRUB_OFLAG_INCOMPLETE;
315c517b3aaSDarrick J. Wong 	trace_xchk_incomplete(sc, __return_address);
3164700d229SDarrick J. Wong }
3174700d229SDarrick J. Wong 
318b6c1beb9SDarrick J. Wong /*
319d852657cSDarrick J. Wong  * rmap scrubbing -- compute the number of blocks with a given owner,
320d852657cSDarrick J. Wong  * at least according to the reverse mapping data.
321d852657cSDarrick J. Wong  */
322d852657cSDarrick J. Wong 
323c517b3aaSDarrick J. Wong struct xchk_rmap_ownedby_info {
32466e3237eSDarrick J. Wong 	const struct xfs_owner_info	*oinfo;
325d852657cSDarrick J. Wong 	xfs_filblks_t			*blocks;
326d852657cSDarrick J. Wong };
327d852657cSDarrick J. Wong 
328d852657cSDarrick J. Wong STATIC int
xchk_count_rmap_ownedby_irec(struct xfs_btree_cur * cur,const struct xfs_rmap_irec * rec,void * priv)329c517b3aaSDarrick J. Wong xchk_count_rmap_ownedby_irec(
330d852657cSDarrick J. Wong 	struct xfs_btree_cur		*cur,
331159eb69dSDarrick J. Wong 	const struct xfs_rmap_irec	*rec,
332d852657cSDarrick J. Wong 	void				*priv)
333d852657cSDarrick J. Wong {
334c517b3aaSDarrick J. Wong 	struct xchk_rmap_ownedby_info	*sroi = priv;
335d852657cSDarrick J. Wong 	bool				irec_attr;
336d852657cSDarrick J. Wong 	bool				oinfo_attr;
337d852657cSDarrick J. Wong 
338d852657cSDarrick J. Wong 	irec_attr = rec->rm_flags & XFS_RMAP_ATTR_FORK;
339d852657cSDarrick J. Wong 	oinfo_attr = sroi->oinfo->oi_flags & XFS_OWNER_INFO_ATTR_FORK;
340d852657cSDarrick J. Wong 
341d852657cSDarrick J. Wong 	if (rec->rm_owner != sroi->oinfo->oi_owner)
342d852657cSDarrick J. Wong 		return 0;
343d852657cSDarrick J. Wong 
344d852657cSDarrick J. Wong 	if (XFS_RMAP_NON_INODE_OWNER(rec->rm_owner) || irec_attr == oinfo_attr)
345d852657cSDarrick J. Wong 		(*sroi->blocks) += rec->rm_blockcount;
346d852657cSDarrick J. Wong 
347d852657cSDarrick J. Wong 	return 0;
348d852657cSDarrick J. Wong }
349d852657cSDarrick J. Wong 
350d852657cSDarrick J. Wong /*
351d852657cSDarrick J. Wong  * Calculate the number of blocks the rmap thinks are owned by something.
352d852657cSDarrick J. Wong  * The caller should pass us an rmapbt cursor.
353d852657cSDarrick J. Wong  */
354d852657cSDarrick J. Wong int
xchk_count_rmap_ownedby_ag(struct xfs_scrub * sc,struct xfs_btree_cur * cur,const struct xfs_owner_info * oinfo,xfs_filblks_t * blocks)355c517b3aaSDarrick J. Wong xchk_count_rmap_ownedby_ag(
3561d8a748aSDarrick J. Wong 	struct xfs_scrub		*sc,
357d852657cSDarrick J. Wong 	struct xfs_btree_cur		*cur,
35866e3237eSDarrick J. Wong 	const struct xfs_owner_info	*oinfo,
359d852657cSDarrick J. Wong 	xfs_filblks_t			*blocks)
360d852657cSDarrick J. Wong {
36166e3237eSDarrick J. Wong 	struct xchk_rmap_ownedby_info	sroi = {
36266e3237eSDarrick J. Wong 		.oinfo			= oinfo,
36366e3237eSDarrick J. Wong 		.blocks			= blocks,
36466e3237eSDarrick J. Wong 	};
365d852657cSDarrick J. Wong 
366d852657cSDarrick J. Wong 	*blocks = 0;
367c517b3aaSDarrick J. Wong 	return xfs_rmap_query_all(cur, xchk_count_rmap_ownedby_irec,
368d852657cSDarrick J. Wong 			&sroi);
369d852657cSDarrick J. Wong }
370d852657cSDarrick J. Wong 
371d852657cSDarrick J. Wong /*
372b6c1beb9SDarrick J. Wong  * AG scrubbing
373b6c1beb9SDarrick J. Wong  *
374b6c1beb9SDarrick J. Wong  * These helpers facilitate locking an allocation group's header
375b6c1beb9SDarrick J. Wong  * buffers, setting up cursors for all btrees that are present, and
376b6c1beb9SDarrick J. Wong  * cleaning everything up once we're through.
377b6c1beb9SDarrick J. Wong  */
378b6c1beb9SDarrick J. Wong 
379ab9d5dc5SDarrick J. Wong /* Decide if we want to return an AG header read failure. */
380ab9d5dc5SDarrick J. Wong static inline bool
want_ag_read_header_failure(struct xfs_scrub * sc,unsigned int type)381ab9d5dc5SDarrick J. Wong want_ag_read_header_failure(
3821d8a748aSDarrick J. Wong 	struct xfs_scrub	*sc,
383ab9d5dc5SDarrick J. Wong 	unsigned int		type)
384ab9d5dc5SDarrick J. Wong {
385ab9d5dc5SDarrick J. Wong 	/* Return all AG header read failures when scanning btrees. */
386ab9d5dc5SDarrick J. Wong 	if (sc->sm->sm_type != XFS_SCRUB_TYPE_AGF &&
387a12890aeSDarrick J. Wong 	    sc->sm->sm_type != XFS_SCRUB_TYPE_AGFL &&
388a12890aeSDarrick J. Wong 	    sc->sm->sm_type != XFS_SCRUB_TYPE_AGI)
389ab9d5dc5SDarrick J. Wong 		return true;
390ab9d5dc5SDarrick J. Wong 	/*
391ab9d5dc5SDarrick J. Wong 	 * If we're scanning a given type of AG header, we only want to
392ab9d5dc5SDarrick J. Wong 	 * see read failures from that specific header.  We'd like the
393ab9d5dc5SDarrick J. Wong 	 * other headers to cross-check them, but this isn't required.
394ab9d5dc5SDarrick J. Wong 	 */
395ab9d5dc5SDarrick J. Wong 	if (sc->sm->sm_type == type)
396ab9d5dc5SDarrick J. Wong 		return true;
397ab9d5dc5SDarrick J. Wong 	return false;
398ab9d5dc5SDarrick J. Wong }
399ab9d5dc5SDarrick J. Wong 
400b6c1beb9SDarrick J. Wong /*
401d5c88131SDarrick J. Wong  * Grab the AG header buffers for the attached perag structure.
402b6c1beb9SDarrick J. Wong  *
40348c6615cSDarrick J. Wong  * The headers should be released by xchk_ag_free, but as a fail safe we attach
40448c6615cSDarrick J. Wong  * all the buffers we grab to the scrub transaction so they'll all be freed
405d5c88131SDarrick J. Wong  * when we cancel it.
406b6c1beb9SDarrick J. Wong  */
407d5c88131SDarrick J. Wong static inline int
xchk_perag_read_headers(struct xfs_scrub * sc,struct xchk_ag * sa)408d5c88131SDarrick J. Wong xchk_perag_read_headers(
4091d8a748aSDarrick J. Wong 	struct xfs_scrub	*sc,
410de9d2a78SDarrick J. Wong 	struct xchk_ag		*sa)
411b6c1beb9SDarrick J. Wong {
412b6c1beb9SDarrick J. Wong 	int			error;
413b6c1beb9SDarrick J. Wong 
41499b13c7fSDave Chinner 	error = xfs_ialloc_read_agi(sa->pag, sc->tp, &sa->agi_bp);
415a12890aeSDarrick J. Wong 	if (error && want_ag_read_header_failure(sc, XFS_SCRUB_TYPE_AGI))
41648c6615cSDarrick J. Wong 		return error;
417b6c1beb9SDarrick J. Wong 
41808d3e84fSDave Chinner 	error = xfs_alloc_read_agf(sa->pag, sc->tp, 0, &sa->agf_bp);
419ab9d5dc5SDarrick J. Wong 	if (error && want_ag_read_header_failure(sc, XFS_SCRUB_TYPE_AGF))
42048c6615cSDarrick J. Wong 		return error;
421b6c1beb9SDarrick J. Wong 
42248c6615cSDarrick J. Wong 	return 0;
423b6c1beb9SDarrick J. Wong }
424b6c1beb9SDarrick J. Wong 
425d5c88131SDarrick J. Wong /*
426d5c88131SDarrick J. Wong  * Grab the AG headers for the attached perag structure and wait for pending
427d5c88131SDarrick J. Wong  * intents to drain.
428d5c88131SDarrick J. Wong  */
429d5c88131SDarrick J. Wong static int
xchk_perag_drain_and_lock(struct xfs_scrub * sc)430d5c88131SDarrick J. Wong xchk_perag_drain_and_lock(
431d5c88131SDarrick J. Wong 	struct xfs_scrub	*sc)
432d5c88131SDarrick J. Wong {
433d5c88131SDarrick J. Wong 	struct xchk_ag		*sa = &sc->sa;
434d5c88131SDarrick J. Wong 	int			error = 0;
435d5c88131SDarrick J. Wong 
436d5c88131SDarrick J. Wong 	ASSERT(sa->pag != NULL);
437d5c88131SDarrick J. Wong 	ASSERT(sa->agi_bp == NULL);
438d5c88131SDarrick J. Wong 	ASSERT(sa->agf_bp == NULL);
439d5c88131SDarrick J. Wong 
440d5c88131SDarrick J. Wong 	do {
441d5c88131SDarrick J. Wong 		if (xchk_should_terminate(sc, &error))
442d5c88131SDarrick J. Wong 			return error;
443d5c88131SDarrick J. Wong 
444d5c88131SDarrick J. Wong 		error = xchk_perag_read_headers(sc, sa);
445d5c88131SDarrick J. Wong 		if (error)
446d5c88131SDarrick J. Wong 			return error;
447d5c88131SDarrick J. Wong 
448d5c88131SDarrick J. Wong 		/*
449d5c88131SDarrick J. Wong 		 * If we've grabbed an inode for scrubbing then we assume that
450d5c88131SDarrick J. Wong 		 * holding its ILOCK will suffice to coordinate with any intent
451d5c88131SDarrick J. Wong 		 * chains involving this inode.
452d5c88131SDarrick J. Wong 		 */
453d5c88131SDarrick J. Wong 		if (sc->ip)
454d5c88131SDarrick J. Wong 			return 0;
455d5c88131SDarrick J. Wong 
456d5c88131SDarrick J. Wong 		/*
457d5c88131SDarrick J. Wong 		 * Decide if this AG is quiet enough for all metadata to be
458d5c88131SDarrick J. Wong 		 * consistent with each other.  XFS allows the AG header buffer
459d5c88131SDarrick J. Wong 		 * locks to cycle across transaction rolls while processing
460d5c88131SDarrick J. Wong 		 * chains of deferred ops, which means that there could be
461d5c88131SDarrick J. Wong 		 * other threads in the middle of processing a chain of
462d5c88131SDarrick J. Wong 		 * deferred ops.  For regular operations we are careful about
463d5c88131SDarrick J. Wong 		 * ordering operations to prevent collisions between threads
464d5c88131SDarrick J. Wong 		 * (which is why we don't need a per-AG lock), but scrub and
465d5c88131SDarrick J. Wong 		 * repair have to serialize against chained operations.
466d5c88131SDarrick J. Wong 		 *
467d5c88131SDarrick J. Wong 		 * We just locked all the AG headers buffers; now take a look
468d5c88131SDarrick J. Wong 		 * to see if there are any intents in progress.  If there are,
469d5c88131SDarrick J. Wong 		 * drop the AG headers and wait for the intents to drain.
470d5c88131SDarrick J. Wong 		 * Since we hold all the AG header locks for the duration of
471d5c88131SDarrick J. Wong 		 * the scrub, this is the only time we have to sample the
472d5c88131SDarrick J. Wong 		 * intents counter; any threads increasing it after this point
473d5c88131SDarrick J. Wong 		 * can't possibly be in the middle of a chain of AG metadata
474d5c88131SDarrick J. Wong 		 * updates.
475d5c88131SDarrick J. Wong 		 *
476d5c88131SDarrick J. Wong 		 * Obviously, this should be slanted against scrub and in favor
477d5c88131SDarrick J. Wong 		 * of runtime threads.
478d5c88131SDarrick J. Wong 		 */
479d5c88131SDarrick J. Wong 		if (!xfs_perag_intent_busy(sa->pag))
480d5c88131SDarrick J. Wong 			return 0;
481d5c88131SDarrick J. Wong 
482d5c88131SDarrick J. Wong 		if (sa->agf_bp) {
483d5c88131SDarrick J. Wong 			xfs_trans_brelse(sc->tp, sa->agf_bp);
484d5c88131SDarrick J. Wong 			sa->agf_bp = NULL;
485d5c88131SDarrick J. Wong 		}
486d5c88131SDarrick J. Wong 
487d5c88131SDarrick J. Wong 		if (sa->agi_bp) {
488d5c88131SDarrick J. Wong 			xfs_trans_brelse(sc->tp, sa->agi_bp);
489d5c88131SDarrick J. Wong 			sa->agi_bp = NULL;
490d5c88131SDarrick J. Wong 		}
491d5c88131SDarrick J. Wong 
492466c525dSDarrick J. Wong 		if (!(sc->flags & XCHK_FSGATES_DRAIN))
49388accf17SDarrick J. Wong 			return -ECHRNG;
494d5c88131SDarrick J. Wong 		error = xfs_perag_intent_drain(sa->pag);
495d5c88131SDarrick J. Wong 		if (error == -ERESTARTSYS)
496d5c88131SDarrick J. Wong 			error = -EINTR;
497d5c88131SDarrick J. Wong 	} while (!error);
498d5c88131SDarrick J. Wong 
499d5c88131SDarrick J. Wong 	return error;
500d5c88131SDarrick J. Wong }
501d5c88131SDarrick J. Wong 
502d5c88131SDarrick J. Wong /*
503d5c88131SDarrick J. Wong  * Grab the per-AG structure, grab all AG header buffers, and wait until there
504d5c88131SDarrick J. Wong  * aren't any pending intents.  Returns -ENOENT if we can't grab the perag
505d5c88131SDarrick J. Wong  * structure.
506d5c88131SDarrick J. Wong  */
507d5c88131SDarrick J. Wong int
xchk_ag_read_headers(struct xfs_scrub * sc,xfs_agnumber_t agno,struct xchk_ag * sa)508d5c88131SDarrick J. Wong xchk_ag_read_headers(
509d5c88131SDarrick J. Wong 	struct xfs_scrub	*sc,
510d5c88131SDarrick J. Wong 	xfs_agnumber_t		agno,
511d5c88131SDarrick J. Wong 	struct xchk_ag		*sa)
512d5c88131SDarrick J. Wong {
513d5c88131SDarrick J. Wong 	struct xfs_mount	*mp = sc->mp;
514d5c88131SDarrick J. Wong 
515d5c88131SDarrick J. Wong 	ASSERT(!sa->pag);
516d5c88131SDarrick J. Wong 	sa->pag = xfs_perag_get(mp, agno);
517d5c88131SDarrick J. Wong 	if (!sa->pag)
518d5c88131SDarrick J. Wong 		return -ENOENT;
519d5c88131SDarrick J. Wong 
520d5c88131SDarrick J. Wong 	return xchk_perag_drain_and_lock(sc);
521d5c88131SDarrick J. Wong }
522d5c88131SDarrick J. Wong 
523b6c1beb9SDarrick J. Wong /* Release all the AG btree cursors. */
524b6c1beb9SDarrick J. Wong void
xchk_ag_btcur_free(struct xchk_ag * sa)525c517b3aaSDarrick J. Wong xchk_ag_btcur_free(
526c517b3aaSDarrick J. Wong 	struct xchk_ag		*sa)
527b6c1beb9SDarrick J. Wong {
528b6c1beb9SDarrick J. Wong 	if (sa->refc_cur)
529b6c1beb9SDarrick J. Wong 		xfs_btree_del_cursor(sa->refc_cur, XFS_BTREE_ERROR);
530b6c1beb9SDarrick J. Wong 	if (sa->rmap_cur)
531b6c1beb9SDarrick J. Wong 		xfs_btree_del_cursor(sa->rmap_cur, XFS_BTREE_ERROR);
532b6c1beb9SDarrick J. Wong 	if (sa->fino_cur)
533b6c1beb9SDarrick J. Wong 		xfs_btree_del_cursor(sa->fino_cur, XFS_BTREE_ERROR);
534b6c1beb9SDarrick J. Wong 	if (sa->ino_cur)
535b6c1beb9SDarrick J. Wong 		xfs_btree_del_cursor(sa->ino_cur, XFS_BTREE_ERROR);
536b6c1beb9SDarrick J. Wong 	if (sa->cnt_cur)
537b6c1beb9SDarrick J. Wong 		xfs_btree_del_cursor(sa->cnt_cur, XFS_BTREE_ERROR);
538b6c1beb9SDarrick J. Wong 	if (sa->bno_cur)
539b6c1beb9SDarrick J. Wong 		xfs_btree_del_cursor(sa->bno_cur, XFS_BTREE_ERROR);
540b6c1beb9SDarrick J. Wong 
541b6c1beb9SDarrick J. Wong 	sa->refc_cur = NULL;
542b6c1beb9SDarrick J. Wong 	sa->rmap_cur = NULL;
543b6c1beb9SDarrick J. Wong 	sa->fino_cur = NULL;
544b6c1beb9SDarrick J. Wong 	sa->ino_cur = NULL;
545b6c1beb9SDarrick J. Wong 	sa->bno_cur = NULL;
546b6c1beb9SDarrick J. Wong 	sa->cnt_cur = NULL;
547b6c1beb9SDarrick J. Wong }
548b6c1beb9SDarrick J. Wong 
549b6c1beb9SDarrick J. Wong /* Initialize all the btree cursors for an AG. */
550f53acfacSDarrick J. Wong void
xchk_ag_btcur_init(struct xfs_scrub * sc,struct xchk_ag * sa)551c517b3aaSDarrick J. Wong xchk_ag_btcur_init(
5521d8a748aSDarrick J. Wong 	struct xfs_scrub	*sc,
553c517b3aaSDarrick J. Wong 	struct xchk_ag		*sa)
554b6c1beb9SDarrick J. Wong {
555b6c1beb9SDarrick J. Wong 	struct xfs_mount	*mp = sc->mp;
556b6c1beb9SDarrick J. Wong 
5574fb7951fSDarrick J. Wong 	if (sa->agf_bp &&
5584fb7951fSDarrick J. Wong 	    xchk_ag_btree_healthy_enough(sc, sa->pag, XFS_BTNUM_BNO)) {
559b6c1beb9SDarrick J. Wong 		/* Set up a bnobt cursor for cross-referencing. */
560b6c1beb9SDarrick J. Wong 		sa->bno_cur = xfs_allocbt_init_cursor(mp, sc->tp, sa->agf_bp,
561289d38d2SDave Chinner 				sa->pag, XFS_BTNUM_BNO);
5624fb7951fSDarrick J. Wong 	}
563b6c1beb9SDarrick J. Wong 
5644fb7951fSDarrick J. Wong 	if (sa->agf_bp &&
5654fb7951fSDarrick J. Wong 	    xchk_ag_btree_healthy_enough(sc, sa->pag, XFS_BTNUM_CNT)) {
566b6c1beb9SDarrick J. Wong 		/* Set up a cntbt cursor for cross-referencing. */
567b6c1beb9SDarrick J. Wong 		sa->cnt_cur = xfs_allocbt_init_cursor(mp, sc->tp, sa->agf_bp,
568289d38d2SDave Chinner 				sa->pag, XFS_BTNUM_CNT);
569b6c1beb9SDarrick J. Wong 	}
570b6c1beb9SDarrick J. Wong 
571b6c1beb9SDarrick J. Wong 	/* Set up a inobt cursor for cross-referencing. */
5724fb7951fSDarrick J. Wong 	if (sa->agi_bp &&
5734fb7951fSDarrick J. Wong 	    xchk_ag_btree_healthy_enough(sc, sa->pag, XFS_BTNUM_INO)) {
574bab8b795SDave Chinner 		sa->ino_cur = xfs_inobt_init_cursor(sa->pag, sc->tp, sa->agi_bp,
575bab8b795SDave Chinner 				XFS_BTNUM_INO);
576b6c1beb9SDarrick J. Wong 	}
577b6c1beb9SDarrick J. Wong 
578b6c1beb9SDarrick J. Wong 	/* Set up a finobt cursor for cross-referencing. */
579ebd9027dSDave Chinner 	if (sa->agi_bp && xfs_has_finobt(mp) &&
5804fb7951fSDarrick J. Wong 	    xchk_ag_btree_healthy_enough(sc, sa->pag, XFS_BTNUM_FINO)) {
581bab8b795SDave Chinner 		sa->fino_cur = xfs_inobt_init_cursor(sa->pag, sc->tp, sa->agi_bp,
582bab8b795SDave Chinner 				XFS_BTNUM_FINO);
583b6c1beb9SDarrick J. Wong 	}
584b6c1beb9SDarrick J. Wong 
585b6c1beb9SDarrick J. Wong 	/* Set up a rmapbt cursor for cross-referencing. */
586ebd9027dSDave Chinner 	if (sa->agf_bp && xfs_has_rmapbt(mp) &&
5874fb7951fSDarrick J. Wong 	    xchk_ag_btree_healthy_enough(sc, sa->pag, XFS_BTNUM_RMAP)) {
588b6c1beb9SDarrick J. Wong 		sa->rmap_cur = xfs_rmapbt_init_cursor(mp, sc->tp, sa->agf_bp,
589fa9c3c19SDave Chinner 				sa->pag);
590b6c1beb9SDarrick J. Wong 	}
591b6c1beb9SDarrick J. Wong 
592b6c1beb9SDarrick J. Wong 	/* Set up a refcountbt cursor for cross-referencing. */
593ebd9027dSDave Chinner 	if (sa->agf_bp && xfs_has_reflink(mp) &&
5944fb7951fSDarrick J. Wong 	    xchk_ag_btree_healthy_enough(sc, sa->pag, XFS_BTNUM_REFC)) {
595b6c1beb9SDarrick J. Wong 		sa->refc_cur = xfs_refcountbt_init_cursor(mp, sc->tp,
596a81a0621SDave Chinner 				sa->agf_bp, sa->pag);
597b6c1beb9SDarrick J. Wong 	}
598b6c1beb9SDarrick J. Wong }
599b6c1beb9SDarrick J. Wong 
600b6c1beb9SDarrick J. Wong /* Release the AG header context and btree cursors. */
601b6c1beb9SDarrick J. Wong void
xchk_ag_free(struct xfs_scrub * sc,struct xchk_ag * sa)602c517b3aaSDarrick J. Wong xchk_ag_free(
6031d8a748aSDarrick J. Wong 	struct xfs_scrub	*sc,
604c517b3aaSDarrick J. Wong 	struct xchk_ag		*sa)
605b6c1beb9SDarrick J. Wong {
606c517b3aaSDarrick J. Wong 	xchk_ag_btcur_free(sa);
607b6c1beb9SDarrick J. Wong 	if (sa->agf_bp) {
608b6c1beb9SDarrick J. Wong 		xfs_trans_brelse(sc->tp, sa->agf_bp);
609b6c1beb9SDarrick J. Wong 		sa->agf_bp = NULL;
610b6c1beb9SDarrick J. Wong 	}
611b6c1beb9SDarrick J. Wong 	if (sa->agi_bp) {
612b6c1beb9SDarrick J. Wong 		xfs_trans_brelse(sc->tp, sa->agi_bp);
613b6c1beb9SDarrick J. Wong 		sa->agi_bp = NULL;
614b6c1beb9SDarrick J. Wong 	}
61551863d7dSDarrick J. Wong 	if (sa->pag) {
61651863d7dSDarrick J. Wong 		xfs_perag_put(sa->pag);
61751863d7dSDarrick J. Wong 		sa->pag = NULL;
61851863d7dSDarrick J. Wong 	}
619b6c1beb9SDarrick J. Wong }
620b6c1beb9SDarrick J. Wong 
621b6c1beb9SDarrick J. Wong /*
62248c6615cSDarrick J. Wong  * For scrub, grab the perag structure, the AGI, and the AGF headers, in that
62348c6615cSDarrick J. Wong  * order.  Locking order requires us to get the AGI before the AGF.  We use the
62448c6615cSDarrick J. Wong  * transaction to avoid deadlocking on crosslinked metadata buffers; either the
62548c6615cSDarrick J. Wong  * caller passes one in (bmap scrub) or we have to create a transaction
62648c6615cSDarrick J. Wong  * ourselves.  Returns ENOENT if the perag struct cannot be grabbed.
627b6c1beb9SDarrick J. Wong  */
628b6c1beb9SDarrick J. Wong int
xchk_ag_init(struct xfs_scrub * sc,xfs_agnumber_t agno,struct xchk_ag * sa)629c517b3aaSDarrick J. Wong xchk_ag_init(
6301d8a748aSDarrick J. Wong 	struct xfs_scrub	*sc,
631b6c1beb9SDarrick J. Wong 	xfs_agnumber_t		agno,
632c517b3aaSDarrick J. Wong 	struct xchk_ag		*sa)
633b6c1beb9SDarrick J. Wong {
634b6c1beb9SDarrick J. Wong 	int			error;
635b6c1beb9SDarrick J. Wong 
636de9d2a78SDarrick J. Wong 	error = xchk_ag_read_headers(sc, agno, sa);
637b6c1beb9SDarrick J. Wong 	if (error)
638b6c1beb9SDarrick J. Wong 		return error;
639b6c1beb9SDarrick J. Wong 
640f53acfacSDarrick J. Wong 	xchk_ag_btcur_init(sc, sa);
641f53acfacSDarrick J. Wong 	return 0;
642b6c1beb9SDarrick J. Wong }
643b6c1beb9SDarrick J. Wong 
644dcb660f9SDarrick J. Wong /* Per-scrubber setup functions */
645dcb660f9SDarrick J. Wong 
646302436c2SDarrick J. Wong void
xchk_trans_cancel(struct xfs_scrub * sc)647302436c2SDarrick J. Wong xchk_trans_cancel(
648302436c2SDarrick J. Wong 	struct xfs_scrub	*sc)
649302436c2SDarrick J. Wong {
650302436c2SDarrick J. Wong 	xfs_trans_cancel(sc->tp);
651302436c2SDarrick J. Wong 	sc->tp = NULL;
652302436c2SDarrick J. Wong }
653302436c2SDarrick J. Wong 
6549d9c9028SDarrick J. Wong /*
6559d9c9028SDarrick J. Wong  * Grab an empty transaction so that we can re-grab locked buffers if
6569d9c9028SDarrick J. Wong  * one of our btrees turns out to be cyclic.
6570a9633faSDarrick J. Wong  *
6580a9633faSDarrick J. Wong  * If we're going to repair something, we need to ask for the largest possible
6590a9633faSDarrick J. Wong  * log reservation so that we can handle the worst case scenario for metadata
6600a9633faSDarrick J. Wong  * updates while rebuilding a metadata item.  We also need to reserve as many
6610a9633faSDarrick J. Wong  * blocks in the head transaction as we think we're going to need to rebuild
6620a9633faSDarrick J. Wong  * the metadata object.
6639d9c9028SDarrick J. Wong  */
6649d9c9028SDarrick J. Wong int
xchk_trans_alloc(struct xfs_scrub * sc,uint resblks)665c517b3aaSDarrick J. Wong xchk_trans_alloc(
6661d8a748aSDarrick J. Wong 	struct xfs_scrub	*sc,
6670a9633faSDarrick J. Wong 	uint			resblks)
6689d9c9028SDarrick J. Wong {
6690a9633faSDarrick J. Wong 	if (sc->sm->sm_flags & XFS_SCRUB_IFLAG_REPAIR)
6700a9633faSDarrick J. Wong 		return xfs_trans_alloc(sc->mp, &M_RES(sc->mp)->tr_itruncate,
6710a9633faSDarrick J. Wong 				resblks, 0, 0, &sc->tp);
6720a9633faSDarrick J. Wong 
6739d9c9028SDarrick J. Wong 	return xfs_trans_alloc_empty(sc->mp, &sc->tp);
6749d9c9028SDarrick J. Wong }
6759d9c9028SDarrick J. Wong 
676dcb660f9SDarrick J. Wong /* Set us up with a transaction and an empty context. */
677dcb660f9SDarrick J. Wong int
xchk_setup_fs(struct xfs_scrub * sc)678c517b3aaSDarrick J. Wong xchk_setup_fs(
679026f57ebSDarrick J. Wong 	struct xfs_scrub	*sc)
680dcb660f9SDarrick J. Wong {
6810a9633faSDarrick J. Wong 	uint			resblks;
6820a9633faSDarrick J. Wong 
683b5e2196eSDarrick J. Wong 	resblks = xrep_calc_ag_resblks(sc);
684c517b3aaSDarrick J. Wong 	return xchk_trans_alloc(sc, resblks);
685dcb660f9SDarrick J. Wong }
686efa7a99cSDarrick J. Wong 
687efa7a99cSDarrick J. Wong /* Set us up with AG headers and btree cursors. */
688efa7a99cSDarrick J. Wong int
xchk_setup_ag_btree(struct xfs_scrub * sc,bool force_log)689c517b3aaSDarrick J. Wong xchk_setup_ag_btree(
6901d8a748aSDarrick J. Wong 	struct xfs_scrub	*sc,
691efa7a99cSDarrick J. Wong 	bool			force_log)
692efa7a99cSDarrick J. Wong {
6933daa6641SDarrick J. Wong 	struct xfs_mount	*mp = sc->mp;
694efa7a99cSDarrick J. Wong 	int			error;
695efa7a99cSDarrick J. Wong 
6963daa6641SDarrick J. Wong 	/*
6973daa6641SDarrick J. Wong 	 * If the caller asks us to checkpont the log, do so.  This
6983daa6641SDarrick J. Wong 	 * expensive operation should be performed infrequently and only
6993daa6641SDarrick J. Wong 	 * as a last resort.  Any caller that sets force_log should
7003daa6641SDarrick J. Wong 	 * document why they need to do so.
7013daa6641SDarrick J. Wong 	 */
7023daa6641SDarrick J. Wong 	if (force_log) {
703c517b3aaSDarrick J. Wong 		error = xchk_checkpoint_log(mp);
7043daa6641SDarrick J. Wong 		if (error)
7053daa6641SDarrick J. Wong 			return error;
7063daa6641SDarrick J. Wong 	}
7073daa6641SDarrick J. Wong 
708026f57ebSDarrick J. Wong 	error = xchk_setup_fs(sc);
709efa7a99cSDarrick J. Wong 	if (error)
710efa7a99cSDarrick J. Wong 		return error;
711efa7a99cSDarrick J. Wong 
712c517b3aaSDarrick J. Wong 	return xchk_ag_init(sc, sc->sm->sm_agno, &sc->sa);
713efa7a99cSDarrick J. Wong }
7143daa6641SDarrick J. Wong 
7153daa6641SDarrick J. Wong /* Push everything out of the log onto disk. */
7163daa6641SDarrick J. Wong int
xchk_checkpoint_log(struct xfs_mount * mp)717c517b3aaSDarrick J. Wong xchk_checkpoint_log(
7183daa6641SDarrick J. Wong 	struct xfs_mount	*mp)
7193daa6641SDarrick J. Wong {
7203daa6641SDarrick J. Wong 	int			error;
7213daa6641SDarrick J. Wong 
72260e5bb78SChristoph Hellwig 	error = xfs_log_force(mp, XFS_LOG_SYNC);
7233daa6641SDarrick J. Wong 	if (error)
7243daa6641SDarrick J. Wong 		return error;
7253daa6641SDarrick J. Wong 	xfs_ail_push_all_sync(mp->m_ail);
7263daa6641SDarrick J. Wong 	return 0;
7273daa6641SDarrick J. Wong }
72880e4e126SDarrick J. Wong 
729a03297a0SDarrick J. Wong /* Verify that an inode is allocated ondisk, then return its cached inode. */
730a03297a0SDarrick J. Wong int
xchk_iget(struct xfs_scrub * sc,xfs_ino_t inum,struct xfs_inode ** ipp)731a03297a0SDarrick J. Wong xchk_iget(
732a03297a0SDarrick J. Wong 	struct xfs_scrub	*sc,
733a03297a0SDarrick J. Wong 	xfs_ino_t		inum,
734a03297a0SDarrick J. Wong 	struct xfs_inode	**ipp)
735a03297a0SDarrick J. Wong {
736fb6e584eSDarrick J. Wong 	ASSERT(sc->tp != NULL);
737fb6e584eSDarrick J. Wong 
738a03297a0SDarrick J. Wong 	return xfs_iget(sc->mp, sc->tp, inum, XFS_IGET_UNTRUSTED, 0, ipp);
739a03297a0SDarrick J. Wong }
740a03297a0SDarrick J. Wong 
74180e4e126SDarrick J. Wong /*
742302436c2SDarrick J. Wong  * Try to grab an inode in a manner that avoids races with physical inode
743302436c2SDarrick J. Wong  * allocation.  If we can't, return the locked AGI buffer so that the caller
744302436c2SDarrick J. Wong  * can single-step the loading process to see where things went wrong.
745302436c2SDarrick J. Wong  * Callers must have a valid scrub transaction.
746302436c2SDarrick J. Wong  *
747302436c2SDarrick J. Wong  * If the iget succeeds, return 0, a NULL AGI, and the inode.
748302436c2SDarrick J. Wong  *
749302436c2SDarrick J. Wong  * If the iget fails, return the error, the locked AGI, and a NULL inode.  This
750302436c2SDarrick J. Wong  * can include -EINVAL and -ENOENT for invalid inode numbers or inodes that are
751302436c2SDarrick J. Wong  * no longer allocated; or any other corruption or runtime error.
752302436c2SDarrick J. Wong  *
753302436c2SDarrick J. Wong  * If the AGI read fails, return the error, a NULL AGI, and NULL inode.
754302436c2SDarrick J. Wong  *
755302436c2SDarrick J. Wong  * If a fatal signal is pending, return -EINTR, a NULL AGI, and a NULL inode.
756302436c2SDarrick J. Wong  */
757302436c2SDarrick J. Wong int
xchk_iget_agi(struct xfs_scrub * sc,xfs_ino_t inum,struct xfs_buf ** agi_bpp,struct xfs_inode ** ipp)758302436c2SDarrick J. Wong xchk_iget_agi(
759302436c2SDarrick J. Wong 	struct xfs_scrub	*sc,
760302436c2SDarrick J. Wong 	xfs_ino_t		inum,
761302436c2SDarrick J. Wong 	struct xfs_buf		**agi_bpp,
762302436c2SDarrick J. Wong 	struct xfs_inode	**ipp)
763302436c2SDarrick J. Wong {
764302436c2SDarrick J. Wong 	struct xfs_mount	*mp = sc->mp;
765302436c2SDarrick J. Wong 	struct xfs_trans	*tp = sc->tp;
766302436c2SDarrick J. Wong 	struct xfs_perag	*pag;
767302436c2SDarrick J. Wong 	int			error;
768302436c2SDarrick J. Wong 
769302436c2SDarrick J. Wong 	ASSERT(sc->tp != NULL);
770302436c2SDarrick J. Wong 
771302436c2SDarrick J. Wong again:
772302436c2SDarrick J. Wong 	*agi_bpp = NULL;
773302436c2SDarrick J. Wong 	*ipp = NULL;
774302436c2SDarrick J. Wong 	error = 0;
775302436c2SDarrick J. Wong 
776302436c2SDarrick J. Wong 	if (xchk_should_terminate(sc, &error))
777302436c2SDarrick J. Wong 		return error;
778302436c2SDarrick J. Wong 
779302436c2SDarrick J. Wong 	/*
780302436c2SDarrick J. Wong 	 * Attach the AGI buffer to the scrub transaction to avoid deadlocks
781302436c2SDarrick J. Wong 	 * in the iget cache miss path.
782302436c2SDarrick J. Wong 	 */
783302436c2SDarrick J. Wong 	pag = xfs_perag_get(mp, XFS_INO_TO_AGNO(mp, inum));
784302436c2SDarrick J. Wong 	error = xfs_ialloc_read_agi(pag, tp, agi_bpp);
785302436c2SDarrick J. Wong 	xfs_perag_put(pag);
786302436c2SDarrick J. Wong 	if (error)
787302436c2SDarrick J. Wong 		return error;
788302436c2SDarrick J. Wong 
789302436c2SDarrick J. Wong 	error = xfs_iget(mp, tp, inum,
790302436c2SDarrick J. Wong 			XFS_IGET_NORETRY | XFS_IGET_UNTRUSTED, 0, ipp);
791302436c2SDarrick J. Wong 	if (error == -EAGAIN) {
792302436c2SDarrick J. Wong 		/*
793302436c2SDarrick J. Wong 		 * The inode may be in core but temporarily unavailable and may
794302436c2SDarrick J. Wong 		 * require the AGI buffer before it can be returned.  Drop the
795302436c2SDarrick J. Wong 		 * AGI buffer and retry the lookup.
796302436c2SDarrick J. Wong 		 *
797302436c2SDarrick J. Wong 		 * Incore lookup will fail with EAGAIN on a cache hit if the
798302436c2SDarrick J. Wong 		 * inode is queued to the inactivation list.  The inactivation
799302436c2SDarrick J. Wong 		 * worker may remove the inode from the unlinked list and hence
800302436c2SDarrick J. Wong 		 * needs the AGI.
801302436c2SDarrick J. Wong 		 *
802302436c2SDarrick J. Wong 		 * Hence xchk_iget_agi() needs to drop the AGI lock on EAGAIN
803302436c2SDarrick J. Wong 		 * to allow inodegc to make progress and move the inode to
804302436c2SDarrick J. Wong 		 * IRECLAIMABLE state where xfs_iget will be able to return it
805302436c2SDarrick J. Wong 		 * again if it can lock the inode.
806302436c2SDarrick J. Wong 		 */
807302436c2SDarrick J. Wong 		xfs_trans_brelse(tp, *agi_bpp);
808302436c2SDarrick J. Wong 		delay(1);
809302436c2SDarrick J. Wong 		goto again;
810302436c2SDarrick J. Wong 	}
811302436c2SDarrick J. Wong 	if (error)
812302436c2SDarrick J. Wong 		return error;
813302436c2SDarrick J. Wong 
814302436c2SDarrick J. Wong 	/* We got the inode, so we can release the AGI. */
815302436c2SDarrick J. Wong 	ASSERT(*ipp != NULL);
816302436c2SDarrick J. Wong 	xfs_trans_brelse(tp, *agi_bpp);
817302436c2SDarrick J. Wong 	*agi_bpp = NULL;
818302436c2SDarrick J. Wong 	return 0;
819302436c2SDarrick J. Wong }
820302436c2SDarrick J. Wong 
821302436c2SDarrick J. Wong /* Install an inode that we opened by handle for scrubbing. */
82238bb1310SDarrick J. Wong int
xchk_install_handle_inode(struct xfs_scrub * sc,struct xfs_inode * ip)823302436c2SDarrick J. Wong xchk_install_handle_inode(
824302436c2SDarrick J. Wong 	struct xfs_scrub	*sc,
825302436c2SDarrick J. Wong 	struct xfs_inode	*ip)
826302436c2SDarrick J. Wong {
827302436c2SDarrick J. Wong 	if (VFS_I(ip)->i_generation != sc->sm->sm_gen) {
828302436c2SDarrick J. Wong 		xchk_irele(sc, ip);
829302436c2SDarrick J. Wong 		return -ENOENT;
830302436c2SDarrick J. Wong 	}
831302436c2SDarrick J. Wong 
832302436c2SDarrick J. Wong 	sc->ip = ip;
833302436c2SDarrick J. Wong 	return 0;
834302436c2SDarrick J. Wong }
835302436c2SDarrick J. Wong 
836302436c2SDarrick J. Wong /*
83717308539SDarrick J. Wong  * Install an already-referenced inode for scrubbing.  Get our own reference to
83817308539SDarrick J. Wong  * the inode to make disposal simpler.  The inode must not be in I_FREEING or
83917308539SDarrick J. Wong  * I_WILL_FREE state!
84017308539SDarrick J. Wong  */
84117308539SDarrick J. Wong int
xchk_install_live_inode(struct xfs_scrub * sc,struct xfs_inode * ip)84217308539SDarrick J. Wong xchk_install_live_inode(
84317308539SDarrick J. Wong 	struct xfs_scrub	*sc,
84417308539SDarrick J. Wong 	struct xfs_inode	*ip)
84517308539SDarrick J. Wong {
84617308539SDarrick J. Wong 	if (!igrab(VFS_I(ip))) {
84717308539SDarrick J. Wong 		xchk_ino_set_corrupt(sc, ip->i_ino);
84817308539SDarrick J. Wong 		return -EFSCORRUPTED;
84917308539SDarrick J. Wong 	}
85017308539SDarrick J. Wong 
85117308539SDarrick J. Wong 	sc->ip = ip;
85217308539SDarrick J. Wong 	return 0;
85317308539SDarrick J. Wong }
85417308539SDarrick J. Wong 
85517308539SDarrick J. Wong /*
85646e0dd89SDarrick J. Wong  * In preparation to scrub metadata structures that hang off of an inode,
85746e0dd89SDarrick J. Wong  * grab either the inode referenced in the scrub control structure or the
85846e0dd89SDarrick J. Wong  * inode passed in.  If the inumber does not reference an allocated inode
85946e0dd89SDarrick J. Wong  * record, the function returns ENOENT to end the scrub early.  The inode
86046e0dd89SDarrick J. Wong  * is not locked.
86180e4e126SDarrick J. Wong  */
86280e4e126SDarrick J. Wong int
xchk_iget_for_scrubbing(struct xfs_scrub * sc)86346e0dd89SDarrick J. Wong xchk_iget_for_scrubbing(
864026f57ebSDarrick J. Wong 	struct xfs_scrub	*sc)
86580e4e126SDarrick J. Wong {
866d658e72bSDarrick J. Wong 	struct xfs_imap		imap;
86780e4e126SDarrick J. Wong 	struct xfs_mount	*mp = sc->mp;
868498f0adbSDave Chinner 	struct xfs_perag	*pag;
869302436c2SDarrick J. Wong 	struct xfs_buf		*agi_bp;
870026f57ebSDarrick J. Wong 	struct xfs_inode	*ip_in = XFS_I(file_inode(sc->file));
87180e4e126SDarrick J. Wong 	struct xfs_inode	*ip = NULL;
872302436c2SDarrick J. Wong 	xfs_agnumber_t		agno = XFS_INO_TO_AGNO(mp, sc->sm->sm_ino);
87380e4e126SDarrick J. Wong 	int			error;
87480e4e126SDarrick J. Wong 
875302436c2SDarrick J. Wong 	ASSERT(sc->tp == NULL);
876302436c2SDarrick J. Wong 
87780e4e126SDarrick J. Wong 	/* We want to scan the inode we already had opened. */
87817308539SDarrick J. Wong 	if (sc->sm->sm_ino == 0 || sc->sm->sm_ino == ip_in->i_ino)
87917308539SDarrick J. Wong 		return xchk_install_live_inode(sc, ip_in);
88080e4e126SDarrick J. Wong 
881302436c2SDarrick J. Wong 	/* Reject internal metadata files and obviously bad inode numbers. */
88280e4e126SDarrick J. Wong 	if (xfs_internal_inum(mp, sc->sm->sm_ino))
88380e4e126SDarrick J. Wong 		return -ENOENT;
884302436c2SDarrick J. Wong 	if (!xfs_verify_ino(sc->mp, sc->sm->sm_ino))
885302436c2SDarrick J. Wong 		return -ENOENT;
886302436c2SDarrick J. Wong 
887fb6e584eSDarrick J. Wong 	/* Try a safe untrusted iget. */
888fb6e584eSDarrick J. Wong 	error = xchk_iget_safe(sc, sc->sm->sm_ino, &ip);
889302436c2SDarrick J. Wong 	if (!error)
890302436c2SDarrick J. Wong 		return xchk_install_handle_inode(sc, ip);
891302436c2SDarrick J. Wong 	if (error == -ENOENT)
892d658e72bSDarrick J. Wong 		return error;
893302436c2SDarrick J. Wong 	if (error != -EINVAL)
894302436c2SDarrick J. Wong 		goto out_error;
895302436c2SDarrick J. Wong 
896d658e72bSDarrick J. Wong 	/*
897302436c2SDarrick J. Wong 	 * EINVAL with IGET_UNTRUSTED probably means one of several things:
898302436c2SDarrick J. Wong 	 * userspace gave us an inode number that doesn't correspond to fs
899302436c2SDarrick J. Wong 	 * space; the inode btree lacks a record for this inode; or there is a
900302436c2SDarrick J. Wong 	 * record, and it says this inode is free.
901d658e72bSDarrick J. Wong 	 *
902302436c2SDarrick J. Wong 	 * We want to look up this inode in the inobt to distinguish two
903302436c2SDarrick J. Wong 	 * scenarios: (1) the inobt says the inode is free, in which case
904302436c2SDarrick J. Wong 	 * there's nothing to do; and (2) the inobt says the inode is
905302436c2SDarrick J. Wong 	 * allocated, but loading it failed due to corruption.
906302436c2SDarrick J. Wong 	 *
907302436c2SDarrick J. Wong 	 * Allocate a transaction and grab the AGI to prevent inobt activity
908302436c2SDarrick J. Wong 	 * in this AG.  Retry the iget in case someone allocated a new inode
909302436c2SDarrick J. Wong 	 * after the first iget failed.
910302436c2SDarrick J. Wong 	 */
911302436c2SDarrick J. Wong 	error = xchk_trans_alloc(sc, 0);
912302436c2SDarrick J. Wong 	if (error)
913302436c2SDarrick J. Wong 		goto out_error;
914302436c2SDarrick J. Wong 
915302436c2SDarrick J. Wong 	error = xchk_iget_agi(sc, sc->sm->sm_ino, &agi_bp, &ip);
916302436c2SDarrick J. Wong 	if (error == 0) {
917302436c2SDarrick J. Wong 		/* Actually got the inode, so install it. */
918302436c2SDarrick J. Wong 		xchk_trans_cancel(sc);
919302436c2SDarrick J. Wong 		return xchk_install_handle_inode(sc, ip);
920302436c2SDarrick J. Wong 	}
921302436c2SDarrick J. Wong 	if (error == -ENOENT)
922302436c2SDarrick J. Wong 		goto out_gone;
923302436c2SDarrick J. Wong 	if (error != -EINVAL)
924302436c2SDarrick J. Wong 		goto out_cancel;
925302436c2SDarrick J. Wong 
926302436c2SDarrick J. Wong 	/* Ensure that we have protected against inode allocation/freeing. */
927302436c2SDarrick J. Wong 	if (agi_bp == NULL) {
928302436c2SDarrick J. Wong 		ASSERT(agi_bp != NULL);
929302436c2SDarrick J. Wong 		error = -ECANCELED;
930302436c2SDarrick J. Wong 		goto out_cancel;
931302436c2SDarrick J. Wong 	}
932302436c2SDarrick J. Wong 
933302436c2SDarrick J. Wong 	/*
934302436c2SDarrick J. Wong 	 * Untrusted iget failed a second time.  Let's try an inobt lookup.
935302436c2SDarrick J. Wong 	 * If the inobt thinks this the inode neither can exist inside the
936302436c2SDarrick J. Wong 	 * filesystem nor is allocated, return ENOENT to signal that the check
937302436c2SDarrick J. Wong 	 * can be skipped.
938302436c2SDarrick J. Wong 	 *
939302436c2SDarrick J. Wong 	 * If the lookup returns corruption, we'll mark this inode corrupt and
940302436c2SDarrick J. Wong 	 * exit to userspace.  There's little chance of fixing anything until
941302436c2SDarrick J. Wong 	 * the inobt is straightened out, but there's nothing we can do here.
942302436c2SDarrick J. Wong 	 *
943302436c2SDarrick J. Wong 	 * If the lookup encounters any other error, exit to userspace.
944302436c2SDarrick J. Wong 	 *
945302436c2SDarrick J. Wong 	 * If the lookup succeeds, something else must be very wrong in the fs
946302436c2SDarrick J. Wong 	 * such that setting up the incore inode failed in some strange way.
947302436c2SDarrick J. Wong 	 * Treat those as corruptions.
948d658e72bSDarrick J. Wong 	 */
949498f0adbSDave Chinner 	pag = xfs_perag_get(mp, XFS_INO_TO_AGNO(mp, sc->sm->sm_ino));
950302436c2SDarrick J. Wong 	if (!pag) {
951302436c2SDarrick J. Wong 		error = -EFSCORRUPTED;
952302436c2SDarrick J. Wong 		goto out_cancel;
953302436c2SDarrick J. Wong 	}
954302436c2SDarrick J. Wong 
955498f0adbSDave Chinner 	error = xfs_imap(pag, sc->tp, sc->sm->sm_ino, &imap,
956a03297a0SDarrick J. Wong 			XFS_IGET_UNTRUSTED);
957498f0adbSDave Chinner 	xfs_perag_put(pag);
958302436c2SDarrick J. Wong 	if (error == -EINVAL || error == -ENOENT)
959302436c2SDarrick J. Wong 		goto out_gone;
960302436c2SDarrick J. Wong 	if (!error)
961d658e72bSDarrick J. Wong 		error = -EFSCORRUPTED;
962302436c2SDarrick J. Wong 
963302436c2SDarrick J. Wong out_cancel:
964302436c2SDarrick J. Wong 	xchk_trans_cancel(sc);
965302436c2SDarrick J. Wong out_error:
966302436c2SDarrick J. Wong 	trace_xchk_op_error(sc, agno, XFS_INO_TO_AGBNO(mp, sc->sm->sm_ino),
96780e4e126SDarrick J. Wong 			error, __return_address);
96880e4e126SDarrick J. Wong 	return error;
969302436c2SDarrick J. Wong out_gone:
970302436c2SDarrick J. Wong 	/* The file is gone, so there's nothing to check. */
971302436c2SDarrick J. Wong 	xchk_trans_cancel(sc);
97280e4e126SDarrick J. Wong 	return -ENOENT;
97380e4e126SDarrick J. Wong }
97480e4e126SDarrick J. Wong 
975a03297a0SDarrick J. Wong /* Release an inode, possibly dropping it in the process. */
976a03297a0SDarrick J. Wong void
xchk_irele(struct xfs_scrub * sc,struct xfs_inode * ip)977a03297a0SDarrick J. Wong xchk_irele(
978a03297a0SDarrick J. Wong 	struct xfs_scrub	*sc,
979a03297a0SDarrick J. Wong 	struct xfs_inode	*ip)
980a03297a0SDarrick J. Wong {
9818bb04028SDave Chinner 	if (sc->tp) {
982a03297a0SDarrick J. Wong 		/*
983a03297a0SDarrick J. Wong 		 * If we are in a transaction, we /cannot/ drop the inode
984a03297a0SDarrick J. Wong 		 * ourselves, because the VFS will trigger writeback, which
985a03297a0SDarrick J. Wong 		 * can require a transaction.  Clear DONTCACHE to force the
986a03297a0SDarrick J. Wong 		 * inode to the LRU, where someone else can take care of
987a03297a0SDarrick J. Wong 		 * dropping it.
988a03297a0SDarrick J. Wong 		 *
989a03297a0SDarrick J. Wong 		 * Note that when we grabbed our reference to the inode, it
990a03297a0SDarrick J. Wong 		 * could have had an active ref and DONTCACHE set if a sysadmin
991a03297a0SDarrick J. Wong 		 * is trying to coerce a change in file access mode.  icache
992a03297a0SDarrick J. Wong 		 * hits do not clear DONTCACHE, so we must do it here.
993a03297a0SDarrick J. Wong 		 */
994a03297a0SDarrick J. Wong 		spin_lock(&VFS_I(ip)->i_lock);
995a03297a0SDarrick J. Wong 		VFS_I(ip)->i_state &= ~I_DONTCACHE;
996a03297a0SDarrick J. Wong 		spin_unlock(&VFS_I(ip)->i_lock);
997a03297a0SDarrick J. Wong 	} else if (atomic_read(&VFS_I(ip)->i_count) == 1) {
998a03297a0SDarrick J. Wong 		/*
999a03297a0SDarrick J. Wong 		 * If this is the last reference to the inode and the caller
1000a03297a0SDarrick J. Wong 		 * permits it, set DONTCACHE to avoid thrashing.
1001a03297a0SDarrick J. Wong 		 */
1002a03297a0SDarrick J. Wong 		d_mark_dontcache(VFS_I(ip));
1003a03297a0SDarrick J. Wong 	}
1004a03297a0SDarrick J. Wong 
1005a03297a0SDarrick J. Wong 	xfs_irele(ip);
1006a03297a0SDarrick J. Wong }
1007a03297a0SDarrick J. Wong 
10081fc7a059SDarrick J. Wong /*
10091fc7a059SDarrick J. Wong  * Set us up to scrub metadata mapped by a file's fork.  Callers must not use
10101fc7a059SDarrick J. Wong  * this to operate on user-accessible regular file data because the MMAPLOCK is
10111fc7a059SDarrick J. Wong  * not taken.
10121fc7a059SDarrick J. Wong  */
1013a5c46e5eSDarrick J. Wong int
xchk_setup_inode_contents(struct xfs_scrub * sc,unsigned int resblks)1014c517b3aaSDarrick J. Wong xchk_setup_inode_contents(
10151d8a748aSDarrick J. Wong 	struct xfs_scrub	*sc,
1016a5c46e5eSDarrick J. Wong 	unsigned int		resblks)
1017a5c46e5eSDarrick J. Wong {
1018a5c46e5eSDarrick J. Wong 	int			error;
1019a5c46e5eSDarrick J. Wong 
102046e0dd89SDarrick J. Wong 	error = xchk_iget_for_scrubbing(sc);
1021a5c46e5eSDarrick J. Wong 	if (error)
1022a5c46e5eSDarrick J. Wong 		return error;
1023a5c46e5eSDarrick J. Wong 
10241fc7a059SDarrick J. Wong 	/* Lock the inode so the VFS cannot touch this file. */
1025294012fbSDarrick J. Wong 	xchk_ilock(sc, XFS_IOLOCK_EXCL);
10261fc7a059SDarrick J. Wong 
1027c517b3aaSDarrick J. Wong 	error = xchk_trans_alloc(sc, resblks);
1028a5c46e5eSDarrick J. Wong 	if (error)
1029a5c46e5eSDarrick J. Wong 		goto out;
1030294012fbSDarrick J. Wong 	xchk_ilock(sc, XFS_ILOCK_EXCL);
1031a5c46e5eSDarrick J. Wong out:
1032a5c46e5eSDarrick J. Wong 	/* scrub teardown will unlock and release the inode for us */
1033a5c46e5eSDarrick J. Wong 	return error;
1034a5c46e5eSDarrick J. Wong }
103564b12563SDarrick J. Wong 
1036294012fbSDarrick J. Wong void
xchk_ilock(struct xfs_scrub * sc,unsigned int ilock_flags)1037294012fbSDarrick J. Wong xchk_ilock(
1038294012fbSDarrick J. Wong 	struct xfs_scrub	*sc,
1039294012fbSDarrick J. Wong 	unsigned int		ilock_flags)
1040294012fbSDarrick J. Wong {
1041294012fbSDarrick J. Wong 	xfs_ilock(sc->ip, ilock_flags);
1042294012fbSDarrick J. Wong 	sc->ilock_flags |= ilock_flags;
1043294012fbSDarrick J. Wong }
1044294012fbSDarrick J. Wong 
1045294012fbSDarrick J. Wong bool
xchk_ilock_nowait(struct xfs_scrub * sc,unsigned int ilock_flags)1046294012fbSDarrick J. Wong xchk_ilock_nowait(
1047294012fbSDarrick J. Wong 	struct xfs_scrub	*sc,
1048294012fbSDarrick J. Wong 	unsigned int		ilock_flags)
1049294012fbSDarrick J. Wong {
1050294012fbSDarrick J. Wong 	if (xfs_ilock_nowait(sc->ip, ilock_flags)) {
1051294012fbSDarrick J. Wong 		sc->ilock_flags |= ilock_flags;
1052294012fbSDarrick J. Wong 		return true;
1053294012fbSDarrick J. Wong 	}
1054294012fbSDarrick J. Wong 
1055294012fbSDarrick J. Wong 	return false;
1056294012fbSDarrick J. Wong }
1057294012fbSDarrick J. Wong 
1058294012fbSDarrick J. Wong void
xchk_iunlock(struct xfs_scrub * sc,unsigned int ilock_flags)1059294012fbSDarrick J. Wong xchk_iunlock(
1060294012fbSDarrick J. Wong 	struct xfs_scrub	*sc,
1061294012fbSDarrick J. Wong 	unsigned int		ilock_flags)
1062294012fbSDarrick J. Wong {
1063294012fbSDarrick J. Wong 	sc->ilock_flags &= ~ilock_flags;
1064294012fbSDarrick J. Wong 	xfs_iunlock(sc->ip, ilock_flags);
1065294012fbSDarrick J. Wong }
1066294012fbSDarrick J. Wong 
106764b12563SDarrick J. Wong /*
106864b12563SDarrick J. Wong  * Predicate that decides if we need to evaluate the cross-reference check.
106964b12563SDarrick J. Wong  * If there was an error accessing the cross-reference btree, just delete
107064b12563SDarrick J. Wong  * the cursor and skip the check.
107164b12563SDarrick J. Wong  */
107264b12563SDarrick J. Wong bool
xchk_should_check_xref(struct xfs_scrub * sc,int * error,struct xfs_btree_cur ** curpp)1073c517b3aaSDarrick J. Wong xchk_should_check_xref(
10741d8a748aSDarrick J. Wong 	struct xfs_scrub	*sc,
107564b12563SDarrick J. Wong 	int			*error,
107664b12563SDarrick J. Wong 	struct xfs_btree_cur	**curpp)
107764b12563SDarrick J. Wong {
10788389f3ffSDarrick J. Wong 	/* No point in xref if we already know we're corrupt. */
1079c517b3aaSDarrick J. Wong 	if (xchk_skip_xref(sc->sm))
10808389f3ffSDarrick J. Wong 		return false;
10818389f3ffSDarrick J. Wong 
108264b12563SDarrick J. Wong 	if (*error == 0)
108364b12563SDarrick J. Wong 		return true;
108464b12563SDarrick J. Wong 
108564b12563SDarrick J. Wong 	if (curpp) {
108664b12563SDarrick J. Wong 		/* If we've already given up on xref, just bail out. */
108764b12563SDarrick J. Wong 		if (!*curpp)
108864b12563SDarrick J. Wong 			return false;
108964b12563SDarrick J. Wong 
109064b12563SDarrick J. Wong 		/* xref error, delete cursor and bail out. */
109164b12563SDarrick J. Wong 		xfs_btree_del_cursor(*curpp, XFS_BTREE_ERROR);
109264b12563SDarrick J. Wong 		*curpp = NULL;
109364b12563SDarrick J. Wong 	}
109464b12563SDarrick J. Wong 
109564b12563SDarrick J. Wong 	sc->sm->sm_flags |= XFS_SCRUB_OFLAG_XFAIL;
1096c517b3aaSDarrick J. Wong 	trace_xchk_xref_error(sc, *error, __return_address);
109764b12563SDarrick J. Wong 
109864b12563SDarrick J. Wong 	/*
109964b12563SDarrick J. Wong 	 * Errors encountered during cross-referencing with another
110064b12563SDarrick J. Wong 	 * data structure should not cause this scrubber to abort.
110164b12563SDarrick J. Wong 	 */
110264b12563SDarrick J. Wong 	*error = 0;
110364b12563SDarrick J. Wong 	return false;
110464b12563SDarrick J. Wong }
1105cf1b0b8bSDarrick J. Wong 
1106cf1b0b8bSDarrick J. Wong /* Run the structure verifiers on in-memory buffers to detect bad memory. */
1107cf1b0b8bSDarrick J. Wong void
xchk_buffer_recheck(struct xfs_scrub * sc,struct xfs_buf * bp)1108c517b3aaSDarrick J. Wong xchk_buffer_recheck(
11091d8a748aSDarrick J. Wong 	struct xfs_scrub	*sc,
1110cf1b0b8bSDarrick J. Wong 	struct xfs_buf		*bp)
1111cf1b0b8bSDarrick J. Wong {
1112cf1b0b8bSDarrick J. Wong 	xfs_failaddr_t		fa;
1113cf1b0b8bSDarrick J. Wong 
1114cf1b0b8bSDarrick J. Wong 	if (bp->b_ops == NULL) {
1115c517b3aaSDarrick J. Wong 		xchk_block_set_corrupt(sc, bp);
1116cf1b0b8bSDarrick J. Wong 		return;
1117cf1b0b8bSDarrick J. Wong 	}
1118cf1b0b8bSDarrick J. Wong 	if (bp->b_ops->verify_struct == NULL) {
1119c517b3aaSDarrick J. Wong 		xchk_set_incomplete(sc);
1120cf1b0b8bSDarrick J. Wong 		return;
1121cf1b0b8bSDarrick J. Wong 	}
1122cf1b0b8bSDarrick J. Wong 	fa = bp->b_ops->verify_struct(bp);
1123cf1b0b8bSDarrick J. Wong 	if (!fa)
1124cf1b0b8bSDarrick J. Wong 		return;
1125cf1b0b8bSDarrick J. Wong 	sc->sm->sm_flags |= XFS_SCRUB_OFLAG_CORRUPT;
11269343ee76SDave Chinner 	trace_xchk_block_error(sc, xfs_buf_daddr(bp), fa);
1127cf1b0b8bSDarrick J. Wong }
112887d9d609SDarrick J. Wong 
1129f36b954aSDarrick J. Wong static inline int
xchk_metadata_inode_subtype(struct xfs_scrub * sc,unsigned int scrub_type)1130f36b954aSDarrick J. Wong xchk_metadata_inode_subtype(
1131f36b954aSDarrick J. Wong 	struct xfs_scrub	*sc,
1132f36b954aSDarrick J. Wong 	unsigned int		scrub_type)
1133f36b954aSDarrick J. Wong {
1134f36b954aSDarrick J. Wong 	__u32			smtype = sc->sm->sm_type;
1135f36b954aSDarrick J. Wong 	int			error;
1136f36b954aSDarrick J. Wong 
1137f36b954aSDarrick J. Wong 	sc->sm->sm_type = scrub_type;
1138f36b954aSDarrick J. Wong 
1139f36b954aSDarrick J. Wong 	switch (scrub_type) {
1140f36b954aSDarrick J. Wong 	case XFS_SCRUB_TYPE_INODE:
1141f36b954aSDarrick J. Wong 		error = xchk_inode(sc);
1142f36b954aSDarrick J. Wong 		break;
1143f36b954aSDarrick J. Wong 	case XFS_SCRUB_TYPE_BMBTD:
1144f36b954aSDarrick J. Wong 		error = xchk_bmap_data(sc);
1145f36b954aSDarrick J. Wong 		break;
1146f36b954aSDarrick J. Wong 	default:
1147f36b954aSDarrick J. Wong 		ASSERT(0);
1148f36b954aSDarrick J. Wong 		error = -EFSCORRUPTED;
1149f36b954aSDarrick J. Wong 		break;
1150f36b954aSDarrick J. Wong 	}
1151f36b954aSDarrick J. Wong 
1152f36b954aSDarrick J. Wong 	sc->sm->sm_type = smtype;
1153f36b954aSDarrick J. Wong 	return error;
1154f36b954aSDarrick J. Wong }
1155f36b954aSDarrick J. Wong 
115687d9d609SDarrick J. Wong /*
115787d9d609SDarrick J. Wong  * Scrub the attr/data forks of a metadata inode.  The metadata inode must be
115887d9d609SDarrick J. Wong  * pointed to by sc->ip and the ILOCK must be held.
115987d9d609SDarrick J. Wong  */
116087d9d609SDarrick J. Wong int
xchk_metadata_inode_forks(struct xfs_scrub * sc)1161c517b3aaSDarrick J. Wong xchk_metadata_inode_forks(
11621d8a748aSDarrick J. Wong 	struct xfs_scrub	*sc)
116387d9d609SDarrick J. Wong {
116487d9d609SDarrick J. Wong 	bool			shared;
116587d9d609SDarrick J. Wong 	int			error;
116687d9d609SDarrick J. Wong 
116787d9d609SDarrick J. Wong 	if (sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT)
116887d9d609SDarrick J. Wong 		return 0;
116987d9d609SDarrick J. Wong 
1170f36b954aSDarrick J. Wong 	/* Check the inode record. */
1171f36b954aSDarrick J. Wong 	error = xchk_metadata_inode_subtype(sc, XFS_SCRUB_TYPE_INODE);
1172f36b954aSDarrick J. Wong 	if (error || (sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT))
1173f36b954aSDarrick J. Wong 		return error;
1174f36b954aSDarrick J. Wong 
117587d9d609SDarrick J. Wong 	/* Metadata inodes don't live on the rt device. */
1176db07349dSChristoph Hellwig 	if (sc->ip->i_diflags & XFS_DIFLAG_REALTIME) {
1177c517b3aaSDarrick J. Wong 		xchk_ino_set_corrupt(sc, sc->ip->i_ino);
117887d9d609SDarrick J. Wong 		return 0;
117987d9d609SDarrick J. Wong 	}
118087d9d609SDarrick J. Wong 
118187d9d609SDarrick J. Wong 	/* They should never participate in reflink. */
118287d9d609SDarrick J. Wong 	if (xfs_is_reflink_inode(sc->ip)) {
1183c517b3aaSDarrick J. Wong 		xchk_ino_set_corrupt(sc, sc->ip->i_ino);
118487d9d609SDarrick J. Wong 		return 0;
118587d9d609SDarrick J. Wong 	}
118687d9d609SDarrick J. Wong 
118787d9d609SDarrick J. Wong 	/* They also should never have extended attributes. */
118887d9d609SDarrick J. Wong 	if (xfs_inode_hasattr(sc->ip)) {
1189c517b3aaSDarrick J. Wong 		xchk_ino_set_corrupt(sc, sc->ip->i_ino);
119087d9d609SDarrick J. Wong 		return 0;
119187d9d609SDarrick J. Wong 	}
119287d9d609SDarrick J. Wong 
119387d9d609SDarrick J. Wong 	/* Invoke the data fork scrubber. */
1194f36b954aSDarrick J. Wong 	error = xchk_metadata_inode_subtype(sc, XFS_SCRUB_TYPE_BMBTD);
119587d9d609SDarrick J. Wong 	if (error || (sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT))
119687d9d609SDarrick J. Wong 		return error;
119787d9d609SDarrick J. Wong 
119887d9d609SDarrick J. Wong 	/* Look for incorrect shared blocks. */
119938c26bfdSDave Chinner 	if (xfs_has_reflink(sc->mp)) {
120087d9d609SDarrick J. Wong 		error = xfs_reflink_inode_has_shared_extents(sc->tp, sc->ip,
120187d9d609SDarrick J. Wong 				&shared);
1202c517b3aaSDarrick J. Wong 		if (!xchk_fblock_process_error(sc, XFS_DATA_FORK, 0,
120387d9d609SDarrick J. Wong 				&error))
120487d9d609SDarrick J. Wong 			return error;
120587d9d609SDarrick J. Wong 		if (shared)
1206c517b3aaSDarrick J. Wong 			xchk_ino_set_corrupt(sc, sc->ip->i_ino);
120787d9d609SDarrick J. Wong 	}
120887d9d609SDarrick J. Wong 
1209f36b954aSDarrick J. Wong 	return 0;
121087d9d609SDarrick J. Wong }
1211ddd10c2fSDarrick J. Wong 
1212466c525dSDarrick J. Wong /*
1213466c525dSDarrick J. Wong  * Enable filesystem hooks (i.e. runtime code patching) before starting a scrub
1214466c525dSDarrick J. Wong  * operation.  Callers must not hold any locks that intersect with the CPU
1215466c525dSDarrick J. Wong  * hotplug lock (e.g. writeback locks) because code patching must halt the CPUs
1216466c525dSDarrick J. Wong  * to change kernel code.
1217466c525dSDarrick J. Wong  */
1218466c525dSDarrick J. Wong void
xchk_fsgates_enable(struct xfs_scrub * sc,unsigned int scrub_fsgates)1219466c525dSDarrick J. Wong xchk_fsgates_enable(
1220466c525dSDarrick J. Wong 	struct xfs_scrub	*sc,
1221466c525dSDarrick J. Wong 	unsigned int		scrub_fsgates)
1222466c525dSDarrick J. Wong {
1223466c525dSDarrick J. Wong 	ASSERT(!(scrub_fsgates & ~XCHK_FSGATES_ALL));
1224466c525dSDarrick J. Wong 	ASSERT(!(sc->flags & scrub_fsgates));
1225466c525dSDarrick J. Wong 
1226466c525dSDarrick J. Wong 	trace_xchk_fsgates_enable(sc, scrub_fsgates);
1227466c525dSDarrick J. Wong 
1228466c525dSDarrick J. Wong 	if (scrub_fsgates & XCHK_FSGATES_DRAIN)
1229466c525dSDarrick J. Wong 		xfs_drain_wait_enable();
1230466c525dSDarrick J. Wong 
1231466c525dSDarrick J. Wong 	sc->flags |= scrub_fsgates;
1232466c525dSDarrick J. Wong }
12330d296634SDarrick J. Wong 
12340d296634SDarrick J. Wong /*
1235369c001bSDarrick J. Wong  * Decide if this is this a cached inode that's also allocated.  The caller
1236369c001bSDarrick J. Wong  * must hold a reference to an AG and the AGI buffer lock to prevent inodes
1237369c001bSDarrick J. Wong  * from being allocated or freed.
12380d296634SDarrick J. Wong  *
1239369c001bSDarrick J. Wong  * Look up an inode by number in the given file system.  If the inode number
1240369c001bSDarrick J. Wong  * is invalid, return -EINVAL.  If the inode is not in cache, return -ENODATA.
1241369c001bSDarrick J. Wong  * If the inode is being reclaimed, return -ENODATA because we know the inode
1242369c001bSDarrick J. Wong  * cache cannot be updating the ondisk metadata.
12430d296634SDarrick J. Wong  *
1244369c001bSDarrick J. Wong  * Otherwise, the incore inode is the one we want, and it is either live,
1245369c001bSDarrick J. Wong  * somewhere in the inactivation machinery, or reclaimable.  The inode is
1246369c001bSDarrick J. Wong  * allocated if i_mode is nonzero.  In all three cases, the cached inode will
1247369c001bSDarrick J. Wong  * be more up to date than the ondisk inode buffer, so we must use the incore
1248369c001bSDarrick J. Wong  * i_mode.
12490d296634SDarrick J. Wong  */
12500d296634SDarrick J. Wong int
xchk_inode_is_allocated(struct xfs_scrub * sc,xfs_agino_t agino,bool * inuse)12510d296634SDarrick J. Wong xchk_inode_is_allocated(
12520d296634SDarrick J. Wong 	struct xfs_scrub	*sc,
1253369c001bSDarrick J. Wong 	xfs_agino_t		agino,
12540d296634SDarrick J. Wong 	bool			*inuse)
12550d296634SDarrick J. Wong {
1256369c001bSDarrick J. Wong 	struct xfs_mount	*mp = sc->mp;
1257369c001bSDarrick J. Wong 	struct xfs_perag	*pag = sc->sa.pag;
1258369c001bSDarrick J. Wong 	xfs_ino_t		ino;
12590d296634SDarrick J. Wong 	struct xfs_inode	*ip;
12600d296634SDarrick J. Wong 	int			error;
12610d296634SDarrick J. Wong 
1262369c001bSDarrick J. Wong 	/* caller must hold perag reference */
1263369c001bSDarrick J. Wong 	if (pag == NULL) {
1264369c001bSDarrick J. Wong 		ASSERT(pag != NULL);
1265369c001bSDarrick J. Wong 		return -EINVAL;
1266369c001bSDarrick J. Wong 	}
12670d296634SDarrick J. Wong 
1268369c001bSDarrick J. Wong 	/* caller must have AGI buffer */
1269369c001bSDarrick J. Wong 	if (sc->sa.agi_bp == NULL) {
1270369c001bSDarrick J. Wong 		ASSERT(sc->sa.agi_bp != NULL);
1271369c001bSDarrick J. Wong 		return -EINVAL;
1272369c001bSDarrick J. Wong 	}
1273369c001bSDarrick J. Wong 
1274369c001bSDarrick J. Wong 	/* reject inode numbers outside existing AGs */
1275369c001bSDarrick J. Wong 	ino = XFS_AGINO_TO_INO(sc->mp, pag->pag_agno, agino);
1276369c001bSDarrick J. Wong 	if (!xfs_verify_ino(mp, ino))
1277369c001bSDarrick J. Wong 		return -EINVAL;
1278369c001bSDarrick J. Wong 
1279369c001bSDarrick J. Wong 	error = -ENODATA;
1280369c001bSDarrick J. Wong 	rcu_read_lock();
1281369c001bSDarrick J. Wong 	ip = radix_tree_lookup(&pag->pag_ici_root, agino);
1282369c001bSDarrick J. Wong 	if (!ip) {
1283369c001bSDarrick J. Wong 		/* cache miss */
1284369c001bSDarrick J. Wong 		goto out_rcu;
1285369c001bSDarrick J. Wong 	}
1286369c001bSDarrick J. Wong 
1287369c001bSDarrick J. Wong 	/*
1288369c001bSDarrick J. Wong 	 * If the inode number doesn't match, the incore inode got reused
1289369c001bSDarrick J. Wong 	 * during an RCU grace period and the radix tree hasn't been updated.
1290369c001bSDarrick J. Wong 	 * This isn't the inode we want.
1291369c001bSDarrick J. Wong 	 */
1292369c001bSDarrick J. Wong 	spin_lock(&ip->i_flags_lock);
1293369c001bSDarrick J. Wong 	if (ip->i_ino != ino)
1294369c001bSDarrick J. Wong 		goto out_skip;
1295369c001bSDarrick J. Wong 
1296369c001bSDarrick J. Wong 	trace_xchk_inode_is_allocated(ip);
1297369c001bSDarrick J. Wong 
1298369c001bSDarrick J. Wong 	/*
1299369c001bSDarrick J. Wong 	 * We have an incore inode that matches the inode we want, and the
1300369c001bSDarrick J. Wong 	 * caller holds the perag structure and the AGI buffer.  Let's check
1301369c001bSDarrick J. Wong 	 * our assumptions below:
1302369c001bSDarrick J. Wong 	 */
1303369c001bSDarrick J. Wong 
1304369c001bSDarrick J. Wong #ifdef DEBUG
1305369c001bSDarrick J. Wong 	/*
1306369c001bSDarrick J. Wong 	 * (1) If the incore inode is live (i.e. referenced from the dcache),
1307369c001bSDarrick J. Wong 	 * it will not be INEW, nor will it be in the inactivation or reclaim
1308369c001bSDarrick J. Wong 	 * machinery.  The ondisk inode had better be allocated.  This is the
1309369c001bSDarrick J. Wong 	 * most trivial case.
1310369c001bSDarrick J. Wong 	 */
1311369c001bSDarrick J. Wong 	if (!(ip->i_flags & (XFS_NEED_INACTIVE | XFS_INEW | XFS_IRECLAIMABLE |
1312369c001bSDarrick J. Wong 			     XFS_INACTIVATING))) {
1313369c001bSDarrick J. Wong 		/* live inode */
1314369c001bSDarrick J. Wong 		ASSERT(VFS_I(ip)->i_mode != 0);
1315369c001bSDarrick J. Wong 	}
1316369c001bSDarrick J. Wong 
1317369c001bSDarrick J. Wong 	/*
1318369c001bSDarrick J. Wong 	 * If the incore inode is INEW, there are several possibilities:
1319369c001bSDarrick J. Wong 	 *
1320369c001bSDarrick J. Wong 	 * (2) For a file that is being created, note that we allocate the
1321369c001bSDarrick J. Wong 	 * ondisk inode before allocating, initializing, and adding the incore
1322369c001bSDarrick J. Wong 	 * inode to the radix tree.
1323369c001bSDarrick J. Wong 	 *
1324369c001bSDarrick J. Wong 	 * (3) If the incore inode is being recycled, the inode has to be
1325369c001bSDarrick J. Wong 	 * allocated because we don't allow freed inodes to be recycled.
1326369c001bSDarrick J. Wong 	 * Recycling doesn't touch i_mode.
1327369c001bSDarrick J. Wong 	 */
1328369c001bSDarrick J. Wong 	if (ip->i_flags & XFS_INEW) {
1329369c001bSDarrick J. Wong 		/* created on disk already or recycling */
1330369c001bSDarrick J. Wong 		ASSERT(VFS_I(ip)->i_mode != 0);
1331369c001bSDarrick J. Wong 	}
1332369c001bSDarrick J. Wong 
1333369c001bSDarrick J. Wong 	/*
1334369c001bSDarrick J. Wong 	 * (4) If the inode is queued for inactivation (NEED_INACTIVE) but
1335369c001bSDarrick J. Wong 	 * inactivation has not started (!INACTIVATING), it is still allocated.
1336369c001bSDarrick J. Wong 	 */
1337369c001bSDarrick J. Wong 	if ((ip->i_flags & XFS_NEED_INACTIVE) &&
1338369c001bSDarrick J. Wong 	    !(ip->i_flags & XFS_INACTIVATING)) {
1339369c001bSDarrick J. Wong 		/* definitely before difree */
1340369c001bSDarrick J. Wong 		ASSERT(VFS_I(ip)->i_mode != 0);
1341369c001bSDarrick J. Wong 	}
1342369c001bSDarrick J. Wong #endif
1343369c001bSDarrick J. Wong 
1344369c001bSDarrick J. Wong 	/*
1345369c001bSDarrick J. Wong 	 * If the incore inode is undergoing inactivation (INACTIVATING), there
1346369c001bSDarrick J. Wong 	 * are two possibilities:
1347369c001bSDarrick J. Wong 	 *
1348369c001bSDarrick J. Wong 	 * (5) It is before the point where it would get freed ondisk, in which
1349369c001bSDarrick J. Wong 	 * case i_mode is still nonzero.
1350369c001bSDarrick J. Wong 	 *
1351369c001bSDarrick J. Wong 	 * (6) It has already been freed, in which case i_mode is zero.
1352369c001bSDarrick J. Wong 	 *
1353369c001bSDarrick J. Wong 	 * We don't take the ILOCK here, but difree and dialloc update the AGI,
1354369c001bSDarrick J. Wong 	 * and we've taken the AGI buffer lock, which prevents that from
1355369c001bSDarrick J. Wong 	 * happening.
1356369c001bSDarrick J. Wong 	 */
1357369c001bSDarrick J. Wong 
1358369c001bSDarrick J. Wong 	/*
1359369c001bSDarrick J. Wong 	 * (7) Inodes undergoing inactivation (INACTIVATING) or queued for
1360369c001bSDarrick J. Wong 	 * reclaim (IRECLAIMABLE) could be allocated or free.  i_mode still
1361369c001bSDarrick J. Wong 	 * reflects the ondisk state.
1362369c001bSDarrick J. Wong 	 */
1363369c001bSDarrick J. Wong 
1364369c001bSDarrick J. Wong 	/*
1365369c001bSDarrick J. Wong 	 * (8) If the inode is in IFLUSHING, it's safe to query i_mode because
1366369c001bSDarrick J. Wong 	 * the flush code uses i_mode to format the ondisk inode.
1367369c001bSDarrick J. Wong 	 */
1368369c001bSDarrick J. Wong 
1369369c001bSDarrick J. Wong 	/*
1370369c001bSDarrick J. Wong 	 * (9) If the inode is in IRECLAIM and was reachable via the radix
1371369c001bSDarrick J. Wong 	 * tree, it still has the same i_mode as it did before it entered
1372369c001bSDarrick J. Wong 	 * reclaim.  The inode object is still alive because we hold the RCU
1373369c001bSDarrick J. Wong 	 * read lock.
1374369c001bSDarrick J. Wong 	 */
1375369c001bSDarrick J. Wong 
1376369c001bSDarrick J. Wong 	*inuse = VFS_I(ip)->i_mode != 0;
1377369c001bSDarrick J. Wong 	error = 0;
1378369c001bSDarrick J. Wong 
1379369c001bSDarrick J. Wong out_skip:
1380369c001bSDarrick J. Wong 	spin_unlock(&ip->i_flags_lock);
1381369c001bSDarrick J. Wong out_rcu:
1382369c001bSDarrick J. Wong 	rcu_read_unlock();
1383369c001bSDarrick J. Wong 	return error;
13840d296634SDarrick J. Wong }
1385