|
@@ -37,6 +37,7 @@
|
|
|
#include "xfs_trans.h"
|
|
|
#include "xfs_buf_item.h"
|
|
|
#include "xfs_log.h"
|
|
|
+#include "xfs_ag_resv.h"
|
|
|
|
|
|
struct workqueue_struct *xfs_alloc_wq;
|
|
|
|
|
@@ -74,14 +75,8 @@ xfs_prealloc_blocks(
|
|
|
* extents need to be actually allocated. To get around this, we explicitly set
|
|
|
* aside a few blocks which will not be reserved in delayed allocation.
|
|
|
*
|
|
|
- * When rmap is disabled, we need to reserve 4 fsbs _per AG_ for the freelist
|
|
|
- * and 4 more to handle a potential split of the file's bmap btree.
|
|
|
- *
|
|
|
- * When rmap is enabled, we must also be able to handle two rmap btree inserts
|
|
|
- * to record both the file data extent and a new bmbt block. The bmbt block
|
|
|
- * might not be in the same AG as the file data extent. In the worst case
|
|
|
- * the bmap btree splits multiple levels and all the new blocks come from
|
|
|
- * different AGs, so set aside enough to handle rmap btree splits in all AGs.
|
|
|
+ * We need to reserve 4 fsbs _per AG_ for the freelist and 4 more to handle a
|
|
|
+ * potential split of the file's bmap btree.
|
|
|
*/
|
|
|
unsigned int
|
|
|
xfs_alloc_set_aside(
|
|
@@ -90,8 +85,6 @@ xfs_alloc_set_aside(
|
|
|
unsigned int blocks;
|
|
|
|
|
|
blocks = 4 + (mp->m_sb.sb_agcount * XFS_ALLOC_AGFL_RESERVE);
|
|
|
- if (xfs_sb_version_hasrmapbt(&mp->m_sb))
|
|
|
- blocks += mp->m_sb.sb_agcount * mp->m_rmap_maxlevels;
|
|
|
return blocks;
|
|
|
}
|
|
|
|
|
@@ -680,12 +673,29 @@ xfs_alloc_ag_vextent(
|
|
|
xfs_alloc_arg_t *args) /* argument structure for allocation */
|
|
|
{
|
|
|
int error=0;
|
|
|
+ xfs_extlen_t reservation;
|
|
|
+ xfs_extlen_t oldmax;
|
|
|
|
|
|
ASSERT(args->minlen > 0);
|
|
|
ASSERT(args->maxlen > 0);
|
|
|
ASSERT(args->minlen <= args->maxlen);
|
|
|
ASSERT(args->mod < args->prod);
|
|
|
ASSERT(args->alignment > 0);
|
|
|
+
|
|
|
+ /*
|
|
|
+ * Clamp maxlen to the amount of free space minus any reservations
|
|
|
+ * that have been made.
|
|
|
+ */
|
|
|
+ oldmax = args->maxlen;
|
|
|
+ reservation = xfs_ag_resv_needed(args->pag, args->resv);
|
|
|
+ if (args->maxlen > args->pag->pagf_freeblks - reservation)
|
|
|
+ args->maxlen = args->pag->pagf_freeblks - reservation;
|
|
|
+ if (args->maxlen == 0) {
|
|
|
+ args->agbno = NULLAGBLOCK;
|
|
|
+ args->maxlen = oldmax;
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+
|
|
|
/*
|
|
|
* Branch to correct routine based on the type.
|
|
|
*/
|
|
@@ -705,12 +715,14 @@ xfs_alloc_ag_vextent(
|
|
|
/* NOTREACHED */
|
|
|
}
|
|
|
|
|
|
+ args->maxlen = oldmax;
|
|
|
+
|
|
|
if (error || args->agbno == NULLAGBLOCK)
|
|
|
return error;
|
|
|
|
|
|
ASSERT(args->len >= args->minlen);
|
|
|
ASSERT(args->len <= args->maxlen);
|
|
|
- ASSERT(!args->wasfromfl || !args->isfl);
|
|
|
+ ASSERT(!args->wasfromfl || args->resv != XFS_AG_RESV_AGFL);
|
|
|
ASSERT(args->agbno % args->alignment == 0);
|
|
|
|
|
|
/* if not file data, insert new block into the reverse map btree */
|
|
@@ -732,12 +744,7 @@ xfs_alloc_ag_vextent(
|
|
|
args->agbno, args->len));
|
|
|
}
|
|
|
|
|
|
- if (!args->isfl) {
|
|
|
- xfs_trans_mod_sb(args->tp, args->wasdel ?
|
|
|
- XFS_TRANS_SB_RES_FDBLOCKS :
|
|
|
- XFS_TRANS_SB_FDBLOCKS,
|
|
|
- -((long)(args->len)));
|
|
|
- }
|
|
|
+ xfs_ag_resv_alloc_extent(args->pag, args->resv, args);
|
|
|
|
|
|
XFS_STATS_INC(args->mp, xs_allocx);
|
|
|
XFS_STATS_ADD(args->mp, xs_allocb, args->len);
|
|
@@ -1583,6 +1590,7 @@ xfs_alloc_ag_vextent_small(
|
|
|
int *stat) /* status: 0-freelist, 1-normal/none */
|
|
|
{
|
|
|
struct xfs_owner_info oinfo;
|
|
|
+ struct xfs_perag *pag;
|
|
|
int error;
|
|
|
xfs_agblock_t fbno;
|
|
|
xfs_extlen_t flen;
|
|
@@ -1600,7 +1608,8 @@ xfs_alloc_ag_vextent_small(
|
|
|
* to respect minleft even when pulling from the
|
|
|
* freelist.
|
|
|
*/
|
|
|
- else if (args->minlen == 1 && args->alignment == 1 && !args->isfl &&
|
|
|
+ else if (args->minlen == 1 && args->alignment == 1 &&
|
|
|
+ args->resv != XFS_AG_RESV_AGFL &&
|
|
|
(be32_to_cpu(XFS_BUF_TO_AGF(args->agbp)->agf_flcount)
|
|
|
> args->minleft)) {
|
|
|
error = xfs_alloc_get_freelist(args->tp, args->agbp, &fbno, 0);
|
|
@@ -1629,13 +1638,18 @@ xfs_alloc_ag_vextent_small(
|
|
|
/*
|
|
|
* If we're feeding an AGFL block to something that
|
|
|
* doesn't live in the free space, we need to clear
|
|
|
- * out the OWN_AG rmap.
|
|
|
+ * out the OWN_AG rmap and add the block back to
|
|
|
+ * the AGFL per-AG reservation.
|
|
|
*/
|
|
|
xfs_rmap_ag_owner(&oinfo, XFS_RMAP_OWN_AG);
|
|
|
error = xfs_rmap_free(args->tp, args->agbp, args->agno,
|
|
|
fbno, 1, &oinfo);
|
|
|
if (error)
|
|
|
goto error0;
|
|
|
+ pag = xfs_perag_get(args->mp, args->agno);
|
|
|
+ xfs_ag_resv_free_extent(pag, XFS_AG_RESV_AGFL,
|
|
|
+ args->tp, 1);
|
|
|
+ xfs_perag_put(pag);
|
|
|
|
|
|
*stat = 0;
|
|
|
return 0;
|
|
@@ -1683,7 +1697,7 @@ xfs_free_ag_extent(
|
|
|
xfs_agblock_t bno,
|
|
|
xfs_extlen_t len,
|
|
|
struct xfs_owner_info *oinfo,
|
|
|
- int isfl)
|
|
|
+ enum xfs_ag_resv_type type)
|
|
|
{
|
|
|
xfs_btree_cur_t *bno_cur; /* cursor for by-block btree */
|
|
|
xfs_btree_cur_t *cnt_cur; /* cursor for by-size btree */
|
|
@@ -1911,21 +1925,22 @@ xfs_free_ag_extent(
|
|
|
*/
|
|
|
pag = xfs_perag_get(mp, agno);
|
|
|
error = xfs_alloc_update_counters(tp, pag, agbp, len);
|
|
|
+ xfs_ag_resv_free_extent(pag, type, tp, len);
|
|
|
xfs_perag_put(pag);
|
|
|
if (error)
|
|
|
goto error0;
|
|
|
|
|
|
- if (!isfl)
|
|
|
- xfs_trans_mod_sb(tp, XFS_TRANS_SB_FDBLOCKS, (long)len);
|
|
|
XFS_STATS_INC(mp, xs_freex);
|
|
|
XFS_STATS_ADD(mp, xs_freeb, len);
|
|
|
|
|
|
- trace_xfs_free_extent(mp, agno, bno, len, isfl, haveleft, haveright);
|
|
|
+ trace_xfs_free_extent(mp, agno, bno, len, type == XFS_AG_RESV_AGFL,
|
|
|
+ haveleft, haveright);
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
error0:
|
|
|
- trace_xfs_free_extent(mp, agno, bno, len, isfl, -1, -1);
|
|
|
+ trace_xfs_free_extent(mp, agno, bno, len, type == XFS_AG_RESV_AGFL,
|
|
|
+ -1, -1);
|
|
|
if (bno_cur)
|
|
|
xfs_btree_del_cursor(bno_cur, XFS_BTREE_ERROR);
|
|
|
if (cnt_cur)
|
|
@@ -1950,21 +1965,43 @@ xfs_alloc_compute_maxlevels(
|
|
|
}
|
|
|
|
|
|
/*
|
|
|
- * Find the length of the longest extent in an AG.
|
|
|
+ * Find the length of the longest extent in an AG. The 'need' parameter
|
|
|
+ * specifies how much space we're going to need for the AGFL and the
|
|
|
+ * 'reserved' parameter tells us how many blocks in this AG are reserved for
|
|
|
+ * other callers.
|
|
|
*/
|
|
|
xfs_extlen_t
|
|
|
xfs_alloc_longest_free_extent(
|
|
|
struct xfs_mount *mp,
|
|
|
struct xfs_perag *pag,
|
|
|
- xfs_extlen_t need)
|
|
|
+ xfs_extlen_t need,
|
|
|
+ xfs_extlen_t reserved)
|
|
|
{
|
|
|
xfs_extlen_t delta = 0;
|
|
|
|
|
|
+ /*
|
|
|
+ * If the AGFL needs a recharge, we'll have to subtract that from the
|
|
|
+ * longest extent.
|
|
|
+ */
|
|
|
if (need > pag->pagf_flcount)
|
|
|
delta = need - pag->pagf_flcount;
|
|
|
|
|
|
+ /*
|
|
|
+ * If we cannot maintain others' reservations with space from the
|
|
|
+ * not-longest freesp extents, we'll have to subtract /that/ from
|
|
|
+ * the longest extent too.
|
|
|
+ */
|
|
|
+ if (pag->pagf_freeblks - pag->pagf_longest < reserved)
|
|
|
+ delta += reserved - (pag->pagf_freeblks - pag->pagf_longest);
|
|
|
+
|
|
|
+ /*
|
|
|
+ * If the longest extent is long enough to satisfy all the
|
|
|
+ * reservations and AGFL rules in place, we can return this extent.
|
|
|
+ */
|
|
|
if (pag->pagf_longest > delta)
|
|
|
return pag->pagf_longest - delta;
|
|
|
+
|
|
|
+ /* Otherwise, let the caller try for 1 block if there's space. */
|
|
|
return pag->pagf_flcount > 0 || pag->pagf_longest > 0;
|
|
|
}
|
|
|
|
|
@@ -2004,20 +2041,24 @@ xfs_alloc_space_available(
|
|
|
{
|
|
|
struct xfs_perag *pag = args->pag;
|
|
|
xfs_extlen_t longest;
|
|
|
+ xfs_extlen_t reservation; /* blocks that are still reserved */
|
|
|
int available;
|
|
|
|
|
|
if (flags & XFS_ALLOC_FLAG_FREEING)
|
|
|
return true;
|
|
|
|
|
|
+ reservation = xfs_ag_resv_needed(pag, args->resv);
|
|
|
+
|
|
|
/* do we have enough contiguous free space for the allocation? */
|
|
|
- longest = xfs_alloc_longest_free_extent(args->mp, pag, min_free);
|
|
|
+ longest = xfs_alloc_longest_free_extent(args->mp, pag, min_free,
|
|
|
+ reservation);
|
|
|
if ((args->minlen + args->alignment + args->minalignslop - 1) > longest)
|
|
|
return false;
|
|
|
|
|
|
- /* do have enough free space remaining for the allocation? */
|
|
|
+ /* do we 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)
|
|
|
+ reservation - min_free - args->total);
|
|
|
+ if (available < (int)args->minleft || available <= 0)
|
|
|
return false;
|
|
|
|
|
|
return true;
|
|
@@ -2124,7 +2165,7 @@ xfs_alloc_fix_freelist(
|
|
|
if (error)
|
|
|
goto out_agbp_relse;
|
|
|
error = xfs_free_ag_extent(tp, agbp, args->agno, bno, 1,
|
|
|
- &targs.oinfo, 1);
|
|
|
+ &targs.oinfo, XFS_AG_RESV_AGFL);
|
|
|
if (error)
|
|
|
goto out_agbp_relse;
|
|
|
bp = xfs_btree_get_bufs(mp, tp, args->agno, bno, 0);
|
|
@@ -2135,7 +2176,7 @@ xfs_alloc_fix_freelist(
|
|
|
targs.mp = mp;
|
|
|
targs.agbp = agbp;
|
|
|
targs.agno = args->agno;
|
|
|
- targs.alignment = targs.minlen = targs.prod = targs.isfl = 1;
|
|
|
+ targs.alignment = targs.minlen = targs.prod = 1;
|
|
|
targs.type = XFS_ALLOCTYPE_THIS_AG;
|
|
|
targs.pag = pag;
|
|
|
error = xfs_alloc_read_agfl(mp, tp, targs.agno, &agflbp);
|
|
@@ -2146,6 +2187,7 @@ xfs_alloc_fix_freelist(
|
|
|
while (pag->pagf_flcount < need) {
|
|
|
targs.agbno = 0;
|
|
|
targs.maxlen = need - pag->pagf_flcount;
|
|
|
+ targs.resv = XFS_AG_RESV_AGFL;
|
|
|
|
|
|
/* Allocate as many blocks as possible at once. */
|
|
|
error = xfs_alloc_ag_vextent(&targs);
|
|
@@ -2825,7 +2867,8 @@ xfs_free_extent(
|
|
|
struct xfs_trans *tp, /* transaction pointer */
|
|
|
xfs_fsblock_t bno, /* starting block number of extent */
|
|
|
xfs_extlen_t len, /* length of extent */
|
|
|
- struct xfs_owner_info *oinfo) /* extent owner */
|
|
|
+ struct xfs_owner_info *oinfo, /* extent owner */
|
|
|
+ enum xfs_ag_resv_type type) /* block reservation type */
|
|
|
{
|
|
|
struct xfs_mount *mp = tp->t_mountp;
|
|
|
struct xfs_buf *agbp;
|
|
@@ -2834,6 +2877,7 @@ xfs_free_extent(
|
|
|
int error;
|
|
|
|
|
|
ASSERT(len != 0);
|
|
|
+ ASSERT(type != XFS_AG_RESV_AGFL);
|
|
|
|
|
|
if (XFS_TEST_ERROR(false, mp,
|
|
|
XFS_ERRTAG_FREE_EXTENT,
|
|
@@ -2851,7 +2895,7 @@ xfs_free_extent(
|
|
|
agbno + len <= be32_to_cpu(XFS_BUF_TO_AGF(agbp)->agf_length),
|
|
|
err);
|
|
|
|
|
|
- error = xfs_free_ag_extent(tp, agbp, agno, agbno, len, oinfo, 0);
|
|
|
+ error = xfs_free_ag_extent(tp, agbp, agno, agbno, len, oinfo, type);
|
|
|
if (error)
|
|
|
goto err;
|
|
|
|