0800169e3e
There is a lot of overhead in functions like xfs_verify_agbno() that repeatedly calculate the geometry limits of an AG. These can be pre-calculated as they are static and the verification context has a per-ag context it can quickly reference. In the case of xfs_verify_agbno(), we now always have a perag context handy, so we can store the AG length and the minimum valid block in the AG in the perag. This means we don't have to calculate it on every call and it can be inlined in callers if we move it to xfs_ag.h. Move xfs_ag_block_count() to xfs_ag.c because it's really a per-ag function and not an XFS type function. We need a little bit of rework that is specific to xfs_initialise_perag() to allow growfs to calculate the new perag sizes before we've updated the primary superblock during the grow (chicken/egg situation). Note that we leave the original xfs_verify_agbno in place in xfs_types.c as a static function as other callers in that file do not have per-ag contexts so still need to go the long way. It's been renamed to xfs_verify_agno_agbno() to indicate it takes both an agno and an agbno to differentiate it from new function. Future commits will make similar changes for other per-ag geometry validation functions. Further: $ size --totals fs/xfs/built-in.a text data bss dec hex filename before 1483006 329588 572 1813166 1baaae (TOTALS) after 1482185 329588 572 1812345 1ba779 (TOTALS) This rework reduces the binary size by ~820 bytes, indicating that much less work is being done to bounds check the agbno values against on per-ag geometry information. Signed-off-by: Dave Chinner <dchinner@redhat.com> Reviewed-by: Christoph Hellwig <hch@lst.de> Reviewed-by: Darrick J. Wong <djwong@kernel.org>
158 lines
3.3 KiB
C
158 lines
3.3 KiB
C
// SPDX-License-Identifier: GPL-2.0+
|
|
/*
|
|
* Copyright (C) 2017 Oracle. All Rights Reserved.
|
|
* Author: Darrick J. Wong <darrick.wong@oracle.com>
|
|
*/
|
|
#include "xfs.h"
|
|
#include "xfs_fs.h"
|
|
#include "xfs_shared.h"
|
|
#include "xfs_format.h"
|
|
#include "xfs_trans_resv.h"
|
|
#include "xfs_mount.h"
|
|
#include "xfs_btree.h"
|
|
#include "xfs_alloc.h"
|
|
#include "xfs_rmap.h"
|
|
#include "scrub/scrub.h"
|
|
#include "scrub/common.h"
|
|
#include "scrub/btree.h"
|
|
#include "xfs_ag.h"
|
|
|
|
/*
|
|
* Set us up to scrub free space btrees.
|
|
*/
|
|
int
|
|
xchk_setup_ag_allocbt(
|
|
struct xfs_scrub *sc)
|
|
{
|
|
return xchk_setup_ag_btree(sc, false);
|
|
}
|
|
|
|
/* Free space btree scrubber. */
|
|
/*
|
|
* Ensure there's a corresponding cntbt/bnobt record matching this
|
|
* bnobt/cntbt record, respectively.
|
|
*/
|
|
STATIC void
|
|
xchk_allocbt_xref_other(
|
|
struct xfs_scrub *sc,
|
|
xfs_agblock_t agbno,
|
|
xfs_extlen_t len)
|
|
{
|
|
struct xfs_btree_cur **pcur;
|
|
xfs_agblock_t fbno;
|
|
xfs_extlen_t flen;
|
|
int has_otherrec;
|
|
int error;
|
|
|
|
if (sc->sm->sm_type == XFS_SCRUB_TYPE_BNOBT)
|
|
pcur = &sc->sa.cnt_cur;
|
|
else
|
|
pcur = &sc->sa.bno_cur;
|
|
if (!*pcur || xchk_skip_xref(sc->sm))
|
|
return;
|
|
|
|
error = xfs_alloc_lookup_le(*pcur, agbno, len, &has_otherrec);
|
|
if (!xchk_should_check_xref(sc, &error, pcur))
|
|
return;
|
|
if (!has_otherrec) {
|
|
xchk_btree_xref_set_corrupt(sc, *pcur, 0);
|
|
return;
|
|
}
|
|
|
|
error = xfs_alloc_get_rec(*pcur, &fbno, &flen, &has_otherrec);
|
|
if (!xchk_should_check_xref(sc, &error, pcur))
|
|
return;
|
|
if (!has_otherrec) {
|
|
xchk_btree_xref_set_corrupt(sc, *pcur, 0);
|
|
return;
|
|
}
|
|
|
|
if (fbno != agbno || flen != len)
|
|
xchk_btree_xref_set_corrupt(sc, *pcur, 0);
|
|
}
|
|
|
|
/* Cross-reference with the other btrees. */
|
|
STATIC void
|
|
xchk_allocbt_xref(
|
|
struct xfs_scrub *sc,
|
|
xfs_agblock_t agbno,
|
|
xfs_extlen_t len)
|
|
{
|
|
if (sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT)
|
|
return;
|
|
|
|
xchk_allocbt_xref_other(sc, agbno, len);
|
|
xchk_xref_is_not_inode_chunk(sc, agbno, len);
|
|
xchk_xref_has_no_owner(sc, agbno, len);
|
|
xchk_xref_is_not_shared(sc, agbno, len);
|
|
}
|
|
|
|
/* Scrub a bnobt/cntbt record. */
|
|
STATIC int
|
|
xchk_allocbt_rec(
|
|
struct xchk_btree *bs,
|
|
const union xfs_btree_rec *rec)
|
|
{
|
|
struct xfs_perag *pag = bs->cur->bc_ag.pag;
|
|
xfs_agblock_t bno;
|
|
xfs_extlen_t len;
|
|
|
|
bno = be32_to_cpu(rec->alloc.ar_startblock);
|
|
len = be32_to_cpu(rec->alloc.ar_blockcount);
|
|
|
|
if (bno + len <= bno ||
|
|
!xfs_verify_agbno(pag, bno) ||
|
|
!xfs_verify_agbno(pag, bno + len - 1))
|
|
xchk_btree_set_corrupt(bs->sc, bs->cur, 0);
|
|
|
|
xchk_allocbt_xref(bs->sc, bno, len);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* Scrub the freespace btrees for some AG. */
|
|
STATIC int
|
|
xchk_allocbt(
|
|
struct xfs_scrub *sc,
|
|
xfs_btnum_t which)
|
|
{
|
|
struct xfs_btree_cur *cur;
|
|
|
|
cur = which == XFS_BTNUM_BNO ? sc->sa.bno_cur : sc->sa.cnt_cur;
|
|
return xchk_btree(sc, cur, xchk_allocbt_rec, &XFS_RMAP_OINFO_AG, NULL);
|
|
}
|
|
|
|
int
|
|
xchk_bnobt(
|
|
struct xfs_scrub *sc)
|
|
{
|
|
return xchk_allocbt(sc, XFS_BTNUM_BNO);
|
|
}
|
|
|
|
int
|
|
xchk_cntbt(
|
|
struct xfs_scrub *sc)
|
|
{
|
|
return xchk_allocbt(sc, XFS_BTNUM_CNT);
|
|
}
|
|
|
|
/* xref check that the extent is not free */
|
|
void
|
|
xchk_xref_is_used_space(
|
|
struct xfs_scrub *sc,
|
|
xfs_agblock_t agbno,
|
|
xfs_extlen_t len)
|
|
{
|
|
bool is_freesp;
|
|
int error;
|
|
|
|
if (!sc->sa.bno_cur || xchk_skip_xref(sc->sm))
|
|
return;
|
|
|
|
error = xfs_alloc_has_record(sc->sa.bno_cur, agbno, len, &is_freesp);
|
|
if (!xchk_should_check_xref(sc, &error, &sc->sa.bno_cur))
|
|
return;
|
|
if (is_freesp)
|
|
xchk_btree_xref_set_corrupt(sc, sc->sa.bno_cur, 0);
|
|
}
|