Commit 76257a15 authored by Dave Chinner's avatar Dave Chinner
Browse files

xfs: introduce xfs_for_each_perag_wrap()



In several places we iterate every AG from a specific start agno and
wrap back to the first AG when we reach the end of the filesystem to
continue searching. We don't have a primitive for this iteration
yet, so add one for conversion of these algorithms to per-ag based
iteration.

The filestream AG select code is a mess, and this initially makes it
worse. The per-ag selection needs to be driven completely into the
filestream code to clean this up and it will be done in a future
patch that makes the filestream allocator use active per-ag
references correctly.

Signed-off-by: default avatarDave Chinner <dchinner@redhat.com>
Reviewed-by: default avatarDarrick J. Wong <djwong@kernel.org>
parent 7ac2ff8b
Loading
Loading
Loading
Loading
+44 −1
Original line number Diff line number Diff line
@@ -237,7 +237,6 @@ xfs_perag_next(
#define for_each_perag_from(mp, agno, pag) \
	for_each_perag_range((mp), (agno), (mp)->m_sb.sb_agcount - 1, (pag))


#define for_each_perag(mp, agno, pag) \
	(agno) = 0; \
	for_each_perag_from((mp), (agno), (pag))
@@ -249,6 +248,50 @@ xfs_perag_next(
		xfs_perag_rele(pag), \
		(pag) = xfs_perag_grab_tag((mp), (agno), (tag)))

static inline struct xfs_perag *
xfs_perag_next_wrap(
	struct xfs_perag	*pag,
	xfs_agnumber_t		*agno,
	xfs_agnumber_t		stop_agno,
	xfs_agnumber_t		wrap_agno)
{
	struct xfs_mount	*mp = pag->pag_mount;

	*agno = pag->pag_agno + 1;
	xfs_perag_rele(pag);
	while (*agno != stop_agno) {
		if (*agno >= wrap_agno)
			*agno = 0;
		if (*agno == stop_agno)
			break;

		pag = xfs_perag_grab(mp, *agno);
		if (pag)
			return pag;
		(*agno)++;
	}
	return NULL;
}

/*
 * Iterate all AGs from start_agno through wrap_agno, then 0 through
 * (start_agno - 1).
 */
#define for_each_perag_wrap_at(mp, start_agno, wrap_agno, agno, pag) \
	for ((agno) = (start_agno), (pag) = xfs_perag_grab((mp), (agno)); \
		(pag) != NULL; \
		(pag) = xfs_perag_next_wrap((pag), &(agno), (start_agno), \
				(wrap_agno)))

/*
 * Iterate all AGs from start_agno through to the end of the filesystem, then 0
 * through (start_agno - 1).
 */
#define for_each_perag_wrap(mp, start_agno, agno, pag) \
	for_each_perag_wrap_at((mp), (start_agno), (mp)->m_sb.sb_agcount, \
				(agno), (pag))


struct aghdr_init_data {
	/* per ag data */
	xfs_agblock_t		agno;		/* ag to init */
+46 −32
Original line number Diff line number Diff line
@@ -3136,17 +3136,14 @@ xfs_bmap_adjacent(

static int
xfs_bmap_longest_free_extent(
	struct xfs_perag	*pag,
	struct xfs_trans	*tp,
	xfs_agnumber_t		ag,
	xfs_extlen_t		*blen,
	int			*notinit)
{
	struct xfs_mount	*mp = tp->t_mountp;
	struct xfs_perag	*pag;
	xfs_extlen_t		longest;
	int			error = 0;

	pag = xfs_perag_get(mp, ag);
	if (!xfs_perag_initialised_agf(pag)) {
		error = xfs_alloc_read_agf(pag, tp, XFS_ALLOC_FLAG_TRYLOCK,
				NULL);
@@ -3156,19 +3153,17 @@ xfs_bmap_longest_free_extent(
				*notinit = 1;
				error = 0;
			}
			goto out;
			return error;
		}
	}

	longest = xfs_alloc_longest_free_extent(pag,
				xfs_alloc_min_freelist(mp, pag),
				xfs_alloc_min_freelist(pag->pag_mount, pag),
				xfs_ag_resv_needed(pag, XFS_AG_RESV_NONE));
	if (*blen < longest)
		*blen = longest;

out:
	xfs_perag_put(pag);
	return error;
	return 0;
}

static void
@@ -3206,9 +3201,10 @@ xfs_bmap_btalloc_select_lengths(
	xfs_extlen_t		*blen)
{
	struct xfs_mount	*mp = ap->ip->i_mount;
	xfs_agnumber_t		ag, startag;
	struct xfs_perag	*pag;
	xfs_agnumber_t		agno, startag;
	int			notinit = 0;
	int			error;
	int			error = 0;

	args->type = XFS_ALLOCTYPE_START_BNO;
	if (ap->tp->t_flags & XFS_TRANS_LOWMODE) {
@@ -3218,24 +3214,24 @@ xfs_bmap_btalloc_select_lengths(
	}

	args->total = ap->total;
	startag = ag = XFS_FSB_TO_AGNO(mp, args->fsbno);
	startag = XFS_FSB_TO_AGNO(mp, args->fsbno);
	if (startag == NULLAGNUMBER)
		startag = ag = 0;
		startag = 0;

	while (*blen < args->maxlen) {
		error = xfs_bmap_longest_free_extent(args->tp, ag, blen,
	*blen = 0;
	for_each_perag_wrap(mp, startag, agno, pag) {
		error = xfs_bmap_longest_free_extent(pag, args->tp, blen,
						     &notinit);
		if (error)
			return error;

		if (++ag == mp->m_sb.sb_agcount)
			ag = 0;
		if (ag == startag)
			break;
		if (*blen >= args->maxlen)
			break;
	}
	if (pag)
		xfs_perag_rele(pag);

	xfs_bmap_select_minlen(ap, args, blen, notinit);
	return 0;
	return error;
}

STATIC int
@@ -3245,7 +3241,8 @@ xfs_bmap_btalloc_filestreams(
	xfs_extlen_t		*blen)
{
	struct xfs_mount	*mp = ap->ip->i_mount;
	xfs_agnumber_t		ag;
	struct xfs_perag	*pag;
	xfs_agnumber_t		start_agno;
	int			notinit = 0;
	int			error;

@@ -3259,33 +3256,50 @@ xfs_bmap_btalloc_filestreams(
	args->type = XFS_ALLOCTYPE_NEAR_BNO;
	args->total = ap->total;

	ag = XFS_FSB_TO_AGNO(mp, args->fsbno);
	if (ag == NULLAGNUMBER)
		ag = 0;
	start_agno = XFS_FSB_TO_AGNO(mp, args->fsbno);
	if (start_agno == NULLAGNUMBER)
		start_agno = 0;

	error = xfs_bmap_longest_free_extent(args->tp, ag, blen, &notinit);
	pag = xfs_perag_grab(mp, start_agno);
	if (pag) {
		error = xfs_bmap_longest_free_extent(pag, args->tp, blen,
				&notinit);
		xfs_perag_rele(pag);
		if (error)
			return error;
	}

	if (*blen < args->maxlen) {
		error = xfs_filestream_new_ag(ap, &ag);
		xfs_agnumber_t	agno = start_agno;

		error = xfs_filestream_new_ag(ap, &agno);
		if (error)
			return error;
		if (agno == NULLAGNUMBER)
			goto out_select;

		error = xfs_bmap_longest_free_extent(args->tp, ag, blen,
						     &notinit);
		pag = xfs_perag_grab(mp, agno);
		if (!pag)
			goto out_select;

		error = xfs_bmap_longest_free_extent(pag, args->tp,
				blen, &notinit);
		xfs_perag_rele(pag);
		if (error)
			return error;

		start_agno = agno;

	}

out_select:
	xfs_bmap_select_minlen(ap, args, blen, notinit);

	/*
	 * Set the failure fallback case to look in the selected AG as stream
	 * may have moved.
	 */
	ap->blkno = args->fsbno = XFS_AGB_TO_FSB(mp, ag, 0);
	ap->blkno = args->fsbno = XFS_AGB_TO_FSB(mp, start_agno, 0);
	return 0;
}

+15 −17
Original line number Diff line number Diff line
@@ -1725,7 +1725,7 @@ xfs_dialloc(
	bool			ok_alloc = true;
	bool			low_space = false;
	int			flags;
	xfs_ino_t		ino;
	xfs_ino_t		ino = NULLFSINO;

	/*
	 * Directories, symlinks, and regular files frequently allocate at least
@@ -1773,39 +1773,37 @@ xfs_dialloc(
	 * or in which we can allocate some inodes.  Iterate through the
	 * allocation groups upward, wrapping at the end.
	 */
	agno = start_agno;
	flags = XFS_ALLOC_FLAG_TRYLOCK;
	for (;;) {
		pag = xfs_perag_grab(mp, agno);
retry:
	for_each_perag_wrap_at(mp, start_agno, mp->m_maxagi, agno, pag) {
		if (xfs_dialloc_good_ag(pag, *tpp, mode, flags, ok_alloc)) {
			error = xfs_dialloc_try_ag(pag, tpp, parent,
					&ino, ok_alloc);
			if (error != -EAGAIN)
				break;
			error = 0;
		}

		if (xfs_is_shutdown(mp)) {
			error = -EFSCORRUPTED;
			break;
		}
		if (++agno == mp->m_maxagi)
			agno = 0;
		if (agno == start_agno) {
			if (!flags) {
				error = -ENOSPC;
				break;
	}
	if (pag)
		xfs_perag_rele(pag);
	if (error)
		return error;
	if (ino == NULLFSINO) {
		if (flags) {
			flags = 0;
			if (low_space)
				ok_alloc = true;
			goto retry;
		}
		xfs_perag_rele(pag);
		return -ENOSPC;
	}

	if (!error)
	*new_ino = ino;
	xfs_perag_rele(pag);
	return error;
	return 0;
}

/*