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: Dave Chinner <dchinner@redhat.com>
Reviewed-by: Darrick J. Wong <djwong@kernel.org>
This commit is contained in:
Dave Chinner 2023-02-13 09:14:53 +11:00
parent 74c36a8689
commit 8584332709

View file

@ -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,19 +3496,212 @@ xfs_bmap_exact_minlen_extent_alloc(
#endif
STATIC int
/*
* 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 = args->mp;
xfs_alloctype_t atype;
int error;
/*
* 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;
atype = args->type;
args->type = XFS_ALLOCTYPE_THIS_BNO;
args->alignment = 1;
/*
* 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 (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;
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;
/*
* Exact allocation failed. Reset to try an aligned allocation
* according to the original allocation specification.
*/
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;
}
error = xfs_alloc_vextent(args);
if (error)
return error;
if (args->fsbno != NULLFSBLOCK)
return 0;
/*
* 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.
*/
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;
/*
* Determine the initial block number we will target for allocation.
*/
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;
/*
* 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;
/*
* 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.
*/
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);
if (error)
return error;
if (args->fsbno != NULLFSBLOCK)
return 0;
/*
* Try a locality first full filesystem minimum length allocation whilst
* still maintaining necessary total block reservation requirements.
*/
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)
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;
}
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 };
xfs_alloctype_t atype = 0;
xfs_agnumber_t ag;
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;
xfs_extlen_t blen;
xfs_extlen_t nextminlen = 0;
int isaligned = 0;
int error;
int stripe_align;
@ -3518,148 +3711,14 @@ xfs_bmap_btalloc(
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;
/* Trim the allocation back to the maximum an AG can fit. */
args.maxlen = min(ap->length, mp->m_ag_max_usable);
blen = 0;
/*
* 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);
error = xfs_bmap_btalloc_best_length(ap, &args, stripe_align);
if (error)
return error;
/*
* 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.
*/
if (!(ap->tp->t_flags & XFS_TRANS_LOWMODE) && ap->aeof) {
if (!ap->offset) {
args.alignment = stripe_align;
atype = args.type;
isaligned = 1;
/*
* 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 {
/*
* First try an exact bno allocation.
* If it fails then do a near or start bno
* allocation with alignment turned on.
*/
atype = args.type;
args.type = XFS_ALLOCTYPE_THIS_BNO;
args.alignment = 1;
/*
* 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 (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;
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)
goto out_success;
/*
* Exact allocation failed. Now try with alignment
* turned on.
*/
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;
}
error = xfs_alloc_vextent(&args);
if (error)
return error;
if (isaligned && args.fsbno == NULLFSBLOCK) {
/*
* allocation failed, so turn off alignment and
* try again.
*/
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)))
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)))
return error;
ap->tp->t_flags |= XFS_TRANS_LOWMODE;
}
args.minleft = ap->minleft;
args.wasdel = ap->wasdel;
args.resv = XFS_AG_RESV_NONE;
args.datatype = ap->datatype;
if (args.fsbno != NULLFSBLOCK) {
out_success:
xfs_bmap_process_allocated_extent(ap, &args, orig_offset,
orig_length);
} else {