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

xfs: convert xfs_ialloc_has_inodes_at_extent to return keyfill scan results



Convert the xfs_ialloc_has_inodes_at_extent function to return keyfill
scan results because for a given range of inode numbers, we might have
no indexed inodes at all; the entire region might be allocated ondisk
inodes; or there might be a mix of the two.

Unfortunately, sparse inodes adds to the complexity, because each inode
record can have holes, which means that we cannot use the generic btree
_scan_keyfill function because we must look for holes in individual
records to decide the result.  On the plus side, online fsck can now
detect sub-chunk discrepancies in the inobt.

Signed-off-by: default avatarDarrick J. Wong <djwong@kernel.org>
Reviewed-by: default avatarDave Chinner <dchinner@redhat.com>
parent bc0f3b55
Loading
Loading
Loading
Loading
+50 −32
Original line number Diff line number Diff line
@@ -2641,61 +2641,79 @@ xfs_ialloc_read_agi(
	return 0;
}

/* Is there an inode record covering a given range of inode numbers? */
int
xfs_ialloc_has_inode_record(
/* How many inodes are backed by inode clusters ondisk? */
STATIC int
xfs_ialloc_count_ondisk(
	struct xfs_btree_cur		*cur,
	xfs_agino_t			low,
	xfs_agino_t			high,
	bool			*exists)
	unsigned int			*allocated)
{
	struct xfs_inobt_rec_incore	irec;
	xfs_agino_t		agino;
	uint16_t		holemask;
	unsigned int			ret = 0;
	int				has_record;
	int			i;
	int				error;

	*exists = false;
	error = xfs_inobt_lookup(cur, low, XFS_LOOKUP_LE, &has_record);
	while (error == 0 && has_record) {
	if (error)
		return error;

	while (has_record) {
		unsigned int		i, hole_idx;

		error = xfs_inobt_get_rec(cur, &irec, &has_record);
		if (error || irec.ir_startino > high)
		if (error)
			return error;
		if (irec.ir_startino > high)
			break;

		agino = irec.ir_startino;
		holemask = irec.ir_holemask;
		for (i = 0; i < XFS_INOBT_HOLEMASK_BITS; holemask >>= 1,
				i++, agino += XFS_INODES_PER_HOLEMASK_BIT) {
			if (holemask & 1)
		for (i = 0; i < XFS_INODES_PER_CHUNK; i++) {
			if (irec.ir_startino + i < low)
				continue;
			if (agino + XFS_INODES_PER_HOLEMASK_BIT > low &&
					agino <= high) {
				*exists = true;
				return 0;
			}
			if (irec.ir_startino + i > high)
				break;

			hole_idx = i / XFS_INODES_PER_HOLEMASK_BIT;
			if (!(irec.ir_holemask & (1U << hole_idx)))
				ret++;
		}

		error = xfs_btree_increment(cur, 0, &has_record);
	}
		if (error)
			return error;
	}

	*allocated = ret;
	return 0;
}

/* Is there an inode record covering a given extent? */
int
xfs_ialloc_has_inodes_at_extent(
	struct xfs_btree_cur	*cur,
	xfs_agblock_t		bno,
	xfs_extlen_t		len,
	bool			*exists)
	enum xbtree_recpacking	*outcome)
{
	xfs_agino_t		low;
	xfs_agino_t		high;
	xfs_agino_t		agino;
	xfs_agino_t		last_agino;
	unsigned int		allocated;
	int			error;

	agino = XFS_AGB_TO_AGINO(cur->bc_mp, bno);
	last_agino = XFS_AGB_TO_AGINO(cur->bc_mp, bno + len) - 1;

	low = XFS_AGB_TO_AGINO(cur->bc_mp, bno);
	high = XFS_AGB_TO_AGINO(cur->bc_mp, bno + len) - 1;
	error = xfs_ialloc_count_ondisk(cur, agino, last_agino, &allocated);
	if (error)
		return error;

	return xfs_ialloc_has_inode_record(cur, low, high, exists);
	if (allocated == 0)
		*outcome = XBTREE_RECPACKING_EMPTY;
	else if (allocated == last_agino - agino + 1)
		*outcome = XBTREE_RECPACKING_FULL;
	else
		*outcome = XBTREE_RECPACKING_SPARSE;
	return 0;
}

struct xfs_ialloc_count_inodes {
+2 −3
Original line number Diff line number Diff line
@@ -96,9 +96,8 @@ void xfs_inobt_btrec_to_irec(struct xfs_mount *mp,
xfs_failaddr_t xfs_inobt_check_irec(struct xfs_btree_cur *cur,
		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, bool *exists);
int xfs_ialloc_has_inode_record(struct xfs_btree_cur *cur, xfs_agino_t low,
		xfs_agino_t high, bool *exists);
		xfs_agblock_t bno, xfs_extlen_t len,
		enum xbtree_recpacking *outcome);
int xfs_ialloc_count_inodes(struct xfs_btree_cur *cur, xfs_agino_t *count,
		xfs_agino_t *freecount);
int xfs_inobt_insert_rec(struct xfs_btree_cur *cur, uint16_t holemask,
+10 −7
Original line number Diff line number Diff line
@@ -765,18 +765,18 @@ xchk_xref_inode_check(
	xfs_agblock_t		agbno,
	xfs_extlen_t		len,
	struct xfs_btree_cur	**icur,
	bool			should_have_inodes)
	enum xbtree_recpacking	expected)
{
	bool			has_inodes;
	enum xbtree_recpacking	outcome;
	int			error;

	if (!(*icur) || xchk_skip_xref(sc->sm))
		return;

	error = xfs_ialloc_has_inodes_at_extent(*icur, agbno, len, &has_inodes);
	error = xfs_ialloc_has_inodes_at_extent(*icur, agbno, len, &outcome);
	if (!xchk_should_check_xref(sc, &error, icur))
		return;
	if (has_inodes != should_have_inodes)
	if (outcome != expected)
		xchk_btree_xref_set_corrupt(sc, *icur, 0);
}

@@ -787,8 +787,10 @@ xchk_xref_is_not_inode_chunk(
	xfs_agblock_t		agbno,
	xfs_extlen_t		len)
{
	xchk_xref_inode_check(sc, agbno, len, &sc->sa.ino_cur, false);
	xchk_xref_inode_check(sc, agbno, len, &sc->sa.fino_cur, false);
	xchk_xref_inode_check(sc, agbno, len, &sc->sa.ino_cur,
			XBTREE_RECPACKING_EMPTY);
	xchk_xref_inode_check(sc, agbno, len, &sc->sa.fino_cur,
			XBTREE_RECPACKING_EMPTY);
}

/* xref check that the extent is covered by inodes */
@@ -798,5 +800,6 @@ xchk_xref_is_inode_chunk(
	xfs_agblock_t		agbno,
	xfs_extlen_t		len)
{
	xchk_xref_inode_check(sc, agbno, len, &sc->sa.ino_cur, true);
	xchk_xref_inode_check(sc, agbno, len, &sc->sa.ino_cur,
			XBTREE_RECPACKING_FULL);
}