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

xfs: cross-reference the realtime rmapbt



Teach the data fork and realtime bitmap scrubbers to cross-reference
information with the realtime rmap btree.

Signed-off-by: default avatar"Darrick J. Wong" <djwong@kernel.org>
Reviewed-by: default avatarChristoph Hellwig <hch@lst.de>
parent 1ebecab5
Loading
Loading
Loading
Loading
+37 −15
Original line number Diff line number Diff line
@@ -143,15 +143,22 @@ static inline bool
xchk_bmap_get_rmap(
	struct xchk_bmap_info	*info,
	struct xfs_bmbt_irec	*irec,
	xfs_agblock_t		agbno,
	xfs_agblock_t		bno,
	uint64_t		owner,
	struct xfs_rmap_irec	*rmap)
{
	struct xfs_btree_cur	**curp = &info->sc->sa.rmap_cur;
	xfs_fileoff_t		offset;
	unsigned int		rflags = 0;
	int			has_rmap;
	int			error;

	if (xfs_ifork_is_realtime(info->sc->ip, info->whichfork))
		curp = &info->sc->sr.rmap_cur;

	if (*curp == NULL)
		return false;

	if (info->whichfork == XFS_ATTR_FORK)
		rflags |= XFS_RMAP_ATTR_FORK;
	if (irec->br_state == XFS_EXT_UNWRITTEN)
@@ -172,13 +179,13 @@ xchk_bmap_get_rmap(
	 * range rmap lookup to make sure we get the correct owner/offset.
	 */
	if (info->is_shared) {
		error = xfs_rmap_lookup_le_range(info->sc->sa.rmap_cur, agbno,
				owner, offset, rflags, rmap, &has_rmap);
		error = xfs_rmap_lookup_le_range(*curp, bno, owner, offset,
				rflags, rmap, &has_rmap);
	} else {
		error = xfs_rmap_lookup_le(info->sc->sa.rmap_cur, agbno,
				owner, offset, rflags, rmap, &has_rmap);
		error = xfs_rmap_lookup_le(*curp, bno, owner, offset,
				rflags, rmap, &has_rmap);
	}
	if (!xchk_should_check_xref(info->sc, &error, &info->sc->sa.rmap_cur))
	if (!xchk_should_check_xref(info->sc, &error, curp))
		return false;

	if (!has_rmap)
@@ -192,29 +199,29 @@ STATIC void
xchk_bmap_xref_rmap(
	struct xchk_bmap_info	*info,
	struct xfs_bmbt_irec	*irec,
	xfs_agblock_t		agbno)
	xfs_agblock_t		bno)
{
	struct xfs_rmap_irec	rmap;
	unsigned long long	rmap_end;
	uint64_t		owner = info->sc->ip->i_ino;

	if (!info->sc->sa.rmap_cur || xchk_skip_xref(info->sc->sm))
	if (xchk_skip_xref(info->sc->sm))
		return;

	/* Find the rmap record for this irec. */
	if (!xchk_bmap_get_rmap(info, irec, agbno, owner, &rmap))
	if (!xchk_bmap_get_rmap(info, irec, bno, owner, &rmap))
		return;

	/*
	 * The rmap must be an exact match for this incore file mapping record,
	 * which may have arisen from multiple ondisk records.
	 */
	if (rmap.rm_startblock != agbno)
	if (rmap.rm_startblock != bno)
		xchk_fblock_xref_set_corrupt(info->sc, info->whichfork,
				irec->br_startoff);

	rmap_end = (unsigned long long)rmap.rm_startblock + rmap.rm_blockcount;
	if (rmap_end != agbno + irec->br_blockcount)
	if (rmap_end != bno + irec->br_blockcount)
		xchk_fblock_xref_set_corrupt(info->sc, info->whichfork,
				irec->br_startoff);

@@ -259,7 +266,7 @@ STATIC void
xchk_bmap_xref_rmap_cow(
	struct xchk_bmap_info	*info,
	struct xfs_bmbt_irec	*irec,
	xfs_agblock_t		agbno)
	xfs_agblock_t		bno)
{
	struct xfs_rmap_irec	rmap;
	unsigned long long	rmap_end;
@@ -269,7 +276,7 @@ xchk_bmap_xref_rmap_cow(
		return;

	/* Find the rmap record for this irec. */
	if (!xchk_bmap_get_rmap(info, irec, agbno, owner, &rmap))
	if (!xchk_bmap_get_rmap(info, irec, bno, owner, &rmap))
		return;

	/*
@@ -277,12 +284,12 @@ xchk_bmap_xref_rmap_cow(
	 * can start before and end after the physical space allocated to this
	 * mapping.  There are no offsets to check.
	 */
	if (rmap.rm_startblock > agbno)
	if (rmap.rm_startblock > bno)
		xchk_fblock_xref_set_corrupt(info->sc, info->whichfork,
				irec->br_startoff);

	rmap_end = (unsigned long long)rmap.rm_startblock + rmap.rm_blockcount;
	if (rmap_end < agbno + irec->br_blockcount)
	if (rmap_end < bno + irec->br_blockcount)
		xchk_fblock_xref_set_corrupt(info->sc, info->whichfork,
				irec->br_startoff);

@@ -315,6 +322,8 @@ xchk_bmap_rt_iextent_xref(
	struct xchk_bmap_info	*info,
	struct xfs_bmbt_irec	*irec)
{
	struct xfs_owner_info	oinfo;
	xfs_rgblock_t		rgbno;
	int			error;

	error = xchk_rtgroup_init_existing(info->sc,
@@ -332,6 +341,19 @@ xchk_bmap_rt_iextent_xref(
	xchk_xref_is_used_rt_space(info->sc, irec->br_startblock,
			irec->br_blockcount);

	if (!xfs_has_rtrmapbt(info->sc->mp))
		goto out_cur;

	rgbno = xfs_rtb_to_rgbno(info->sc->mp, irec->br_startblock);
	xchk_bmap_xref_rmap(info, irec, rgbno);

	xfs_rmap_ino_owner(&oinfo, info->sc->ip->i_ino, info->whichfork,
			irec->br_startoff);
	xchk_xref_is_only_rt_owned_by(info->sc, rgbno,
			irec->br_blockcount, &oinfo);

out_cur:
	xchk_rtgroup_btcur_free(&info->sc->sr);
out_free:
	xchk_rtgroup_free(info->sc, &info->sc->sr);
}
+2 −0
Original line number Diff line number Diff line
@@ -13,6 +13,7 @@
#include "xfs_log_format.h"
#include "xfs_trans.h"
#include "xfs_sb.h"
#include "xfs_rmap.h"
#include "scrub/scrub.h"
#include "scrub/common.h"
#include "scrub/repair.h"
@@ -34,6 +35,7 @@ xchk_rgsuperblock_xref(
		return;

	xchk_xref_is_used_rt_space(sc, xfs_rgbno_to_rtb(sc->sr.rtg, 0), 1);
	xchk_xref_is_only_rt_owned_by(sc, 0, 1, &XFS_RMAP_OINFO_FS);
}

int
+51 −4
Original line number Diff line number Diff line
@@ -9,17 +9,22 @@
#include "xfs_format.h"
#include "xfs_trans_resv.h"
#include "xfs_mount.h"
#include "xfs_btree.h"
#include "xfs_log_format.h"
#include "xfs_trans.h"
#include "xfs_rtbitmap.h"
#include "xfs_inode.h"
#include "xfs_bmap.h"
#include "xfs_bit.h"
#include "xfs_rtgroup.h"
#include "xfs_sb.h"
#include "xfs_rmap.h"
#include "xfs_rtrmap_btree.h"
#include "scrub/scrub.h"
#include "scrub/common.h"
#include "scrub/repair.h"
#include "scrub/rtbitmap.h"
#include "scrub/btree.h"

/* Set us up with the realtime metadata locked. */
int
@@ -37,6 +42,7 @@ xchk_setup_rtbitmap(
	if (!rtb)
		return -ENOMEM;
	sc->buf = rtb;
	rtb->sc = sc;

	error = xchk_rtgroup_init(sc, sc->sm->sm_agno, &sc->sr);
	if (error)
@@ -78,7 +84,30 @@ xchk_setup_rtbitmap(
	return 0;
}

/* Realtime bitmap. */
/* Per-rtgroup bitmap contents. */

/* Cross-reference rtbitmap entries with other metadata. */
STATIC void
xchk_rtbitmap_xref(
	struct xchk_rtbitmap	*rtb,
	xfs_rtblock_t		startblock,
	xfs_rtblock_t		blockcount)
{
	struct xfs_scrub	*sc = rtb->sc;
	xfs_rgblock_t		rgbno = xfs_rtb_to_rgbno(sc->mp, startblock);

	if (sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT)
		return;
	if (!sc->sr.rmap_cur)
		return;

	xchk_xref_has_no_rt_owner(sc, rgbno, blockcount);

	if (rtb->next_free_rgbno < rgbno)
		xchk_xref_has_rt_owner(sc, rtb->next_free_rgbno,
				rgbno - rtb->next_free_rgbno);
	rtb->next_free_rgbno = rgbno + blockcount;
}

/* Scrub a free extent record from the realtime bitmap. */
STATIC int
@@ -88,7 +117,8 @@ xchk_rtbitmap_rec(
	const struct xfs_rtalloc_rec *rec,
	void			*priv)
{
	struct xfs_scrub	*sc = priv;
	struct xchk_rtbitmap	*rtb = priv;
	struct xfs_scrub	*sc = rtb->sc;
	xfs_rtblock_t		startblock;
	xfs_filblks_t		blockcount;

@@ -97,6 +127,12 @@ xchk_rtbitmap_rec(

	if (!xfs_verify_rtbext(rtg_mount(rtg), startblock, blockcount))
		xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, 0);

	xchk_rtbitmap_xref(rtb, startblock, blockcount);

	if (sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT)
		return -ECANCELED;

	return 0;
}

@@ -144,7 +180,7 @@ xchk_rtbitmap_check_extents(
	return error;
}

/* Scrub the realtime bitmap. */
/* Scrub this group's realtime bitmap. */
int
xchk_rtbitmap(
	struct xfs_scrub	*sc)
@@ -153,6 +189,7 @@ xchk_rtbitmap(
	struct xfs_rtgroup	*rtg = sc->sr.rtg;
	struct xfs_inode	*rbmip = rtg_bitmap(rtg);
	struct xchk_rtbitmap	*rtb = sc->buf;
	xfs_rgblock_t		last_rgbno;
	int			error;

	/* Is sb_rextents correct? */
@@ -205,10 +242,20 @@ xchk_rtbitmap(
	if (error || (sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT))
		return error;

	error = xfs_rtalloc_query_all(rtg, sc->tp, xchk_rtbitmap_rec, sc);
	rtb->next_free_rgbno = 0;
	error = xfs_rtalloc_query_all(rtg, sc->tp, xchk_rtbitmap_rec, rtb);
	if (!xchk_fblock_process_error(sc, XFS_DATA_FORK, 0, &error))
		return error;

	/*
	 * Check that the are rmappings for all rt extents between the end of
	 * the last free extent we saw and the last possible extent in the rt
	 * group.
	 */
	last_rgbno = rtg->rtg_extents * mp->m_sb.sb_rextsize - 1;
	if (rtb->next_free_rgbno < last_rgbno)
		xchk_xref_has_rt_owner(sc, rtb->next_free_rgbno,
				last_rgbno - rtb->next_free_rgbno);
	return 0;
}

+5 −0
Original line number Diff line number Diff line
@@ -7,10 +7,15 @@
#define __XFS_SCRUB_RTBITMAP_H__

struct xchk_rtbitmap {
	struct xfs_scrub	*sc;

	uint64_t		rextents;
	uint64_t		rbmblocks;
	unsigned int		rextslog;
	unsigned int		resblks;

	/* The next free rt group block number that we expect to see. */
	xfs_rgblock_t		next_free_rgbno;
};

#ifdef CONFIG_XFS_ONLINE_REPAIR
+65 −0
Original line number Diff line number Diff line
@@ -197,3 +197,68 @@ xchk_rtrmapbt(
	xfs_rmap_ino_bmbt_owner(&oinfo, ip->i_ino, XFS_DATA_FORK);
	return xchk_btree(sc, sc->sr.rmap_cur, xchk_rtrmapbt_rec, &oinfo, &cr);
}

/* xref check that the extent has no realtime reverse mapping at all */
void
xchk_xref_has_no_rt_owner(
	struct xfs_scrub	*sc,
	xfs_rgblock_t		bno,
	xfs_extlen_t		len)
{
	enum xbtree_recpacking	outcome;
	int			error;

	if (!sc->sr.rmap_cur || xchk_skip_xref(sc->sm))
		return;

	error = xfs_rmap_has_records(sc->sr.rmap_cur, bno, len, &outcome);
	if (!xchk_should_check_xref(sc, &error, &sc->sr.rmap_cur))
		return;
	if (outcome != XBTREE_RECPACKING_EMPTY)
		xchk_btree_xref_set_corrupt(sc, sc->sr.rmap_cur, 0);
}

/* xref check that the extent is completely mapped */
void
xchk_xref_has_rt_owner(
	struct xfs_scrub	*sc,
	xfs_rgblock_t		bno,
	xfs_extlen_t		len)
{
	enum xbtree_recpacking	outcome;
	int			error;

	if (!sc->sr.rmap_cur || xchk_skip_xref(sc->sm))
		return;

	error = xfs_rmap_has_records(sc->sr.rmap_cur, bno, len, &outcome);
	if (!xchk_should_check_xref(sc, &error, &sc->sr.rmap_cur))
		return;
	if (outcome != XBTREE_RECPACKING_FULL)
		xchk_btree_xref_set_corrupt(sc, sc->sr.rmap_cur, 0);
}

/* xref check that the extent is only owned by a given owner */
void
xchk_xref_is_only_rt_owned_by(
	struct xfs_scrub		*sc,
	xfs_agblock_t			bno,
	xfs_extlen_t			len,
	const struct xfs_owner_info	*oinfo)
{
	struct xfs_rmap_matches		res;
	int				error;

	if (!sc->sr.rmap_cur || xchk_skip_xref(sc->sm))
		return;

	error = xfs_rmap_count_owners(sc->sr.rmap_cur, bno, len, oinfo, &res);
	if (!xchk_should_check_xref(sc, &error, &sc->sr.rmap_cur))
		return;
	if (res.matches != 1)
		xchk_btree_xref_set_corrupt(sc, sc->sr.rmap_cur, 0);
	if (res.bad_non_owner_matches)
		xchk_btree_xref_set_corrupt(sc, sc->sr.rmap_cur, 0);
	if (res.non_owner_matches)
		xchk_btree_xref_set_corrupt(sc, sc->sr.rmap_cur, 0);
}
Loading