Skip to content

Commit

Permalink
xfs: set up scrub cross-referencing helpers
Browse files Browse the repository at this point in the history
Create some helper functions that we'll use later to deal with problems
we might encounter while cross referencing metadata with other metadata.

Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
Reviewed-by: Dave Chinner <dchinner@redhat.com>
  • Loading branch information
Darrick J. Wong committed Jan 18, 2018
1 parent 49db55e commit 64b1256
Show file tree
Hide file tree
Showing 6 changed files with 240 additions and 22 deletions.
70 changes: 58 additions & 12 deletions fs/xfs/scrub/btree.c
Original file line number Diff line number Diff line change
Expand Up @@ -42,12 +42,14 @@
* Check for btree operation errors. See the section about handling
* operational errors in common.c.
*/
bool
xfs_scrub_btree_process_error(
static bool
__xfs_scrub_btree_process_error(
struct xfs_scrub_context *sc,
struct xfs_btree_cur *cur,
int level,
int *error)
int *error,
__u32 errflag,
void *ret_ip)
{
if (*error == 0)
return true;
Expand All @@ -60,36 +62,80 @@ xfs_scrub_btree_process_error(
case -EFSBADCRC:
case -EFSCORRUPTED:
/* Note the badness but don't abort. */
sc->sm->sm_flags |= XFS_SCRUB_OFLAG_CORRUPT;
sc->sm->sm_flags |= errflag;
*error = 0;
/* fall through */
default:
if (cur->bc_flags & XFS_BTREE_ROOT_IN_INODE)
trace_xfs_scrub_ifork_btree_op_error(sc, cur, level,
*error, __return_address);
*error, ret_ip);
else
trace_xfs_scrub_btree_op_error(sc, cur, level,
*error, __return_address);
*error, ret_ip);
break;
}
return false;
}

bool
xfs_scrub_btree_process_error(
struct xfs_scrub_context *sc,
struct xfs_btree_cur *cur,
int level,
int *error)
{
return __xfs_scrub_btree_process_error(sc, cur, level, error,
XFS_SCRUB_OFLAG_CORRUPT, __return_address);
}

bool
xfs_scrub_btree_xref_process_error(
struct xfs_scrub_context *sc,
struct xfs_btree_cur *cur,
int level,
int *error)
{
return __xfs_scrub_btree_process_error(sc, cur, level, error,
XFS_SCRUB_OFLAG_XFAIL, __return_address);
}

/* Record btree block corruption. */
void
xfs_scrub_btree_set_corrupt(
static void
__xfs_scrub_btree_set_corrupt(
struct xfs_scrub_context *sc,
struct xfs_btree_cur *cur,
int level)
int level,
__u32 errflag,
void *ret_ip)
{
sc->sm->sm_flags |= XFS_SCRUB_OFLAG_CORRUPT;
sc->sm->sm_flags |= errflag;

if (cur->bc_flags & XFS_BTREE_ROOT_IN_INODE)
trace_xfs_scrub_ifork_btree_error(sc, cur, level,
__return_address);
ret_ip);
else
trace_xfs_scrub_btree_error(sc, cur, level,
__return_address);
ret_ip);
}

void
xfs_scrub_btree_set_corrupt(
struct xfs_scrub_context *sc,
struct xfs_btree_cur *cur,
int level)
{
__xfs_scrub_btree_set_corrupt(sc, cur, level, XFS_SCRUB_OFLAG_CORRUPT,
__return_address);
}

void
xfs_scrub_btree_xref_set_corrupt(
struct xfs_scrub_context *sc,
struct xfs_btree_cur *cur,
int level)
{
__xfs_scrub_btree_set_corrupt(sc, cur, level, XFS_SCRUB_OFLAG_XCORRUPT,
__return_address);
}

/*
Expand Down
9 changes: 9 additions & 0 deletions fs/xfs/scrub/btree.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,19 @@
bool xfs_scrub_btree_process_error(struct xfs_scrub_context *sc,
struct xfs_btree_cur *cur, int level, int *error);

/* Check for btree xref operation errors. */
bool xfs_scrub_btree_xref_process_error(struct xfs_scrub_context *sc,
struct xfs_btree_cur *cur, int level,
int *error);

/* Check for btree corruption. */
void xfs_scrub_btree_set_corrupt(struct xfs_scrub_context *sc,
struct xfs_btree_cur *cur, int level);

/* Check for btree xref discrepancies. */
void xfs_scrub_btree_xref_set_corrupt(struct xfs_scrub_context *sc,
struct xfs_btree_cur *cur, int level);

