Merge branch 'xfs-freelist-cleanup' into for-next

This commit is contained in:
Dave Chinner 2015-06-23 08:48:43 +10:00
commit 3d238b7e0e
7 changed files with 142 additions and 130 deletions

View File

@ -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;
}
/*

View File

@ -130,11 +130,9 @@ typedef struct xfs_alloc_arg {
#define XFS_ALLOC_USERDATA 1 /* allocation is for user data*/
#define XFS_ALLOC_INITIAL_USER_DATA 2 /* special case start of file */
/*
* Find the length of the longest extent in an AG.
*/
xfs_extlen_t
xfs_alloc_longest_free_extent(struct xfs_mount *mp,
xfs_extlen_t xfs_alloc_longest_free_extent(struct xfs_mount *mp,
struct xfs_perag *pag, xfs_extlen_t need);
unsigned int xfs_alloc_min_freelist(struct xfs_mount *mp,
struct xfs_perag *pag);
/*

View File

@ -3518,7 +3518,8 @@ xfs_bmap_longest_free_extent(
}
}
longest = xfs_alloc_longest_free_extent(mp, pag);
longest = xfs_alloc_longest_free_extent(mp, pag,
xfs_alloc_min_freelist(mp, pag));
if (*blen < longest)
*blen = longest;

View File

@ -766,19 +766,6 @@ typedef struct xfs_agfl {
#define XFS_AGFL_CRC_OFF offsetof(struct xfs_agfl, agfl_crc)
#define XFS_AG_MAXLEVELS(mp) ((mp)->m_ag_maxlevels)
#define XFS_MIN_FREELIST_RAW(bl,cl,mp) \
(MIN(bl + 1, XFS_AG_MAXLEVELS(mp)) + MIN(cl + 1, XFS_AG_MAXLEVELS(mp)))
#define XFS_MIN_FREELIST(a,mp) \
(XFS_MIN_FREELIST_RAW( \
be32_to_cpu((a)->agf_levels[XFS_BTNUM_BNOi]), \
be32_to_cpu((a)->agf_levels[XFS_BTNUM_CNTi]), mp))
#define XFS_MIN_FREELIST_PAG(pag,mp) \
(XFS_MIN_FREELIST_RAW( \
(unsigned int)(pag)->pagf_levels[XFS_BTNUM_BNOi], \
(unsigned int)(pag)->pagf_levels[XFS_BTNUM_CNTi], mp))
#define XFS_AGB_TO_FSB(mp,agno,agbno) \
(((xfs_fsblock_t)(agno) << (mp)->m_sb.sb_agblklog) | (agbno))
#define XFS_FSB_TO_AGNO(mp,fsbno) \

View File

@ -73,9 +73,9 @@ struct xfs_trans_resv {
* 2 trees * (2 blocks/level * max depth - 1) * block size
*/
#define XFS_ALLOCFREE_LOG_RES(mp,nx) \
((nx) * (2 * XFS_FSB_TO_B((mp), 2 * XFS_AG_MAXLEVELS(mp) - 1)))
((nx) * (2 * XFS_FSB_TO_B((mp), 2 * (mp)->m_ag_maxlevels - 1)))
#define XFS_ALLOCFREE_LOG_COUNT(mp,nx) \
((nx) * (2 * (2 * XFS_AG_MAXLEVELS(mp) - 1)))
((nx) * (2 * (2 * (mp)->m_ag_maxlevels - 1)))
/*
* Per-directory log reservation for any directory change.

View File

@ -67,7 +67,7 @@
#define XFS_DIOSTRAT_SPACE_RES(mp, v) \
(XFS_EXTENTADD_SPACE_RES(mp, XFS_DATA_FORK) + (v))
#define XFS_GROWFS_SPACE_RES(mp) \
(2 * XFS_AG_MAXLEVELS(mp))
(2 * (mp)->m_ag_maxlevels)
#define XFS_GROWFSRT_SPACE_RES(mp,b) \
((b) + XFS_EXTENTADD_SPACE_RES(mp, XFS_DATA_FORK))
#define XFS_LINK_SPACE_RES(mp,nl) \

View File

@ -196,7 +196,8 @@ xfs_filestream_pick_ag(
goto next_ag;
}
longest = xfs_alloc_longest_free_extent(mp, pag);
longest = xfs_alloc_longest_free_extent(mp, pag,
xfs_alloc_min_freelist(mp, pag));
if (((minlen && longest >= minlen) ||
(!minlen && pag->pagf_freeblks >= minfree)) &&
(!pag->pagf_metadata || !(flags & XFS_PICK_USERDATA) ||