Commit dbfbf3bd authored by Darrick J. Wong's avatar Darrick J. Wong
Browse files

xfs: repair inode btrees



Use the rmapbt to find inode chunks, query the chunks to compute hole
and free masks, and with that information rebuild the inobt and finobt.
Refer to the case study in
Documentation/filesystems/xfs-online-fsck-design.rst for more details.

Signed-off-by: default avatarDarrick J. Wong <djwong@kernel.org>
Reviewed-by: default avatarDave Chinner <dchinner@redhat.com>
Reviewed-by: default avatarChristoph Hellwig <hch@lst.de>
parent 4bdfd7d1
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -183,6 +183,7 @@ ifeq ($(CONFIG_XFS_ONLINE_REPAIR),y)
xfs-y				+= $(addprefix scrub/, \
				   agheader_repair.o \
				   alloc_repair.o \
				   ialloc_repair.o \
				   newbt.o \
				   reap.o \
				   repair.o \
+18 −13
Original line number Diff line number Diff line
@@ -95,18 +95,28 @@ xfs_inobt_btrec_to_irec(
	irec->ir_free = be64_to_cpu(rec->inobt.ir_free);
}

/* Compute the freecount of an incore inode record. */
uint8_t
xfs_inobt_rec_freecount(
	const struct xfs_inobt_rec_incore	*irec)
{
	uint64_t				realfree = irec->ir_free;

	if (xfs_inobt_issparse(irec->ir_holemask))
		realfree &= xfs_inobt_irec_to_allocmask(irec);
	return hweight64(realfree);
}

/* Simple checks for inode records. */
xfs_failaddr_t
xfs_inobt_check_irec(
	struct xfs_btree_cur			*cur,
	struct xfs_perag			*pag,
	const struct xfs_inobt_rec_incore	*irec)
{
	uint64_t			realfree;

	/* Record has to be properly aligned within the AG. */
	if (!xfs_verify_agino(cur->bc_ag.pag, irec->ir_startino))
	if (!xfs_verify_agino(pag, irec->ir_startino))
		return __this_address;
	if (!xfs_verify_agino(cur->bc_ag.pag,
	if (!xfs_verify_agino(pag,
				irec->ir_startino + XFS_INODES_PER_CHUNK - 1))
		return __this_address;
	if (irec->ir_count < XFS_INODES_PER_HOLEMASK_BIT ||
@@ -115,12 +125,7 @@ xfs_inobt_check_irec(
	if (irec->ir_freecount > XFS_INODES_PER_CHUNK)
		return __this_address;

	/* if there are no holes, return the first available offset */
	if (!xfs_inobt_issparse(irec->ir_holemask))
		realfree = irec->ir_free;
	else
		realfree = irec->ir_free & xfs_inobt_irec_to_allocmask(irec);
	if (hweight64(realfree) != irec->ir_freecount)
	if (xfs_inobt_rec_freecount(irec) != irec->ir_freecount)
		return __this_address;

	return NULL;
@@ -164,7 +169,7 @@ xfs_inobt_get_rec(
		return error;

	xfs_inobt_btrec_to_irec(mp, rec, irec);
	fa = xfs_inobt_check_irec(cur, irec);
	fa = xfs_inobt_check_irec(cur->bc_ag.pag, irec);
	if (fa)
		return xfs_inobt_complain_bad_rec(cur, fa, irec);

@@ -2740,7 +2745,7 @@ xfs_ialloc_count_inodes_rec(
	xfs_failaddr_t			fa;

	xfs_inobt_btrec_to_irec(cur->bc_mp, rec, &irec);
	fa = xfs_inobt_check_irec(cur, &irec);
	fa = xfs_inobt_check_irec(cur->bc_ag.pag, &irec);
	if (fa)
		return xfs_inobt_complain_bad_rec(cur, fa, &irec);

+2 −1
Original line number Diff line number Diff line
@@ -79,6 +79,7 @@ int xfs_inobt_lookup(struct xfs_btree_cur *cur, xfs_agino_t ino,
 */
int xfs_inobt_get_rec(struct xfs_btree_cur *cur,
		xfs_inobt_rec_incore_t *rec, int *stat);
uint8_t xfs_inobt_rec_freecount(const struct xfs_inobt_rec_incore *irec);

/*
 * Inode chunk initialisation routine
@@ -93,7 +94,7 @@ union xfs_btree_rec;
void xfs_inobt_btrec_to_irec(struct xfs_mount *mp,
		const union xfs_btree_rec *rec,
		struct xfs_inobt_rec_incore *irec);
xfs_failaddr_t xfs_inobt_check_irec(struct xfs_btree_cur *cur,
xfs_failaddr_t xfs_inobt_check_irec(struct xfs_perag *pag,
		const struct xfs_inobt_rec_incore *irec);
int xfs_ialloc_has_inodes_at_extent(struct xfs_btree_cur *cur,
		xfs_agblock_t bno, xfs_extlen_t len,
+1 −0
Original line number Diff line number Diff line
@@ -604,6 +604,7 @@ xchk_ag_free(
	struct xchk_ag		*sa)
{
	xchk_ag_btcur_free(sa);
	xrep_reset_perag_resv(sc);
	if (sa->agf_bp) {
		xfs_trans_brelse(sc->tp, sa->agf_bp);
		sa->agf_bp = NULL;
+1 −1
Original line number Diff line number Diff line
@@ -585,7 +585,7 @@ xchk_iallocbt_rec(
	uint16_t			holemask;

	xfs_inobt_btrec_to_irec(mp, rec, &irec);
	if (xfs_inobt_check_irec(bs->cur, &irec) != NULL) {
	if (xfs_inobt_check_irec(bs->cur->bc_ag.pag, &irec) != NULL) {
		xchk_btree_set_corrupt(bs->sc, bs->cur, 0);
		return 0;
	}
Loading