struct xfs_scrub_btree;
typedef int (*xfs_scrub_btree_rec_fn)(
struct xfs_scrub_btree *bs,
Expand Down
135 changes: 125 additions & 10 deletions fs/xfs/scrub/common.c
Original file line number Diff line number Diff line change
Expand Up @@ -78,12 +78,14 @@
*/

/* Check for operational errors. */
bool
xfs_scrub_process_error(
static bool
__xfs_scrub_process_error(
struct xfs_scrub_context *sc,
xfs_agnumber_t agno,
xfs_agblock_t bno,
int *error)
int *error,
__u32 errflag,
void *ret_ip)
{
switch (*error) {
case 0:
Expand All @@ -95,24 +97,48 @@ xfs_scrub_process_error(
case -EFSBADCRC:
case -EFSCORRUPTED:
/* Note the badness but don't abort. */
sc->sm->sm_flags |= XFS_SCRUB_OFLAG_CORRUPT;
sc->sm->sm_flags |= errflag;
*error = 0;
/* fall through */
default:
trace_xfs_scrub_op_error(sc, agno, bno, *error,
__return_address);
ret_ip);
break;
}
return false;
}

/* Check for operational errors for a file offset. */
bool
xfs_scrub_fblock_process_error(
xfs_scrub_process_error(
struct xfs_scrub_context *sc,
xfs_agnumber_t agno,
xfs_agblock_t bno,
int *error)
{
return __xfs_scrub_process_error(sc, agno, bno, error,
XFS_SCRUB_OFLAG_CORRUPT, __return_address);
}

bool
xfs_scrub_xref_process_error(
struct xfs_scrub_context *sc,
xfs_agnumber_t agno,
xfs_agblock_t bno,
int *error)
{
return __xfs_scrub_process_error(sc, agno, bno, error,
XFS_SCRUB_OFLAG_XFAIL, __return_address);
}

/* Check for operational errors for a file offset. */
static bool
__xfs_scrub_fblock_process_error(
struct xfs_scrub_context *sc,
int whichfork,
xfs_fileoff_t offset,
int *error)
int *error,
__u32 errflag,
void *ret_ip)
{
switch (*error) {
case 0:
Expand All @@ -124,17 +150,39 @@ xfs_scrub_fblock_process_error(
case -EFSBADCRC:
case -EFSCORRUPTED:
/* Note the badness but don't abort. */
sc->sm->sm_flags |= XFS_SCRUB_OFLAG_CORRUPT;
sc->sm->sm_flags |= errflag;
*error = 0;
/* fall through */
default:
trace_xfs_scrub_file_op_error(sc, whichfork, offset, *error,
__return_address);
ret_ip);
break;
}
return false;
}

bool
xfs_scrub_fblock_process_error(
struct xfs_scrub_context *sc,
int whichfork,
xfs_fileoff_t offset,
int *error)
{
return __xfs_scrub_fblock_process_error(sc, whichfork, offset, error,
XFS_SCRUB_OFLAG_CORRUPT, __return_address);
}

bool
xfs_scrub_fblock_xref_process_error(
struct xfs_scrub_context *sc,
int whichfork,
xfs_fileoff_t offset,
int *error)
{
return __xfs_scrub_fblock_process_error(sc, whichfork, offset, error,
XFS_SCRUB_OFLAG_XFAIL, __return_address);
}

/*
* Handling scrub corruption/optimization/warning checks.
*
Expand Down Expand Up @@ -183,6 +231,16 @@ xfs_scrub_block_set_corrupt(
trace_xfs_scrub_block_error(sc, bp->b_bn, __return_address);
}

/* Record a corruption while cross-referencing. */
void
xfs_scrub_block_xref_set_corrupt(
struct xfs_scrub_context *sc,
struct xfs_buf *bp)
{
sc->sm->sm_flags |= XFS_SCRUB_OFLAG_XCORRUPT;
trace_xfs_scrub_block_error(sc, bp->b_bn, __return_address);
}

/*
* Record a corrupt inode. The trace data will include the block given
* by bp if bp is given; otherwise it will use the block location of the
Expand All @@ -198,6 +256,17 @@ xfs_scrub_ino_set_corrupt(
trace_xfs_scrub_ino_error(sc, ino, bp ? bp->b_bn : 0, __return_address);
}

/* Record a corruption while cross-referencing with an inode. */
void
xfs_scrub_ino_xref_set_corrupt(
struct xfs_scrub_context *sc,
xfs_ino_t ino,
struct xfs_buf *bp)
{
sc->sm->sm_flags |= XFS_SCRUB_OFLAG_XCORRUPT;
trace_xfs_scrub_ino_error(sc, ino, bp ? bp->b_bn : 0, __return_address);
}

/* Record corruption in a block indexed by a file fork. */
void
xfs_scrub_fblock_set_corrupt(
Expand All @@ -209,6 +278,17 @@ xfs_scrub_fblock_set_corrupt(
trace_xfs_scrub_fblock_error(sc, whichfork, offset, __return_address);
}

/* Record a corruption while cross-referencing a fork block. */
void
xfs_scrub_fblock_xref_set_corrupt(
struct xfs_scrub_context *sc,
int whichfork,
xfs_fileoff_t offset)
{
sc->sm->sm_flags |= XFS_SCRUB_OFLAG_XCORRUPT;
trace_xfs_scrub_fblock_error(sc, whichfork, offset, __return_address);
}

/*
* Warn about inodes that need administrative review but is not
* incorrect.
Expand Down Expand Up @@ -588,3 +668,38 @@ xfs_scrub_setup_inode_contents(
/* scrub teardown will unlock and release the inode for us */
return error;
}

/*
* Predicate that decides if we need to evaluate the cross-reference check.
* If there was an error accessing the cross-reference btree, just delete
* the cursor and skip the check.
*/
bool
xfs_scrub_should_check_xref(
struct xfs_scrub_context *sc,
int *error,
struct xfs_btree_cur **curpp)
{
if (*error == 0)
return true;

if (curpp) {
/* If we've already given up on xref, just bail out. */
if (!*curpp)
return false;

/* xref error, delete cursor and bail out. */
xfs_btree_del_cursor(*curpp, XFS_BTREE_ERROR);
*curpp = NULL;
}

sc->sm->sm_flags |= XFS_SCRUB_OFLAG_XFAIL;
trace_xfs_scrub_xref_error(sc, *error, __return_address);

/*
* Errors encountered during cross-referencing with another
* data structure should not cause this scrubber to abort.
*/
*error = 0;
return false;
}
16 changes: 16 additions & 0 deletions fs/xfs/scrub/common.h
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,11 @@ bool xfs_scrub_process_error(struct xfs_scrub_context *sc, xfs_agnumber_t agno,
bool xfs_scrub_fblock_process_error(struct xfs_scrub_context *sc, int whichfork,
xfs_fileoff_t offset, int *error);

bool xfs_scrub_xref_process_error(struct xfs_scrub_context *sc,
xfs_agnumber_t agno, xfs_agblock_t bno, int *error);
bool xfs_scrub_fblock_xref_process_error(struct xfs_scrub_context *sc,
int whichfork, xfs_fileoff_t offset, int *error);

void xfs_scrub_block_set_preen(struct xfs_scrub_context *sc,
struct xfs_buf *bp);
void xfs_scrub_ino_set_preen(struct xfs_scrub_context *sc, xfs_ino_t ino,
Expand All @@ -68,6 +73,13 @@ void xfs_scrub_ino_set_corrupt(struct xfs_scrub_context *sc, xfs_ino_t ino,
void xfs_scrub_fblock_set_corrupt(struct xfs_scrub_context *sc, int whichfork,
xfs_fileoff_t offset);

void xfs_scrub_block_xref_set_corrupt(struct xfs_scrub_context *sc,
struct xfs_buf *bp);
void xfs_scrub_ino_xref_set_corrupt(struct xfs_scrub_context *sc, xfs_ino_t ino,
struct xfs_buf *bp);
void xfs_scrub_fblock_xref_set_corrupt(struct xfs_scrub_context *sc,
int whichfork, xfs_fileoff_t offset);

void xfs_scrub_ino_set_warning(struct xfs_scrub_context *sc, xfs_ino_t ino,
struct xfs_buf *bp);
void xfs_scrub_fblock_set_warning(struct xfs_scrub_context *sc, int whichfork,
Expand All @@ -76,6 +88,10 @@ void xfs_scrub_fblock_set_warning(struct xfs_scrub_context *sc, int whichfork,
void xfs_scrub_set_incomplete(struct xfs_scrub_context *sc);
int xfs_scrub_checkpoint_log(struct xfs_mount *mp);

/* Are we set up for a cross-referencing check? */
bool xfs_scrub_should_check_xref(struct xfs_scrub_context *sc, int *error,
struct xfs_btree_cur **curpp);

/* Setup functions */
int xfs_scrub_setup_fs(struct xfs_scrub_context *sc, struct xfs_inode *ip);
int xfs_scrub_setup_ag_allocbt(struct xfs_scrub_context *sc,
Expand Down
10 changes: 10 additions & 0 deletions fs/xfs/scrub/scrub.c
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,16 @@
* structure itself is corrupt, the CORRUPT flag will be set. If
* the metadata is correct but otherwise suboptimal, the PREEN flag
* will be set.
*
* We perform secondary validation of filesystem metadata by
* cross-referencing every record with all other available metadata.
* For example, for block mapping extents, we verify that there are no
* records in the free space and inode btrees corresponding to that
* space extent and that there is a corresponding entry in the reverse
* mapping btree. Inconsistent metadata is noted by setting the
* XCORRUPT flag; btree query function errors are noted by setting the
* XFAIL flag and deleting the cursor to prevent further attempts to
* cross-reference with a defective btree.
*/

/*
Expand Down
Loading

0 comments on commit 64b1256

Please sign in to comment.