Commit 10d587ec authored by Darrick J. Wong's avatar Darrick J. Wong

xfs: check AGI unlinked inode buckets

Look for corruptions in the AGI unlinked bucket chains.
Signed-off-by: default avatarDarrick J. Wong <djwong@kernel.org>
Reviewed-by: default avatarChristoph Hellwig <hch@lst.de>
parent 2651923d
...@@ -15,6 +15,7 @@ ...@@ -15,6 +15,7 @@
#include "xfs_ialloc.h" #include "xfs_ialloc.h"
#include "xfs_rmap.h" #include "xfs_rmap.h"
#include "xfs_ag.h" #include "xfs_ag.h"
#include "xfs_inode.h"
#include "scrub/scrub.h" #include "scrub/scrub.h"
#include "scrub/common.h" #include "scrub/common.h"
...@@ -865,6 +866,43 @@ xchk_agi_xref( ...@@ -865,6 +866,43 @@ xchk_agi_xref(
/* scrub teardown will take care of sc->sa for us */ /* scrub teardown will take care of sc->sa for us */
} }
/*
* Check the unlinked buckets for links to bad inodes. We hold the AGI, so
* there cannot be any threads updating unlinked list pointers in this AG.
*/
STATIC void
xchk_iunlink(
struct xfs_scrub *sc,
struct xfs_agi *agi)
{
unsigned int i;
struct xfs_inode *ip;
for (i = 0; i < XFS_AGI_UNLINKED_BUCKETS; i++) {
xfs_agino_t agino = be32_to_cpu(agi->agi_unlinked[i]);
while (agino != NULLAGINO) {
if (agino % XFS_AGI_UNLINKED_BUCKETS != i) {
xchk_block_set_corrupt(sc, sc->sa.agi_bp);
return;
}
ip = xfs_iunlink_lookup(sc->sa.pag, agino);
if (!ip) {
xchk_block_set_corrupt(sc, sc->sa.agi_bp);
return;
}
if (!xfs_inode_on_unlinked_list(ip)) {
xchk_block_set_corrupt(sc, sc->sa.agi_bp);
return;
}
agino = ip->i_next_unlinked;
}
}
}
/* Scrub the AGI. */ /* Scrub the AGI. */
int int
xchk_agi( xchk_agi(
...@@ -949,6 +987,8 @@ xchk_agi( ...@@ -949,6 +987,8 @@ xchk_agi(
if (pag->pagi_freecount != be32_to_cpu(agi->agi_freecount)) if (pag->pagi_freecount != be32_to_cpu(agi->agi_freecount))
xchk_block_set_corrupt(sc, sc->sa.agi_bp); xchk_block_set_corrupt(sc, sc->sa.agi_bp);
xchk_iunlink(sc, agi);
xchk_agi_xref(sc); xchk_agi_xref(sc);
out: out:
return error; return error;
......
...@@ -1985,7 +1985,7 @@ xfs_inactive( ...@@ -1985,7 +1985,7 @@ xfs_inactive(
* only unlinked, referenced inodes can be on the unlinked inode list. If we * only unlinked, referenced inodes can be on the unlinked inode list. If we
* don't find the inode in cache, then let the caller handle the situation. * don't find the inode in cache, then let the caller handle the situation.
*/ */
static struct xfs_inode * struct xfs_inode *
xfs_iunlink_lookup( xfs_iunlink_lookup(
struct xfs_perag *pag, struct xfs_perag *pag,
xfs_agino_t agino) xfs_agino_t agino)
......
...@@ -619,6 +619,7 @@ bool xfs_inode_needs_inactive(struct xfs_inode *ip); ...@@ -619,6 +619,7 @@ bool xfs_inode_needs_inactive(struct xfs_inode *ip);
int xfs_iunlink(struct xfs_trans *tp, struct xfs_inode *ip); int xfs_iunlink(struct xfs_trans *tp, struct xfs_inode *ip);
int xfs_iunlink_remove(struct xfs_trans *tp, struct xfs_perag *pag, int xfs_iunlink_remove(struct xfs_trans *tp, struct xfs_perag *pag,
struct xfs_inode *ip); struct xfs_inode *ip);
struct xfs_inode *xfs_iunlink_lookup(struct xfs_perag *pag, xfs_agino_t agino);
void xfs_end_io(struct work_struct *work); void xfs_end_io(struct work_struct *work);
......
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