Commit 0800169e authored by Dave Chinner's avatar Dave Chinner Committed by Dave Chinner
Browse files

xfs: Pre-calculate per-AG agbno geometry



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: default avatarDave Chinner <dchinner@redhat.com>
Reviewed-by: default avatarChristoph Hellwig <hch@lst.de>
Reviewed-by: default avatarDarrick J. Wong <djwong@kernel.org>
parent cec7bb7d
Loading
Loading
Loading
Loading
+39 −1
Original line number Diff line number Diff line
@@ -201,10 +201,35 @@ xfs_free_perag(
	}
}

/* Find the size of the AG, in blocks. */
static xfs_agblock_t
__xfs_ag_block_count(
	struct xfs_mount	*mp,
	xfs_agnumber_t		agno,
	xfs_agnumber_t		agcount,
	xfs_rfsblock_t		dblocks)
{
	ASSERT(agno < agcount);

	if (agno < agcount - 1)
		return mp->m_sb.sb_agblocks;
	return dblocks - (agno * mp->m_sb.sb_agblocks);
}

xfs_agblock_t
xfs_ag_block_count(
	struct xfs_mount	*mp,
	xfs_agnumber_t		agno)
{
	return __xfs_ag_block_count(mp, agno, mp->m_sb.sb_agcount,
			mp->m_sb.sb_dblocks);
}

