Commit 8660c7b7 authored by Darrick J. Wong's avatar Darrick J. Wong

xfs: implement live inode scan for scrub

This patch implements a live file scanner for online fsck functions that
require the ability to walk a filesystem to gather metadata records and
stay informed about metadata changes to files that have already been
visited.

The iscan structure consists of two inode number cursors: one to track
which inode we want to visit next, and a second one to track which
inodes have already been visited.  This second cursor is key to
capturing live updates to files previously scanned while the main thread
continues scanning -- any inode greater than this value hasn't been
scanned and can go on its way; any other update must be incorporated
into the collected data.  It is critical for the scanning thraad to hold
exclusive access on the inode until after marking the inode visited.

This new code is a separate patch from the patchsets adding callers for
the sake of enabling the author to move patches around his tree with
ease.  The intended usage model for this code is roughly:

	xchk_iscan_start(iscan, 0, 0);
	while ((error = xchk_iscan_iter(sc, iscan, &ip)) == 1) {
		xfs_ilock(ip, ...);
		/* capture inode metadata */
		xchk_iscan_mark_visited(iscan, ip);
		xfs_iunlock(ip, ...);

		xfs_irele(ip);
	}
	xchk_iscan_stop(iscan);
	if (error)
		return error;

Hook functions for live updates can then do:

	if (xchk_iscan_want_live_update(...))
		/* update the captured inode metadata */
