2023-04-12 01:59:56 +00:00
|
|
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
2017-10-18 04:37:36 +00:00
|
|
|
/*
|
2023-04-12 01:59:57 +00:00
|
|
|
* Copyright (C) 2017-2023 Oracle. All Rights Reserved.
|
2023-04-12 01:59:56 +00:00
|
|
|
* Author: Darrick J. Wong <djwong@kernel.org>
|
2017-10-18 04:37:36 +00:00
|
|
|
*/
|
|
|
|
#ifndef __XFS_SCRUB_COMMON_H__
|
|
|
|
#define __XFS_SCRUB_COMMON_H__
|
|
|
|
|
|
|
|
/*
|
|
|
|
* We /could/ terminate a scrub/repair operation early. If we're not
|
|
|
|
* in a good place to continue (fatal signal, etc.) then bail out.
|
|
|
|
* Note that we're careful not to make any judgements about *error.
|
|
|
|
*/
|
|
|
|
static inline bool
|
2018-07-19 19:29:11 +00:00
|
|
|
xchk_should_terminate(
|
2018-07-19 19:29:12 +00:00
|
|
|
struct xfs_scrub *sc,
|
2019-11-05 23:33:57 +00:00
|
|
|
int *error)
|
2017-10-18 04:37:36 +00:00
|
|
|
{
|
2019-11-05 23:33:57 +00:00
|
|
|
/*
|
|
|
|
* If preemption is disabled, we need to yield to the scheduler every
|
|
|
|
* few seconds so that we don't run afoul of the soft lockup watchdog
|
|
|
|
* or RCU stall detector.
|
|
|
|
*/
|
|
|
|
cond_resched();
|
|
|
|
|
2017-10-18 04:37:36 +00:00
|
|
|
if (fatal_signal_pending(current)) {
|
|
|
|
if (*error == 0)
|
2022-11-07 01:03:16 +00:00
|
|
|
*error = -EINTR;
|
2017-10-18 04:37:36 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2018-07-19 19:29:12 +00:00
|
|
|
int xchk_trans_alloc(struct xfs_scrub *sc, uint resblks);
|
2023-04-12 02:00:21 +00:00
|
|
|
void xchk_trans_cancel(struct xfs_scrub *sc);
|
|
|
|
|
2018-07-19 19:29:12 +00:00
|
|
|
bool xchk_process_error(struct xfs_scrub *sc, xfs_agnumber_t agno,
|
2017-10-18 04:37:36 +00:00
|
|
|
xfs_agblock_t bno, int *error);
|
2018-07-19 19:29:12 +00:00
|
|
|
bool xchk_fblock_process_error(struct xfs_scrub *sc, int whichfork,
|
2017-10-18 04:37:36 +00:00
|
|
|
xfs_fileoff_t offset, int *error);
|
|
|
|
|
2018-07-19 19:29:12 +00:00
|
|
|
bool xchk_xref_process_error(struct xfs_scrub *sc,
|
2018-01-17 02:52:14 +00:00
|
|
|
xfs_agnumber_t agno, xfs_agblock_t bno, int *error);
|
2018-07-19 19:29:12 +00:00
|
|
|
bool xchk_fblock_xref_process_error(struct xfs_scrub *sc,
|
2018-01-17 02:52:14 +00:00
|
|
|
int whichfork, xfs_fileoff_t offset, int *error);
|
|
|
|
|
2018-07-19 19:29:12 +00:00
|
|
|
void xchk_block_set_preen(struct xfs_scrub *sc,
|
2017-10-18 04:37:36 +00:00
|
|
|
struct xfs_buf *bp);
|
2018-07-19 19:29:12 +00:00
|
|
|
void xchk_ino_set_preen(struct xfs_scrub *sc, xfs_ino_t ino);
|
2017-10-18 04:37:36 +00:00
|
|
|
|
2019-04-26 01:26:24 +00:00
|
|
|
void xchk_set_corrupt(struct xfs_scrub *sc);
|
2018-07-19 19:29:12 +00:00
|
|
|
void xchk_block_set_corrupt(struct xfs_scrub *sc,
|
2017-10-18 04:37:36 +00:00
|
|
|
struct xfs_buf *bp);
|
2018-07-19 19:29:12 +00:00
|
|
|
void xchk_ino_set_corrupt(struct xfs_scrub *sc, xfs_ino_t ino);
|
|
|
|
void xchk_fblock_set_corrupt(struct xfs_scrub *sc, int whichfork,
|
2017-10-18 04:37:36 +00:00
|
|
|
xfs_fileoff_t offset);
|
|
|
|
|
2018-07-19 19:29:12 +00:00
|
|
|
void xchk_block_xref_set_corrupt(struct xfs_scrub *sc,
|
2018-01-17 02:52:14 +00:00
|
|
|
struct xfs_buf *bp);
|
2018-07-19 19:29:12 +00:00
|
|
|
void xchk_ino_xref_set_corrupt(struct xfs_scrub *sc,
|
2018-03-23 17:06:54 +00:00
|
|
|
xfs_ino_t ino);
|
2018-07-19 19:29:12 +00:00
|
|
|
void xchk_fblock_xref_set_corrupt(struct xfs_scrub *sc,
|
2018-01-17 02:52:14 +00:00
|
|
|
int whichfork, xfs_fileoff_t offset);
|
|
|
|
|
2018-07-19 19:29:12 +00:00
|
|
|
void xchk_ino_set_warning(struct xfs_scrub *sc, xfs_ino_t ino);
|
|
|
|
void xchk_fblock_set_warning(struct xfs_scrub *sc, int whichfork,
|
2017-10-18 04:37:36 +00:00
|
|
|
xfs_fileoff_t offset);
|
|
|
|
|
2018-07-19 19:29:12 +00:00
|
|
|
void xchk_set_incomplete(struct xfs_scrub *sc);
|
2018-07-19 19:29:11 +00:00
|
|
|
int xchk_checkpoint_log(struct xfs_mount *mp);
|
2017-10-18 04:37:36 +00:00
|
|
|
|
2018-01-17 02:52:14 +00:00
|
|
|
/* Are we set up for a cross-referencing check? */
|
2018-07-19 19:29:12 +00:00
|
|
|
bool xchk_should_check_xref(struct xfs_scrub *sc, int *error,
|
2018-01-17 02:52:14 +00:00
|
|
|
struct xfs_btree_cur **curpp);
|
|
|
|
|
2017-10-18 04:37:36 +00:00
|
|
|
/* Setup functions */
|
2023-04-12 01:59:59 +00:00
|
|
|
int xchk_setup_agheader(struct xfs_scrub *sc);
|
2021-04-08 00:59:39 +00:00
|
|
|
int xchk_setup_fs(struct xfs_scrub *sc);
|
|
|
|
int xchk_setup_ag_allocbt(struct xfs_scrub *sc);
|
|
|
|
int xchk_setup_ag_iallocbt(struct xfs_scrub *sc);
|
|
|
|
int xchk_setup_ag_rmapbt(struct xfs_scrub *sc);
|
|
|
|
int xchk_setup_ag_refcountbt(struct xfs_scrub *sc);
|
|
|
|
int xchk_setup_inode(struct xfs_scrub *sc);
|
|
|
|
int xchk_setup_inode_bmap(struct xfs_scrub *sc);
|
|
|
|
int xchk_setup_inode_bmap_data(struct xfs_scrub *sc);
|
|
|
|
int xchk_setup_directory(struct xfs_scrub *sc);
|
|
|
|
int xchk_setup_xattr(struct xfs_scrub *sc);
|
|
|
|
int xchk_setup_symlink(struct xfs_scrub *sc);
|
|
|
|
int xchk_setup_parent(struct xfs_scrub *sc);
|
2017-10-18 04:37:46 +00:00
|
|
|
#ifdef CONFIG_XFS_RT
|
2023-08-10 14:48:09 +00:00
|
|
|
int xchk_setup_rtbitmap(struct xfs_scrub *sc);
|
|
|
|
int xchk_setup_rtsummary(struct xfs_scrub *sc);
|
2017-10-18 04:37:46 +00:00
|
|
|
#else
|
|
|
|
static inline int
|
2023-08-10 14:48:09 +00:00
|
|
|
xchk_setup_rtbitmap(struct xfs_scrub *sc)
|
|
|
|
{
|
|
|
|
return -ENOENT;
|
|
|
|
}
|
|
|
|
static inline int
|
|
|
|
xchk_setup_rtsummary(struct xfs_scrub *sc)
|
2017-10-18 04:37:47 +00:00
|
|
|
{
|
|
|
|
return -ENOENT;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
#ifdef CONFIG_XFS_QUOTA
|
2021-04-08 00:59:39 +00:00
|
|
|
int xchk_setup_quota(struct xfs_scrub *sc);
|
2017-10-18 04:37:47 +00:00
|
|
|
#else
|
|
|
|
static inline int
|
2021-04-08 00:59:39 +00:00
|
|
|
xchk_setup_quota(struct xfs_scrub *sc)
|
2017-10-18 04:37:46 +00:00
|
|
|
{
|
|
|
|
return -ENOENT;
|
|
|
|
}
|
|
|
|
#endif
|
2021-04-08 00:59:39 +00:00
|
|
|
int xchk_setup_fscounters(struct xfs_scrub *sc);
|
2017-10-18 04:37:36 +00:00
|
|
|
|
2018-07-19 19:29:12 +00:00
|
|
|
void xchk_ag_free(struct xfs_scrub *sc, struct xchk_ag *sa);
|
|
|
|
int xchk_ag_init(struct xfs_scrub *sc, xfs_agnumber_t agno,
|
2018-07-19 19:29:12 +00:00
|
|
|
struct xchk_ag *sa);
|
2021-08-06 18:06:35 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Grab all AG resources, treating the inability to grab the perag structure as
|
|
|
|
* a fs corruption. This is intended for callers checking an ondisk reference
|
|
|
|
* to a given AG, which means that the AG must still exist.
|
|
|
|
*/
|
|
|
|
static inline int
|
|
|
|
xchk_ag_init_existing(
|
|
|
|
struct xfs_scrub *sc,
|
|
|
|
xfs_agnumber_t agno,
|
|
|
|
struct xchk_ag *sa)
|
|
|
|
{
|
|
|
|
int error = xchk_ag_init(sc, agno, sa);
|
|
|
|
|
|
|
|
return error == -ENOENT ? -EFSCORRUPTED : error;
|
|
|
|
}
|
|
|
|
|
2018-07-19 19:29:12 +00:00
|
|
|
int xchk_ag_read_headers(struct xfs_scrub *sc, xfs_agnumber_t agno,
|
2021-03-22 16:51:53 +00:00
|
|
|
struct xchk_ag *sa);
|
2018-07-19 19:29:11 +00:00
|
|
|
void xchk_ag_btcur_free(struct xchk_ag *sa);
|
2021-03-22 16:51:53 +00:00
|
|
|
void xchk_ag_btcur_init(struct xfs_scrub *sc, struct xchk_ag *sa);
|
2018-07-19 19:29:12 +00:00
|
|
|
int xchk_count_rmap_ownedby_ag(struct xfs_scrub *sc, struct xfs_btree_cur *cur,
|
2018-12-12 16:46:23 +00:00
|
|
|
const struct xfs_owner_info *oinfo, xfs_filblks_t *blocks);
|
2017-10-18 04:37:38 +00:00
|
|
|
|
2021-04-08 00:59:39 +00:00
|
|
|
int xchk_setup_ag_btree(struct xfs_scrub *sc, bool force_log);
|
2023-04-12 02:00:21 +00:00
|
|
|
int xchk_iget_for_scrubbing(struct xfs_scrub *sc);
|
2021-04-08 00:59:39 +00:00
|
|
|
int xchk_setup_inode_contents(struct xfs_scrub *sc, unsigned int resblks);
|
2023-08-10 14:48:08 +00:00
|
|
|
int xchk_install_live_inode(struct xfs_scrub *sc, struct xfs_inode *ip);
|
2023-08-10 14:48:08 +00:00
|
|
|
|
|
|
|
void xchk_ilock(struct xfs_scrub *sc, unsigned int ilock_flags);
|
|
|
|
bool xchk_ilock_nowait(struct xfs_scrub *sc, unsigned int ilock_flags);
|
|
|
|
void xchk_iunlock(struct xfs_scrub *sc, unsigned int ilock_flags);
|
|
|
|
|
2018-07-19 19:29:12 +00:00
|
|
|
void xchk_buffer_recheck(struct xfs_scrub *sc, struct xfs_buf *bp);
|
2017-10-18 04:37:40 +00:00
|
|
|
|
xfs: manage inode DONTCACHE status at irele time
Right now, there are statements scattered all over the online fsck
codebase about how we can't use XFS_IGET_DONTCACHE because of concerns
about scrub's unusual practice of releasing inodes with transactions
held.
However, iget is the wrong place to handle this -- the DONTCACHE state
doesn't matter at all until we try to *release* the inode, and here we
get things wrong in multiple ways:
First, if we /do/ have a transaction, we must NOT drop the inode,
because the inode could have dirty pages, dropping the inode will
trigger writeback, and writeback can trigger a nested transaction.
Second, if the inode already had an active reference and the DONTCACHE
flag set, the icache hit when scrub grabs another ref will not clear
DONTCACHE. This is sort of by design, since DONTCACHE is now used to
initiate cache drops so that sysadmins can change a file's access mode
between pagecache and DAX.
Third, if we do actually have the last active reference to the inode, we
can set DONTCACHE to avoid polluting the cache. This is the /one/ case
where we actually want that flag.
Create an xchk_irele helper to encode all that logic and switch the
online fsck code to use it. Since this now means that nearly all
scrubbers use the same xfs_iget flags, we can wrap them too.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Dave Chinner <dchinner@redhat.com>
2023-04-12 02:00:20 +00:00
|
|
|
int xchk_iget(struct xfs_scrub *sc, xfs_ino_t inum, struct xfs_inode **ipp);
|
2023-04-12 02:00:21 +00:00
|
|
|
int xchk_iget_agi(struct xfs_scrub *sc, xfs_ino_t inum,
|
|
|
|
struct xfs_buf **agi_bpp, struct xfs_inode **ipp);
|
xfs: manage inode DONTCACHE status at irele time
Right now, there are statements scattered all over the online fsck
codebase about how we can't use XFS_IGET_DONTCACHE because of concerns
about scrub's unusual practice of releasing inodes with transactions
held.
However, iget is the wrong place to handle this -- the DONTCACHE state
doesn't matter at all until we try to *release* the inode, and here we
get things wrong in multiple ways:
First, if we /do/ have a transaction, we must NOT drop the inode,
because the inode could have dirty pages, dropping the inode will
trigger writeback, and writeback can trigger a nested transaction.
Second, if the inode already had an active reference and the DONTCACHE
flag set, the icache hit when scrub grabs another ref will not clear
DONTCACHE. This is sort of by design, since DONTCACHE is now used to
initiate cache drops so that sysadmins can change a file's access mode
between pagecache and DAX.
Third, if we do actually have the last active reference to the inode, we
can set DONTCACHE to avoid polluting the cache. This is the /one/ case
where we actually want that flag.
Create an xchk_irele helper to encode all that logic and switch the
online fsck code to use it. Since this now means that nearly all
scrubbers use the same xfs_iget flags, we can wrap them too.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Dave Chinner <dchinner@redhat.com>
2023-04-12 02:00:20 +00:00
|
|
|
void xchk_irele(struct xfs_scrub *sc, struct xfs_inode *ip);
|
xfs: retain the AGI when we can't iget an inode to scrub the core
xchk_get_inode is not quite the right function to be calling from the
inode scrubber setup function. The common get_inode function either
gets an inode and installs it in the scrub context, or it returns an
error code explaining what happened. This is acceptable for most file
scrubbers because it is not in their scope to fix corruptions in the
inode core and fork areas that cause iget to fail.
Dealing with these problems is within the scope of the inode scrubber,
however. If iget fails with EFSCORRUPTED, we need to xchk_inode to flag
that as corruption. Since we can't get our hands on an incore inode, we
need to hold the AGI to prevent inode allocation activity so that
nothing changes in the inode metadata.
Looking ahead to the inode core repair patches, we will also need to
hold the AGI buffer into xrep_inode so that we can make modifications to
the xfs_dinode structure without any other thread swooping in to
allocate or free the inode.
Adapt the xchk_get_inode into xchk_setup_inode since this is a one-off
use case where the error codes we check for are a little different, and
the return state is much different from the common function.
xchk_setup_inode prepares to check or repair an inode record, so it must
continue the scrub operation even if the inode/inobt verifiers cause
xfs_iget to return EFSCORRUPTED. This is done by attaching the locked
AGI buffer to the scrub transaction and returning 0 to move on to the
actual scrub. (Later, the online inode repair code will also want the
xfs_imap structure so that it can reset the ondisk xfs_dinode
structure.)
xchk_get_inode retrieves an inode on behalf of a scrubber that operates
on an incore inode -- data/attr/cow forks, directories, xattrs,
symlinks, parent pointers, etc. If the inode/inobt verifiers fail and
xfs_iget returns EFSCORRUPTED, we want to exit to userspace (because the
caller should be fix the inode first) and drop everything we acquired
along the way.
A behavior common to both functions is that it's possible that xfs_scrub
asked for a scrub-by-handle concurrent with the inode being freed or the
passed-in inumber is invalid. In this case, we call xfs_imap to see if
the inobt index thinks the inode is allocated, and return ENOENT
("nothing to check here") to userspace if this is not the case. The
imap lookup is why both functions call xchk_iget_agi.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Dave Chinner <dchinner@redhat.com>
2023-04-12 02:00:22 +00:00
|
|
|
int xchk_install_handle_inode(struct xfs_scrub *sc, struct xfs_inode *ip);
|
xfs: manage inode DONTCACHE status at irele time
Right now, there are statements scattered all over the online fsck
codebase about how we can't use XFS_IGET_DONTCACHE because of concerns
about scrub's unusual practice of releasing inodes with transactions
held.
However, iget is the wrong place to handle this -- the DONTCACHE state
doesn't matter at all until we try to *release* the inode, and here we
get things wrong in multiple ways:
First, if we /do/ have a transaction, we must NOT drop the inode,
because the inode could have dirty pages, dropping the inode will
trigger writeback, and writeback can trigger a nested transaction.
Second, if the inode already had an active reference and the DONTCACHE
flag set, the icache hit when scrub grabs another ref will not clear
DONTCACHE. This is sort of by design, since DONTCACHE is now used to
initiate cache drops so that sysadmins can change a file's access mode
between pagecache and DAX.
Third, if we do actually have the last active reference to the inode, we
can set DONTCACHE to avoid polluting the cache. This is the /one/ case
where we actually want that flag.
Create an xchk_irele helper to encode all that logic and switch the
online fsck code to use it. Since this now means that nearly all
scrubbers use the same xfs_iget flags, we can wrap them too.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Dave Chinner <dchinner@redhat.com>
2023-04-12 02:00:20 +00:00
|
|
|
|
2018-05-14 13:34:31 +00:00
|
|
|
/*
|
|
|
|
* Don't bother cross-referencing if we already found corruption or cross
|
|
|
|
* referencing discrepancies.
|
|
|
|
*/
|
2018-07-19 19:29:11 +00:00
|
|
|
static inline bool xchk_skip_xref(struct xfs_scrub_metadata *sm)
|
2018-05-14 13:34:31 +00:00
|
|
|
{
|
|
|
|
return sm->sm_flags & (XFS_SCRUB_OFLAG_CORRUPT |
|
|
|
|
XFS_SCRUB_OFLAG_XCORRUPT);
|
|
|
|
}
|
|
|
|
|
2023-08-10 14:48:10 +00:00
|
|
|
#ifdef CONFIG_XFS_ONLINE_REPAIR
|
|
|
|
/* Decide if a repair is required. */
|
|
|
|
static inline bool xchk_needs_repair(const struct xfs_scrub_metadata *sm)
|
|
|
|
{
|
|
|
|
return sm->sm_flags & (XFS_SCRUB_OFLAG_CORRUPT |
|
|
|
|
XFS_SCRUB_OFLAG_XCORRUPT |
|
|
|
|
XFS_SCRUB_OFLAG_PREEN);
|
|
|
|
}
|
|
|
|
#else
|
|
|
|
# define xchk_needs_repair(sc) (false)
|
|
|
|
#endif /* CONFIG_XFS_ONLINE_REPAIR */
|
|
|
|
|
2018-07-19 19:29:12 +00:00
|
|
|
int xchk_metadata_inode_forks(struct xfs_scrub *sc);
|
2018-05-14 13:34:33 +00:00
|
|
|
|
2023-08-10 14:48:09 +00:00
|
|
|
/*
|
|
|
|
* Helper macros to allocate and format xfile description strings.
|
|
|
|
* Callers must kfree the pointer returned.
|
|
|
|
*/
|
|
|
|
#define xchk_xfile_descr(sc, fmt, ...) \
|
|
|
|
kasprintf(XCHK_GFP_FLAGS, "XFS (%s): " fmt, \
|
|
|
|
(sc)->mp->m_super->s_id, ##__VA_ARGS__)
|
|
|
|
|
2023-04-12 01:59:59 +00:00
|
|
|
/*
|
|
|
|
* Setting up a hook to wait for intents to drain is costly -- we have to take
|
|
|
|
* the CPU hotplug lock and force an i-cache flush on all CPUs once to set it
|
|
|
|
* up, and again to tear it down. These costs add up quickly, so we only want
|
|
|
|
* to enable the drain waiter if the drain actually detected a conflict with
|
|
|
|
* running intent chains.
|
|
|
|
*/
|
|
|
|
static inline bool xchk_need_intent_drain(struct xfs_scrub *sc)
|
|
|
|
{
|
2023-04-12 02:00:00 +00:00
|
|
|
return sc->flags & XCHK_NEED_DRAIN;
|
2023-04-12 01:59:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void xchk_fsgates_enable(struct xfs_scrub *sc, unsigned int scrub_fshooks);
|
|
|
|
|
xfs: rewrite xchk_inode_is_allocated to work properly
Back in the mists of time[1], I proposed this function to assist the
inode btree scrubbers in checking the inode btree contents against the
allocation state of the inode records. The original version performed a
direct lookup in the inode cache and returned the allocation status if
the cached inode hadn't been reused and wasn't in an intermediate state.
Brian thought it would be better to use the usual iget/irele mechanisms,
so that was changed for the final version.
Unfortunately, this hasn't aged well -- the IGET_INCORE flag only has
one user and clutters up the regular iget path, which makes it hard to
reason about how it actually works. Worse yet, the inode inactivation
series silently broke it because iget won't return inodes that are
anywhere in the inactivation machinery, even though the caller is
already required to prevent inode allocation and freeing. Inodes in the
inactivation machinery are still allocated, but the current code's
interactions with the iget code prevent us from being able to say that.
Now that I understand the inode lifecycle better than I did in early
2017, I now realize that as long as the cached inode hasn't been reused
and isn't actively being reclaimed, it's safe to access the i_mode field
(with the AGI, rcu, and i_flags locks held), and we don't need to worry
about the inode being freed out from under us.
Therefore, port the original version to modern code structure, which
fixes the brokennes w.r.t. inactivation. In the next patch we'll remove
IGET_INCORE since it's no longer necessary.
[1] https://lore.kernel.org/linux-xfs/149643868294.23065.8094890990886436794.stgit@birch.djwong.org/
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Dave Chinner <dchinner@redhat.com>
2023-08-10 14:48:12 +00:00
|
|
|
int xchk_inode_is_allocated(struct xfs_scrub *sc, xfs_agino_t agino,
|
|
|
|
bool *inuse);
|
2023-08-10 14:48:12 +00:00
|
|
|
|
2017-10-18 04:37:36 +00:00
|
|
|
#endif /* __XFS_SCRUB_COMMON_H__ */
|