int
xfs_initialize_perag(
	struct xfs_mount	*mp,
	xfs_agnumber_t		agcount,
	xfs_rfsblock_t		dblocks,
	xfs_agnumber_t		*maxagi)
{
	struct xfs_perag	*pag;
@@ -270,6 +295,13 @@ xfs_initialize_perag(
		/* first new pag is fully initialized */
		if (first_initialised == NULLAGNUMBER)
			first_initialised = index;

		/*
		 * Pre-calculated geometry
		 */
		pag->block_count = __xfs_ag_block_count(mp, index, agcount,
				dblocks);
		pag->min_block = XFS_AGFL_BLOCK(mp);
	}

	index = xfs_set_inode_alloc(mp, agcount);
@@ -927,10 +959,16 @@ xfs_ag_extend_space(
	if (error)
		return error;

	return  xfs_free_extent(tp, XFS_AGB_TO_FSB(pag->pag_mount, pag->pag_agno,
	error = xfs_free_extent(tp, XFS_AGB_TO_FSB(pag->pag_mount, pag->pag_agno,
					be32_to_cpu(agf->agf_length) - len),
				len, &XFS_RMAP_OINFO_SKIP_UPDATE,
				XFS_AG_RESV_NONE);
	if (error)
		return error;

	/* Update perag geometry */
	pag->block_count = be32_to_cpu(agf->agf_length);
	return 0;
}

/* Retrieve AG geometry. */
+20 −1
Original line number Diff line number Diff line
@@ -67,6 +67,10 @@ struct xfs_perag {
	/* for rcu-safe freeing */
	struct rcu_head	rcu_head;

	/* Precalculated geometry info */
	xfs_agblock_t		block_count;
	xfs_agblock_t		min_block;

#ifdef __KERNEL__
	/* -- kernel only structures below this line -- */

@@ -107,7 +111,7 @@ struct xfs_perag {
};

int xfs_initialize_perag(struct xfs_mount *mp, xfs_agnumber_t agcount,
			xfs_agnumber_t *maxagi);
			xfs_rfsblock_t dcount, xfs_agnumber_t *maxagi);
int xfs_initialize_perag_data(struct xfs_mount *mp, xfs_agnumber_t agno);
void xfs_free_perag(struct xfs_mount *mp);

@@ -116,6 +120,21 @@ struct xfs_perag *xfs_perag_get_tag(struct xfs_mount *mp, xfs_agnumber_t agno,
		unsigned int tag);
void xfs_perag_put(struct xfs_perag *pag);

/*
 * Per-ag geometry infomation and validation
 */
xfs_agblock_t xfs_ag_block_count(struct xfs_mount *mp, xfs_agnumber_t agno);

static inline bool
xfs_verify_agbno(struct xfs_perag *pag, xfs_agblock_t agbno)
{
	if (agbno >= pag->block_count)
		return false;
	if (agbno <= pag->min_block)
		return false;
	return true;
}

/*
 * Perag iteration APIs
 */
+5 −4
Original line number Diff line number Diff line
@@ -248,7 +248,7 @@ xfs_alloc_get_rec(
	int			*stat)	/* output: success/failure */
{
	struct xfs_mount	*mp = cur->bc_mp;
	xfs_agnumber_t		agno = cur->bc_ag.pag->pag_agno;
	struct xfs_perag	*pag = cur->bc_ag.pag;
	union xfs_btree_rec	*rec;
	int			error;

@@ -263,11 +263,11 @@ xfs_alloc_get_rec(
		goto out_bad_rec;

	/* check for valid extent range, including overflow */
	if (!xfs_verify_agbno(mp, agno, *bno))
	if (!xfs_verify_agbno(pag, *bno))
		goto out_bad_rec;
	if (*bno > *bno + *len)
		goto out_bad_rec;
	if (!xfs_verify_agbno(mp, agno, *bno + *len - 1))
	if (!xfs_verify_agbno(pag, *bno + *len - 1))
		goto out_bad_rec;

	return 0;
@@ -275,7 +275,8 @@ xfs_alloc_get_rec(
out_bad_rec:
	xfs_warn(mp,
		"%s Freespace BTree record corruption in AG %d detected!",
		cur->bc_btnum == XFS_BTNUM_BNO ? "Block" : "Size", agno);
		cur->bc_btnum == XFS_BTNUM_BNO ? "Block" : "Size",
		pag->pag_agno);
	xfs_warn(mp,
		"start block 0x%x block count 0x%x", *bno, *len);
	return -EFSCORRUPTED;
+10 −15
Original line number Diff line number Diff line
@@ -91,10 +91,9 @@ xfs_btree_check_lblock_siblings(

static inline xfs_failaddr_t
xfs_btree_check_sblock_siblings(
	struct xfs_mount	*mp,
	struct xfs_perag	*pag,
	struct xfs_btree_cur	*cur,
	int			level,
	xfs_agnumber_t		agno,
	xfs_agblock_t		agbno,
	__be32			dsibling)
{
@@ -110,7 +109,7 @@ xfs_btree_check_sblock_siblings(
		if (!xfs_btree_check_sptr(cur, sibling, level + 1))
			return __this_address;
	} else {
		if (!xfs_verify_agbno(mp, agno, sibling))
		if (!xfs_verify_agbno(pag, sibling))
			return __this_address;
	}
	return NULL;
@@ -195,11 +194,11 @@ __xfs_btree_check_sblock(
	struct xfs_buf		*bp)
{
	struct xfs_mount	*mp = cur->bc_mp;
	struct xfs_perag	*pag = cur->bc_ag.pag;
	xfs_btnum_t		btnum = cur->bc_btnum;
	int			crc = xfs_has_crc(mp);
	xfs_failaddr_t		fa;
	xfs_agblock_t		agbno = NULLAGBLOCK;
	xfs_agnumber_t		agno = NULLAGNUMBER;

	if (crc) {
		if (!uuid_equal(&block->bb_u.s.bb_uuid, &mp->m_sb.sb_meta_uuid))
@@ -217,16 +216,14 @@ __xfs_btree_check_sblock(
	    cur->bc_ops->get_maxrecs(cur, level))
		return __this_address;

	if (bp) {
	if (bp)
		agbno = xfs_daddr_to_agbno(mp, xfs_buf_daddr(bp));
		agno = xfs_daddr_to_agno(mp, xfs_buf_daddr(bp));
	}

	fa = xfs_btree_check_sblock_siblings(mp, cur, level, agno, agbno,
	fa = xfs_btree_check_sblock_siblings(pag, cur, level, agbno,
			block->bb_u.s.bb_leftsib);
	if (!fa)
		fa = xfs_btree_check_sblock_siblings(mp, cur, level, agno,
				 agbno, block->bb_u.s.bb_rightsib);
		fa = xfs_btree_check_sblock_siblings(pag, cur, level, agbno,
				block->bb_u.s.bb_rightsib);
	return fa;
}

@@ -288,7 +285,7 @@ xfs_btree_check_sptr(
{
	if (level <= 0)
		return false;
	return xfs_verify_agbno(cur->bc_mp, cur->bc_ag.pag->pag_agno, agbno);
	return xfs_verify_agbno(cur->bc_ag.pag, agbno);
}

/*
@@ -4595,7 +4592,6 @@ xfs_btree_sblock_verify(
{
	struct xfs_mount	*mp = bp->b_mount;
	struct xfs_btree_block	*block = XFS_BUF_TO_BLOCK(bp);
	xfs_agnumber_t		agno;
	xfs_agblock_t		agbno;
	xfs_failaddr_t		fa;

@@ -4604,12 +4600,11 @@ xfs_btree_sblock_verify(
		return __this_address;

	/* sibling pointer verification */
	agno = xfs_daddr_to_agno(mp, xfs_buf_daddr(bp));
	agbno = xfs_daddr_to_agbno(mp, xfs_buf_daddr(bp));
	fa = xfs_btree_check_sblock_siblings(mp, NULL, -1, agno, agbno,
	fa = xfs_btree_check_sblock_siblings(bp->b_pag, NULL, -1, agbno,
			block->bb_u.s.bb_leftsib);
	if (!fa)
		fa = xfs_btree_check_sblock_siblings(mp, NULL, -1, agno, agbno,
		fa = xfs_btree_check_sblock_siblings(bp->b_pag, NULL, -1, agbno,
				block->bb_u.s.bb_rightsib);
	return fa;
}
+6 −7
Original line number Diff line number Diff line
@@ -111,7 +111,7 @@ xfs_refcount_get_rec(
	int				*stat)
{
	struct xfs_mount		*mp = cur->bc_mp;
	xfs_agnumber_t			agno = cur->bc_ag.pag->pag_agno;
	struct xfs_perag		*pag = cur->bc_ag.pag;
	union xfs_btree_rec		*rec;
	int				error;
	xfs_agblock_t			realstart;
@@ -121,8 +121,6 @@ xfs_refcount_get_rec(
		return error;

	xfs_refcount_btrec_to_irec(rec, irec);

	agno = cur->bc_ag.pag->pag_agno;
	if (irec->rc_blockcount == 0 || irec->rc_blockcount > MAXREFCEXTLEN)
		goto out_bad_rec;

@@ -137,22 +135,23 @@ xfs_refcount_get_rec(
	}

	/* check for valid extent range, including overflow */
	if (!xfs_verify_agbno(mp, agno, realstart))
	if (!xfs_verify_agbno(pag, realstart))
		goto out_bad_rec;
	if (realstart > realstart + irec->rc_blockcount)
		goto out_bad_rec;
	if (!xfs_verify_agbno(mp, agno, realstart + irec->rc_blockcount - 1))
	if (!xfs_verify_agbno(pag, realstart + irec->rc_blockcount - 1))
		goto out_bad_rec;

	if (irec->rc_refcount == 0 || irec->rc_refcount > MAXREFCOUNT)
		goto out_bad_rec;

	trace_xfs_refcount_get(cur->bc_mp, cur->bc_ag.pag->pag_agno, irec);
	trace_xfs_refcount_get(cur->bc_mp, pag->pag_agno, irec);
	return 0;

out_bad_rec:
	xfs_warn(mp,
		"Refcount BTree record corruption in AG %d detected!", agno);
		"Refcount BTree record corruption in AG %d detected!",
		pag->pag_agno);
	xfs_warn(mp,
		"Start block 0x%x, block count 0x%x, references 0x%x",
		irec->rc_startblock, irec->rc_blockcount, irec->rc_refcount);
Loading