Commit 85843327 authored by Dave Chinner's avatar Dave Chinner
Browse files

xfs: factor xfs_bmap_btalloc()



There are several different contexts xfs_bmap_btalloc() handles, and
large chunks of the code execute independent allocation contexts.
Try to untangle this mess a bit.

Signed-off-by: default avatarDave Chinner <dchinner@redhat.com>
Reviewed-by: default avatarDarrick J. Wong <djwong@kernel.org>
parent 74c36a86
Loading
Loading
Loading
Loading
+196 −137
Original line number Diff line number Diff line
@@ -3196,13 +3196,13 @@ xfs_bmap_select_minlen(
	}
}

STATIC int
static int
xfs_bmap_btalloc_select_lengths(
	struct xfs_bmalloca	*ap,
	struct xfs_alloc_arg	*args,
	xfs_extlen_t		*blen)
{
	struct xfs_mount	*mp = ap->ip->i_mount;
	struct xfs_mount	*mp = args->mp;
	struct xfs_perag	*pag;
	xfs_agnumber_t		agno, startag;
	int			notinit = 0;
@@ -3216,7 +3216,7 @@ xfs_bmap_btalloc_select_lengths(
	}

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

@@ -3258,7 +3258,7 @@ xfs_bmap_btalloc_filestreams(
	args->type = XFS_ALLOCTYPE_NEAR_BNO;
	args->total = ap->total;

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

@@ -3496,170 +3496,229 @@ xfs_bmap_exact_minlen_extent_alloc(

#endif

STATIC int
xfs_bmap_btalloc(
	struct xfs_bmalloca	*ap)
/*
 * If we are not low on available data blocks and we are allocating at
 * EOF, optimise allocation for contiguous file extension and/or stripe
 * alignment of the new extent.
 *
 * NOTE: ap->aeof is only set if the allocation length is >= the
 * stripe unit and the allocation offset is at the end of file.
 */
static int
xfs_bmap_btalloc_at_eof(
	struct xfs_bmalloca	*ap,
	struct xfs_alloc_arg	*args,
	xfs_extlen_t		blen,
	int			stripe_align)
{
	struct xfs_mount	*mp = ap->ip->i_mount;
	struct xfs_alloc_arg	args = { .tp = ap->tp, .mp = mp };
	xfs_alloctype_t		atype = 0;
	xfs_agnumber_t		ag;
	xfs_fileoff_t		orig_offset;
	xfs_extlen_t		orig_length;
	xfs_extlen_t		blen;
	xfs_extlen_t		nextminlen = 0;
	int			isaligned = 0;
	struct xfs_mount	*mp = args->mp;
	xfs_alloctype_t		atype;
	int			error;
	int			stripe_align;

	ASSERT(ap->length);
	orig_offset = ap->offset;
	orig_length = ap->length;

	stripe_align = xfs_bmap_compute_alignments(ap, &args);

	if ((ap->datatype & XFS_ALLOC_USERDATA) &&
	    xfs_inode_is_filestream(ap->ip)) {
		ag = xfs_filestream_lookup_ag(ap->ip);
		ag = (ag != NULLAGNUMBER) ? ag : 0;
		ap->blkno = XFS_AGB_TO_FSB(mp, ag, 0);
	} else {
		ap->blkno = XFS_INO_TO_FSB(mp, ap->ip->i_ino);
	}

	xfs_bmap_adjacent(ap);

	args.fsbno = ap->blkno;
	args.oinfo = XFS_RMAP_OINFO_SKIP_UPDATE;
	/*
	 * If there are already extents in the file, try an exact EOF block
	 * allocation to extend the file as a contiguous extent. If that fails,
	 * or it's the first allocation in a file, just try for a stripe aligned
	 * allocation.
	 */
	if (ap->offset) {
		xfs_extlen_t	nextminlen = 0;

	/* Trim the allocation back to the maximum an AG can fit. */
	args.maxlen = min(ap->length, mp->m_ag_max_usable);
	blen = 0;
		atype = args->type;
		args->type = XFS_ALLOCTYPE_THIS_BNO;
		args->alignment = 1;

		/*
	 * Search for an allocation group with a single extent large
	 * enough for the request.  If one isn't found, then adjust
	 * the minimum allocation size to the largest space found.
		 * Compute the minlen+alignment for the next case.  Set slop so
		 * that the value of minlen+alignment+slop doesn't go up between
		 * the calls.
		 */
	if ((ap->datatype & XFS_ALLOC_USERDATA) &&
	    xfs_inode_is_filestream(ap->ip))
		error = xfs_bmap_btalloc_filestreams(ap, &args, &blen);
		if (blen > stripe_align && blen <= args->maxlen)
			nextminlen = blen - stripe_align;
		else
		error = xfs_bmap_btalloc_select_lengths(ap, &args, &blen);
			nextminlen = args->minlen;
		if (nextminlen + stripe_align > args->minlen + 1)
			args->minalignslop = nextminlen + stripe_align -
					args->minlen - 1;
		else
			args->minalignslop = 0;

		args->pag = xfs_perag_get(mp, XFS_FSB_TO_AGNO(mp, args->fsbno));
		error = xfs_alloc_vextent_this_ag(args);
		xfs_perag_put(args->pag);
		if (error)
			return error;

		if (args->fsbno != NULLFSBLOCK)
			return 0;
		/*
	 * If we are not low on available data blocks, and the underlying
	 * logical volume manager is a stripe, and the file offset is zero then
	 * try to allocate data blocks on stripe unit boundary. NOTE: ap->aeof
	 * is only set if the allocation length is >= the stripe unit and the
	 * allocation offset is at the end of file.
		 * Exact allocation failed. Reset to try an aligned allocation
		 * according to the original allocation specification.
		 */
	if (!(ap->tp->t_flags & XFS_TRANS_LOWMODE) && ap->aeof) {
		if (!ap->offset) {
			args.alignment = stripe_align;
			atype = args.type;
			isaligned = 1;
		args->pag = NULL;
		args->type = atype;
		args->fsbno = ap->blkno;
		args->alignment = stripe_align;
		args->minlen = nextminlen;
		args->minalignslop = 0;
	} else {
		args->alignment = stripe_align;
		atype = args->type;
		/*
		 * Adjust minlen to try and preserve alignment if we
		 * can't guarantee an aligned maxlen extent.
		 */
			if (blen > args.alignment &&
			    blen <= args.maxlen + args.alignment)
				args.minlen = blen - args.alignment;
			args.minalignslop = 0;
		} else {
		if (blen > args->alignment &&
		    blen <= args->maxlen + args->alignment)
			args->minlen = blen - args->alignment;
		args->minalignslop = 0;
	}

	error = xfs_alloc_vextent(args);
	if (error)
		return error;

	if (args->fsbno != NULLFSBLOCK)
		return 0;

	/*
			 * First try an exact bno allocation.
			 * If it fails then do a near or start bno
			 * allocation with alignment turned on.
	 * Allocation failed, so turn return the allocation args to their
	 * original non-aligned state so the caller can proceed on allocation
	 * failure as if this function was never called.
	 */
			atype = args.type;
			args.type = XFS_ALLOCTYPE_THIS_BNO;
			args.alignment = 1;
	args->type = atype;
	args->fsbno = ap->blkno;
	args->alignment = 1;
	return 0;
}

static int
xfs_bmap_btalloc_best_length(
	struct xfs_bmalloca	*ap,
	struct xfs_alloc_arg	*args,
	int			stripe_align)
{
	struct xfs_mount	*mp = args->mp;
	xfs_extlen_t		blen = 0;
	int			error;

	/*
			 * Compute the minlen+alignment for the
			 * next case.  Set slop so that the value
			 * of minlen+alignment+slop doesn't go up
			 * between the calls.
	 * Determine the initial block number we will target for allocation.
	 */
			if (blen > stripe_align && blen <= args.maxlen)
				nextminlen = blen - stripe_align;
			else
				nextminlen = args.minlen;
			if (nextminlen + stripe_align > args.minlen + 1)
				args.minalignslop =
					nextminlen + stripe_align -
					args.minlen - 1;
			else
				args.minalignslop = 0;
	if ((ap->datatype & XFS_ALLOC_USERDATA) &&
	    xfs_inode_is_filestream(ap->ip)) {
		xfs_agnumber_t	agno = xfs_filestream_lookup_ag(ap->ip);
		if (agno == NULLAGNUMBER)
			agno = 0;
		ap->blkno = XFS_AGB_TO_FSB(mp, agno, 0);
	} else {
		ap->blkno = XFS_INO_TO_FSB(mp, ap->ip->i_ino);
	}
	xfs_bmap_adjacent(ap);
	args->fsbno = ap->blkno;

			args.pag = xfs_perag_get(mp,
					XFS_FSB_TO_AGNO(mp, args.fsbno));
			error = xfs_alloc_vextent_this_ag(&args);
			xfs_perag_put(args.pag);
	/*
	 * Search for an allocation group with a single extent large enough for
	 * the request.  If one isn't found, then adjust the minimum allocation
	 * size to the largest space found.
	 */
	if ((ap->datatype & XFS_ALLOC_USERDATA) &&
	    xfs_inode_is_filestream(ap->ip))
		error = xfs_bmap_btalloc_filestreams(ap, args, &blen);
	else
		error = xfs_bmap_btalloc_select_lengths(ap, args, &blen);
	if (error)
		return error;

			if (args.fsbno != NULLFSBLOCK)
				goto out_success;
	/*
			 * Exact allocation failed. Now try with alignment
			 * turned on.
	 * Don't attempt optimal EOF allocation if previous allocations barely
	 * succeeded due to being near ENOSPC. It is highly unlikely we'll get
	 * optimal or even aligned allocations in this case, so don't waste time
	 * trying.
	 */
			args.pag = NULL;
			args.type = atype;
			args.fsbno = ap->blkno;
			args.alignment = stripe_align;
			args.minlen = nextminlen;
			args.minalignslop = 0;
			isaligned = 1;
		}
	} else {
		args.alignment = 1;
		args.minalignslop = 0;
	if (ap->aeof && !(ap->tp->t_flags & XFS_TRANS_LOWMODE)) {
		error = xfs_bmap_btalloc_at_eof(ap, args, blen, stripe_align);
		if (error)
			return error;
		if (args->fsbno != NULLFSBLOCK)
			return 0;
	}

	error = xfs_alloc_vextent(&args);
	error = xfs_alloc_vextent(args);
	if (error)
		return error;
	if (args->fsbno != NULLFSBLOCK)
		return 0;

	if (isaligned && args.fsbno == NULLFSBLOCK) {
	/*
		 * allocation failed, so turn off alignment and
		 * try again.
	 * Try a locality first full filesystem minimum length allocation whilst
	 * still maintaining necessary total block reservation requirements.
	 */
		args.type = atype;
		args.fsbno = ap->blkno;
		args.alignment = 0;
		if ((error = xfs_alloc_vextent(&args)))
			return error;
	}
	if (args.fsbno == NULLFSBLOCK &&
	    args.minlen > ap->minlen) {
		args.minlen = ap->minlen;
		args.type = XFS_ALLOCTYPE_START_BNO;
		args.fsbno = ap->blkno;
		if ((error = xfs_alloc_vextent(&args)))
	if (args->minlen > ap->minlen) {
		args->minlen = ap->minlen;
		args->type = XFS_ALLOCTYPE_START_BNO;
		args->fsbno = ap->blkno;
		error = xfs_alloc_vextent(args);
		if (error)
			return error;
	}
	if (args.fsbno == NULLFSBLOCK) {
		args.fsbno = 0;
		args.type = XFS_ALLOCTYPE_FIRST_AG;
		args.total = ap->minlen;
		if ((error = xfs_alloc_vextent(&args)))
	if (args->fsbno != NULLFSBLOCK)
		return 0;

	/*
	 * We are now critically low on space, so this is a last resort
	 * allocation attempt: no reserve, no locality, blocking, minimum
	 * length, full filesystem free space scan. We also indicate to future
	 * allocations in this transaction that we are critically low on space
	 * so they don't waste time on allocation modes that are unlikely to
	 * succeed.
	 */
	args->fsbno = 0;
	args->type = XFS_ALLOCTYPE_FIRST_AG;
	args->total = ap->minlen;
	error = xfs_alloc_vextent(args);
	if (error)
		return error;
	ap->tp->t_flags |= XFS_TRANS_LOWMODE;
	return 0;
}
	args.minleft = ap->minleft;
	args.wasdel = ap->wasdel;
	args.resv = XFS_AG_RESV_NONE;
	args.datatype = ap->datatype;

static int
xfs_bmap_btalloc(
	struct xfs_bmalloca	*ap)
{
	struct xfs_mount	*mp = ap->ip->i_mount;
	struct xfs_alloc_arg	args = {
		.tp		= ap->tp,
		.mp		= mp,
		.fsbno		= NULLFSBLOCK,
		.oinfo		= XFS_RMAP_OINFO_SKIP_UPDATE,
		.minleft	= ap->minleft,
		.wasdel		= ap->wasdel,
		.resv		= XFS_AG_RESV_NONE,
		.datatype	= ap->datatype,
		.alignment	= 1,
		.minalignslop	= 0,
	};
	xfs_fileoff_t		orig_offset;
	xfs_extlen_t		orig_length;
	int			error;
	int			stripe_align;

	ASSERT(ap->length);
	orig_offset = ap->offset;
	orig_length = ap->length;

	stripe_align = xfs_bmap_compute_alignments(ap, &args);

	/* Trim the allocation back to the maximum an AG can fit. */
	args.maxlen = min(ap->length, mp->m_ag_max_usable);

	error = xfs_bmap_btalloc_best_length(ap, &args, stripe_align);
	if (error)
		return error;

	if (args.fsbno != NULLFSBLOCK) {
out_success:
		xfs_bmap_process_allocated_extent(ap, &args, orig_offset,
			orig_length);
	} else {