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: "Darrick J. Wong" <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
This commit is contained in:
Darrick J. Wong 2024-11-20 16:20:36 -08:00
parent 1ebecab5ad
commit 037a44d827
6 changed files with 169 additions and 19 deletions

View File

@ -143,15 +143,22 @@ static inline bool
xchk_bmap_get_rmap( xchk_bmap_get_rmap(
struct xchk_bmap_info *info, struct xchk_bmap_info *info,
struct xfs_bmbt_irec *irec, struct xfs_bmbt_irec *irec,
xfs_agblock_t agbno, xfs_agblock_t bno,
uint64_t owner, uint64_t owner,
struct xfs_rmap_irec *rmap) struct xfs_rmap_irec *rmap)
{ {
struct xfs_btree_cur **curp = &info->sc->sa.rmap_cur;
xfs_fileoff_t offset; xfs_fileoff_t offset;
unsigned int rflags = 0; unsigned int rflags = 0;
int has_rmap; int has_rmap;
int error; 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) if (info->whichfork == XFS_ATTR_FORK)
rflags |= XFS_RMAP_ATTR_FORK; rflags |= XFS_RMAP_ATTR_FORK;
if (irec->br_state == XFS_EXT_UNWRITTEN) 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. * range rmap lookup to make sure we get the correct owner/offset.
*/ */
if (info->is_shared) { if (info->is_shared) {
error = xfs_rmap_lookup_le_range(info->sc->sa.rmap_cur, agbno, error = xfs_rmap_lookup_le_range(*curp, bno, owner, offset,
owner, offset, rflags, rmap, &has_rmap); rflags, rmap, &has_rmap);
} else { } else {
error = xfs_rmap_lookup_le(info->sc->sa.rmap_cur, agbno, error = xfs_rmap_lookup_le(*curp, bno, owner, offset,
owner, offset, rflags, rmap, &has_rmap); 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; return false;
if (!has_rmap) if (!has_rmap)
@ -192,29 +199,29 @@ STATIC void
xchk_bmap_xref_rmap( xchk_bmap_xref_rmap(
struct xchk_bmap_info *info, struct xchk_bmap_info *info,
struct xfs_bmbt_irec *irec, struct xfs_bmbt_irec *irec,
xfs_agblock_t agbno) xfs_agblock_t bno)
{ {
struct xfs_rmap_irec rmap; struct xfs_rmap_irec rmap;
unsigned long long rmap_end; unsigned long long rmap_end;
uint64_t owner = info->sc->ip->i_ino; 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; return;
/* Find the rmap record for this irec. */ /* 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; return;
/* /*
* The rmap must be an exact match for this incore file mapping record, * The rmap must be an exact match for this incore file mapping record,
* which may have arisen from multiple ondisk records. * 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, xchk_fblock_xref_set_corrupt(info->sc, info->whichfork,
irec->br_startoff); irec->br_startoff);
rmap_end = (unsigned long long)rmap.rm_startblock + rmap.rm_blockcount; 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, xchk_fblock_xref_set_corrupt(info->sc, info->whichfork,
irec->br_startoff); irec->br_startoff);
@ -259,7 +266,7 @@ STATIC void
xchk_bmap_xref_rmap_cow( xchk_bmap_xref_rmap_cow(
struct xchk_bmap_info *info, struct xchk_bmap_info *info,
struct xfs_bmbt_irec *irec, struct xfs_bmbt_irec *irec,
xfs_agblock_t agbno) xfs_agblock_t bno)
{ {
struct xfs_rmap_irec rmap; struct xfs_rmap_irec rmap;
unsigned long long rmap_end; unsigned long long rmap_end;
@ -269,7 +276,7 @@ xchk_bmap_xref_rmap_cow(
return; return;
/* Find the rmap record for this irec. */ /* 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; return;
/* /*
@ -277,12 +284,12 @@ xchk_bmap_xref_rmap_cow(
* can start before and end after the physical space allocated to this * can start before and end after the physical space allocated to this
* mapping. There are no offsets to check. * 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, xchk_fblock_xref_set_corrupt(info->sc, info->whichfork,
irec->br_startoff); irec->br_startoff);
rmap_end = (unsigned long long)rmap.rm_startblock + rmap.rm_blockcount; 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, xchk_fblock_xref_set_corrupt(info->sc, info->whichfork,
irec->br_startoff); irec->br_startoff);
@ -315,6 +322,8 @@ xchk_bmap_rt_iextent_xref(
struct xchk_bmap_info *info, struct xchk_bmap_info *info,
struct xfs_bmbt_irec *irec) struct xfs_bmbt_irec *irec)
{ {
struct xfs_owner_info oinfo;
xfs_rgblock_t rgbno;
int error; int error;
error = xchk_rtgroup_init_existing(info->sc, 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, xchk_xref_is_used_rt_space(info->sc, irec->br_startblock,
irec->br_blockcount); 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: out_free:
xchk_rtgroup_free(info->sc, &info->sc->sr); xchk_rtgroup_free(info->sc, &info->sc->sr);
} }

View File

@ -13,6 +13,7 @@
#include "xfs_log_format.h" #include "xfs_log_format.h"
#include "xfs_trans.h" #include "xfs_trans.h"
#include "xfs_sb.h" #include "xfs_sb.h"
#include "xfs_rmap.h"
#include "scrub/scrub.h" #include "scrub/scrub.h"
#include "scrub/common.h" #include "scrub/common.h"
#include "scrub/repair.h" #include "scrub/repair.h"
@ -34,6 +35,7 @@ xchk_rgsuperblock_xref(
return; return;
xchk_xref_is_used_rt_space(sc, xfs_rgbno_to_rtb(sc->sr.rtg, 0), 1); 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 int

View File

@ -9,17 +9,22 @@
#include "xfs_format.h" #include "xfs_format.h"
#include "xfs_trans_resv.h" #include "xfs_trans_resv.h"
#include "xfs_mount.h" #include "xfs_mount.h"
#include "xfs_btree.h"
#include "xfs_log_format.h" #include "xfs_log_format.h"
#include "xfs_trans.h" #include "xfs_trans.h"
#include "xfs_rtbitmap.h" #include "xfs_rtbitmap.h"
#include "xfs_inode.h" #include "xfs_inode.h"
#include "xfs_bmap.h" #include "xfs_bmap.h"
#include "xfs_bit.h" #include "xfs_bit.h"
#include "xfs_rtgroup.h"
#include "xfs_sb.h" #include "xfs_sb.h"
#include "xfs_rmap.h"
#include "xfs_rtrmap_btree.h"
#include "scrub/scrub.h" #include "scrub/scrub.h"
#include "scrub/common.h" #include "scrub/common.h"
#include "scrub/repair.h" #include "scrub/repair.h"
#include "scrub/rtbitmap.h" #include "scrub/rtbitmap.h"
#include "scrub/btree.h"
/* Set us up with the realtime metadata locked. */ /* Set us up with the realtime metadata locked. */
int int
@ -37,6 +42,7 @@ xchk_setup_rtbitmap(
if (!rtb) if (!rtb)
return -ENOMEM; return -ENOMEM;
sc->buf = rtb; sc->buf = rtb;
rtb->sc = sc;
error = xchk_rtgroup_init(sc, sc->sm->sm_agno, &sc->sr); error = xchk_rtgroup_init(sc, sc->sm->sm_agno, &sc->sr);
if (error) if (error)
@ -78,7 +84,30 @@ xchk_setup_rtbitmap(
return 0; 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. */ /* Scrub a free extent record from the realtime bitmap. */
STATIC int STATIC int
@ -88,7 +117,8 @@ xchk_rtbitmap_rec(
const struct xfs_rtalloc_rec *rec, const struct xfs_rtalloc_rec *rec,
void *priv) void *priv)
{ {
struct xfs_scrub *sc = priv; struct xchk_rtbitmap *rtb = priv;
struct xfs_scrub *sc = rtb->sc;
xfs_rtblock_t startblock; xfs_rtblock_t startblock;
xfs_filblks_t blockcount; xfs_filblks_t blockcount;
@ -97,6 +127,12 @@ xchk_rtbitmap_rec(
if (!xfs_verify_rtbext(rtg_mount(rtg), startblock, blockcount)) if (!xfs_verify_rtbext(rtg_mount(rtg), startblock, blockcount))
xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, 0); 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; return 0;
} }
@ -144,7 +180,7 @@ xchk_rtbitmap_check_extents(
return error; return error;
} }
/* Scrub the realtime bitmap. */ /* Scrub this group's realtime bitmap. */
int int
xchk_rtbitmap( xchk_rtbitmap(
struct xfs_scrub *sc) struct xfs_scrub *sc)
@ -153,6 +189,7 @@ xchk_rtbitmap(
struct xfs_rtgroup *rtg = sc->sr.rtg; struct xfs_rtgroup *rtg = sc->sr.rtg;
struct xfs_inode *rbmip = rtg_bitmap(rtg); struct xfs_inode *rbmip = rtg_bitmap(rtg);
struct xchk_rtbitmap *rtb = sc->buf; struct xchk_rtbitmap *rtb = sc->buf;
xfs_rgblock_t last_rgbno;
int error; int error;
/* Is sb_rextents correct? */ /* Is sb_rextents correct? */
@ -205,10 +242,20 @@ xchk_rtbitmap(
if (error || (sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT)) if (error || (sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT))
return error; 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)) if (!xchk_fblock_process_error(sc, XFS_DATA_FORK, 0, &error))
return 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; return 0;
} }

View File

@ -7,10 +7,15 @@
#define __XFS_SCRUB_RTBITMAP_H__ #define __XFS_SCRUB_RTBITMAP_H__
struct xchk_rtbitmap { struct xchk_rtbitmap {
struct xfs_scrub *sc;
uint64_t rextents; uint64_t rextents;
uint64_t rbmblocks; uint64_t rbmblocks;
unsigned int rextslog; unsigned int rextslog;
unsigned int resblks; 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 #ifdef CONFIG_XFS_ONLINE_REPAIR

View File

@ -197,3 +197,68 @@ xchk_rtrmapbt(
xfs_rmap_ino_bmbt_owner(&oinfo, ip->i_ino, XFS_DATA_FORK); 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); 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);
}

View File

@ -322,8 +322,17 @@ void xchk_xref_is_not_cow_staging(struct xfs_scrub *sc, xfs_agblock_t bno,
#ifdef CONFIG_XFS_RT #ifdef CONFIG_XFS_RT
void xchk_xref_is_used_rt_space(struct xfs_scrub *sc, xfs_rtblock_t rtbno, void xchk_xref_is_used_rt_space(struct xfs_scrub *sc, xfs_rtblock_t rtbno,
xfs_extlen_t len); xfs_extlen_t len);
void xchk_xref_has_no_rt_owner(struct xfs_scrub *sc, xfs_rgblock_t rgbno,
xfs_extlen_t len);
void xchk_xref_has_rt_owner(struct xfs_scrub *sc, xfs_rgblock_t rgbno,
xfs_extlen_t len);
void xchk_xref_is_only_rt_owned_by(struct xfs_scrub *sc, xfs_rgblock_t rgbno,
xfs_extlen_t len, const struct xfs_owner_info *oinfo);
#else #else
# define xchk_xref_is_used_rt_space(sc, rtbno, len) do { } while (0) # define xchk_xref_is_used_rt_space(sc, rtbno, len) do { } while (0)
# define xchk_xref_has_no_rt_owner(sc, rtbno, len) do { } while (0)
# define xchk_xref_has_rt_owner(sc, rtbno, len) do { } while (0)
# define xchk_xref_is_only_rt_owned_by(sc, bno, len, oinfo) do { } while (0)
#endif #endif
#endif /* __XFS_SCRUB_SCRUB_H__ */ #endif /* __XFS_SCRUB_SCRUB_H__ */