Commit 928b721a authored by Darrick J. Wong's avatar Darrick J. Wong

xfs: teach online scrub to find directory tree structure problems

Create a new scrubber that detects corruptions within the directory tree
structure itself.  It can detect directories with multiple parents;
loops within the directory tree; and directory loops not accessible from
the root.
Signed-off-by: default avatarDarrick J. Wong <djwong@kernel.org>
Reviewed-by: default avatarChristoph Hellwig <hch@lst.de>
parent 327ed702
......@@ -163,6 +163,7 @@ xfs-y += $(addprefix scrub/, \
common.o \
dabtree.o \
dir.o \
dirtree.o \
fscounters.o \
health.o \
ialloc.o \
......
......@@ -719,9 +719,10 @@ struct xfs_scrub_metadata {
#define XFS_SCRUB_TYPE_QUOTACHECK 25 /* quota counters */
#define XFS_SCRUB_TYPE_NLINKS 26 /* inode link counts */
#define XFS_SCRUB_TYPE_HEALTHY 27 /* everything checked out ok */
#define XFS_SCRUB_TYPE_DIRTREE 28 /* directory tree structure */
/* Number of scrub subcommands. */
#define XFS_SCRUB_TYPE_NR 28
#define XFS_SCRUB_TYPE_NR 29
/* i: Repair this metadata. */
#define XFS_SCRUB_IFLAG_REPAIR (1u << 0)
......
......@@ -92,6 +92,7 @@ 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);
int xchk_setup_dirtree(struct xfs_scrub *sc);
#ifdef CONFIG_XFS_RT
int xchk_setup_rtbitmap(struct xfs_scrub *sc);
int xchk_setup_rtsummary(struct xfs_scrub *sc);
......
This diff is collapsed.
/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* Copyright (c) 2023-2024 Oracle. All Rights Reserved.
* Author: Darrick J. Wong <djwong@kernel.org>
*/
#ifndef __XFS_SCRUB_DIRTREE_H__
#define __XFS_SCRUB_DIRTREE_H__
/*
* Each of these represents one parent pointer path step in a chain going
* up towards the directory tree root. These are stored inside an xfarray.
*/
struct xchk_dirpath_step {
/* Directory entry name associated with this parent link. */
xfblob_cookie name_cookie;
unsigned int name_len;
/* Handle of the parent directory. */
struct xfs_parent_rec pptr_rec;
};
enum xchk_dirpath_outcome {
XCHK_DIRPATH_SCANNING = 0, /* still being put together */
XCHK_DIRPATH_DELETE, /* delete this path */
XCHK_DIRPATH_CORRUPT, /* corruption detected in path */
XCHK_DIRPATH_LOOP, /* cycle detected further up */
XCHK_DIRPATH_STALE, /* path is stale */
XCHK_DIRPATH_OK, /* path reaches the root */
};
/*
* Each of these represents one parent pointer path out of the directory being
* scanned. These exist in-core, and hopefully there aren't more than a
* handful of them.
*/
struct xchk_dirpath {
struct list_head list;
/* Index of the first step in this path. */
xfarray_idx_t first_step;
/* Index of the second step in this path. */
xfarray_idx_t second_step;
/* Inodes seen while walking this path. */
struct xino_bitmap seen_inodes;
/* Number of steps in this path. */
unsigned int nr_steps;
/* Which path is this? */
unsigned int path_nr;
/* What did we conclude from following this path? */
enum xchk_dirpath_outcome outcome;
};
struct xchk_dirtree_outcomes {
/* Number of XCHK_DIRPATH_DELETE */
unsigned int bad;
/* Number of XCHK_DIRPATH_CORRUPT or XCHK_DIRPATH_LOOP */
unsigned int suspect;
/* Number of XCHK_DIRPATH_OK */
unsigned int good;
};
struct xchk_dirtree {
struct xfs_scrub *sc;
/* Root inode that we're looking for. */
xfs_ino_t root_ino;
/* Scratch buffer for scanning pptr xattrs */
struct xfs_parent_rec pptr_rec;
struct xfs_da_args pptr_args;
/* Name buffer */
struct xfs_name xname;
char namebuf[MAXNAMELEN];
/* lock for everything below here */
struct mutex lock;
/*
* All path steps observed during this scan. Each of the path
* steps for a particular pathwalk are recorded in sequential
* order in the xfarray. A pathwalk ends either with a step
* pointing to the root directory (success) or pointing to NULLFSINO
* (loop detected, empty dir detected, etc).
*/
struct xfarray *path_steps;
/* All names observed during this scan. */
struct xfblob *path_names;
/* All paths being tracked by this scanner. */
struct list_head path_list;
/* Number of paths in path_list. */
unsigned int nr_paths;
/* Number of parents found by a pptr scan. */
unsigned int parents_found;
/* Have the path data been invalidated by a concurrent update? */
bool stale:1;
};
#define xchk_dirtree_for_each_path_safe(dl, path, n) \
list_for_each_entry_safe((path), (n), &(dl)->path_list, list)
#define xchk_dirtree_for_each_path(dl, path) \
list_for_each_entry((path), &(dl)->path_list, list)
static inline bool
xchk_dirtree_parentless(const struct xchk_dirtree *dl)
{
struct xfs_scrub *sc = dl->sc;
if (sc->ip == sc->mp->m_rootip)
return true;
if (VFS_I(sc->ip)->i_nlink == 0)
return true;
return false;
}
#endif /* __XFS_SCRUB_DIRTREE_H__ */
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (c) 2023-2024 Oracle. All Rights Reserved.
* Author: Darrick J. Wong <djwong@kernel.org>
*/
#ifndef __XFS_SCRUB_INO_BITMAP_H__
#define __XFS_SCRUB_INO_BITMAP_H__
/* Bitmaps, but for type-checked for xfs_ino_t */
struct xino_bitmap {
struct xbitmap64 inobitmap;
};
static inline void xino_bitmap_init(struct xino_bitmap *bitmap)
{
xbitmap64_init(&bitmap->inobitmap);
}
static inline void xino_bitmap_destroy(struct xino_bitmap *bitmap)
{
xbitmap64_destroy(&bitmap->inobitmap);
}
static inline int xino_bitmap_set(struct xino_bitmap *bitmap, xfs_ino_t ino)
{
return xbitmap64_set(&bitmap->inobitmap, ino, 1);
}
static inline int xino_bitmap_test(struct xino_bitmap *bitmap, xfs_ino_t ino)
{
uint64_t len = 1;
return xbitmap64_test(&bitmap->inobitmap, ino, &len);
}
#endif /* __XFS_SCRUB_INO_BITMAP_H__ */
......@@ -436,6 +436,13 @@ static const struct xchk_meta_ops meta_scrub_ops[] = {
.scrub = xchk_health_record,
.repair = xrep_notsupported,
},
[XFS_SCRUB_TYPE_DIRTREE] = { /* directory tree structure */
.type = ST_INODE,
.setup = xchk_setup_dirtree,
.scrub = xchk_dirtree,
.has = xfs_has_parent,
.repair = xrep_notsupported,
},
};
static int
......
......@@ -185,6 +185,7 @@ int xchk_directory(struct xfs_scrub *sc);
int xchk_xattr(struct xfs_scrub *sc);
int xchk_symlink(struct xfs_scrub *sc);
int xchk_parent(struct xfs_scrub *sc);
int xchk_dirtree(struct xfs_scrub *sc);
#ifdef CONFIG_XFS_RT
int xchk_rtbitmap(struct xfs_scrub *sc);
int xchk_rtsummary(struct xfs_scrub *sc);
......
......@@ -79,6 +79,7 @@ static const char *name_map[XFS_SCRUB_TYPE_NR] = {
[XFS_SCRUB_TYPE_FSCOUNTERS] = "fscounters",
[XFS_SCRUB_TYPE_QUOTACHECK] = "quotacheck",
[XFS_SCRUB_TYPE_NLINKS] = "nlinks",
[XFS_SCRUB_TYPE_DIRTREE] = "dirtree",
};
/* Format the scrub stats into a text buffer, similar to pcp style. */
......
......@@ -28,6 +28,10 @@
#include "scrub/orphanage.h"
#include "scrub/nlinks.h"
#include "scrub/fscounters.h"
#include "scrub/bitmap.h"
#include "scrub/ino_bitmap.h"
#include "scrub/xfblob.h"
#include "scrub/dirtree.h"
/* Figure out which block the btree cursor was pointing to. */
static inline xfs_fsblock_t
......
......@@ -27,6 +27,9 @@ struct xchk_nlink;
struct xchk_fscounters;
struct xfs_rmap_update_params;
struct xfs_parent_rec;
enum xchk_dirpath_outcome;
struct xchk_dirtree;
struct xchk_dirtree_outcomes;
/*
* ftrace's __print_symbolic requires that all enum values be wrapped in the
......@@ -65,6 +68,7 @@ TRACE_DEFINE_ENUM(XFS_SCRUB_TYPE_FSCOUNTERS);
TRACE_DEFINE_ENUM(XFS_SCRUB_TYPE_QUOTACHECK);
TRACE_DEFINE_ENUM(XFS_SCRUB_TYPE_NLINKS);
TRACE_DEFINE_ENUM(XFS_SCRUB_TYPE_HEALTHY);
TRACE_DEFINE_ENUM(XFS_SCRUB_TYPE_DIRTREE);
#define XFS_SCRUB_TYPE_STRINGS \
{ XFS_SCRUB_TYPE_PROBE, "probe" }, \
......@@ -94,7 +98,8 @@ TRACE_DEFINE_ENUM(XFS_SCRUB_TYPE_HEALTHY);
{ XFS_SCRUB_TYPE_FSCOUNTERS, "fscounters" }, \
{ XFS_SCRUB_TYPE_QUOTACHECK, "quotacheck" }, \
{ XFS_SCRUB_TYPE_NLINKS, "nlinks" }, \
{ XFS_SCRUB_TYPE_HEALTHY, "healthy" }
{ XFS_SCRUB_TYPE_HEALTHY, "healthy" }, \
{ XFS_SCRUB_TYPE_DIRTREE, "dirtree" }
#define XFS_SCRUB_FLAG_STRINGS \
{ XFS_SCRUB_IFLAG_REPAIR, "repair" }, \
......@@ -171,6 +176,8 @@ DEFINE_EVENT(xchk_class, name, \
DEFINE_SCRUB_EVENT(xchk_start);
DEFINE_SCRUB_EVENT(xchk_done);
DEFINE_SCRUB_EVENT(xchk_deadlock_retry);
DEFINE_SCRUB_EVENT(xchk_dirtree_start);
DEFINE_SCRUB_EVENT(xchk_dirtree_done);
DEFINE_SCRUB_EVENT(xrep_attempt);
DEFINE_SCRUB_EVENT(xrep_done);
......@@ -1576,6 +1583,187 @@ DEFINE_XCHK_PPTR_EVENT(xchk_parent_defer);
DEFINE_XCHK_PPTR_EVENT(xchk_parent_slowpath);
DEFINE_XCHK_PPTR_EVENT(xchk_parent_ultraslowpath);
DECLARE_EVENT_CLASS(xchk_dirtree_class,
TP_PROTO(struct xfs_scrub *sc, struct xfs_inode *ip,
unsigned int path_nr, const struct xfs_name *name,
const struct xfs_parent_rec *pptr),
TP_ARGS(sc, ip, path_nr, name, pptr),
TP_STRUCT__entry(
__field(dev_t, dev)
__field(unsigned int, path_nr)
__field(xfs_ino_t, child_ino)
__field(unsigned int, child_gen)
__field(xfs_ino_t, parent_ino)
__field(unsigned int, parent_gen)
__field(unsigned int, namelen)
__dynamic_array(char, name, name->len)
),
TP_fast_assign(
__entry->dev = sc->mp->m_super->s_dev;
__entry->path_nr = path_nr;
__entry->child_ino = ip->i_ino;
__entry->child_gen = VFS_I(ip)->i_generation;
__entry->parent_ino = be64_to_cpu(pptr->p_ino);
__entry->parent_gen = be32_to_cpu(pptr->p_gen);
__entry->namelen = name->len;
memcpy(__get_str(name), name->name, name->len);
),
TP_printk("dev %d:%d path %u child_ino 0x%llx child_gen 0x%x parent_ino 0x%llx parent_gen 0x%x name '%.*s'",
MAJOR(__entry->dev), MINOR(__entry->dev),
__entry->path_nr,
__entry->child_ino,
__entry->child_gen,
__entry->parent_ino,
__entry->parent_gen,
__entry->namelen,
__get_str(name))
);
#define DEFINE_XCHK_DIRTREE_EVENT(name) \
DEFINE_EVENT(xchk_dirtree_class, name, \
TP_PROTO(struct xfs_scrub *sc, struct xfs_inode *ip, \
unsigned int path_nr, const struct xfs_name *name, \
const struct xfs_parent_rec *pptr), \
TP_ARGS(sc, ip, path_nr, name, pptr))
DEFINE_XCHK_DIRTREE_EVENT(xchk_dirtree_create_path);
DEFINE_XCHK_DIRTREE_EVENT(xchk_dirpath_walk_upwards);
DECLARE_EVENT_CLASS(xchk_dirpath_class,
TP_PROTO(struct xfs_scrub *sc, struct xfs_inode *ip,
unsigned int path_nr, unsigned int step_nr,
const struct xfs_name *name,
const struct xfs_parent_rec *pptr),
TP_ARGS(sc, ip, path_nr, step_nr, name, pptr),
TP_STRUCT__entry(
__field(dev_t, dev)
__field(unsigned int, path_nr)
__field(unsigned int, step_nr)
__field(xfs_ino_t, child_ino)
__field(unsigned int, child_gen)
__field(xfs_ino_t, parent_ino)
__field(unsigned int, parent_gen)
__field(unsigned int, namelen)
__dynamic_array(char, name, name->len)
),
TP_fast_assign(
__entry->dev = sc->mp->m_super->s_dev;
__entry->path_nr = path_nr;
__entry->step_nr = step_nr;
__entry->child_ino = ip->i_ino;
__entry->child_gen = VFS_I(ip)->i_generation;
__entry->parent_ino = be64_to_cpu(pptr->p_ino);
__entry->parent_gen = be32_to_cpu(pptr->p_gen);
__entry->namelen = name->len;
memcpy(__get_str(name), name->name, name->len);
),
TP_printk("dev %d:%d path %u step %u child_ino 0x%llx child_gen 0x%x parent_ino 0x%llx parent_gen 0x%x name '%.*s'",
MAJOR(__entry->dev), MINOR(__entry->dev),
__entry->path_nr,
__entry->step_nr,
__entry->child_ino,
__entry->child_gen,
__entry->parent_ino,
__entry->parent_gen,
__entry->namelen,
__get_str(name))
);
#define DEFINE_XCHK_DIRPATH_EVENT(name) \
DEFINE_EVENT(xchk_dirpath_class, name, \
TP_PROTO(struct xfs_scrub *sc, struct xfs_inode *ip, \
unsigned int path_nr, unsigned int step_nr, \
const struct xfs_name *name, \
const struct xfs_parent_rec *pptr), \
TP_ARGS(sc, ip, path_nr, step_nr, name, pptr))
DEFINE_XCHK_DIRPATH_EVENT(xchk_dirpath_disappeared);
DEFINE_XCHK_DIRPATH_EVENT(xchk_dirpath_badgen);
DEFINE_XCHK_DIRPATH_EVENT(xchk_dirpath_nondir_parent);
DEFINE_XCHK_DIRPATH_EVENT(xchk_dirpath_unlinked_parent);
DEFINE_XCHK_DIRPATH_EVENT(xchk_dirpath_found_next_step);
TRACE_DEFINE_ENUM(XCHK_DIRPATH_SCANNING);
TRACE_DEFINE_ENUM(XCHK_DIRPATH_DELETE);
TRACE_DEFINE_ENUM(XCHK_DIRPATH_CORRUPT);
TRACE_DEFINE_ENUM(XCHK_DIRPATH_LOOP);
TRACE_DEFINE_ENUM(XCHK_DIRPATH_STALE);
TRACE_DEFINE_ENUM(XCHK_DIRPATH_OK);
#define XCHK_DIRPATH_OUTCOME_STRINGS \
{ XCHK_DIRPATH_SCANNING, "scanning" }, \
{ XCHK_DIRPATH_DELETE, "delete" }, \
{ XCHK_DIRPATH_CORRUPT, "corrupt" }, \
{ XCHK_DIRPATH_LOOP, "loop" }, \
{ XCHK_DIRPATH_STALE, "stale" }, \
{ XCHK_DIRPATH_OK, "ok" }
DECLARE_EVENT_CLASS(xchk_dirpath_outcome_class,
TP_PROTO(struct xfs_scrub *sc, unsigned long long path_nr,
unsigned int nr_steps, \
unsigned int outcome),
TP_ARGS(sc, path_nr, nr_steps, outcome),
TP_STRUCT__entry(
__field(dev_t, dev)
__field(unsigned long long, path_nr)
__field(unsigned int, nr_steps)
__field(unsigned int, outcome)
),
TP_fast_assign(
__entry->dev = sc->mp->m_super->s_dev;
__entry->path_nr = path_nr;
__entry->nr_steps = nr_steps;
__entry->outcome = outcome;
),
TP_printk("dev %d:%d path %llu steps %u outcome %s",
MAJOR(__entry->dev), MINOR(__entry->dev),
__entry->path_nr,
__entry->nr_steps,
__print_symbolic(__entry->outcome, XCHK_DIRPATH_OUTCOME_STRINGS))
);
#define DEFINE_XCHK_DIRPATH_OUTCOME_EVENT(name) \
DEFINE_EVENT(xchk_dirpath_outcome_class, name, \
TP_PROTO(struct xfs_scrub *sc, unsigned long long path_nr, \
unsigned int nr_steps, \
unsigned int outcome), \
TP_ARGS(sc, path_nr, nr_steps, outcome))
DEFINE_XCHK_DIRPATH_OUTCOME_EVENT(xchk_dirpath_set_outcome);
DEFINE_XCHK_DIRPATH_OUTCOME_EVENT(xchk_dirpath_evaluate_path);
DECLARE_EVENT_CLASS(xchk_dirtree_evaluate_class,
TP_PROTO(const struct xchk_dirtree *dl,
const struct xchk_dirtree_outcomes *oc),
TP_ARGS(dl, oc),
TP_STRUCT__entry(
__field(dev_t, dev)
__field(xfs_ino_t, ino)
__field(xfs_ino_t, rootino)
__field(unsigned int, nr_paths)
__field(unsigned int, bad)
__field(unsigned int, suspect)
__field(unsigned int, good)
),
TP_fast_assign(
__entry->dev = dl->sc->mp->m_super->s_dev;
__entry->ino = dl->sc->ip->i_ino;
__entry->rootino = dl->root_ino;
__entry->nr_paths = dl->nr_paths;
__entry->bad = oc->bad;
__entry->suspect = oc->suspect;
__entry->good = oc->good;
),
TP_printk("dev %d:%d ino 0x%llx rootino 0x%llx nr_paths %u bad %u suspect %u good %u",
MAJOR(__entry->dev), MINOR(__entry->dev),
__entry->ino,
__entry->rootino,
__entry->nr_paths,
__entry->bad,
__entry->suspect,
__entry->good)
);
#define DEFINE_XCHK_DIRTREE_EVALUATE_EVENT(name) \
DEFINE_EVENT(xchk_dirtree_evaluate_class, name, \
TP_PROTO(const struct xchk_dirtree *dl, \
const struct xchk_dirtree_outcomes *oc), \
TP_ARGS(dl, oc))
DEFINE_XCHK_DIRTREE_EVALUATE_EVENT(xchk_dirtree_evaluate);
/* repair tracepoints */
#if IS_ENABLED(CONFIG_XFS_ONLINE_REPAIR)
......
......@@ -8,6 +8,7 @@
/* xfile array index type, along with cursor initialization */
typedef uint64_t xfarray_idx_t;
#define XFARRAY_NULLIDX ((__force xfarray_idx_t)-1ULL)
#define XFARRAY_CURSOR_INIT ((__force xfarray_idx_t)0)
/* Iterate each index of an xfile array. */
......
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