brintos

brintos / linux-shallow public Read only

0
0
Text · 13.6 KiB · 2aa14b7 Raw
569 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*3 * Copyright (C) 2022-2023 Oracle.  All Rights Reserved.4 * Author: Darrick J. Wong <djwong@kernel.org>5 */6#include "xfs.h"7#include "xfs_fs.h"8#include "xfs_shared.h"9#include "xfs_format.h"10#include "xfs_trans_resv.h"11#include "xfs_mount.h"12#include "xfs_btree.h"13#include "xfs_btree_staging.h"14#include "xfs_log_format.h"15#include "xfs_trans.h"16#include "xfs_sb.h"17#include "xfs_inode.h"18#include "xfs_alloc.h"19#include "xfs_rmap.h"20#include "xfs_ag.h"21#include "xfs_defer.h"22#include "scrub/scrub.h"23#include "scrub/common.h"24#include "scrub/trace.h"25#include "scrub/repair.h"26#include "scrub/newbt.h"27 28/*29 * Estimate proper slack values for a btree that's being reloaded.30 *31 * Under most circumstances, we'll take whatever default loading value the32 * btree bulk loading code calculates for us.  However, there are some33 * exceptions to this rule:34 *35 * (0) If someone turned one of the debug knobs.36 * (1) If this is a per-AG btree and the AG has less than 10% space free.37 * (2) If this is an inode btree and the FS has less than 10% space free.38 39 * In either case, format the new btree blocks almost completely full to40 * minimize space usage.41 */42static void43xrep_newbt_estimate_slack(44	struct xrep_newbt	*xnr)45{46	struct xfs_scrub	*sc = xnr->sc;47	struct xfs_btree_bload	*bload = &xnr->bload;48	uint64_t		free;49	uint64_t		sz;50 51	/*52	 * The xfs_globals values are set to -1 (i.e. take the bload defaults)53	 * unless someone has set them otherwise, so we just pull the values54	 * here.55	 */56	bload->leaf_slack = xfs_globals.bload_leaf_slack;57	bload->node_slack = xfs_globals.bload_node_slack;58 59	if (sc->ops->type == ST_PERAG) {60		free = sc->sa.pag->pagf_freeblks;61		sz = xfs_ag_block_count(sc->mp, sc->sa.pag->pag_agno);62	} else {63		free = percpu_counter_sum(&sc->mp->m_fdblocks);64		sz = sc->mp->m_sb.sb_dblocks;65	}66 67	/* No further changes if there's more than 10% free space left. */68	if (free >= div_u64(sz, 10))69		return;70 71	/*72	 * We're low on space; load the btrees as tightly as possible.  Leave73	 * a couple of open slots in each btree block so that we don't end up74	 * splitting the btrees like crazy after a mount.75	 */76	if (bload->leaf_slack < 0)77		bload->leaf_slack = 2;78	if (bload->node_slack < 0)79		bload->node_slack = 2;80}81 82/* Initialize accounting resources for staging a new AG btree. */83void84xrep_newbt_init_ag(85	struct xrep_newbt		*xnr,86	struct xfs_scrub		*sc,87	const struct xfs_owner_info	*oinfo,88	xfs_fsblock_t			alloc_hint,89	enum xfs_ag_resv_type		resv)90{91	memset(xnr, 0, sizeof(struct xrep_newbt));92	xnr->sc = sc;93	xnr->oinfo = *oinfo; /* structure copy */94	xnr->alloc_hint = alloc_hint;95	xnr->resv = resv;96	INIT_LIST_HEAD(&xnr->resv_list);97	xnr->bload.max_dirty = XFS_B_TO_FSBT(sc->mp, 256U << 10); /* 256K */98	xrep_newbt_estimate_slack(xnr);99}100 101/* Initialize accounting resources for staging a new inode fork btree. */102int103xrep_newbt_init_inode(104	struct xrep_newbt		*xnr,105	struct xfs_scrub		*sc,106	int				whichfork,107	const struct xfs_owner_info	*oinfo)108{109	struct xfs_ifork		*ifp;110 111	ifp = kmem_cache_zalloc(xfs_ifork_cache, XCHK_GFP_FLAGS);112	if (!ifp)113		return -ENOMEM;114 115	xrep_newbt_init_ag(xnr, sc, oinfo,116			XFS_INO_TO_FSB(sc->mp, sc->ip->i_ino),117			XFS_AG_RESV_NONE);118	xnr->ifake.if_fork = ifp;119	xnr->ifake.if_fork_size = xfs_inode_fork_size(sc->ip, whichfork);120	return 0;121}122 123/*124 * Initialize accounting resources for staging a new btree.  Callers are125 * expected to add their own reservations (and clean them up) manually.126 */127void128xrep_newbt_init_bare(129	struct xrep_newbt		*xnr,130	struct xfs_scrub		*sc)131{132	xrep_newbt_init_ag(xnr, sc, &XFS_RMAP_OINFO_ANY_OWNER, NULLFSBLOCK,133			XFS_AG_RESV_NONE);134}135 136/*137 * Designate specific blocks to be used to build our new btree.  @pag must be138 * a passive reference.139 */140STATIC int141xrep_newbt_add_blocks(142	struct xrep_newbt		*xnr,143	struct xfs_perag		*pag,144	const struct xfs_alloc_arg	*args)145{146	struct xfs_mount		*mp = xnr->sc->mp;147	struct xrep_newbt_resv		*resv;148	int				error;149 150	resv = kmalloc(sizeof(struct xrep_newbt_resv), XCHK_GFP_FLAGS);151	if (!resv)152		return -ENOMEM;153 154	INIT_LIST_HEAD(&resv->list);155	resv->agbno = XFS_FSB_TO_AGBNO(mp, args->fsbno);156	resv->len = args->len;157	resv->used = 0;158	resv->pag = xfs_perag_hold(pag);159 160	if (args->tp) {161		ASSERT(xnr->oinfo.oi_offset == 0);162 163		error = xfs_alloc_schedule_autoreap(args,164				XFS_FREE_EXTENT_SKIP_DISCARD, &resv->autoreap);165		if (error)166			goto out_pag;167	}168 169	list_add_tail(&resv->list, &xnr->resv_list);170	return 0;171out_pag:172	xfs_perag_put(resv->pag);173	kfree(resv);174	return error;175}176 177/*178 * Add an extent to the new btree reservation pool.  Callers are required to179 * reap this reservation manually if the repair is cancelled.  @pag must be a180 * passive reference.181 */182int183xrep_newbt_add_extent(184	struct xrep_newbt	*xnr,185	struct xfs_perag	*pag,186	xfs_agblock_t		agbno,187	xfs_extlen_t		len)188{189	struct xfs_mount	*mp = xnr->sc->mp;190	struct xfs_alloc_arg	args = {191		.tp		= NULL, /* no autoreap */192		.oinfo		= xnr->oinfo,193		.fsbno		= XFS_AGB_TO_FSB(mp, pag->pag_agno, agbno),194		.len		= len,195		.resv		= xnr->resv,196	};197 198	return xrep_newbt_add_blocks(xnr, pag, &args);199}200 201/* Don't let our allocation hint take us beyond this AG */202static inline void203xrep_newbt_validate_ag_alloc_hint(204	struct xrep_newbt	*xnr)205{206	struct xfs_scrub	*sc = xnr->sc;207	xfs_agnumber_t		agno = XFS_FSB_TO_AGNO(sc->mp, xnr->alloc_hint);208 209	if (agno == sc->sa.pag->pag_agno &&210	    xfs_verify_fsbno(sc->mp, xnr->alloc_hint))211		return;212 213	xnr->alloc_hint = XFS_AGB_TO_FSB(sc->mp, sc->sa.pag->pag_agno,214					 XFS_AGFL_BLOCK(sc->mp) + 1);215}216 217/* Allocate disk space for a new per-AG btree. */218STATIC int219xrep_newbt_alloc_ag_blocks(220	struct xrep_newbt	*xnr,221	uint64_t		nr_blocks)222{223	struct xfs_scrub	*sc = xnr->sc;224	struct xfs_mount	*mp = sc->mp;225	int			error = 0;226 227	ASSERT(sc->sa.pag != NULL);228 229	while (nr_blocks > 0) {230		struct xfs_alloc_arg	args = {231			.tp		= sc->tp,232			.mp		= mp,233			.oinfo		= xnr->oinfo,234			.minlen		= 1,235			.maxlen		= nr_blocks,236			.prod		= 1,237			.resv		= xnr->resv,238		};239		xfs_agnumber_t		agno;240 241		xrep_newbt_validate_ag_alloc_hint(xnr);242 243		if (xnr->alloc_vextent)244			error = xnr->alloc_vextent(sc, &args, xnr->alloc_hint);245		else246			error = xfs_alloc_vextent_near_bno(&args,247					xnr->alloc_hint);248		if (error)249			return error;250		if (args.fsbno == NULLFSBLOCK)251			return -ENOSPC;252 253		agno = XFS_FSB_TO_AGNO(mp, args.fsbno);254 255		trace_xrep_newbt_alloc_ag_blocks(mp, agno,256				XFS_FSB_TO_AGBNO(mp, args.fsbno), args.len,257				xnr->oinfo.oi_owner);258 259		if (agno != sc->sa.pag->pag_agno) {260			ASSERT(agno == sc->sa.pag->pag_agno);261			return -EFSCORRUPTED;262		}263 264		error = xrep_newbt_add_blocks(xnr, sc->sa.pag, &args);265		if (error)266			return error;267 268		nr_blocks -= args.len;269		xnr->alloc_hint = args.fsbno + args.len;270 271		error = xrep_defer_finish(sc);272		if (error)273			return error;274	}275 276	return 0;277}278 279/* Don't let our allocation hint take us beyond EOFS */280static inline void281xrep_newbt_validate_file_alloc_hint(282	struct xrep_newbt	*xnr)283{284	struct xfs_scrub	*sc = xnr->sc;285 286	if (xfs_verify_fsbno(sc->mp, xnr->alloc_hint))287		return;288 289	xnr->alloc_hint = XFS_AGB_TO_FSB(sc->mp, 0, XFS_AGFL_BLOCK(sc->mp) + 1);290}291 292/* Allocate disk space for our new file-based btree. */293STATIC int294xrep_newbt_alloc_file_blocks(295	struct xrep_newbt	*xnr,296	uint64_t		nr_blocks)297{298	struct xfs_scrub	*sc = xnr->sc;299	struct xfs_mount	*mp = sc->mp;300	int			error = 0;301 302	while (nr_blocks > 0) {303		struct xfs_alloc_arg	args = {304			.tp		= sc->tp,305			.mp		= mp,306			.oinfo		= xnr->oinfo,307			.minlen		= 1,308			.maxlen		= nr_blocks,309			.prod		= 1,310			.resv		= xnr->resv,311		};312		struct xfs_perag	*pag;313		xfs_agnumber_t		agno;314 315		xrep_newbt_validate_file_alloc_hint(xnr);316 317		if (xnr->alloc_vextent)318			error = xnr->alloc_vextent(sc, &args, xnr->alloc_hint);319		else320			error = xfs_alloc_vextent_start_ag(&args,321					xnr->alloc_hint);322		if (error)323			return error;324		if (args.fsbno == NULLFSBLOCK)325			return -ENOSPC;326 327		agno = XFS_FSB_TO_AGNO(mp, args.fsbno);328 329		trace_xrep_newbt_alloc_file_blocks(mp, agno,330				XFS_FSB_TO_AGBNO(mp, args.fsbno), args.len,331				xnr->oinfo.oi_owner);332 333		pag = xfs_perag_get(mp, agno);334		if (!pag) {335			ASSERT(0);336			return -EFSCORRUPTED;337		}338 339		error = xrep_newbt_add_blocks(xnr, pag, &args);340		xfs_perag_put(pag);341		if (error)342			return error;343 344		nr_blocks -= args.len;345		xnr->alloc_hint = args.fsbno + args.len;346 347		error = xrep_defer_finish(sc);348		if (error)349			return error;350	}351 352	return 0;353}354 355/* Allocate disk space for our new btree. */356int357xrep_newbt_alloc_blocks(358	struct xrep_newbt	*xnr,359	uint64_t		nr_blocks)360{361	if (xnr->sc->ip)362		return xrep_newbt_alloc_file_blocks(xnr, nr_blocks);363	return xrep_newbt_alloc_ag_blocks(xnr, nr_blocks);364}365 366/*367 * Free the unused part of a space extent that was reserved for a new ondisk368 * structure.  Returns the number of EFIs logged or a negative errno.369 */370STATIC int371xrep_newbt_free_extent(372	struct xrep_newbt	*xnr,373	struct xrep_newbt_resv	*resv,374	bool			btree_committed)375{376	struct xfs_scrub	*sc = xnr->sc;377	xfs_agblock_t		free_agbno = resv->agbno;378	xfs_extlen_t		free_aglen = resv->len;379	xfs_fsblock_t		fsbno;380	int			error;381 382	if (!btree_committed || resv->used == 0) {383		/*384		 * If we're not committing a new btree or we didn't use the385		 * space reservation, let the existing EFI free the entire386		 * space extent.387		 */388		trace_xrep_newbt_free_blocks(sc->mp, resv->pag->pag_agno,389				free_agbno, free_aglen, xnr->oinfo.oi_owner);390		xfs_alloc_commit_autoreap(sc->tp, &resv->autoreap);391		return 1;392	}393 394	/*395	 * We used space and committed the btree.  Cancel the autoreap, remove396	 * the written blocks from the reservation, and possibly log a new EFI397	 * to free any unused reservation space.398	 */399	xfs_alloc_cancel_autoreap(sc->tp, &resv->autoreap);400	free_agbno += resv->used;401	free_aglen -= resv->used;402 403	if (free_aglen == 0)404		return 0;405 406	trace_xrep_newbt_free_blocks(sc->mp, resv->pag->pag_agno, free_agbno,407			free_aglen, xnr->oinfo.oi_owner);408 409	ASSERT(xnr->resv != XFS_AG_RESV_AGFL);410	ASSERT(xnr->resv != XFS_AG_RESV_IGNORE);411 412	/*413	 * Use EFIs to free the reservations.  This reduces the chance414	 * that we leak blocks if the system goes down.415	 */416	fsbno = XFS_AGB_TO_FSB(sc->mp, resv->pag->pag_agno, free_agbno);417	error = xfs_free_extent_later(sc->tp, fsbno, free_aglen, &xnr->oinfo,418			xnr->resv, XFS_FREE_EXTENT_SKIP_DISCARD);419	if (error)420		return error;421 422	return 1;423}424 425/* Free all the accounting info and disk space we reserved for a new btree. */426STATIC int427xrep_newbt_free(428	struct xrep_newbt	*xnr,429	bool			btree_committed)430{431	struct xfs_scrub	*sc = xnr->sc;432	struct xrep_newbt_resv	*resv, *n;433	unsigned int		freed = 0;434	int			error = 0;435 436	/*437	 * If the filesystem already went down, we can't free the blocks.  Skip438	 * ahead to freeing the incore metadata because we can't fix anything.439	 */440	if (xfs_is_shutdown(sc->mp))441		goto junkit;442 443	list_for_each_entry_safe(resv, n, &xnr->resv_list, list) {444		int		ret;445 446		ret = xrep_newbt_free_extent(xnr, resv, btree_committed);447		list_del(&resv->list);448		xfs_perag_put(resv->pag);449		kfree(resv);450		if (ret < 0) {451			error = ret;452			goto junkit;453		}454 455		freed += ret;456		if (freed >= XREP_MAX_ITRUNCATE_EFIS) {457			error = xrep_defer_finish(sc);458			if (error)459				goto junkit;460			freed = 0;461		}462	}463 464	if (freed)465		error = xrep_defer_finish(sc);466 467junkit:468	/*469	 * If we still have reservations attached to @newbt, cleanup must have470	 * failed and the filesystem is about to go down.  Clean up the incore471	 * reservations and try to commit to freeing the space we used.472	 */473	list_for_each_entry_safe(resv, n, &xnr->resv_list, list) {474		xfs_alloc_commit_autoreap(sc->tp, &resv->autoreap);475		list_del(&resv->list);476		xfs_perag_put(resv->pag);477		kfree(resv);478	}479 480	if (sc->ip) {481		kmem_cache_free(xfs_ifork_cache, xnr->ifake.if_fork);482		xnr->ifake.if_fork = NULL;483	}484 485	return error;486}487 488/*489 * Free all the accounting info and unused disk space allocations after490 * committing a new btree.491 */492int493xrep_newbt_commit(494	struct xrep_newbt	*xnr)495{496	return xrep_newbt_free(xnr, true);497}498 499/*500 * Free all the accounting info and all of the disk space we reserved for a new501 * btree that we're not going to commit.  We want to try to roll things back502 * cleanly for things like ENOSPC midway through allocation.503 */504void505xrep_newbt_cancel(506	struct xrep_newbt	*xnr)507{508	xrep_newbt_free(xnr, false);509}510 511/* Feed one of the reserved btree blocks to the bulk loader. */512int513xrep_newbt_claim_block(514	struct xfs_btree_cur	*cur,515	struct xrep_newbt	*xnr,516	union xfs_btree_ptr	*ptr)517{518	struct xrep_newbt_resv	*resv;519	struct xfs_mount	*mp = cur->bc_mp;520	xfs_agblock_t		agbno;521 522	/*523	 * The first item in the list should always have a free block unless524	 * we're completely out.525	 */526	resv = list_first_entry(&xnr->resv_list, struct xrep_newbt_resv, list);527	if (resv->used == resv->len)528		return -ENOSPC;529 530	/*531	 * Peel off a block from the start of the reservation.  We allocate532	 * blocks in order to place blocks on disk in increasing record or key533	 * order.  The block reservations tend to end up on the list in534	 * decreasing order, which hopefully results in leaf blocks ending up535	 * together.536	 */537	agbno = resv->agbno + resv->used;538	resv->used++;539 540	/* If we used all the blocks in this reservation, move it to the end. */541	if (resv->used == resv->len)542		list_move_tail(&resv->list, &xnr->resv_list);543 544	trace_xrep_newbt_claim_block(mp, resv->pag->pag_agno, agbno, 1,545			xnr->oinfo.oi_owner);546 547	if (cur->bc_ops->ptr_len == XFS_BTREE_LONG_PTR_LEN)548		ptr->l = cpu_to_be64(XFS_AGB_TO_FSB(mp, resv->pag->pag_agno,549								agbno));550	else551		ptr->s = cpu_to_be32(agbno);552 553	/* Relog all the EFIs. */554	return xrep_defer_finish(xnr->sc);555}556 557/* How many reserved blocks are unused? */558unsigned int559xrep_newbt_unused_blocks(560	struct xrep_newbt	*xnr)561{562	struct xrep_newbt_resv	*resv;563	unsigned int		unused = 0;564 565	list_for_each_entry(resv, &xnr->resv_list, list)566		unused += resv->len - resv->used;567	return unused;568}569