linux/fs/xfs/scrub/iscan.c

// SPDX-License-Identifier: GPL-2.0-or-later
/*
 * Copyright (c) 2021-2024 Oracle.  All Rights Reserved.
 * Author: Darrick J. Wong <[email protected]>
 */
#include "xfs.h"
#include "xfs_fs.h"
#include "xfs_shared.h"
#include "xfs_format.h"
#include "xfs_trans_resv.h"
#include "xfs_mount.h"
#include "xfs_log_format.h"
#include "xfs_trans.h"
#include "xfs_inode.h"
#include "xfs_btree.h"
#include "xfs_ialloc.h"
#include "xfs_ialloc_btree.h"
#include "xfs_ag.h"
#include "xfs_error.h"
#include "xfs_bit.h"
#include "xfs_icache.h"
#include "scrub/scrub.h"
#include "scrub/iscan.h"
#include "scrub/common.h"
#include "scrub/trace.h"

/*
 * Live File Scan
 * ==============
 *
 * Live file scans walk every inode in a live filesystem.  This is more or
 * less like a regular iwalk, except that when we're advancing the scan cursor,
 * we must ensure that inodes cannot be added or deleted anywhere between the
 * old cursor value and the new cursor value.  If we're advancing the cursor
 * by one inode, the caller must hold that inode; if we're finding the next
 * inode to scan, we must grab the AGI and hold it until we've updated the
 * scan cursor.
 *
 * Callers are expected to use this code to scan all files in the filesystem to
 * construct a new metadata index of some kind.  The scan races against other
 * live updates, which means there must be a provision to update the new index
 * when updates are made to inodes that already been scanned.  The iscan lock
 * can be used in live update hook code to stop the scan and protect this data
 * structure.
 *
 * To keep the new index up to date with other metadata updates being made to
 * the live filesystem, it is assumed that the caller will add hooks as needed
 * to be notified when a metadata update occurs.  The inode scanner must tell
 * the hook code when an inode has been visited with xchk_iscan_mark_visit.
 * Hook functions can use xchk_iscan_want_live_update to decide if the
 * scanner's observations must be updated.
 */

/*
 * If the inobt record @rec covers @iscan->skip_ino, mark the inode free so
 * that the scan ignores that inode.
 */
STATIC void
xchk_iscan_mask_skipino(
	struct xchk_iscan	*iscan,
	struct xfs_perag	*pag,
	struct xfs_inobt_rec_incore	*rec,
	xfs_agino_t		lastrecino)
{}

/*
 * Set *cursor to the next allocated inode after whatever it's set to now.
 * If there are no more inodes in this AG, cursor is set to NULLAGINO.
 */
STATIC int
xchk_iscan_find_next(
	struct xchk_iscan	*iscan,
	struct xfs_buf		*agi_bp,
	struct xfs_perag	*pag,
	xfs_inofree_t		*allocmaskp,
	xfs_agino_t		*cursor,
	uint8_t			*nr_inodesp)
{}

/*
 * Advance both the scan and the visited cursors.
 *
 * The inumber address space for a given filesystem is sparse, which means that
 * the scan cursor can jump a long ways in a single iter() call.  There are no
 * inodes in these sparse areas, so we must move the visited cursor forward at
 * the same time so that the scan user can receive live updates for inodes that
 * may get created once we release the AGI buffer.
 */
static inline void
xchk_iscan_move_cursor(
	struct xchk_iscan	*iscan,
	xfs_agnumber_t		agno,
	xfs_agino_t		agino)
{}

/*
 * Prepare to return agno/agino to the iscan caller by moving the lastino
 * cursor to the previous inode.  Do this while we still hold the AGI so that
 * no other threads can create or delete inodes in this AG.
 */
static inline void
xchk_iscan_finish(
	struct xchk_iscan	*iscan)
{}

/* Mark an inode scan finished before we actually scan anything. */
void
xchk_iscan_finish_early(
	struct xchk_iscan	*iscan)
{}

/*
 * Grab the AGI to advance the inode scan.  Returns 0 if *agi_bpp is now set,
 * -ECANCELED if the live scan aborted, -EBUSY if the AGI could not be grabbed,
 * or the usual negative errno.
 */
STATIC int
xchk_iscan_read_agi(
	struct xchk_iscan	*iscan,
	struct xfs_perag	*pag,
	struct xfs_buf		**agi_bpp)
{}

/*
 * Advance ino to the next inode that the inobt thinks is allocated, being
 * careful to jump to the next AG if we've reached the right end of this AG's
 * inode btree.  Advancing ino effectively means that we've pushed the inode
 * scan forward, so set the iscan cursor to (ino - 1) so that our live update
 * predicates will track inode allocations in that part of the inode number
 * key space once we release the AGI buffer.
 *
 * Returns 1 if there's a new inode to examine, 0 if we've run out of inodes,
 * -ECANCELED if the live scan aborted, or the usual negative errno.
 */
STATIC int
xchk_iscan_advance(
	struct xchk_iscan	*iscan,
	struct xfs_perag	**pagp,
	struct xfs_buf		**agi_bpp,
	xfs_inofree_t		*allocmaskp,
	uint8_t			*nr_inodesp)
{}

/*
 * Grabbing the inode failed, so we need to back up the scan and ask the caller
 * to try to _advance the scan again.  Returns -EBUSY if we've run out of retry
 * opportunities, -ECANCELED if the process has a fatal signal pending, or
 * -EAGAIN if we should try again.
 */
STATIC int
xchk_iscan_iget_retry(
	struct xchk_iscan	*iscan,
	bool			wait)
{}

/*
 * For an inode scan, we hold the AGI and want to try to grab a batch of
 * inodes.  Holding the AGI prevents inodegc from clearing freed inodes,
 * so we must use noretry here.  For every inode after the first one in the
 * batch, we don't want to wait, so we use retry there too.  Finally, use
 * dontcache to avoid polluting the cache.
 */
#define ISCAN_IGET_FLAGS

/*
 * Grab an inode as part of an inode scan.  While scanning this inode, the
 * caller must ensure that no other threads can modify the inode until a call
 * to xchk_iscan_visit succeeds.
 *
 * Returns the number of incore inodes grabbed; -EAGAIN if the caller should
 * call again xchk_iscan_advance; -EBUSY if we couldn't grab an inode;
 * -ECANCELED if there's a fatal signal pending; or some other negative errno.
 */
STATIC int
xchk_iscan_iget(
	struct xchk_iscan	*iscan,
	struct xfs_perag	*pag,
	struct xfs_buf		*agi_bp,
	xfs_inofree_t		allocmask,
	uint8_t			nr_inodes)
{}

/*
 * Advance the visit cursor to reflect skipped inodes beyond whatever we
 * scanned.
 */
STATIC void
xchk_iscan_finish_batch(
	struct xchk_iscan	*iscan)
{}

/*
 * Advance the inode scan cursor to the next allocated inode and return up to
 * 64 consecutive allocated inodes starting with the cursor position.
 */
STATIC int
xchk_iscan_iter_batch(
	struct xchk_iscan	*iscan)
{}

/*
 * Advance the inode scan cursor to the next allocated inode and return the
 * incore inode structure associated with it.
 *
 * Returns 1 if there's a new inode to examine, 0 if we've run out of inodes,
 * -ECANCELED if the live scan aborted, -EBUSY if the incore inode could not be
 * grabbed, or the usual negative errno.
 *
 * If the function returns -EBUSY and the caller can handle skipping an inode,
 * it may call this function again to continue the scan with the next allocated
 * inode.
 */
int
xchk_iscan_iter(
	struct xchk_iscan	*iscan,
	struct xfs_inode	**ipp)
{}

/* Clean up an xfs_iscan_iter call by dropping any inodes that we still hold. */
void
xchk_iscan_iter_finish(
	struct xchk_iscan	*iscan)
{}

/* Mark this inode scan finished and release resources. */
void
xchk_iscan_teardown(
	struct xchk_iscan	*iscan)
{}

/* Pick an AG from which to start a scan. */
static inline xfs_ino_t
xchk_iscan_rotor(
	struct xfs_mount	*mp)
{}

/*
 * Set ourselves up to start an inode scan.  If the @iget_timeout and
 * @iget_retry_delay parameters are set, the scan will try to iget each inode
 * for @iget_timeout milliseconds.  If an iget call indicates that the inode is
 * waiting to be inactivated, the CPU will relax for @iget_retry_delay
 * milliseconds after pushing the inactivation workers.
 */
void
xchk_iscan_start(
	struct xfs_scrub	*sc,
	unsigned int		iget_timeout,
	unsigned int		iget_retry_delay,
	struct xchk_iscan	*iscan)
{}

/*
 * Mark this inode as having been visited.  Callers must hold a sufficiently
 * exclusive lock on the inode to prevent concurrent modifications.
 */
void
xchk_iscan_mark_visited(
	struct xchk_iscan	*iscan,
	struct xfs_inode	*ip)
{}

/*
 * Did we skip this inode because it wasn't allocated when we loaded the batch?
 * If so, it is newly allocated and will not be scanned.  All live updates to
 * this inode must be passed to the caller to maintain scan correctness.
 */
static inline bool
xchk_iscan_skipped(
	const struct xchk_iscan	*iscan,
	xfs_ino_t		ino)
{}

/*
 * Do we need a live update for this inode?  This is true if the scanner thread
 * has visited this inode and the scan hasn't been aborted due to errors.
 * Callers must hold a sufficiently exclusive lock on the inode to prevent
 * scanners from reading any inode metadata.
 */
bool
xchk_iscan_want_live_update(
	struct xchk_iscan	*iscan,
	xfs_ino_t		ino)
{}