diff options
Diffstat (limited to 'fs/xfs/libxfs/xfs_ialloc.c')
| -rw-r--r-- | fs/xfs/libxfs/xfs_ialloc.c | 170 | 
1 files changed, 88 insertions, 82 deletions
diff --git a/fs/xfs/libxfs/xfs_ialloc.c b/fs/xfs/libxfs/xfs_ialloc.c index 974e71bc4a3a..69b228fce81a 100644 --- a/fs/xfs/libxfs/xfs_ialloc.c +++ b/fs/xfs/libxfs/xfs_ialloc.c @@ -607,13 +607,13 @@ error:  /*   * Allocate new inodes in the allocation group specified by agbp. - * Return 0 for success, else error code. + * Returns 0 if inodes were allocated in this AG; 1 if there was no space + * in this AG; or the usual negative error code.   */  STATIC int  xfs_ialloc_ag_alloc(  	struct xfs_trans	*tp, -	struct xfs_buf		*agbp, -	int			*alloc) +	struct xfs_buf		*agbp)  {  	struct xfs_agi		*agi;  	struct xfs_alloc_arg	args; @@ -795,10 +795,9 @@ sparse_alloc:  		allocmask = (1 << (newlen / XFS_INODES_PER_HOLEMASK_BIT)) - 1;  	} -	if (args.fsbno == NULLFSBLOCK) { -		*alloc = 0; -		return 0; -	} +	if (args.fsbno == NULLFSBLOCK) +		return 1; +  	ASSERT(args.len == args.minlen);  	/* @@ -903,7 +902,6 @@ sparse_alloc:  	 */  	xfs_trans_mod_sb(tp, XFS_TRANS_SB_ICOUNT, (long)newlen);  	xfs_trans_mod_sb(tp, XFS_TRANS_SB_IFREE, (long)newlen); -	*alloc = 1;  	return 0;  } @@ -1570,7 +1568,7 @@ xfs_dialloc_ag_update_inobt(   * The caller selected an AG for us, and made sure that free inodes are   * available.   */ -STATIC int +int  xfs_dialloc_ag(  	struct xfs_trans	*tp,  	struct xfs_buf		*agbp, @@ -1682,65 +1680,78 @@ error_cur:  	return error;  } +static int +xfs_dialloc_roll( +	struct xfs_trans	**tpp, +	struct xfs_buf		*agibp) +{ +	struct xfs_trans	*tp = *tpp; +	struct xfs_dquot_acct	*dqinfo; +	int			error; + +	/* +	 * Hold to on to the agibp across the commit so no other allocation can +	 * come in and take the free inodes we just allocated for our caller. +	 */ +	xfs_trans_bhold(tp, agibp); + +	/* +	 * We want the quota changes to be associated with the next transaction, +	 * NOT this one. So, detach the dqinfo from this and attach it to the +	 * next transaction. +	 */ +	dqinfo = tp->t_dqinfo; +	tp->t_dqinfo = NULL; + +	error = xfs_trans_roll(&tp); + +	/* Re-attach the quota info that we detached from prev trx. */ +	tp->t_dqinfo = dqinfo; + +	*tpp = tp; +	if (error) +		return error; +	xfs_trans_bjoin(tp, agibp); +	return 0; +} +  /* - * Allocate an inode on disk. - * - * Mode is used to tell whether the new inode will need space, and whether it - * is a directory. + * Select and prepare an AG for inode allocation.   * - * This function is designed to be called twice if it has to do an allocation - * to make more free inodes.  On the first call, *IO_agbp should be set to NULL. - * If an inode is available without having to performn an allocation, an inode - * number is returned.  In this case, *IO_agbp is set to NULL.  If an allocation - * needs to be done, xfs_dialloc returns the current AGI buffer in *IO_agbp. - * The caller should then commit the current transaction, allocate a - * new transaction, and call xfs_dialloc() again, passing in the previous value - * of *IO_agbp.  IO_agbp should be held across the transactions. Since the AGI - * buffer is locked across the two calls, the second call is guaranteed to have - * a free inode available. + * Mode is used to tell whether the new inode is a directory and hence where to + * locate it.   * - * Once we successfully pick an inode its number is returned and the on-disk - * data structures are updated.  The inode itself is not read in, since doing so - * would break ordering constraints with xfs_reclaim. + * This function will ensure that the selected AG has free inodes available to + * allocate from. The selected AGI will be returned locked to the caller, and it + * will allocate more free inodes if required. If no free inodes are found or + * can be allocated, no AGI will be returned.   */  int -xfs_dialloc( -	struct xfs_trans	*tp, +xfs_dialloc_select_ag( +	struct xfs_trans	**tpp,  	xfs_ino_t		parent,  	umode_t			mode, -	struct xfs_buf		**IO_agbp, -	xfs_ino_t		*inop) +	struct xfs_buf		**IO_agbp)  { -	struct xfs_mount	*mp = tp->t_mountp; +	struct xfs_mount	*mp = (*tpp)->t_mountp;  	struct xfs_buf		*agbp;  	xfs_agnumber_t		agno;  	int			error; -	int			ialloced; -	int			noroom = 0; +	bool			noroom = false;  	xfs_agnumber_t		start_agno;  	struct xfs_perag	*pag;  	struct xfs_ino_geometry	*igeo = M_IGEO(mp); -	int			okalloc = 1; +	bool			okalloc = true; -	if (*IO_agbp) { -		/* -		 * If the caller passes in a pointer to the AGI buffer, -		 * continue where we left off before.  In this case, we -		 * know that the allocation group has free inodes. -		 */ -		agbp = *IO_agbp; -		goto out_alloc; -	} +	*IO_agbp = NULL;  	/*  	 * We do not have an agbp, so select an initial allocation  	 * group for inode allocation.  	 */ -	start_agno = xfs_ialloc_ag_select(tp, parent, mode); -	if (start_agno == NULLAGNUMBER) { -		*inop = NULLFSINO; +	start_agno = xfs_ialloc_ag_select(*tpp, parent, mode); +	if (start_agno == NULLAGNUMBER)  		return 0; -	}  	/*  	 * If we have already hit the ceiling of inode blocks then clear @@ -1753,8 +1764,8 @@ xfs_dialloc(  	if (igeo->maxicount &&  	    percpu_counter_read_positive(&mp->m_icount) + igeo->ialloc_inos  							> igeo->maxicount) { -		noroom = 1; -		okalloc = 0; +		noroom = true; +		okalloc = false;  	}  	/* @@ -1771,9 +1782,9 @@ xfs_dialloc(  		}  		if (!pag->pagi_init) { -			error = xfs_ialloc_pagi_init(mp, tp, agno); +			error = xfs_ialloc_pagi_init(mp, *tpp, agno);  			if (error) -				goto out_error; +				break;  		}  		/* @@ -1786,64 +1797,59 @@ xfs_dialloc(  		 * Then read in the AGI buffer and recheck with the AGI buffer  		 * lock held.  		 */ -		error = xfs_ialloc_read_agi(mp, tp, agno, &agbp); +		error = xfs_ialloc_read_agi(mp, *tpp, agno, &agbp);  		if (error) -			goto out_error; +			break;  		if (pag->pagi_freecount) {  			xfs_perag_put(pag); -			goto out_alloc; +			goto found_ag;  		}  		if (!okalloc)  			goto nextag_relse_buffer; +		error = xfs_ialloc_ag_alloc(*tpp, agbp); +		if (error < 0) { +			xfs_trans_brelse(*tpp, agbp); -		error = xfs_ialloc_ag_alloc(tp, agbp, &ialloced); -		if (error) { -			xfs_trans_brelse(tp, agbp); - -			if (error != -ENOSPC) -				goto out_error; - -			xfs_perag_put(pag); -			*inop = NULLFSINO; -			return 0; +			if (error == -ENOSPC) +				error = 0; +			break;  		} -		if (ialloced) { +		if (error == 0) {  			/* -			 * We successfully allocated some inodes, return -			 * the current context to the caller so that it -			 * can commit the current transaction and call -			 * us again where we left off. +			 * We successfully allocated space for an inode cluster +			 * in this AG.  Roll the transaction so that we can +			 * allocate one of the new inodes.  			 */  			ASSERT(pag->pagi_freecount > 0);  			xfs_perag_put(pag); -			*IO_agbp = agbp; -			*inop = NULLFSINO; -			return 0; +			error = xfs_dialloc_roll(tpp, agbp); +			if (error) { +				xfs_buf_relse(agbp); +				return error; +			} +			goto found_ag;  		}  nextag_relse_buffer: -		xfs_trans_brelse(tp, agbp); +		xfs_trans_brelse(*tpp, agbp);  nextag:  		xfs_perag_put(pag);  		if (++agno == mp->m_sb.sb_agcount)  			agno = 0; -		if (agno == start_agno) { -			*inop = NULLFSINO; +		if (agno == start_agno)  			return noroom ? -ENOSPC : 0; -		}  	} -out_alloc: -	*IO_agbp = NULL; -	return xfs_dialloc_ag(tp, agbp, parent, inop); -out_error:  	xfs_perag_put(pag);  	return error; +found_ag: +	*IO_agbp = agbp; +	return 0;  }  /* @@ -2453,7 +2459,7 @@ out_map:  void  xfs_ialloc_log_agi(  	xfs_trans_t	*tp,		/* transaction pointer */ -	xfs_buf_t	*bp,		/* allocation group header buffer */ +	struct xfs_buf	*bp,		/* allocation group header buffer */  	int		fields)		/* bitmask of fields to log */  {  	int			first;		/* first byte number */ @@ -2674,7 +2680,7 @@ xfs_ialloc_pagi_init(  	xfs_trans_t	*tp,		/* transaction pointer */  	xfs_agnumber_t	agno)		/* allocation group number */  { -	xfs_buf_t	*bp = NULL; +	struct xfs_buf	*bp = NULL;  	int		error;  	error = xfs_ialloc_read_agi(mp, tp, agno, &bp);  | 
