@@ -3241,41 +3241,6 @@ xfs_bmap_select_minlen(
}
}
-STATIC int
-xfs_bmap_btalloc_nullfb(
- struct xfs_bmalloca *ap,
- struct xfs_alloc_arg *args,
- xfs_extlen_t *blen)
-{
- struct xfs_mount *mp = ap->ip->i_mount;
- struct xfs_perag *pag;
- xfs_agnumber_t agno, startag;
- int notinit = 0;
- int error = 0;
-
- args->type = XFS_ALLOCTYPE_START_BNO;
- args->total = ap->total;
-
- startag = XFS_FSB_TO_AGNO(mp, args->fsbno);
- if (startag == NULLAGNUMBER)
- startag = 0;
-
- *blen = 0;
- for_each_perag_wrap(mp, startag, agno, pag) {
- error = xfs_bmap_longest_free_extent(pag, args->tp, blen,
- ¬init);
- if (error)
- break;
- if (*blen >= args->maxlen)
- break;
- }
- if (pag)
- xfs_perag_rele(pag);
-
- xfs_bmap_select_minlen(ap, args, blen, notinit);
- return 0;
-}
-
STATIC int
xfs_bmap_btalloc_filestreams(
struct xfs_bmalloca *ap,
@@ -3546,202 +3511,315 @@ xfs_bmap_exact_minlen_extent_alloc(
#define xfs_bmap_exact_minlen_extent_alloc(bma) (-EFSCORRUPTED)
#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_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 fb_agno; /* ag number of ap->firstblock */
- xfs_agnumber_t ag;
- xfs_fileoff_t orig_offset;
- xfs_extlen_t orig_length;
- xfs_extlen_t blen;
- xfs_extlen_t nextminlen = 0;
- int nullfb; /* true if ap->firstblock isn't set */
- int isaligned;
+ 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);
-
- nullfb = ap->tp->t_firstblock == NULLFSBLOCK;
- fb_agno = nullfb ? NULLAGNUMBER : XFS_FSB_TO_AGNO(mp,
- ap->tp->t_firstblock);
- if (nullfb) {
- 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);
- }
- } else
- ap->blkno = ap->tp->t_firstblock;
-
- xfs_bmap_adjacent(ap);
/*
- * If allowed, use ap->blkno; otherwise must use firstblock since
- * it's in the right allocation group.
+ * 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 (nullfb || XFS_FSB_TO_AGNO(mp, ap->blkno) == fb_agno)
- ;
- else
- ap->blkno = ap->tp->t_firstblock;
- /*
- * Normal allocation, done through xfs_alloc_vextent.
- */
- isaligned = 0;
- args.fsbno = ap->blkno;
- args.oinfo = XFS_RMAP_OINFO_SKIP_UPDATE;
+ if (ap->offset) {
+ xfs_extlen_t nextminlen = 0;
+
+ atype = args->type;
+ args->type = XFS_ALLOCTYPE_THIS_BNO;
+ args->alignment = 1;
- /* Trim the allocation back to the maximum an AG can fit. */
- args.maxlen = min(ap->length, mp->m_ag_max_usable);
- blen = 0;
- if (nullfb) {
/*
- * 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_nullfb(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;
- } else if (ap->tp->t_flags & XFS_TRANS_LOWMODE) {
- if (xfs_inode_is_filestream(ap->ip))
- args.type = XFS_ALLOCTYPE_FIRST_AG;
- else
- args.type = XFS_ALLOCTYPE_START_BNO;
- args.total = args.minlen = ap->minlen;
+
+ 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.type = XFS_ALLOCTYPE_NEAR_BNO;
- args.total = ap->total;
- args.minlen = ap->minlen;
+ 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;
}
- args.minleft = ap->minleft;
- args.wasdel = ap->wasdel;
- args.resv = XFS_AG_RESV_NONE;
- args.datatype = ap->datatype;
+
+ error = xfs_alloc_vextent(args);
+ 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.
+ * 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.
*/
- 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;
+ args->type = atype;
+ args->fsbno = ap->blkno;
+ args->alignment = 1;
+ return 0;
+}
- /*
- * 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;
+static int
+xfs_btalloc_nullfb_bestlen(
+ struct xfs_bmalloca *ap,
+ struct xfs_alloc_arg *args,
+ xfs_extlen_t *blen)
+{
+ struct xfs_mount *mp = args->mp;
+ struct xfs_perag *pag;
+ xfs_agnumber_t agno, startag;
+ int notinit = 0;
+ int error = 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;
+ args->type = XFS_ALLOCTYPE_START_BNO;
+ args->total = ap->total;
- 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;
- }
+ startag = XFS_FSB_TO_AGNO(mp, args->fsbno);
+ if (startag == NULLAGNUMBER)
+ startag = 0;
+
+ *blen = 0;
+ for_each_perag_wrap(mp, startag, agno, pag) {
+ error = xfs_bmap_longest_free_extent(pag, args->tp, blen,
+ ¬init);
+ if (error)
+ break;
+ if (*blen >= args->maxlen)
+ break;
+ }
+ if (pag)
+ xfs_perag_rele(pag);
+
+ xfs_bmap_select_minlen(ap, args, blen, notinit);
+ return 0;
+}
+
+static int
+xfs_btalloc_nullfb(
+ 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 {
- args.alignment = 1;
- args.minalignslop = 0;
+ ap->blkno = XFS_INO_TO_FSB(mp, ap->ip->i_ino);
}
+ xfs_bmap_adjacent(ap);
+ args->fsbno = ap->blkno;
- error = xfs_alloc_vextent(&args);
+ /*
+ * 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_btalloc_nullfb_bestlen(ap, args, &blen);
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)))
+ if (ap->aeof) {
+ error = xfs_btalloc_at_eof(ap, args, blen, stripe_align);
+ if (error)
return error;
+ if (args->fsbno != NULLFSBLOCK)
+ return 0;
}
- if (args.fsbno == NULLFSBLOCK && nullfb &&
- args.minlen > ap->minlen) {
- args.minlen = ap->minlen;
- args.type = XFS_ALLOCTYPE_START_BNO;
- args.fsbno = ap->blkno;
- if ((error = xfs_alloc_vextent(&args)))
+
+ 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 && nullfb) {
- 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;
+}
+
+/*
+ * We are near ENOSPC, so try an exhaustive minimum length allocation. If this
+ * fails, we really are at ENOSPC.
+ */
+static int
+xfs_btalloc_low_mode(
+ struct xfs_bmalloca *ap,
+ struct xfs_alloc_arg *args)
+{
+ ap->blkno = ap->tp->t_firstblock;
+ xfs_bmap_adjacent(ap);
+ args->fsbno = ap->blkno;
+ args->total = args->minlen = ap->minlen;
+ if (xfs_inode_is_filestream(ap->ip))
+ args->type = XFS_ALLOCTYPE_FIRST_AG;
+ else
+ args->type = XFS_ALLOCTYPE_START_BNO;
+
+ return xfs_alloc_vextent(args);
+}
+
+/*
+ * Attempt to allocate near the current target. We attempt optimal EOF
+ * allocation, but then if that fails we simply try somewhere near in the same
+ * AG. If we can't get a block in the same AG, then we fail the allocation.
+ */
+static int
+xfs_btalloc_near(
+ struct xfs_bmalloca *ap,
+ struct xfs_alloc_arg *args,
+ int stripe_align)
+{
+ xfs_extlen_t blen = 0;
+ int error;
+
+ ap->blkno = ap->tp->t_firstblock;
+ xfs_bmap_adjacent(ap);
+ args->fsbno = ap->blkno;
+ args->type = XFS_ALLOCTYPE_NEAR_BNO;
+ args->total = ap->total;
+ args->minlen = ap->minlen;
+
+ if (ap->aeof) {
+ error = xfs_btalloc_at_eof(ap, args, blen, stripe_align);
+ if (error)
return error;
- ap->tp->t_flags |= XFS_TRANS_LOWMODE;
+ if (args->fsbno != NULLFSBLOCK)
+ return 0;
}
+ return xfs_alloc_vextent(args);
+}
+
+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,
+ .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);
+
+ if (ap->tp->t_firstblock == NULLFSBLOCK) {
+ error = xfs_btalloc_nullfb(ap, &args, stripe_align);
+ } else if (ap->tp->t_flags & XFS_TRANS_LOWMODE) {
+ error = xfs_btalloc_low_mode(ap, &args);
+ } else {
+ error = xfs_btalloc_near(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 {