brintos

brintos / linux-shallow public Read only

0
0
Text · 11.6 KiB · 216423d Raw
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