diff options
| author | Ingo Molnar <mingo@kernel.org> | 2024-03-25 13:32:29 +0300 | 
|---|---|---|
| committer | Ingo Molnar <mingo@kernel.org> | 2024-03-25 13:32:29 +0300 | 
| commit | f4566a1e73957800df75a3dd2dccee8a4697f327 (patch) | |
| tree | b043b875228c0b25988af66c680d60cae69d761d /fs/xfs/libxfs/xfs_ialloc.c | |
| parent | b9e6e28663928cab836a19abbdec3d036a07db3b (diff) | |
| parent | 4cece764965020c22cff7665b18a012006359095 (diff) | |
| download | linux-f4566a1e73957800df75a3dd2dccee8a4697f327.tar.xz | |
Merge tag 'v6.9-rc1' into sched/core, to pick up fixes and to refresh the branch
Signed-off-by: Ingo Molnar <mingo@kernel.org>
Diffstat (limited to 'fs/xfs/libxfs/xfs_ialloc.c')
| -rw-r--r-- | fs/xfs/libxfs/xfs_ialloc.c | 232 | 
1 files changed, 158 insertions, 74 deletions
diff --git a/fs/xfs/libxfs/xfs_ialloc.c b/fs/xfs/libxfs/xfs_ialloc.c index 2361a22035b0..e5ac3e5430c4 100644 --- a/fs/xfs/libxfs/xfs_ialloc.c +++ b/fs/xfs/libxfs/xfs_ialloc.c @@ -27,6 +27,7 @@  #include "xfs_log.h"  #include "xfs_rmap.h"  #include "xfs_ag.h" +#include "xfs_health.h"  /*   * Lookup a record by ino in the btree given by cur. @@ -140,13 +141,13 @@ xfs_inobt_complain_bad_rec(  	struct xfs_mount		*mp = cur->bc_mp;  	xfs_warn(mp, -		"%s Inode BTree record corruption in AG %d detected at %pS!", -		cur->bc_btnum == XFS_BTNUM_INO ? "Used" : "Free", -		cur->bc_ag.pag->pag_agno, fa); +		"%sbt record corruption in AG %d detected at %pS!", +		cur->bc_ops->name, cur->bc_ag.pag->pag_agno, fa);  	xfs_warn(mp,  "start inode 0x%x, count 0x%x, free 0x%x freemask 0x%llx, holemask 0x%x",  		irec->ir_startino, irec->ir_count, irec->ir_freecount,  		irec->ir_free, irec->ir_holemask); +	xfs_btree_mark_sick(cur);  	return -EFSCORRUPTED;  } @@ -205,14 +206,17 @@ xfs_inobt_insert(  	struct xfs_buf		*agbp,  	xfs_agino_t		newino,  	xfs_agino_t		newlen, -	xfs_btnum_t		btnum) +	bool			is_finobt)  {  	struct xfs_btree_cur	*cur;  	xfs_agino_t		thisino;  	int			i;  	int			error; -	cur = xfs_inobt_init_cursor(pag, tp, agbp, btnum); +	if (is_finobt) +		cur = xfs_finobt_init_cursor(pag, tp, agbp); +	else +		cur = xfs_inobt_init_cursor(pag, tp, agbp);  	for (thisino = newino;  	     thisino < newino + newlen; @@ -528,16 +532,14 @@ __xfs_inobt_rec_merge(  }  /* - * Insert a new sparse inode chunk into the associated inode btree. The inode - * record for the sparse chunk is pre-aligned to a startino that should match - * any pre-existing sparse inode record in the tree. This allows sparse chunks - * to fill over time. + * Insert a new sparse inode chunk into the associated inode allocation btree. + * The inode record for the sparse chunk is pre-aligned to a startino that + * should match any pre-existing sparse inode record in the tree. This allows + * sparse chunks to fill over time.   * - * This function supports two modes of handling preexisting records depending on - * the merge flag. If merge is true, the provided record is merged with the + * If no preexisting record exists, the provided record is inserted. + * If there is a preexisting record, the provided record is merged with the   * existing record and updated in place. The merged record is returned in nrec. - * If merge is false, an existing record is replaced with the provided record. - * If no preexisting record exists, the provided record is always inserted.   *   * It is considered corruption if a merge is requested and not possible. Given   * the sparse inode alignment constraints, this should never happen. @@ -547,9 +549,7 @@ xfs_inobt_insert_sprec(  	struct xfs_perag		*pag,  	struct xfs_trans		*tp,  	struct xfs_buf			*agbp, -	int				btnum, -	struct xfs_inobt_rec_incore	*nrec,	/* in/out: new/merged rec. */ -	bool				merge)	/* merge or replace */ +	struct xfs_inobt_rec_incore	*nrec)	/* in/out: new/merged rec. */  {  	struct xfs_mount		*mp = pag->pag_mount;  	struct xfs_btree_cur		*cur; @@ -557,7 +557,7 @@ xfs_inobt_insert_sprec(  	int				i;  	struct xfs_inobt_rec_incore	rec; -	cur = xfs_inobt_init_cursor(pag, tp, agbp, btnum); +	cur = xfs_inobt_init_cursor(pag, tp, agbp);  	/* the new record is pre-aligned so we know where to look */  	error = xfs_inobt_lookup(cur, nrec->ir_startino, XFS_LOOKUP_EQ, &i); @@ -571,6 +571,7 @@ xfs_inobt_insert_sprec(  		if (error)  			goto error;  		if (XFS_IS_CORRUPT(mp, i != 1)) { +			xfs_btree_mark_sick(cur);  			error = -EFSCORRUPTED;  			goto error;  		} @@ -579,45 +580,45 @@ xfs_inobt_insert_sprec(  	}  	/* -	 * A record exists at this startino. Merge or replace the record -	 * depending on what we've been asked to do. +	 * A record exists at this startino.  Merge the records.  	 */ -	if (merge) { -		error = xfs_inobt_get_rec(cur, &rec, &i); -		if (error) -			goto error; -		if (XFS_IS_CORRUPT(mp, i != 1)) { -			error = -EFSCORRUPTED; -			goto error; -		} -		if (XFS_IS_CORRUPT(mp, rec.ir_startino != nrec->ir_startino)) { -			error = -EFSCORRUPTED; -			goto error; -		} +	error = xfs_inobt_get_rec(cur, &rec, &i); +	if (error) +		goto error; +	if (XFS_IS_CORRUPT(mp, i != 1)) { +		xfs_btree_mark_sick(cur); +		error = -EFSCORRUPTED; +		goto error; +	} +	if (XFS_IS_CORRUPT(mp, rec.ir_startino != nrec->ir_startino)) { +		xfs_btree_mark_sick(cur); +		error = -EFSCORRUPTED; +		goto error; +	} -		/* -		 * This should never fail. If we have coexisting records that -		 * cannot merge, something is seriously wrong. -		 */ -		if (XFS_IS_CORRUPT(mp, !__xfs_inobt_can_merge(nrec, &rec))) { -			error = -EFSCORRUPTED; -			goto error; -		} +	/* +	 * This should never fail. If we have coexisting records that +	 * cannot merge, something is seriously wrong. +	 */ +	if (XFS_IS_CORRUPT(mp, !__xfs_inobt_can_merge(nrec, &rec))) { +		xfs_btree_mark_sick(cur); +		error = -EFSCORRUPTED; +		goto error; +	} -		trace_xfs_irec_merge_pre(mp, pag->pag_agno, rec.ir_startino, -					 rec.ir_holemask, nrec->ir_startino, -					 nrec->ir_holemask); +	trace_xfs_irec_merge_pre(mp, pag->pag_agno, rec.ir_startino, +				 rec.ir_holemask, nrec->ir_startino, +				 nrec->ir_holemask); -		/* merge to nrec to output the updated record */ -		__xfs_inobt_rec_merge(nrec, &rec); +	/* merge to nrec to output the updated record */ +	__xfs_inobt_rec_merge(nrec, &rec); -		trace_xfs_irec_merge_post(mp, pag->pag_agno, nrec->ir_startino, -					  nrec->ir_holemask); +	trace_xfs_irec_merge_post(mp, pag->pag_agno, nrec->ir_startino, +				  nrec->ir_holemask); -		error = xfs_inobt_rec_check_count(mp, nrec); -		if (error) -			goto error; -	} +	error = xfs_inobt_rec_check_count(mp, nrec); +	if (error) +		goto error;  	error = xfs_inobt_update(cur, nrec);  	if (error) @@ -632,6 +633,59 @@ error:  }  /* + * Insert a new sparse inode chunk into the free inode btree. The inode + * record for the sparse chunk is pre-aligned to a startino that should match + * any pre-existing sparse inode record in the tree. This allows sparse chunks + * to fill over time. + * + * The new record is always inserted, overwriting a pre-existing record if + * there is one. + */ +STATIC int +xfs_finobt_insert_sprec( +	struct xfs_perag		*pag, +	struct xfs_trans		*tp, +	struct xfs_buf			*agbp, +	struct xfs_inobt_rec_incore	*nrec)	/* in/out: new rec. */ +{ +	struct xfs_mount		*mp = pag->pag_mount; +	struct xfs_btree_cur		*cur; +	int				error; +	int				i; + +	cur = xfs_finobt_init_cursor(pag, tp, agbp); + +	/* the new record is pre-aligned so we know where to look */ +	error = xfs_inobt_lookup(cur, nrec->ir_startino, XFS_LOOKUP_EQ, &i); +	if (error) +		goto error; +	/* if nothing there, insert a new record and return */ +	if (i == 0) { +		error = xfs_inobt_insert_rec(cur, nrec->ir_holemask, +					     nrec->ir_count, nrec->ir_freecount, +					     nrec->ir_free, &i); +		if (error) +			goto error; +		if (XFS_IS_CORRUPT(mp, i != 1)) { +			xfs_btree_mark_sick(cur); +			error = -EFSCORRUPTED; +			goto error; +		} +	} else { +		error = xfs_inobt_update(cur, nrec); +		if (error) +			goto error; +	} + +	xfs_btree_del_cursor(cur, XFS_BTREE_NOERROR); +	return 0; +error: +	xfs_btree_del_cursor(cur, XFS_BTREE_ERROR); +	return error; +} + + +/*   * Allocate new inodes in the allocation group specified by agbp.  Returns 0 if   * inodes were allocated in this AG; -EAGAIN if there was no space in this AG so   * the caller knows it can try another AG, a hard -ENOSPC when over the maximum @@ -857,8 +911,7 @@ sparse_alloc:  		 * if necessary. If a merge does occur, rec is updated to the  		 * merged record.  		 */ -		error = xfs_inobt_insert_sprec(pag, tp, agbp, -				XFS_BTNUM_INO, &rec, true); +		error = xfs_inobt_insert_sprec(pag, tp, agbp, &rec);  		if (error == -EFSCORRUPTED) {  			xfs_alert(args.mp,  	"invalid sparse inode record: ino 0x%llx holemask 0x%x count %u", @@ -882,21 +935,19 @@ sparse_alloc:  		 * existing record with this one.  		 */  		if (xfs_has_finobt(args.mp)) { -			error = xfs_inobt_insert_sprec(pag, tp, agbp, -				       XFS_BTNUM_FINO, &rec, false); +			error = xfs_finobt_insert_sprec(pag, tp, agbp, &rec);  			if (error)  				return error;  		}  	} else {  		/* full chunk - insert new records to both btrees */ -		error = xfs_inobt_insert(pag, tp, agbp, newino, newlen, -					 XFS_BTNUM_INO); +		error = xfs_inobt_insert(pag, tp, agbp, newino, newlen, false);  		if (error)  			return error;  		if (xfs_has_finobt(args.mp)) {  			error = xfs_inobt_insert(pag, tp, agbp, newino, -						 newlen, XFS_BTNUM_FINO); +						 newlen, true);  			if (error)  				return error;  		} @@ -949,8 +1000,10 @@ xfs_ialloc_next_rec(  		error = xfs_inobt_get_rec(cur, rec, &i);  		if (error)  			return error; -		if (XFS_IS_CORRUPT(cur->bc_mp, i != 1)) +		if (XFS_IS_CORRUPT(cur->bc_mp, i != 1)) { +			xfs_btree_mark_sick(cur);  			return -EFSCORRUPTED; +		}  	}  	return 0; @@ -974,8 +1027,10 @@ xfs_ialloc_get_rec(  		error = xfs_inobt_get_rec(cur, rec, &i);  		if (error)  			return error; -		if (XFS_IS_CORRUPT(cur->bc_mp, i != 1)) +		if (XFS_IS_CORRUPT(cur->bc_mp, i != 1)) { +			xfs_btree_mark_sick(cur);  			return -EFSCORRUPTED; +		}  	}  	return 0; @@ -1030,7 +1085,7 @@ xfs_dialloc_ag_inobt(  	ASSERT(pag->pagi_freecount > 0);   restart_pagno: -	cur = xfs_inobt_init_cursor(pag, tp, agbp, XFS_BTNUM_INO); +	cur = xfs_inobt_init_cursor(pag, tp, agbp);  	/*  	 * If pagino is 0 (this is the root inode allocation) use newino.  	 * This must work because we've just allocated some. @@ -1053,6 +1108,7 @@ xfs_dialloc_ag_inobt(  		if (error)  			goto error0;  		if (XFS_IS_CORRUPT(mp, i != 1)) { +			xfs_btree_mark_sick(cur);  			error = -EFSCORRUPTED;  			goto error0;  		} @@ -1061,6 +1117,7 @@ xfs_dialloc_ag_inobt(  		if (error)  			goto error0;  		if (XFS_IS_CORRUPT(mp, j != 1)) { +			xfs_btree_mark_sick(cur);  			error = -EFSCORRUPTED;  			goto error0;  		} @@ -1219,6 +1276,7 @@ xfs_dialloc_ag_inobt(  	if (error)  		goto error0;  	if (XFS_IS_CORRUPT(mp, i != 1)) { +		xfs_btree_mark_sick(cur);  		error = -EFSCORRUPTED;  		goto error0;  	} @@ -1228,6 +1286,7 @@ xfs_dialloc_ag_inobt(  		if (error)  			goto error0;  		if (XFS_IS_CORRUPT(mp, i != 1)) { +			xfs_btree_mark_sick(cur);  			error = -EFSCORRUPTED;  			goto error0;  		} @@ -1237,6 +1296,7 @@ xfs_dialloc_ag_inobt(  		if (error)  			goto error0;  		if (XFS_IS_CORRUPT(mp, i != 1)) { +			xfs_btree_mark_sick(cur);  			error = -EFSCORRUPTED;  			goto error0;  		} @@ -1297,8 +1357,10 @@ xfs_dialloc_ag_finobt_near(  		error = xfs_inobt_get_rec(lcur, rec, &i);  		if (error)  			return error; -		if (XFS_IS_CORRUPT(lcur->bc_mp, i != 1)) +		if (XFS_IS_CORRUPT(lcur->bc_mp, i != 1)) { +			xfs_btree_mark_sick(lcur);  			return -EFSCORRUPTED; +		}  		/*  		 * See if we've landed in the parent inode record. The finobt @@ -1322,12 +1384,14 @@ xfs_dialloc_ag_finobt_near(  		if (error)  			goto error_rcur;  		if (XFS_IS_CORRUPT(lcur->bc_mp, j != 1)) { +			xfs_btree_mark_sick(lcur);  			error = -EFSCORRUPTED;  			goto error_rcur;  		}  	}  	if (XFS_IS_CORRUPT(lcur->bc_mp, i != 1 && j != 1)) { +		xfs_btree_mark_sick(lcur);  		error = -EFSCORRUPTED;  		goto error_rcur;  	} @@ -1383,8 +1447,10 @@ xfs_dialloc_ag_finobt_newino(  			error = xfs_inobt_get_rec(cur, rec, &i);  			if (error)  				return error; -			if (XFS_IS_CORRUPT(cur->bc_mp, i != 1)) +			if (XFS_IS_CORRUPT(cur->bc_mp, i != 1)) { +				xfs_btree_mark_sick(cur);  				return -EFSCORRUPTED; +			}  			return 0;  		}  	} @@ -1395,14 +1461,18 @@ xfs_dialloc_ag_finobt_newino(  	error = xfs_inobt_lookup(cur, 0, XFS_LOOKUP_GE, &i);  	if (error)  		return error; -	if (XFS_IS_CORRUPT(cur->bc_mp, i != 1)) +	if (XFS_IS_CORRUPT(cur->bc_mp, i != 1)) { +		xfs_btree_mark_sick(cur);  		return -EFSCORRUPTED; +	}  	error = xfs_inobt_get_rec(cur, rec, &i);  	if (error)  		return error; -	if (XFS_IS_CORRUPT(cur->bc_mp, i != 1)) +	if (XFS_IS_CORRUPT(cur->bc_mp, i != 1)) { +		xfs_btree_mark_sick(cur);  		return -EFSCORRUPTED; +	}  	return 0;  } @@ -1424,14 +1494,18 @@ xfs_dialloc_ag_update_inobt(  	error = xfs_inobt_lookup(cur, frec->ir_startino, XFS_LOOKUP_EQ, &i);  	if (error)  		return error; -	if (XFS_IS_CORRUPT(cur->bc_mp, i != 1)) +	if (XFS_IS_CORRUPT(cur->bc_mp, i != 1)) { +		xfs_btree_mark_sick(cur);  		return -EFSCORRUPTED; +	}  	error = xfs_inobt_get_rec(cur, &rec, &i);  	if (error)  		return error; -	if (XFS_IS_CORRUPT(cur->bc_mp, i != 1)) +	if (XFS_IS_CORRUPT(cur->bc_mp, i != 1)) { +		xfs_btree_mark_sick(cur);  		return -EFSCORRUPTED; +	}  	ASSERT((XFS_AGINO_TO_OFFSET(cur->bc_mp, rec.ir_startino) %  				   XFS_INODES_PER_CHUNK) == 0); @@ -1440,8 +1514,10 @@ xfs_dialloc_ag_update_inobt(  	if (XFS_IS_CORRUPT(cur->bc_mp,  			   rec.ir_free != frec->ir_free || -			   rec.ir_freecount != frec->ir_freecount)) +			   rec.ir_freecount != frec->ir_freecount)) { +		xfs_btree_mark_sick(cur);  		return -EFSCORRUPTED; +	}  	return xfs_inobt_update(cur, &rec);  } @@ -1483,7 +1559,7 @@ xfs_dialloc_ag(  	if (!pagino)  		pagino = be32_to_cpu(agi->agi_newino); -	cur = xfs_inobt_init_cursor(pag, tp, agbp, XFS_BTNUM_FINO); +	cur = xfs_finobt_init_cursor(pag, tp, agbp);  	error = xfs_check_agi_freecount(cur);  	if (error) @@ -1526,7 +1602,7 @@ xfs_dialloc_ag(  	 * the original freecount. If all is well, make the equivalent update to  	 * the inobt using the finobt record and offset information.  	 */ -	icur = xfs_inobt_init_cursor(pag, tp, agbp, XFS_BTNUM_INO); +	icur = xfs_inobt_init_cursor(pag, tp, agbp);  	error = xfs_check_agi_freecount(icur);  	if (error) @@ -1943,7 +2019,7 @@ xfs_difree_inobt(  	/*  	 * Initialize the cursor.  	 */ -	cur = xfs_inobt_init_cursor(pag, tp, agbp, XFS_BTNUM_INO); +	cur = xfs_inobt_init_cursor(pag, tp, agbp);  	error = xfs_check_agi_freecount(cur);  	if (error) @@ -1958,6 +2034,7 @@ xfs_difree_inobt(  		goto error0;  	}  	if (XFS_IS_CORRUPT(mp, i != 1)) { +		xfs_btree_mark_sick(cur);  		error = -EFSCORRUPTED;  		goto error0;  	} @@ -1968,6 +2045,7 @@ xfs_difree_inobt(  		goto error0;  	}  	if (XFS_IS_CORRUPT(mp, i != 1)) { +		xfs_btree_mark_sick(cur);  		error = -EFSCORRUPTED;  		goto error0;  	} @@ -2068,7 +2146,7 @@ xfs_difree_finobt(  	int				error;  	int				i; -	cur = xfs_inobt_init_cursor(pag, tp, agbp, XFS_BTNUM_FINO); +	cur = xfs_finobt_init_cursor(pag, tp, agbp);  	error = xfs_inobt_lookup(cur, ibtrec->ir_startino, XFS_LOOKUP_EQ, &i);  	if (error) @@ -2080,6 +2158,7 @@ xfs_difree_finobt(  		 * something is out of sync.  		 */  		if (XFS_IS_CORRUPT(mp, ibtrec->ir_freecount != 1)) { +			xfs_btree_mark_sick(cur);  			error = -EFSCORRUPTED;  			goto error;  		} @@ -2106,6 +2185,7 @@ xfs_difree_finobt(  	if (error)  		goto error;  	if (XFS_IS_CORRUPT(mp, i != 1)) { +		xfs_btree_mark_sick(cur);  		error = -EFSCORRUPTED;  		goto error;  	} @@ -2116,6 +2196,7 @@ xfs_difree_finobt(  	if (XFS_IS_CORRUPT(mp,  			   rec.ir_free != ibtrec->ir_free ||  			   rec.ir_freecount != ibtrec->ir_freecount)) { +		xfs_btree_mark_sick(cur);  		error = -EFSCORRUPTED;  		goto error;  	} @@ -2265,7 +2346,7 @@ xfs_imap_lookup(  	 * we have a record, we need to ensure it contains the inode number  	 * we are looking up.  	 */ -	cur = xfs_inobt_init_cursor(pag, tp, agbp, XFS_BTNUM_INO); +	cur = xfs_inobt_init_cursor(pag, tp, agbp);  	error = xfs_inobt_lookup(cur, agino, XFS_LOOKUP_LE, &i);  	if (!error) {  		if (i) @@ -2604,6 +2685,8 @@ xfs_read_agi(  	error = xfs_trans_read_buf(mp, tp, mp->m_ddev_targp,  			XFS_AG_DADDR(mp, pag->pag_agno, XFS_AGI_DADDR(mp)),  			XFS_FSS_TO_BB(mp, 1), 0, agibpp, &xfs_agi_buf_ops); +	if (xfs_metadata_is_sick(error)) +		xfs_ag_mark_sick(pag, XFS_SICK_AG_AGI);  	if (error)  		return error;  	if (tp) @@ -2765,7 +2848,7 @@ xfs_ialloc_count_inodes(  	struct xfs_ialloc_count_inodes	ci = {0};  	int				error; -	ASSERT(cur->bc_btnum == XFS_BTNUM_INO); +	ASSERT(xfs_btree_is_ino(cur->bc_ops));  	error = xfs_btree_query_all(cur, xfs_ialloc_count_inodes_rec, &ci);  	if (error)  		return error; @@ -2982,7 +3065,7 @@ xfs_ialloc_check_shrink(  	if (!xfs_has_sparseinodes(pag->pag_mount))  		return 0; -	cur = xfs_inobt_init_cursor(pag, tp, agibp, XFS_BTNUM_INO); +	cur = xfs_inobt_init_cursor(pag, tp, agibp);  	/* Look up the inobt record that would correspond to the new EOFS. */  	agino = XFS_AGB_TO_AGINO(pag->pag_mount, new_length); @@ -2995,6 +3078,7 @@ xfs_ialloc_check_shrink(  		goto out;  	if (!has) { +		xfs_ag_mark_sick(pag, XFS_SICK_AG_INOBT);  		error = -EFSCORRUPTED;  		goto out;  	}  | 
