diff options
author | Dave Chinner <dchinner@redhat.com> | 2023-02-13 09:14:53 +1100 |
---|---|---|
committer | Dave Chinner <dchinner@redhat.com> | 2023-02-13 09:14:53 +1100 |
commit | 85843327094f9de9cf0129cd9a3a43128c6f5ac8 (patch) | |
tree | 8b980d792c7a8029c11cf16968e19cf10942dabb | |
parent | 74c36a8689d3d8ca9d9e96759c9bbf337e049097 (diff) |
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>
-rw-r--r-- | fs/xfs/libxfs/xfs_bmap.c | 333 |
1 files changed, 196 insertions, 137 deletions
diff --git a/fs/xfs/libxfs/xfs_bmap.c b/fs/xfs/libxfs/xfs_bmap.c index baf11bc4d091..a28d57b82396 100644 --- a/fs/xfs/libxfs/xfs_bmap.c +++ b/fs/xfs/libxfs/xfs_bmap.c @@ -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; + /* + * 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; - stripe_align = xfs_bmap_compute_alignments(ap, &args); + 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)) { - ag = xfs_filestream_lookup_ag(ap->ip); - ag = (ag != NULLAGNUMBER) ? ag : 0; - ap->blkno = XFS_AGB_TO_FSB(mp, ag, 0); + 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.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; + 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. + * 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); + error = xfs_bmap_btalloc_filestreams(ap, args, &blen); else - error = xfs_bmap_btalloc_select_lengths(ap, &args, &blen); + error = xfs_bmap_btalloc_select_lengths(ap, args, &blen); 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. + * 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->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; + 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. - */ - 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))) + /* + * 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; - 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) + 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, + .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 { |