@@ -13,6 +13,8 @@
#define trace_xfbtree_trans_cancel_buf(...) ((void) 0)
#define trace_xfbtree_trans_commit_buf(...) ((void) 0)
+#define trace_xfs_ag_clear_noalloc(a) ((void) 0)
+#define trace_xfs_ag_set_noalloc(a) ((void) 0)
#define trace_xfs_agfl_free_defer(...) ((void) 0)
#define trace_xfs_agfl_reset(a,b,c,d) ((void) 0)
#define trace_xfs_alloc_cur_check(a,b,c,d,e,f) ((void) 0)
@@ -1117,3 +1117,63 @@ xfs_ag_get_geometry(
xfs_buf_relse(agi_bp);
return error;
}
+
+/* How many blocks does this AG contribute to fdblocks? */
+xfs_extlen_t
+xfs_ag_fdblocks(
+ struct xfs_perag *pag)
+{
+ xfs_extlen_t ret;
+
+ ASSERT(xfs_perag_initialised_agf(pag));
+
+ ret = pag->pagf_freeblks + pag->pagf_flcount + pag->pagf_btreeblks;
+ ret -= pag->pag_meta_resv.ar_reserved;
+ ret -= pag->pag_rmapbt_resv.ar_orig_reserved;
+ return ret;
+}
+
+/*
+ * Hide all the free space in this AG. Caller must hold both the AGI and the
+ * AGF buffers or have otherwise prevented concurrent access.
+ */
+int
+xfs_ag_set_noalloc(
+ struct xfs_perag *pag)
+{
+ struct xfs_mount *mp = pag->pag_mount;
+ int error;
+
+ ASSERT(xfs_perag_initialised_agf(pag));
+ ASSERT(xfs_perag_initialised_agi(pag));
+
+ if (xfs_perag_prohibits_alloc(pag))
+ return 0;
+
+ error = xfs_mod_fdblocks(mp, -(int64_t)xfs_ag_fdblocks(pag), false);
+ if (error)
+ return error;
+
+ trace_xfs_ag_set_noalloc(pag);
+ set_bit(XFS_AGSTATE_NOALLOC, &pag->pag_opstate);
+ return 0;
+}
+
+/*
+ * Unhide all the free space in this AG. Caller must hold both the AGI and
+ * the AGF buffers or have otherwise prevented concurrent access.
+ */
+void
+xfs_ag_clear_noalloc(
+ struct xfs_perag *pag)
+{
+ struct xfs_mount *mp = pag->pag_mount;
+
+ if (!xfs_perag_prohibits_alloc(pag))
+ return;
+
+ xfs_mod_fdblocks(mp, xfs_ag_fdblocks(pag), false);
+
+ trace_xfs_ag_clear_noalloc(pag);
+ clear_bit(XFS_AGSTATE_NOALLOC, &pag->pag_opstate);
+}
@@ -131,6 +131,7 @@ struct xfs_perag {
#define XFS_AGSTATE_PREFERS_METADATA 2
#define XFS_AGSTATE_ALLOWS_INODES 3
#define XFS_AGSTATE_AGFL_NEEDS_RESET 4
+#define XFS_AGSTATE_NOALLOC 5
#define __XFS_AG_OPSTATE(name, NAME) \
static inline bool xfs_perag_ ## name (struct xfs_perag *pag) \
@@ -143,6 +144,7 @@ __XFS_AG_OPSTATE(initialised_agi, AGI_INIT)
__XFS_AG_OPSTATE(prefers_metadata, PREFERS_METADATA)
__XFS_AG_OPSTATE(allows_inodes, ALLOWS_INODES)
__XFS_AG_OPSTATE(agfl_needs_reset, AGFL_NEEDS_RESET)
+__XFS_AG_OPSTATE(prohibits_alloc, NOALLOC)
int xfs_initialize_perag(struct xfs_mount *mp, xfs_agnumber_t agcount,
xfs_rfsblock_t dcount, xfs_agnumber_t *maxagi);
@@ -156,12 +158,18 @@ struct xfs_perag *xfs_perag_get_tag(struct xfs_mount *mp, xfs_agnumber_t agno,
struct xfs_perag *xfs_perag_hold(struct xfs_perag *pag);
void xfs_perag_put(struct xfs_perag *pag);
+
/* Active AG references */
struct xfs_perag *xfs_perag_grab(struct xfs_mount *, xfs_agnumber_t);
struct xfs_perag *xfs_perag_grab_tag(struct xfs_mount *, xfs_agnumber_t,
int tag);
void xfs_perag_rele(struct xfs_perag *pag);
+/* Enable or disable allocation from an AG */
+xfs_extlen_t xfs_ag_fdblocks(struct xfs_perag *pag);
+int xfs_ag_set_noalloc(struct xfs_perag *pag);
+void xfs_ag_clear_noalloc(struct xfs_perag *pag);
+
/*
* Per-ag geometry infomation and validation
*/
@@ -20,6 +20,7 @@
#include "xfs_ialloc_btree.h"
#include "xfs_ag.h"
#include "xfs_ag_resv.h"
+#include "xfs_ag.h"
/*
* Per-AG Block Reservations
@@ -72,6 +73,13 @@ xfs_ag_resv_critical(
xfs_extlen_t avail;
xfs_extlen_t orig;
+ /*
+ * Pretend we're critically low on reservations in this AG to scare
+ * everyone else away.
+ */
+ if (xfs_perag_prohibits_alloc(pag))
+ return true;
+
switch (type) {
case XFS_AG_RESV_METADATA:
avail = pag->pagf_freeblks - pag->pag_rmapbt_resv.ar_reserved;
@@ -114,7 +122,12 @@ xfs_ag_resv_needed(
break;
case XFS_AG_RESV_IMETA:
case XFS_AG_RESV_NONE:
- /* empty */
+ /*
+ * In noalloc mode, we pretend that all the free blocks in this
+ * AG have been allocated. Make this AG look full.
+ */
+ if (xfs_perag_prohibits_alloc(pag))
+ len += xfs_ag_fdblocks(pag);
break;
default:
ASSERT(0);
@@ -343,6 +356,8 @@ xfs_ag_resv_alloc_extent(
xfs_extlen_t len;
uint field;
+ ASSERT(type != XFS_AG_RESV_NONE || !xfs_perag_prohibits_alloc(pag));
+
trace_xfs_ag_resv_alloc_extent(pag, type, args->len);
switch (type) {
@@ -400,7 +415,14 @@ xfs_ag_resv_free_extent(
ASSERT(0);
fallthrough;
case XFS_AG_RESV_NONE:
- xfs_trans_mod_sb(tp, XFS_TRANS_SB_FDBLOCKS, (int64_t)len);
+ /*
+ * Normally we put freed blocks back into fdblocks. In noalloc
+ * mode, however, we pretend that there are no fdblocks in the
+ * AG, so don't put them back.
+ */
+ if (!xfs_perag_prohibits_alloc(pag))
+ xfs_trans_mod_sb(tp, XFS_TRANS_SB_FDBLOCKS,
+ (int64_t)len);
fallthrough;
case XFS_AG_RESV_IGNORE:
return;
@@ -413,6 +435,6 @@ xfs_ag_resv_free_extent(
/* Freeing into the reserved pool only requires on-disk update... */
xfs_trans_mod_sb(tp, XFS_TRANS_SB_RES_FDBLOCKS, len);
/* ...but freeing beyond that requires in-core and on-disk update. */
- if (len > leftover)
+ if (len > leftover && !xfs_perag_prohibits_alloc(pag))
xfs_trans_mod_sb(tp, XFS_TRANS_SB_FDBLOCKS, len - leftover);
}