|
|
|
@ -1857,11 +1857,11 @@ xfs_alloc_compute_maxlevels(
|
|
|
|
|
xfs_extlen_t
|
|
|
|
|
xfs_alloc_longest_free_extent(
|
|
|
|
|
struct xfs_mount *mp,
|
|
|
|
|
struct xfs_perag *pag)
|
|
|
|
|
struct xfs_perag *pag,
|
|
|
|
|
xfs_extlen_t need)
|
|
|
|
|
{
|
|
|
|
|
xfs_extlen_t need, delta = 0;
|
|
|
|
|
xfs_extlen_t delta = 0;
|
|
|
|
|
|
|
|
|
|
need = XFS_MIN_FREELIST_PAG(pag, mp);
|
|
|
|
|
if (need > pag->pagf_flcount)
|
|
|
|
|
delta = need - pag->pagf_flcount;
|
|
|
|
|
|
|
|
|
@ -1870,131 +1870,150 @@ xfs_alloc_longest_free_extent(
|
|
|
|
|
return pag->pagf_flcount > 0 || pag->pagf_longest > 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
unsigned int
|
|
|
|
|
xfs_alloc_min_freelist(
|
|
|
|
|
struct xfs_mount *mp,
|
|
|
|
|
struct xfs_perag *pag)
|
|
|
|
|
{
|
|
|
|
|
unsigned int min_free;
|
|
|
|
|
|
|
|
|
|
/* space needed by-bno freespace btree */
|
|
|
|
|
min_free = min_t(unsigned int, pag->pagf_levels[XFS_BTNUM_BNOi] + 1,
|
|
|
|
|
mp->m_ag_maxlevels);
|
|
|
|
|
/* space needed by-size freespace btree */
|
|
|
|
|
min_free += min_t(unsigned int, pag->pagf_levels[XFS_BTNUM_CNTi] + 1,
|
|
|
|
|
mp->m_ag_maxlevels);
|
|
|
|
|
|
|
|
|
|
return min_free;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Check if the operation we are fixing up the freelist for should go ahead or
|
|
|
|
|
* not. If we are freeing blocks, we always allow it, otherwise the allocation
|
|
|
|
|
* is dependent on whether the size and shape of free space available will
|
|
|
|
|
* permit the requested allocation to take place.
|
|
|
|
|
*/
|
|
|
|
|
static bool
|
|
|
|
|
xfs_alloc_space_available(
|
|
|
|
|
struct xfs_alloc_arg *args,
|
|
|
|
|
xfs_extlen_t min_free,
|
|
|
|
|
int flags)
|
|
|
|
|
{
|
|
|
|
|
struct xfs_perag *pag = args->pag;
|
|
|
|
|
xfs_extlen_t longest;
|
|
|
|
|
int available;
|
|
|
|
|
|
|
|
|
|
if (flags & XFS_ALLOC_FLAG_FREEING)
|
|
|
|
|
return true;
|
|
|
|
|
|
|
|
|
|
/* do we have enough contiguous free space for the allocation? */
|
|
|
|
|
longest = xfs_alloc_longest_free_extent(args->mp, pag, min_free);
|
|
|
|
|
if ((args->minlen + args->alignment + args->minalignslop - 1) > longest)
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
/* do have enough free space remaining for the allocation? */
|
|
|
|
|
available = (int)(pag->pagf_freeblks + pag->pagf_flcount -
|
|
|
|
|
min_free - args->total);
|
|
|
|
|
if (available < (int)args->minleft)
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Decide whether to use this allocation group for this allocation.
|
|
|
|
|
* If so, fix up the btree freelist's size.
|
|
|
|
|
*/
|
|
|
|
|
STATIC int /* error */
|
|
|
|
|
xfs_alloc_fix_freelist(
|
|
|
|
|
xfs_alloc_arg_t *args, /* allocation argument structure */
|
|
|
|
|
int flags) /* XFS_ALLOC_FLAG_... */
|
|
|
|
|
struct xfs_alloc_arg *args, /* allocation argument structure */
|
|
|
|
|
int flags) /* XFS_ALLOC_FLAG_... */
|
|
|
|
|
{
|
|
|
|
|
xfs_buf_t *agbp; /* agf buffer pointer */
|
|
|
|
|
xfs_agf_t *agf; /* a.g. freespace structure pointer */
|
|
|
|
|
xfs_buf_t *agflbp;/* agfl buffer pointer */
|
|
|
|
|
xfs_agblock_t bno; /* freelist block */
|
|
|
|
|
xfs_extlen_t delta; /* new blocks needed in freelist */
|
|
|
|
|
int error; /* error result code */
|
|
|
|
|
xfs_extlen_t longest;/* longest extent in allocation group */
|
|
|
|
|
xfs_mount_t *mp; /* file system mount point structure */
|
|
|
|
|
xfs_extlen_t need; /* total blocks needed in freelist */
|
|
|
|
|
xfs_perag_t *pag; /* per-ag information structure */
|
|
|
|
|
xfs_alloc_arg_t targs; /* local allocation arguments */
|
|
|
|
|
xfs_trans_t *tp; /* transaction pointer */
|
|
|
|
|
struct xfs_mount *mp = args->mp;
|
|
|
|
|
struct xfs_perag *pag = args->pag;
|
|
|
|
|
struct xfs_trans *tp = args->tp;
|
|
|
|
|
struct xfs_buf *agbp = NULL;
|
|
|
|
|
struct xfs_buf *agflbp = NULL;
|
|
|
|
|
struct xfs_alloc_arg targs; /* local allocation arguments */
|
|
|
|
|
xfs_agblock_t bno; /* freelist block */
|
|
|
|
|
xfs_extlen_t need; /* total blocks needed in freelist */
|
|
|
|
|
int error;
|
|
|
|
|
|
|
|
|
|
mp = args->mp;
|
|
|
|
|
|
|
|
|
|
pag = args->pag;
|
|
|
|
|
tp = args->tp;
|
|
|
|
|
if (!pag->pagf_init) {
|
|
|
|
|
if ((error = xfs_alloc_read_agf(mp, tp, args->agno, flags,
|
|
|
|
|
&agbp)))
|
|
|
|
|
return error;
|
|
|
|
|
error = xfs_alloc_read_agf(mp, tp, args->agno, flags, &agbp);
|
|
|
|
|
if (error)
|
|
|
|
|
goto out_no_agbp;
|
|
|
|
|
if (!pag->pagf_init) {
|
|
|
|
|
ASSERT(flags & XFS_ALLOC_FLAG_TRYLOCK);
|
|
|
|
|
ASSERT(!(flags & XFS_ALLOC_FLAG_FREEING));
|
|
|
|
|
args->agbp = NULL;
|
|
|
|
|
return 0;
|
|
|
|
|
goto out_agbp_relse;
|
|
|
|
|
}
|
|
|
|
|
} else
|
|
|
|
|
agbp = NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* If this is a metadata preferred pag and we are user data
|
|
|
|
|
* then try somewhere else if we are not being asked to
|
|
|
|
|
* try harder at this point
|
|
|
|
|
* If this is a metadata preferred pag and we are user data then try
|
|
|
|
|
* somewhere else if we are not being asked to try harder at this
|
|
|
|
|
* point
|
|
|
|
|
*/
|
|
|
|
|
if (pag->pagf_metadata && args->userdata &&
|
|
|
|
|
(flags & XFS_ALLOC_FLAG_TRYLOCK)) {
|
|
|
|
|
ASSERT(!(flags & XFS_ALLOC_FLAG_FREEING));
|
|
|
|
|
args->agbp = NULL;
|
|
|
|
|
return 0;
|
|
|
|
|
goto out_agbp_relse;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!(flags & XFS_ALLOC_FLAG_FREEING)) {
|
|
|
|
|
/*
|
|
|
|
|
* If it looks like there isn't a long enough extent, or enough
|
|
|
|
|
* total blocks, reject it.
|
|
|
|
|
*/
|
|
|
|
|
need = XFS_MIN_FREELIST_PAG(pag, mp);
|
|
|
|
|
longest = xfs_alloc_longest_free_extent(mp, pag);
|
|
|
|
|
if ((args->minlen + args->alignment + args->minalignslop - 1) >
|
|
|
|
|
longest ||
|
|
|
|
|
((int)(pag->pagf_freeblks + pag->pagf_flcount -
|
|
|
|
|
need - args->total) < (int)args->minleft)) {
|
|
|
|
|
if (agbp)
|
|
|
|
|
xfs_trans_brelse(tp, agbp);
|
|
|
|
|
args->agbp = NULL;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
need = xfs_alloc_min_freelist(mp, pag);
|
|
|
|
|
if (!xfs_alloc_space_available(args, need, flags))
|
|
|
|
|
goto out_agbp_relse;
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Get the a.g. freespace buffer.
|
|
|
|
|
* Can fail if we're not blocking on locks, and it's held.
|
|
|
|
|
*/
|
|
|
|
|
if (agbp == NULL) {
|
|
|
|
|
if ((error = xfs_alloc_read_agf(mp, tp, args->agno, flags,
|
|
|
|
|
&agbp)))
|
|
|
|
|
return error;
|
|
|
|
|
if (agbp == NULL) {
|
|
|
|
|
if (!agbp) {
|
|
|
|
|
error = xfs_alloc_read_agf(mp, tp, args->agno, flags, &agbp);
|
|
|
|
|
if (error)
|
|
|
|
|
goto out_no_agbp;
|
|
|
|
|
if (!agbp) {
|
|
|
|
|
ASSERT(flags & XFS_ALLOC_FLAG_TRYLOCK);
|
|
|
|
|
ASSERT(!(flags & XFS_ALLOC_FLAG_FREEING));
|
|
|
|
|
args->agbp = NULL;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
/*
|
|
|
|
|
* Figure out how many blocks we should have in the freelist.
|
|
|
|
|
*/
|
|
|
|
|
agf = XFS_BUF_TO_AGF(agbp);
|
|
|
|
|
need = XFS_MIN_FREELIST(agf, mp);
|
|
|
|
|
/*
|
|
|
|
|
* If there isn't enough total or single-extent, reject it.
|
|
|
|
|
*/
|
|
|
|
|
if (!(flags & XFS_ALLOC_FLAG_FREEING)) {
|
|
|
|
|
delta = need > be32_to_cpu(agf->agf_flcount) ?
|
|
|
|
|
(need - be32_to_cpu(agf->agf_flcount)) : 0;
|
|
|
|
|
longest = be32_to_cpu(agf->agf_longest);
|
|
|
|
|
longest = (longest > delta) ? (longest - delta) :
|
|
|
|
|
(be32_to_cpu(agf->agf_flcount) > 0 || longest > 0);
|
|
|
|
|
if ((args->minlen + args->alignment + args->minalignslop - 1) >
|
|
|
|
|
longest ||
|
|
|
|
|
((int)(be32_to_cpu(agf->agf_freeblks) +
|
|
|
|
|
be32_to_cpu(agf->agf_flcount) - need - args->total) <
|
|
|
|
|
(int)args->minleft)) {
|
|
|
|
|
xfs_trans_brelse(tp, agbp);
|
|
|
|
|
args->agbp = NULL;
|
|
|
|
|
return 0;
|
|
|
|
|
goto out_no_agbp;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* If there isn't enough total space or single-extent, reject it. */
|
|
|
|
|
need = xfs_alloc_min_freelist(mp, pag);
|
|
|
|
|
if (!xfs_alloc_space_available(args, need, flags))
|
|
|
|
|
goto out_agbp_relse;
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Make the freelist shorter if it's too long.
|
|
|
|
|
*
|
|
|
|
|
* Note that from this point onwards, we will always release the agf and
|
|
|
|
|
* agfl buffers on error. This handles the case where we error out and
|
|
|
|
|
* the buffers are clean or may not have been joined to the transaction
|
|
|
|
|
* and hence need to be released manually. If they have been joined to
|
|
|
|
|
* the transaction, then xfs_trans_brelse() will handle them
|
|
|
|
|
* appropriately based on the recursion count and dirty state of the
|
|
|
|
|
* buffer.
|
|
|
|
|
*
|
|
|
|
|
* XXX (dgc): When we have lots of free space, does this buy us
|
|
|
|
|
* anything other than extra overhead when we need to put more blocks
|
|
|
|
|
* back on the free list? Maybe we should only do this when space is
|
|
|
|
|
* getting low or the AGFL is more than half full?
|
|
|
|
|
*/
|
|
|
|
|
while (be32_to_cpu(agf->agf_flcount) > need) {
|
|
|
|
|
xfs_buf_t *bp;
|
|
|
|
|
while (pag->pagf_flcount > need) {
|
|
|
|
|
struct xfs_buf *bp;
|
|
|
|
|
|
|
|
|
|
error = xfs_alloc_get_freelist(tp, agbp, &bno, 0);
|
|
|
|
|
if (error)
|
|
|
|
|
return error;
|
|
|
|
|
if ((error = xfs_free_ag_extent(tp, agbp, args->agno, bno, 1, 1)))
|
|
|
|
|
return error;
|
|
|
|
|
goto out_agbp_relse;
|
|
|
|
|
error = xfs_free_ag_extent(tp, agbp, args->agno, bno, 1, 1);
|
|
|
|
|
if (error)
|
|
|
|
|
goto out_agbp_relse;
|
|
|
|
|
bp = xfs_btree_get_bufs(mp, tp, args->agno, bno, 0);
|
|
|
|
|
xfs_trans_binval(tp, bp);
|
|
|
|
|
}
|
|
|
|
|
/*
|
|
|
|
|
* Initialize the args structure.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
memset(&targs, 0, sizeof(targs));
|
|
|
|
|
targs.tp = tp;
|
|
|
|
|
targs.mp = mp;
|
|
|
|
@ -2003,21 +2022,20 @@ xfs_alloc_fix_freelist(
|
|
|
|
|
targs.alignment = targs.minlen = targs.prod = targs.isfl = 1;
|
|
|
|
|
targs.type = XFS_ALLOCTYPE_THIS_AG;
|
|
|
|
|
targs.pag = pag;
|
|
|
|
|
if ((error = xfs_alloc_read_agfl(mp, tp, targs.agno, &agflbp)))
|
|
|
|
|
return error;
|
|
|
|
|
/*
|
|
|
|
|
* Make the freelist longer if it's too short.
|
|
|
|
|
*/
|
|
|
|
|
while (be32_to_cpu(agf->agf_flcount) < need) {
|
|
|
|
|
error = xfs_alloc_read_agfl(mp, tp, targs.agno, &agflbp);
|
|
|
|
|
if (error)
|
|
|
|
|
goto out_agbp_relse;
|
|
|
|
|
|
|
|
|
|
/* Make the freelist longer if it's too short. */
|
|
|
|
|
while (pag->pagf_flcount < need) {
|
|
|
|
|
targs.agbno = 0;
|
|
|
|
|
targs.maxlen = need - be32_to_cpu(agf->agf_flcount);
|
|
|
|
|
/*
|
|
|
|
|
* Allocate as many blocks as possible at once.
|
|
|
|
|
*/
|
|
|
|
|
if ((error = xfs_alloc_ag_vextent(&targs))) {
|
|
|
|
|
xfs_trans_brelse(tp, agflbp);
|
|
|
|
|
return error;
|
|
|
|
|
}
|
|
|
|
|
targs.maxlen = need - pag->pagf_flcount;
|
|
|
|
|
|
|
|
|
|
/* Allocate as many blocks as possible at once. */
|
|
|
|
|
error = xfs_alloc_ag_vextent(&targs);
|
|
|
|
|
if (error)
|
|
|
|
|
goto out_agflbp_relse;
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Stop if we run out. Won't happen if callers are obeying
|
|
|
|
|
* the restrictions correctly. Can happen for free calls
|
|
|
|
@ -2026,9 +2044,7 @@ xfs_alloc_fix_freelist(
|
|
|
|
|
if (targs.agbno == NULLAGBLOCK) {
|
|
|
|
|
if (flags & XFS_ALLOC_FLAG_FREEING)
|
|
|
|
|
break;
|
|
|
|
|
xfs_trans_brelse(tp, agflbp);
|
|
|
|
|
args->agbp = NULL;
|
|
|
|
|
return 0;
|
|
|
|
|
goto out_agflbp_relse;
|
|
|
|
|
}
|
|
|
|
|
/*
|
|
|
|
|
* Put each allocated block on the list.
|
|
|
|
@ -2037,12 +2053,21 @@ xfs_alloc_fix_freelist(
|
|
|
|
|
error = xfs_alloc_put_freelist(tp, agbp,
|
|
|
|
|
agflbp, bno, 0);
|
|
|
|
|
if (error)
|
|
|
|
|
return error;
|
|
|
|
|
goto out_agflbp_relse;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
xfs_trans_brelse(tp, agflbp);
|
|
|
|
|
args->agbp = agbp;
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
out_agflbp_relse:
|
|
|
|
|
xfs_trans_brelse(tp, agflbp);
|
|
|
|
|
out_agbp_relse:
|
|
|
|
|
if (agbp)
|
|
|
|
|
xfs_trans_brelse(tp, agbp);
|
|
|
|
|
out_no_agbp:
|
|
|
|
|
args->agbp = NULL;
|
|
|
|
|
return error;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|