162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright (C) 2016 Oracle.  All Rights Reserved.
462306a36Sopenharmony_ci * Author: Darrick J. Wong <darrick.wong@oracle.com>
562306a36Sopenharmony_ci */
662306a36Sopenharmony_ci#include "xfs.h"
762306a36Sopenharmony_ci#include "xfs_fs.h"
862306a36Sopenharmony_ci#include "xfs_shared.h"
962306a36Sopenharmony_ci#include "xfs_format.h"
1062306a36Sopenharmony_ci#include "xfs_log_format.h"
1162306a36Sopenharmony_ci#include "xfs_trans_resv.h"
1262306a36Sopenharmony_ci#include "xfs_mount.h"
1362306a36Sopenharmony_ci#include "xfs_alloc.h"
1462306a36Sopenharmony_ci#include "xfs_errortag.h"
1562306a36Sopenharmony_ci#include "xfs_error.h"
1662306a36Sopenharmony_ci#include "xfs_trace.h"
1762306a36Sopenharmony_ci#include "xfs_trans.h"
1862306a36Sopenharmony_ci#include "xfs_rmap_btree.h"
1962306a36Sopenharmony_ci#include "xfs_btree.h"
2062306a36Sopenharmony_ci#include "xfs_refcount_btree.h"
2162306a36Sopenharmony_ci#include "xfs_ialloc_btree.h"
2262306a36Sopenharmony_ci#include "xfs_ag.h"
2362306a36Sopenharmony_ci#include "xfs_ag_resv.h"
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_ci/*
2662306a36Sopenharmony_ci * Per-AG Block Reservations
2762306a36Sopenharmony_ci *
2862306a36Sopenharmony_ci * For some kinds of allocation group metadata structures, it is advantageous
2962306a36Sopenharmony_ci * to reserve a small number of blocks in each AG so that future expansions of
3062306a36Sopenharmony_ci * that data structure do not encounter ENOSPC because errors during a btree
3162306a36Sopenharmony_ci * split cause the filesystem to go offline.
3262306a36Sopenharmony_ci *
3362306a36Sopenharmony_ci * Prior to the introduction of reflink, this wasn't an issue because the free
3462306a36Sopenharmony_ci * space btrees maintain a reserve of space (the AGFL) to handle any expansion
3562306a36Sopenharmony_ci * that may be necessary; and allocations of other metadata (inodes, BMBT,
3662306a36Sopenharmony_ci * dir/attr) aren't restricted to a single AG.  However, with reflink it is
3762306a36Sopenharmony_ci * possible to allocate all the space in an AG, have subsequent reflink/CoW
3862306a36Sopenharmony_ci * activity expand the refcount btree, and discover that there's no space left
3962306a36Sopenharmony_ci * to handle that expansion.  Since we can calculate the maximum size of the
4062306a36Sopenharmony_ci * refcount btree, we can reserve space for it and avoid ENOSPC.
4162306a36Sopenharmony_ci *
4262306a36Sopenharmony_ci * Handling per-AG reservations consists of three changes to the allocator's
4362306a36Sopenharmony_ci * behavior:  First, because these reservations are always needed, we decrease
4462306a36Sopenharmony_ci * the ag_max_usable counter to reflect the size of the AG after the reserved
4562306a36Sopenharmony_ci * blocks are taken.  Second, the reservations must be reflected in the
4662306a36Sopenharmony_ci * fdblocks count to maintain proper accounting.  Third, each AG must maintain
4762306a36Sopenharmony_ci * its own reserved block counter so that we can calculate the amount of space
4862306a36Sopenharmony_ci * that must remain free to maintain the reservations.  Fourth, the "remaining
4962306a36Sopenharmony_ci * reserved blocks" count must be used when calculating the length of the
5062306a36Sopenharmony_ci * longest free extent in an AG and to clamp maxlen in the per-AG allocation
5162306a36Sopenharmony_ci * functions.  In other words, we maintain a virtual allocation via in-core
5262306a36Sopenharmony_ci * accounting tricks so that we don't have to clean up after a crash. :)
5362306a36Sopenharmony_ci *
5462306a36Sopenharmony_ci * Reserved blocks can be managed by passing one of the enum xfs_ag_resv_type
5562306a36Sopenharmony_ci * values via struct xfs_alloc_arg or directly to the xfs_free_extent
5662306a36Sopenharmony_ci * function.  It might seem a little funny to maintain a reservoir of blocks
5762306a36Sopenharmony_ci * to feed another reservoir, but the AGFL only holds enough blocks to get
5862306a36Sopenharmony_ci * through the next transaction.  The per-AG reservation is to ensure (we
5962306a36Sopenharmony_ci * hope) that each AG never runs out of blocks.  Each data structure wanting
6062306a36Sopenharmony_ci * to use the reservation system should update ask/used in xfs_ag_resv_init.
6162306a36Sopenharmony_ci */
6262306a36Sopenharmony_ci
6362306a36Sopenharmony_ci/*
6462306a36Sopenharmony_ci * Are we critically low on blocks?  For now we'll define that as the number
6562306a36Sopenharmony_ci * of blocks we can get our hands on being less than 10% of what we reserved
6662306a36Sopenharmony_ci * or less than some arbitrary number (maximum btree height).
6762306a36Sopenharmony_ci */
6862306a36Sopenharmony_cibool
6962306a36Sopenharmony_cixfs_ag_resv_critical(
7062306a36Sopenharmony_ci	struct xfs_perag		*pag,
7162306a36Sopenharmony_ci	enum xfs_ag_resv_type		type)
7262306a36Sopenharmony_ci{
7362306a36Sopenharmony_ci	xfs_extlen_t			avail;
7462306a36Sopenharmony_ci	xfs_extlen_t			orig;
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_ci	switch (type) {
7762306a36Sopenharmony_ci	case XFS_AG_RESV_METADATA:
7862306a36Sopenharmony_ci		avail = pag->pagf_freeblks - pag->pag_rmapbt_resv.ar_reserved;
7962306a36Sopenharmony_ci		orig = pag->pag_meta_resv.ar_asked;
8062306a36Sopenharmony_ci		break;
8162306a36Sopenharmony_ci	case XFS_AG_RESV_RMAPBT:
8262306a36Sopenharmony_ci		avail = pag->pagf_freeblks + pag->pagf_flcount -
8362306a36Sopenharmony_ci			pag->pag_meta_resv.ar_reserved;
8462306a36Sopenharmony_ci		orig = pag->pag_rmapbt_resv.ar_asked;
8562306a36Sopenharmony_ci		break;
8662306a36Sopenharmony_ci	default:
8762306a36Sopenharmony_ci		ASSERT(0);
8862306a36Sopenharmony_ci		return false;
8962306a36Sopenharmony_ci	}
9062306a36Sopenharmony_ci
9162306a36Sopenharmony_ci	trace_xfs_ag_resv_critical(pag, type, avail);
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_ci	/* Critically low if less than 10% or max btree height remains. */
9462306a36Sopenharmony_ci	return XFS_TEST_ERROR(avail < orig / 10 ||
9562306a36Sopenharmony_ci			      avail < pag->pag_mount->m_agbtree_maxlevels,
9662306a36Sopenharmony_ci			pag->pag_mount, XFS_ERRTAG_AG_RESV_CRITICAL);
9762306a36Sopenharmony_ci}
9862306a36Sopenharmony_ci
9962306a36Sopenharmony_ci/*
10062306a36Sopenharmony_ci * How many blocks are reserved but not used, and therefore must not be
10162306a36Sopenharmony_ci * allocated away?
10262306a36Sopenharmony_ci */
10362306a36Sopenharmony_cixfs_extlen_t
10462306a36Sopenharmony_cixfs_ag_resv_needed(
10562306a36Sopenharmony_ci	struct xfs_perag		*pag,
10662306a36Sopenharmony_ci	enum xfs_ag_resv_type		type)
10762306a36Sopenharmony_ci{
10862306a36Sopenharmony_ci	xfs_extlen_t			len;
10962306a36Sopenharmony_ci
11062306a36Sopenharmony_ci	len = pag->pag_meta_resv.ar_reserved + pag->pag_rmapbt_resv.ar_reserved;
11162306a36Sopenharmony_ci	switch (type) {
11262306a36Sopenharmony_ci	case XFS_AG_RESV_METADATA:
11362306a36Sopenharmony_ci	case XFS_AG_RESV_RMAPBT:
11462306a36Sopenharmony_ci		len -= xfs_perag_resv(pag, type)->ar_reserved;
11562306a36Sopenharmony_ci		break;
11662306a36Sopenharmony_ci	case XFS_AG_RESV_NONE:
11762306a36Sopenharmony_ci		/* empty */
11862306a36Sopenharmony_ci		break;
11962306a36Sopenharmony_ci	default:
12062306a36Sopenharmony_ci		ASSERT(0);
12162306a36Sopenharmony_ci	}
12262306a36Sopenharmony_ci
12362306a36Sopenharmony_ci	trace_xfs_ag_resv_needed(pag, type, len);
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_ci	return len;
12662306a36Sopenharmony_ci}
12762306a36Sopenharmony_ci
12862306a36Sopenharmony_ci/* Clean out a reservation */
12962306a36Sopenharmony_cistatic int
13062306a36Sopenharmony_ci__xfs_ag_resv_free(
13162306a36Sopenharmony_ci	struct xfs_perag		*pag,
13262306a36Sopenharmony_ci	enum xfs_ag_resv_type		type)
13362306a36Sopenharmony_ci{
13462306a36Sopenharmony_ci	struct xfs_ag_resv		*resv;
13562306a36Sopenharmony_ci	xfs_extlen_t			oldresv;
13662306a36Sopenharmony_ci	int				error;
13762306a36Sopenharmony_ci
13862306a36Sopenharmony_ci	trace_xfs_ag_resv_free(pag, type, 0);
13962306a36Sopenharmony_ci
14062306a36Sopenharmony_ci	resv = xfs_perag_resv(pag, type);
14162306a36Sopenharmony_ci	if (pag->pag_agno == 0)
14262306a36Sopenharmony_ci		pag->pag_mount->m_ag_max_usable += resv->ar_asked;
14362306a36Sopenharmony_ci	/*
14462306a36Sopenharmony_ci	 * RMAPBT blocks come from the AGFL and AGFL blocks are always
14562306a36Sopenharmony_ci	 * considered "free", so whatever was reserved at mount time must be
14662306a36Sopenharmony_ci	 * given back at umount.
14762306a36Sopenharmony_ci	 */
14862306a36Sopenharmony_ci	if (type == XFS_AG_RESV_RMAPBT)
14962306a36Sopenharmony_ci		oldresv = resv->ar_orig_reserved;
15062306a36Sopenharmony_ci	else
15162306a36Sopenharmony_ci		oldresv = resv->ar_reserved;
15262306a36Sopenharmony_ci	error = xfs_mod_fdblocks(pag->pag_mount, oldresv, true);
15362306a36Sopenharmony_ci	resv->ar_reserved = 0;
15462306a36Sopenharmony_ci	resv->ar_asked = 0;
15562306a36Sopenharmony_ci	resv->ar_orig_reserved = 0;
15662306a36Sopenharmony_ci
15762306a36Sopenharmony_ci	if (error)
15862306a36Sopenharmony_ci		trace_xfs_ag_resv_free_error(pag->pag_mount, pag->pag_agno,
15962306a36Sopenharmony_ci				error, _RET_IP_);
16062306a36Sopenharmony_ci	return error;
16162306a36Sopenharmony_ci}
16262306a36Sopenharmony_ci
16362306a36Sopenharmony_ci/* Free a per-AG reservation. */
16462306a36Sopenharmony_ciint
16562306a36Sopenharmony_cixfs_ag_resv_free(
16662306a36Sopenharmony_ci	struct xfs_perag		*pag)
16762306a36Sopenharmony_ci{
16862306a36Sopenharmony_ci	int				error;
16962306a36Sopenharmony_ci	int				err2;
17062306a36Sopenharmony_ci
17162306a36Sopenharmony_ci	error = __xfs_ag_resv_free(pag, XFS_AG_RESV_RMAPBT);
17262306a36Sopenharmony_ci	err2 = __xfs_ag_resv_free(pag, XFS_AG_RESV_METADATA);
17362306a36Sopenharmony_ci	if (err2 && !error)
17462306a36Sopenharmony_ci		error = err2;
17562306a36Sopenharmony_ci	return error;
17662306a36Sopenharmony_ci}
17762306a36Sopenharmony_ci
17862306a36Sopenharmony_cistatic int
17962306a36Sopenharmony_ci__xfs_ag_resv_init(
18062306a36Sopenharmony_ci	struct xfs_perag		*pag,
18162306a36Sopenharmony_ci	enum xfs_ag_resv_type		type,
18262306a36Sopenharmony_ci	xfs_extlen_t			ask,
18362306a36Sopenharmony_ci	xfs_extlen_t			used)
18462306a36Sopenharmony_ci{
18562306a36Sopenharmony_ci	struct xfs_mount		*mp = pag->pag_mount;
18662306a36Sopenharmony_ci	struct xfs_ag_resv		*resv;
18762306a36Sopenharmony_ci	int				error;
18862306a36Sopenharmony_ci	xfs_extlen_t			hidden_space;
18962306a36Sopenharmony_ci
19062306a36Sopenharmony_ci	if (used > ask)
19162306a36Sopenharmony_ci		ask = used;
19262306a36Sopenharmony_ci
19362306a36Sopenharmony_ci	switch (type) {
19462306a36Sopenharmony_ci	case XFS_AG_RESV_RMAPBT:
19562306a36Sopenharmony_ci		/*
19662306a36Sopenharmony_ci		 * Space taken by the rmapbt is not subtracted from fdblocks
19762306a36Sopenharmony_ci		 * because the rmapbt lives in the free space.  Here we must
19862306a36Sopenharmony_ci		 * subtract the entire reservation from fdblocks so that we
19962306a36Sopenharmony_ci		 * always have blocks available for rmapbt expansion.
20062306a36Sopenharmony_ci		 */
20162306a36Sopenharmony_ci		hidden_space = ask;
20262306a36Sopenharmony_ci		break;
20362306a36Sopenharmony_ci	case XFS_AG_RESV_METADATA:
20462306a36Sopenharmony_ci		/*
20562306a36Sopenharmony_ci		 * Space taken by all other metadata btrees are accounted
20662306a36Sopenharmony_ci		 * on-disk as used space.  We therefore only hide the space
20762306a36Sopenharmony_ci		 * that is reserved but not used by the trees.
20862306a36Sopenharmony_ci		 */
20962306a36Sopenharmony_ci		hidden_space = ask - used;
21062306a36Sopenharmony_ci		break;
21162306a36Sopenharmony_ci	default:
21262306a36Sopenharmony_ci		ASSERT(0);
21362306a36Sopenharmony_ci		return -EINVAL;
21462306a36Sopenharmony_ci	}
21562306a36Sopenharmony_ci
21662306a36Sopenharmony_ci	if (XFS_TEST_ERROR(false, mp, XFS_ERRTAG_AG_RESV_FAIL))
21762306a36Sopenharmony_ci		error = -ENOSPC;
21862306a36Sopenharmony_ci	else
21962306a36Sopenharmony_ci		error = xfs_mod_fdblocks(mp, -(int64_t)hidden_space, true);
22062306a36Sopenharmony_ci	if (error) {
22162306a36Sopenharmony_ci		trace_xfs_ag_resv_init_error(pag->pag_mount, pag->pag_agno,
22262306a36Sopenharmony_ci				error, _RET_IP_);
22362306a36Sopenharmony_ci		xfs_warn(mp,
22462306a36Sopenharmony_ci"Per-AG reservation for AG %u failed.  Filesystem may run out of space.",
22562306a36Sopenharmony_ci				pag->pag_agno);
22662306a36Sopenharmony_ci		return error;
22762306a36Sopenharmony_ci	}
22862306a36Sopenharmony_ci
22962306a36Sopenharmony_ci	/*
23062306a36Sopenharmony_ci	 * Reduce the maximum per-AG allocation length by however much we're
23162306a36Sopenharmony_ci	 * trying to reserve for an AG.  Since this is a filesystem-wide
23262306a36Sopenharmony_ci	 * counter, we only make the adjustment for AG 0.  This assumes that
23362306a36Sopenharmony_ci	 * there aren't any AGs hungrier for per-AG reservation than AG 0.
23462306a36Sopenharmony_ci	 */
23562306a36Sopenharmony_ci	if (pag->pag_agno == 0)
23662306a36Sopenharmony_ci		mp->m_ag_max_usable -= ask;
23762306a36Sopenharmony_ci
23862306a36Sopenharmony_ci	resv = xfs_perag_resv(pag, type);
23962306a36Sopenharmony_ci	resv->ar_asked = ask;
24062306a36Sopenharmony_ci	resv->ar_orig_reserved = hidden_space;
24162306a36Sopenharmony_ci	resv->ar_reserved = ask - used;
24262306a36Sopenharmony_ci
24362306a36Sopenharmony_ci	trace_xfs_ag_resv_init(pag, type, ask);
24462306a36Sopenharmony_ci	return 0;
24562306a36Sopenharmony_ci}
24662306a36Sopenharmony_ci
24762306a36Sopenharmony_ci/* Create a per-AG block reservation. */
24862306a36Sopenharmony_ciint
24962306a36Sopenharmony_cixfs_ag_resv_init(
25062306a36Sopenharmony_ci	struct xfs_perag		*pag,
25162306a36Sopenharmony_ci	struct xfs_trans		*tp)
25262306a36Sopenharmony_ci{
25362306a36Sopenharmony_ci	struct xfs_mount		*mp = pag->pag_mount;
25462306a36Sopenharmony_ci	xfs_extlen_t			ask;
25562306a36Sopenharmony_ci	xfs_extlen_t			used;
25662306a36Sopenharmony_ci	int				error = 0, error2;
25762306a36Sopenharmony_ci	bool				has_resv = false;
25862306a36Sopenharmony_ci
25962306a36Sopenharmony_ci	/* Create the metadata reservation. */
26062306a36Sopenharmony_ci	if (pag->pag_meta_resv.ar_asked == 0) {
26162306a36Sopenharmony_ci		ask = used = 0;
26262306a36Sopenharmony_ci
26362306a36Sopenharmony_ci		error = xfs_refcountbt_calc_reserves(mp, tp, pag, &ask, &used);
26462306a36Sopenharmony_ci		if (error)
26562306a36Sopenharmony_ci			goto out;
26662306a36Sopenharmony_ci
26762306a36Sopenharmony_ci		error = xfs_finobt_calc_reserves(pag, tp, &ask, &used);
26862306a36Sopenharmony_ci		if (error)
26962306a36Sopenharmony_ci			goto out;
27062306a36Sopenharmony_ci
27162306a36Sopenharmony_ci		error = __xfs_ag_resv_init(pag, XFS_AG_RESV_METADATA,
27262306a36Sopenharmony_ci				ask, used);
27362306a36Sopenharmony_ci		if (error) {
27462306a36Sopenharmony_ci			/*
27562306a36Sopenharmony_ci			 * Because we didn't have per-AG reservations when the
27662306a36Sopenharmony_ci			 * finobt feature was added we might not be able to
27762306a36Sopenharmony_ci			 * reserve all needed blocks.  Warn and fall back to the
27862306a36Sopenharmony_ci			 * old and potentially buggy code in that case, but
27962306a36Sopenharmony_ci			 * ensure we do have the reservation for the refcountbt.
28062306a36Sopenharmony_ci			 */
28162306a36Sopenharmony_ci			ask = used = 0;
28262306a36Sopenharmony_ci
28362306a36Sopenharmony_ci			mp->m_finobt_nores = true;
28462306a36Sopenharmony_ci
28562306a36Sopenharmony_ci			error = xfs_refcountbt_calc_reserves(mp, tp, pag, &ask,
28662306a36Sopenharmony_ci					&used);
28762306a36Sopenharmony_ci			if (error)
28862306a36Sopenharmony_ci				goto out;
28962306a36Sopenharmony_ci
29062306a36Sopenharmony_ci			error = __xfs_ag_resv_init(pag, XFS_AG_RESV_METADATA,
29162306a36Sopenharmony_ci					ask, used);
29262306a36Sopenharmony_ci			if (error)
29362306a36Sopenharmony_ci				goto out;
29462306a36Sopenharmony_ci		}
29562306a36Sopenharmony_ci		if (ask)
29662306a36Sopenharmony_ci			has_resv = true;
29762306a36Sopenharmony_ci	}
29862306a36Sopenharmony_ci
29962306a36Sopenharmony_ci	/* Create the RMAPBT metadata reservation */
30062306a36Sopenharmony_ci	if (pag->pag_rmapbt_resv.ar_asked == 0) {
30162306a36Sopenharmony_ci		ask = used = 0;
30262306a36Sopenharmony_ci
30362306a36Sopenharmony_ci		error = xfs_rmapbt_calc_reserves(mp, tp, pag, &ask, &used);
30462306a36Sopenharmony_ci		if (error)
30562306a36Sopenharmony_ci			goto out;
30662306a36Sopenharmony_ci
30762306a36Sopenharmony_ci		error = __xfs_ag_resv_init(pag, XFS_AG_RESV_RMAPBT, ask, used);
30862306a36Sopenharmony_ci		if (error)
30962306a36Sopenharmony_ci			goto out;
31062306a36Sopenharmony_ci		if (ask)
31162306a36Sopenharmony_ci			has_resv = true;
31262306a36Sopenharmony_ci	}
31362306a36Sopenharmony_ci
31462306a36Sopenharmony_ciout:
31562306a36Sopenharmony_ci	/*
31662306a36Sopenharmony_ci	 * Initialize the pagf if we have at least one active reservation on the
31762306a36Sopenharmony_ci	 * AG. This may have occurred already via reservation calculation, but
31862306a36Sopenharmony_ci	 * fall back to an explicit init to ensure the in-core allocbt usage
31962306a36Sopenharmony_ci	 * counters are initialized as soon as possible. This is important
32062306a36Sopenharmony_ci	 * because filesystems with large perag reservations are susceptible to
32162306a36Sopenharmony_ci	 * free space reservation problems that the allocbt counter is used to
32262306a36Sopenharmony_ci	 * address.
32362306a36Sopenharmony_ci	 */
32462306a36Sopenharmony_ci	if (has_resv) {
32562306a36Sopenharmony_ci		error2 = xfs_alloc_read_agf(pag, tp, 0, NULL);
32662306a36Sopenharmony_ci		if (error2)
32762306a36Sopenharmony_ci			return error2;
32862306a36Sopenharmony_ci
32962306a36Sopenharmony_ci		/*
33062306a36Sopenharmony_ci		 * If there isn't enough space in the AG to satisfy the
33162306a36Sopenharmony_ci		 * reservation, let the caller know that there wasn't enough
33262306a36Sopenharmony_ci		 * space.  Callers are responsible for deciding what to do
33362306a36Sopenharmony_ci		 * next, since (in theory) we can stumble along with
33462306a36Sopenharmony_ci		 * insufficient reservation if data blocks are being freed to
33562306a36Sopenharmony_ci		 * replenish the AG's free space.
33662306a36Sopenharmony_ci		 */
33762306a36Sopenharmony_ci		if (!error &&
33862306a36Sopenharmony_ci		    xfs_perag_resv(pag, XFS_AG_RESV_METADATA)->ar_reserved +
33962306a36Sopenharmony_ci		    xfs_perag_resv(pag, XFS_AG_RESV_RMAPBT)->ar_reserved >
34062306a36Sopenharmony_ci		    pag->pagf_freeblks + pag->pagf_flcount)
34162306a36Sopenharmony_ci			error = -ENOSPC;
34262306a36Sopenharmony_ci	}
34362306a36Sopenharmony_ci
34462306a36Sopenharmony_ci	return error;
34562306a36Sopenharmony_ci}
34662306a36Sopenharmony_ci
34762306a36Sopenharmony_ci/* Allocate a block from the reservation. */
34862306a36Sopenharmony_civoid
34962306a36Sopenharmony_cixfs_ag_resv_alloc_extent(
35062306a36Sopenharmony_ci	struct xfs_perag		*pag,
35162306a36Sopenharmony_ci	enum xfs_ag_resv_type		type,
35262306a36Sopenharmony_ci	struct xfs_alloc_arg		*args)
35362306a36Sopenharmony_ci{
35462306a36Sopenharmony_ci	struct xfs_ag_resv		*resv;
35562306a36Sopenharmony_ci	xfs_extlen_t			len;
35662306a36Sopenharmony_ci	uint				field;
35762306a36Sopenharmony_ci
35862306a36Sopenharmony_ci	trace_xfs_ag_resv_alloc_extent(pag, type, args->len);
35962306a36Sopenharmony_ci
36062306a36Sopenharmony_ci	switch (type) {
36162306a36Sopenharmony_ci	case XFS_AG_RESV_AGFL:
36262306a36Sopenharmony_ci		return;
36362306a36Sopenharmony_ci	case XFS_AG_RESV_METADATA:
36462306a36Sopenharmony_ci	case XFS_AG_RESV_RMAPBT:
36562306a36Sopenharmony_ci		resv = xfs_perag_resv(pag, type);
36662306a36Sopenharmony_ci		break;
36762306a36Sopenharmony_ci	default:
36862306a36Sopenharmony_ci		ASSERT(0);
36962306a36Sopenharmony_ci		fallthrough;
37062306a36Sopenharmony_ci	case XFS_AG_RESV_NONE:
37162306a36Sopenharmony_ci		field = args->wasdel ? XFS_TRANS_SB_RES_FDBLOCKS :
37262306a36Sopenharmony_ci				       XFS_TRANS_SB_FDBLOCKS;
37362306a36Sopenharmony_ci		xfs_trans_mod_sb(args->tp, field, -(int64_t)args->len);
37462306a36Sopenharmony_ci		return;
37562306a36Sopenharmony_ci	}
37662306a36Sopenharmony_ci
37762306a36Sopenharmony_ci	len = min_t(xfs_extlen_t, args->len, resv->ar_reserved);
37862306a36Sopenharmony_ci	resv->ar_reserved -= len;
37962306a36Sopenharmony_ci	if (type == XFS_AG_RESV_RMAPBT)
38062306a36Sopenharmony_ci		return;
38162306a36Sopenharmony_ci	/* Allocations of reserved blocks only need on-disk sb updates... */
38262306a36Sopenharmony_ci	xfs_trans_mod_sb(args->tp, XFS_TRANS_SB_RES_FDBLOCKS, -(int64_t)len);
38362306a36Sopenharmony_ci	/* ...but non-reserved blocks need in-core and on-disk updates. */
38462306a36Sopenharmony_ci	if (args->len > len)
38562306a36Sopenharmony_ci		xfs_trans_mod_sb(args->tp, XFS_TRANS_SB_FDBLOCKS,
38662306a36Sopenharmony_ci				-((int64_t)args->len - len));
38762306a36Sopenharmony_ci}
38862306a36Sopenharmony_ci
38962306a36Sopenharmony_ci/* Free a block to the reservation. */
39062306a36Sopenharmony_civoid
39162306a36Sopenharmony_cixfs_ag_resv_free_extent(
39262306a36Sopenharmony_ci	struct xfs_perag		*pag,
39362306a36Sopenharmony_ci	enum xfs_ag_resv_type		type,
39462306a36Sopenharmony_ci	struct xfs_trans		*tp,
39562306a36Sopenharmony_ci	xfs_extlen_t			len)
39662306a36Sopenharmony_ci{
39762306a36Sopenharmony_ci	xfs_extlen_t			leftover;
39862306a36Sopenharmony_ci	struct xfs_ag_resv		*resv;
39962306a36Sopenharmony_ci
40062306a36Sopenharmony_ci	trace_xfs_ag_resv_free_extent(pag, type, len);
40162306a36Sopenharmony_ci
40262306a36Sopenharmony_ci	switch (type) {
40362306a36Sopenharmony_ci	case XFS_AG_RESV_AGFL:
40462306a36Sopenharmony_ci		return;
40562306a36Sopenharmony_ci	case XFS_AG_RESV_METADATA:
40662306a36Sopenharmony_ci	case XFS_AG_RESV_RMAPBT:
40762306a36Sopenharmony_ci		resv = xfs_perag_resv(pag, type);
40862306a36Sopenharmony_ci		break;
40962306a36Sopenharmony_ci	default:
41062306a36Sopenharmony_ci		ASSERT(0);
41162306a36Sopenharmony_ci		fallthrough;
41262306a36Sopenharmony_ci	case XFS_AG_RESV_NONE:
41362306a36Sopenharmony_ci		xfs_trans_mod_sb(tp, XFS_TRANS_SB_FDBLOCKS, (int64_t)len);
41462306a36Sopenharmony_ci		return;
41562306a36Sopenharmony_ci	}
41662306a36Sopenharmony_ci
41762306a36Sopenharmony_ci	leftover = min_t(xfs_extlen_t, len, resv->ar_asked - resv->ar_reserved);
41862306a36Sopenharmony_ci	resv->ar_reserved += leftover;
41962306a36Sopenharmony_ci	if (type == XFS_AG_RESV_RMAPBT)
42062306a36Sopenharmony_ci		return;
42162306a36Sopenharmony_ci	/* Freeing into the reserved pool only requires on-disk update... */
42262306a36Sopenharmony_ci	xfs_trans_mod_sb(tp, XFS_TRANS_SB_RES_FDBLOCKS, len);
42362306a36Sopenharmony_ci	/* ...but freeing beyond that requires in-core and on-disk update. */
42462306a36Sopenharmony_ci	if (len > leftover)
42562306a36Sopenharmony_ci		xfs_trans_mod_sb(tp, XFS_TRANS_SB_FDBLOCKS, len - leftover);
42662306a36Sopenharmony_ci}
427