Signed-off-by: default avatarDarrick J. Wong <djwong@kernel.org>
Reviewed-by: default avatarChristoph Hellwig <hch@lst.de>
parent ae05eb11
......@@ -158,6 +158,7 @@ xfs-y += $(addprefix scrub/, \
health.o \
ialloc.o \
inode.o \
iscan.o \
parent.o \
readdir.o \
refcount.o \
......
This diff is collapsed.
/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* Copyright (c) 2021-2024 Oracle. All Rights Reserved.
* Author: Darrick J. Wong <djwong@kernel.org>
*/
#ifndef __XFS_SCRUB_ISCAN_H__
#define __XFS_SCRUB_ISCAN_H__
struct xchk_iscan {
struct xfs_scrub *sc;
/* Lock to protect the scan cursor. */
struct mutex lock;
/* This is the inode that will be examined next. */
xfs_ino_t cursor_ino;
/*
* This is the last inode that we've successfully scanned, either
* because the caller scanned it, or we moved the cursor past an empty
* part of the inode address space. Scan callers should only use the
* xchk_iscan_visit function to modify this.
*/
xfs_ino_t __visited_ino;
/* Operational state of the livescan. */
unsigned long __opstate;
/* Give up on iterating @cursor_ino if we can't iget it by this time. */
unsigned long __iget_deadline;
/* Amount of time (in ms) that we will try to iget an inode. */
unsigned int iget_timeout;
/* Wait this many ms to retry an iget. */
unsigned int iget_retry_delay;
};
/* Set if the scan has been aborted due to some event in the fs. */
#define XCHK_ISCAN_OPSTATE_ABORTED (1)
static inline bool
xchk_iscan_aborted(const struct xchk_iscan *iscan)
{
return test_bit(XCHK_ISCAN_OPSTATE_ABORTED, &iscan->__opstate);
}
static inline void
xchk_iscan_abort(struct xchk_iscan *iscan)
{
set_bit(XCHK_ISCAN_OPSTATE_ABORTED, &iscan->__opstate);
}
void xchk_iscan_start(struct xfs_scrub *sc, unsigned int iget_timeout,
unsigned int iget_retry_delay, struct xchk_iscan *iscan);
void xchk_iscan_teardown(struct xchk_iscan *iscan);
int xchk_iscan_iter(struct xchk_iscan *iscan, struct xfs_inode **ipp);
void xchk_iscan_mark_visited(struct xchk_iscan *iscan, struct xfs_inode *ip);
bool xchk_iscan_want_live_update(struct xchk_iscan *iscan, xfs_ino_t ino);
#endif /* __XFS_SCRUB_ISCAN_H__ */
......@@ -20,6 +20,7 @@
#include "scrub/xfile.h"
#include "scrub/xfarray.h"
#include "scrub/quota.h"
#include "scrub/iscan.h"
/* Figure out which block the btree cursor was pointing to. */
static inline xfs_fsblock_t
......
......@@ -16,10 +16,12 @@
#include <linux/tracepoint.h>
#include "xfs_bit.h"
struct xfs_scrub;
struct xfile;
struct xfarray;
struct xfarray_sortinfo;
struct xchk_dqiter;
struct xchk_iscan;
/*
* ftrace's __print_symbolic requires that all enum values be wrapped in the
......@@ -1146,6 +1148,110 @@ TRACE_EVENT(xchk_rtsum_record_free,
);
#endif /* CONFIG_XFS_RT */
DECLARE_EVENT_CLASS(xchk_iscan_class,
TP_PROTO(struct xchk_iscan *iscan),
TP_ARGS(iscan),
TP_STRUCT__entry(
__field(dev_t, dev)
__field(xfs_ino_t, cursor)
__field(xfs_ino_t, visited)
),
TP_fast_assign(
__entry->dev = iscan->sc->mp->m_super->s_dev;
__entry->cursor = iscan->cursor_ino;
__entry->visited = iscan->__visited_ino;
),
TP_printk("dev %d:%d iscan cursor 0x%llx visited 0x%llx",
MAJOR(__entry->dev), MINOR(__entry->dev),
__entry->cursor,
__entry->visited)
)
#define DEFINE_ISCAN_EVENT(name) \
DEFINE_EVENT(xchk_iscan_class, name, \
TP_PROTO(struct xchk_iscan *iscan), \
TP_ARGS(iscan))
DEFINE_ISCAN_EVENT(xchk_iscan_move_cursor);
DEFINE_ISCAN_EVENT(xchk_iscan_visit);
DEFINE_ISCAN_EVENT(xchk_iscan_advance_ag);
DEFINE_ISCAN_EVENT(xchk_iscan_start);
DECLARE_EVENT_CLASS(xchk_iscan_ino_class,
TP_PROTO(struct xchk_iscan *iscan, xfs_ino_t ino),
TP_ARGS(iscan, ino),
TP_STRUCT__entry(
__field(dev_t, dev)
__field(xfs_ino_t, cursor)
__field(xfs_ino_t, visited)
__field(xfs_ino_t, ino)
),
TP_fast_assign(
__entry->dev = iscan->sc->mp->m_super->s_dev;
__entry->cursor = iscan->cursor_ino;
__entry->visited = iscan->__visited_ino;
__entry->ino = ino;
),
TP_printk("dev %d:%d iscan cursor 0x%llx visited 0x%llx ino 0x%llx",
MAJOR(__entry->dev), MINOR(__entry->dev),
__entry->cursor,
__entry->visited,
__entry->ino)
)
#define DEFINE_ISCAN_INO_EVENT(name) \
DEFINE_EVENT(xchk_iscan_ino_class, name, \
TP_PROTO(struct xchk_iscan *iscan, xfs_ino_t ino), \
TP_ARGS(iscan, ino))
DEFINE_ISCAN_INO_EVENT(xchk_iscan_want_live_update);
TRACE_EVENT(xchk_iscan_iget,
TP_PROTO(struct xchk_iscan *iscan, int error),
TP_ARGS(iscan, error),
TP_STRUCT__entry(
__field(dev_t, dev)
__field(xfs_ino_t, cursor)
__field(xfs_ino_t, visited)
__field(int, error)
),
TP_fast_assign(
__entry->dev = iscan->sc->mp->m_super->s_dev;
__entry->cursor = iscan->cursor_ino;
__entry->visited = iscan->__visited_ino;
__entry->error = error;
),
TP_printk("dev %d:%d iscan cursor 0x%llx visited 0x%llx error %d",
MAJOR(__entry->dev), MINOR(__entry->dev),
__entry->cursor,
__entry->visited,
__entry->error)
);
TRACE_EVENT(xchk_iscan_iget_retry_wait,
TP_PROTO(struct xchk_iscan *iscan),
TP_ARGS(iscan),
TP_STRUCT__entry(
__field(dev_t, dev)
__field(xfs_ino_t, cursor)
__field(xfs_ino_t, visited)
__field(unsigned int, retry_delay)
__field(unsigned long, remaining)
__field(unsigned int, iget_timeout)
),
TP_fast_assign(
__entry->dev = iscan->sc->mp->m_super->s_dev;
__entry->cursor = iscan->cursor_ino;
__entry->visited = iscan->__visited_ino;
__entry->retry_delay = iscan->iget_retry_delay;
__entry->remaining = jiffies_to_msecs(iscan->__iget_deadline - jiffies);
__entry->iget_timeout = iscan->iget_timeout;
),
TP_printk("dev %d:%d iscan cursor 0x%llx visited 0x%llx remaining %lu timeout %u delay %u",
MAJOR(__entry->dev), MINOR(__entry->dev),
__entry->cursor,
__entry->visited,
__entry->remaining,
__entry->iget_timeout,
__entry->retry_delay)
);
/* repair tracepoints */
#if IS_ENABLED(CONFIG_XFS_ONLINE_REPAIR)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment