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