diff options
Diffstat (limited to 'fs/xfs/libxfs/xfs_ialloc.c')
| -rw-r--r-- | fs/xfs/libxfs/xfs_ialloc.c | 56 | 
1 files changed, 50 insertions, 6 deletions
diff --git a/fs/xfs/libxfs/xfs_ialloc.c b/fs/xfs/libxfs/xfs_ialloc.c index e5ac3e5430c4..14c81f227c5b 100644 --- a/fs/xfs/libxfs/xfs_ialloc.c +++ b/fs/xfs/libxfs/xfs_ialloc.c @@ -1058,6 +1058,33 @@ xfs_inobt_first_free_inode(  }  /* + * If this AG has corrupt inodes, check if allocating this inode would fail + * with corruption errors.  Returns 0 if we're clear, or EAGAIN to try again + * somewhere else. + */ +static int +xfs_dialloc_check_ino( +	struct xfs_perag	*pag, +	struct xfs_trans	*tp, +	xfs_ino_t		ino) +{ +	struct xfs_imap		imap; +	struct xfs_buf		*bp; +	int			error; + +	error = xfs_imap(pag, tp, ino, &imap, 0); +	if (error) +		return -EAGAIN; + +	error = xfs_imap_to_bp(pag->pag_mount, tp, &imap, &bp); +	if (error) +		return -EAGAIN; + +	xfs_trans_brelse(tp, bp); +	return 0; +} + +/*   * Allocate an inode using the inobt-only algorithm.   */  STATIC int @@ -1309,6 +1336,13 @@ alloc_inode:  	ASSERT((XFS_AGINO_TO_OFFSET(mp, rec.ir_startino) %  				   XFS_INODES_PER_CHUNK) == 0);  	ino = XFS_AGINO_TO_INO(mp, pag->pag_agno, rec.ir_startino + offset); + +	if (xfs_ag_has_sickness(pag, XFS_SICK_AG_INODES)) { +		error = xfs_dialloc_check_ino(pag, tp, ino); +		if (error) +			goto error0; +	} +  	rec.ir_free &= ~XFS_INOBT_MASK(offset);  	rec.ir_freecount--;  	error = xfs_inobt_update(cur, &rec); @@ -1584,6 +1618,12 @@ xfs_dialloc_ag(  				   XFS_INODES_PER_CHUNK) == 0);  	ino = XFS_AGINO_TO_INO(mp, pag->pag_agno, rec.ir_startino + offset); +	if (xfs_ag_has_sickness(pag, XFS_SICK_AG_INODES)) { +		error = xfs_dialloc_check_ino(pag, tp, ino); +		if (error) +			goto error_cur; +	} +  	/*  	 * Modify or remove the finobt record.  	 */ @@ -1699,7 +1739,7 @@ xfs_dialloc_good_ag(  		return false;  	if (!xfs_perag_initialised_agi(pag)) { -		error = xfs_ialloc_read_agi(pag, tp, NULL); +		error = xfs_ialloc_read_agi(pag, tp, 0, NULL);  		if (error)  			return false;  	} @@ -1768,7 +1808,7 @@ xfs_dialloc_try_ag(  	 * Then read in the AGI buffer and recheck with the AGI buffer  	 * lock held.  	 */ -	error = xfs_ialloc_read_agi(pag, *tpp, &agbp); +	error = xfs_ialloc_read_agi(pag, *tpp, 0, &agbp);  	if (error)  		return error; @@ -2286,7 +2326,7 @@ xfs_difree(  	/*  	 * Get the allocation group header.  	 */ -	error = xfs_ialloc_read_agi(pag, tp, &agbp); +	error = xfs_ialloc_read_agi(pag, tp, 0, &agbp);  	if (error) {  		xfs_warn(mp, "%s: xfs_ialloc_read_agi() returned error %d.",  			__func__, error); @@ -2332,7 +2372,7 @@ xfs_imap_lookup(  	int			error;  	int			i; -	error = xfs_ialloc_read_agi(pag, tp, &agbp); +	error = xfs_ialloc_read_agi(pag, tp, 0, &agbp);  	if (error) {  		xfs_alert(mp,  			"%s: xfs_ialloc_read_agi() returned error %d, agno %d", @@ -2675,6 +2715,7 @@ int  xfs_read_agi(  	struct xfs_perag	*pag,  	struct xfs_trans	*tp, +	xfs_buf_flags_t		flags,  	struct xfs_buf		**agibpp)  {  	struct xfs_mount	*mp = pag->pag_mount; @@ -2684,7 +2725,7 @@ 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); +			XFS_FSS_TO_BB(mp, 1), flags, agibpp, &xfs_agi_buf_ops);  	if (xfs_metadata_is_sick(error))  		xfs_ag_mark_sick(pag, XFS_SICK_AG_AGI);  	if (error) @@ -2704,6 +2745,7 @@ int  xfs_ialloc_read_agi(  	struct xfs_perag	*pag,  	struct xfs_trans	*tp, +	int			flags,  	struct xfs_buf		**agibpp)  {  	struct xfs_buf		*agibp; @@ -2712,7 +2754,9 @@ xfs_ialloc_read_agi(  	trace_xfs_ialloc_read_agi(pag->pag_mount, pag->pag_agno); -	error = xfs_read_agi(pag, tp, &agibp); +	error = xfs_read_agi(pag, tp, +			(flags & XFS_IALLOC_FLAG_TRYLOCK) ? XBF_TRYLOCK : 0, +			&agibp);  	if (error)  		return error;  |