417 lines · c
1// SPDX-License-Identifier: GPL-2.0+2/*3 * Copyright (C) 2016 Oracle. All Rights Reserved.4 * Author: Darrick J. Wong <darrick.wong@oracle.com>5 */6#include "xfs.h"7#include "xfs_fs.h"8#include "xfs_shared.h"9#include "xfs_format.h"10#include "xfs_log_format.h"11#include "xfs_trans_resv.h"12#include "xfs_mount.h"13#include "xfs_alloc.h"14#include "xfs_errortag.h"15#include "xfs_error.h"16#include "xfs_trace.h"17#include "xfs_trans.h"18#include "xfs_rmap_btree.h"19#include "xfs_btree.h"20#include "xfs_refcount_btree.h"21#include "xfs_ialloc_btree.h"22#include "xfs_ag.h"23#include "xfs_ag_resv.h"24 25/*26 * Per-AG Block Reservations27 *28 * For some kinds of allocation group metadata structures, it is advantageous29 * to reserve a small number of blocks in each AG so that future expansions of30 * that data structure do not encounter ENOSPC because errors during a btree31 * split cause the filesystem to go offline.32 *33 * Prior to the introduction of reflink, this wasn't an issue because the free34 * space btrees maintain a reserve of space (the AGFL) to handle any expansion35 * that may be necessary; and allocations of other metadata (inodes, BMBT,36 * dir/attr) aren't restricted to a single AG. However, with reflink it is37 * possible to allocate all the space in an AG, have subsequent reflink/CoW38 * activity expand the refcount btree, and discover that there's no space left39 * to handle that expansion. Since we can calculate the maximum size of the40 * refcount btree, we can reserve space for it and avoid ENOSPC.41 *42 * Handling per-AG reservations consists of three changes to the allocator's43 * behavior: First, because these reservations are always needed, we decrease44 * the ag_max_usable counter to reflect the size of the AG after the reserved45 * blocks are taken. Second, the reservations must be reflected in the46 * fdblocks count to maintain proper accounting. Third, each AG must maintain47 * its own reserved block counter so that we can calculate the amount of space48 * that must remain free to maintain the reservations. Fourth, the "remaining49 * reserved blocks" count must be used when calculating the length of the50 * longest free extent in an AG and to clamp maxlen in the per-AG allocation51 * functions. In other words, we maintain a virtual allocation via in-core52 * accounting tricks so that we don't have to clean up after a crash. :)53 *54 * Reserved blocks can be managed by passing one of the enum xfs_ag_resv_type55 * values via struct xfs_alloc_arg or directly to the xfs_free_extent56 * function. It might seem a little funny to maintain a reservoir of blocks57 * to feed another reservoir, but the AGFL only holds enough blocks to get58 * through the next transaction. The per-AG reservation is to ensure (we59 * hope) that each AG never runs out of blocks. Each data structure wanting60 * to use the reservation system should update ask/used in xfs_ag_resv_init.61 */62 63/*64 * Are we critically low on blocks? For now we'll define that as the number65 * of blocks we can get our hands on being less than 10% of what we reserved66 * or less than some arbitrary number (maximum btree height).67 */68bool69xfs_ag_resv_critical(70 struct xfs_perag *pag,71 enum xfs_ag_resv_type type)72{73 xfs_extlen_t avail;74 xfs_extlen_t orig;75 76 switch (type) {77 case XFS_AG_RESV_METADATA:78 avail = pag->pagf_freeblks - pag->pag_rmapbt_resv.ar_reserved;79 orig = pag->pag_meta_resv.ar_asked;80 break;81 case XFS_AG_RESV_RMAPBT:82 avail = pag->pagf_freeblks + pag->pagf_flcount -83 pag->pag_meta_resv.ar_reserved;84 orig = pag->pag_rmapbt_resv.ar_asked;85 break;86 default:87 ASSERT(0);88 return false;89 }90 91 trace_xfs_ag_resv_critical(pag, type, avail);92 93 /* Critically low if less than 10% or max btree height remains. */94 return XFS_TEST_ERROR(avail < orig / 10 ||95 avail < pag->pag_mount->m_agbtree_maxlevels,96 pag->pag_mount, XFS_ERRTAG_AG_RESV_CRITICAL);97}98 99/*100 * How many blocks are reserved but not used, and therefore must not be101 * allocated away?102 */103xfs_extlen_t104xfs_ag_resv_needed(105 struct xfs_perag *pag,106 enum xfs_ag_resv_type type)107{108 xfs_extlen_t len;109 110 len = pag->pag_meta_resv.ar_reserved + pag->pag_rmapbt_resv.ar_reserved;111 switch (type) {112 case XFS_AG_RESV_METADATA:113 case XFS_AG_RESV_RMAPBT:114 len -= xfs_perag_resv(pag, type)->ar_reserved;115 break;116 case XFS_AG_RESV_NONE:117 /* empty */118 break;119 default:120 ASSERT(0);121 }122 123 trace_xfs_ag_resv_needed(pag, type, len);124 125 return len;126}127 128/* Clean out a reservation */129static void130__xfs_ag_resv_free(131 struct xfs_perag *pag,132 enum xfs_ag_resv_type type)133{134 struct xfs_ag_resv *resv;135 xfs_extlen_t oldresv;136 137 trace_xfs_ag_resv_free(pag, type, 0);138 139 resv = xfs_perag_resv(pag, type);140 if (pag->pag_agno == 0)141 pag->pag_mount->m_ag_max_usable += resv->ar_asked;142 /*143 * RMAPBT blocks come from the AGFL and AGFL blocks are always144 * considered "free", so whatever was reserved at mount time must be145 * given back at umount.146 */147 if (type == XFS_AG_RESV_RMAPBT)148 oldresv = resv->ar_orig_reserved;149 else150 oldresv = resv->ar_reserved;151 xfs_add_fdblocks(pag->pag_mount, oldresv);152 resv->ar_reserved = 0;153 resv->ar_asked = 0;154 resv->ar_orig_reserved = 0;155}156 157/* Free a per-AG reservation. */158void159xfs_ag_resv_free(160 struct xfs_perag *pag)161{162 __xfs_ag_resv_free(pag, XFS_AG_RESV_RMAPBT);163 __xfs_ag_resv_free(pag, XFS_AG_RESV_METADATA);164}165 166static int167__xfs_ag_resv_init(168 struct xfs_perag *pag,169 enum xfs_ag_resv_type type,170 xfs_extlen_t ask,171 xfs_extlen_t used)172{173 struct xfs_mount *mp = pag->pag_mount;174 struct xfs_ag_resv *resv;175 int error;176 xfs_extlen_t hidden_space;177 178 if (used > ask)179 ask = used;180 181 switch (type) {182 case XFS_AG_RESV_RMAPBT:183 /*184 * Space taken by the rmapbt is not subtracted from fdblocks185 * because the rmapbt lives in the free space. Here we must186 * subtract the entire reservation from fdblocks so that we187 * always have blocks available for rmapbt expansion.188 */189 hidden_space = ask;190 break;191 case XFS_AG_RESV_METADATA:192 /*193 * Space taken by all other metadata btrees are accounted194 * on-disk as used space. We therefore only hide the space195 * that is reserved but not used by the trees.196 */197 hidden_space = ask - used;198 break;199 default:200 ASSERT(0);201 return -EINVAL;202 }203 204 if (XFS_TEST_ERROR(false, mp, XFS_ERRTAG_AG_RESV_FAIL))205 error = -ENOSPC;206 else207 error = xfs_dec_fdblocks(mp, hidden_space, true);208 if (error) {209 trace_xfs_ag_resv_init_error(pag->pag_mount, pag->pag_agno,210 error, _RET_IP_);211 xfs_warn(mp,212"Per-AG reservation for AG %u failed. Filesystem may run out of space.",213 pag->pag_agno);214 return error;215 }216 217 /*218 * Reduce the maximum per-AG allocation length by however much we're219 * trying to reserve for an AG. Since this is a filesystem-wide220 * counter, we only make the adjustment for AG 0. This assumes that221 * there aren't any AGs hungrier for per-AG reservation than AG 0.222 */223 if (pag->pag_agno == 0)224 mp->m_ag_max_usable -= ask;225 226 resv = xfs_perag_resv(pag, type);227 resv->ar_asked = ask;228 resv->ar_orig_reserved = hidden_space;229 resv->ar_reserved = ask - used;230 231 trace_xfs_ag_resv_init(pag, type, ask);232 return 0;233}234 235/* Create a per-AG block reservation. */236int237xfs_ag_resv_init(238 struct xfs_perag *pag,239 struct xfs_trans *tp)240{241 struct xfs_mount *mp = pag->pag_mount;242 xfs_extlen_t ask;243 xfs_extlen_t used;244 int error = 0, error2;245 bool has_resv = false;246 247 /* Create the metadata reservation. */248 if (pag->pag_meta_resv.ar_asked == 0) {249 ask = used = 0;250 251 error = xfs_refcountbt_calc_reserves(mp, tp, pag, &ask, &used);252 if (error)253 goto out;254 255 error = xfs_finobt_calc_reserves(pag, tp, &ask, &used);256 if (error)257 goto out;258 259 error = __xfs_ag_resv_init(pag, XFS_AG_RESV_METADATA,260 ask, used);261 if (error) {262 /*263 * Because we didn't have per-AG reservations when the264 * finobt feature was added we might not be able to265 * reserve all needed blocks. Warn and fall back to the266 * old and potentially buggy code in that case, but267 * ensure we do have the reservation for the refcountbt.268 */269 ask = used = 0;270 271 mp->m_finobt_nores = true;272 273 error = xfs_refcountbt_calc_reserves(mp, tp, pag, &ask,274 &used);275 if (error)276 goto out;277 278 error = __xfs_ag_resv_init(pag, XFS_AG_RESV_METADATA,279 ask, used);280 if (error)281 goto out;282 }283 if (ask)284 has_resv = true;285 }286 287 /* Create the RMAPBT metadata reservation */288 if (pag->pag_rmapbt_resv.ar_asked == 0) {289 ask = used = 0;290 291 error = xfs_rmapbt_calc_reserves(mp, tp, pag, &ask, &used);292 if (error)293 goto out;294 295 error = __xfs_ag_resv_init(pag, XFS_AG_RESV_RMAPBT, ask, used);296 if (error)297 goto out;298 if (ask)299 has_resv = true;300 }301 302out:303 /*304 * Initialize the pagf if we have at least one active reservation on the305 * AG. This may have occurred already via reservation calculation, but306 * fall back to an explicit init to ensure the in-core allocbt usage307 * counters are initialized as soon as possible. This is important308 * because filesystems with large perag reservations are susceptible to309 * free space reservation problems that the allocbt counter is used to310 * address.311 */312 if (has_resv) {313 error2 = xfs_alloc_read_agf(pag, tp, 0, NULL);314 if (error2)315 return error2;316 317 /*318 * If there isn't enough space in the AG to satisfy the319 * reservation, let the caller know that there wasn't enough320 * space. Callers are responsible for deciding what to do321 * next, since (in theory) we can stumble along with322 * insufficient reservation if data blocks are being freed to323 * replenish the AG's free space.324 */325 if (!error &&326 xfs_perag_resv(pag, XFS_AG_RESV_METADATA)->ar_reserved +327 xfs_perag_resv(pag, XFS_AG_RESV_RMAPBT)->ar_reserved >328 pag->pagf_freeblks + pag->pagf_flcount)329 error = -ENOSPC;330 }331 332 return error;333}334 335/* Allocate a block from the reservation. */336void337xfs_ag_resv_alloc_extent(338 struct xfs_perag *pag,339 enum xfs_ag_resv_type type,340 struct xfs_alloc_arg *args)341{342 struct xfs_ag_resv *resv;343 xfs_extlen_t len;344 uint field;345 346 trace_xfs_ag_resv_alloc_extent(pag, type, args->len);347 348 switch (type) {349 case XFS_AG_RESV_AGFL:350 return;351 case XFS_AG_RESV_METADATA:352 case XFS_AG_RESV_RMAPBT:353 resv = xfs_perag_resv(pag, type);354 break;355 default:356 ASSERT(0);357 fallthrough;358 case XFS_AG_RESV_NONE:359 field = args->wasdel ? XFS_TRANS_SB_RES_FDBLOCKS :360 XFS_TRANS_SB_FDBLOCKS;361 xfs_trans_mod_sb(args->tp, field, -(int64_t)args->len);362 return;363 }364 365 len = min_t(xfs_extlen_t, args->len, resv->ar_reserved);366 resv->ar_reserved -= len;367 if (type == XFS_AG_RESV_RMAPBT)368 return;369 /* Allocations of reserved blocks only need on-disk sb updates... */370 xfs_trans_mod_sb(args->tp, XFS_TRANS_SB_RES_FDBLOCKS, -(int64_t)len);371 /* ...but non-reserved blocks need in-core and on-disk updates. */372 if (args->len > len)373 xfs_trans_mod_sb(args->tp, XFS_TRANS_SB_FDBLOCKS,374 -((int64_t)args->len - len));375}376 377/* Free a block to the reservation. */378void379xfs_ag_resv_free_extent(380 struct xfs_perag *pag,381 enum xfs_ag_resv_type type,382 struct xfs_trans *tp,383 xfs_extlen_t len)384{385 xfs_extlen_t leftover;386 struct xfs_ag_resv *resv;387 388 trace_xfs_ag_resv_free_extent(pag, type, len);389 390 switch (type) {391 case XFS_AG_RESV_AGFL:392 return;393 case XFS_AG_RESV_METADATA:394 case XFS_AG_RESV_RMAPBT:395 resv = xfs_perag_resv(pag, type);396 break;397 default:398 ASSERT(0);399 fallthrough;400 case XFS_AG_RESV_NONE:401 xfs_trans_mod_sb(tp, XFS_TRANS_SB_FDBLOCKS, (int64_t)len);402 fallthrough;403 case XFS_AG_RESV_IGNORE:404 return;405 }406 407 leftover = min_t(xfs_extlen_t, len, resv->ar_asked - resv->ar_reserved);408 resv->ar_reserved += leftover;409 if (type == XFS_AG_RESV_RMAPBT)410 return;411 /* Freeing into the reserved pool only requires on-disk update... */412 xfs_trans_mod_sb(tp, XFS_TRANS_SB_RES_FDBLOCKS, len);413 /* ...but freeing beyond that requires in-core and on-disk update. */414 if (len > leftover)415 xfs_trans_mod_sb(tp, XFS_TRANS_SB_FDBLOCKS, len - leftover);416}417