diff mbox series

[v8,4/5] xfs: support shrinking unused space in the last AG

Message ID 20210305025703.3069469-5-hsiangkao@redhat.com (mailing list archive)
State Superseded
Headers show
Series xfs: support shrinking free space in the last AG | expand

Commit Message

Gao Xiang March 5, 2021, 2:57 a.m. UTC
As the first step of shrinking, this attempts to enable shrinking
unused space in the last allocation group by fixing up freespace
btree, agi, agf and adjusting super block and use a helper
xfs_ag_shrink_space() to fixup the last AG.

This can be all done in one transaction for now, so I think no
additional protection is needed.

Reviewed-by: Darrick J. Wong <djwong@kernel.org>
Signed-off-by: Gao Xiang <hsiangkao@redhat.com>
---
 fs/xfs/xfs_fsops.c | 88 ++++++++++++++++++++++++++++------------------
 fs/xfs/xfs_trans.c |  1 -
 2 files changed, 53 insertions(+), 36 deletions(-)

Comments

Brian Foster March 22, 2021, 11:30 a.m. UTC | #1
On Fri, Mar 05, 2021 at 10:57:02AM +0800, Gao Xiang wrote:
> As the first step of shrinking, this attempts to enable shrinking
> unused space in the last allocation group by fixing up freespace
> btree, agi, agf and adjusting super block and use a helper
> xfs_ag_shrink_space() to fixup the last AG.
> 
> This can be all done in one transaction for now, so I think no
> additional protection is needed.
> 
> Reviewed-by: Darrick J. Wong <djwong@kernel.org>
> Signed-off-by: Gao Xiang <hsiangkao@redhat.com>
> ---
>  fs/xfs/xfs_fsops.c | 88 ++++++++++++++++++++++++++++------------------
>  fs/xfs/xfs_trans.c |  1 -
>  2 files changed, 53 insertions(+), 36 deletions(-)
> 
> diff --git a/fs/xfs/xfs_fsops.c b/fs/xfs/xfs_fsops.c
> index fc9e799b2ae3..71cba61a451c 100644
> --- a/fs/xfs/xfs_fsops.c
> +++ b/fs/xfs/xfs_fsops.c
> @@ -91,23 +91,28 @@ xfs_growfs_data_private(
>  	xfs_agnumber_t		nagcount;
>  	xfs_agnumber_t		nagimax = 0;
>  	xfs_rfsblock_t		nb, nb_div, nb_mod;
> -	xfs_rfsblock_t		delta;
> +	int64_t			delta;
>  	bool			lastag_resetagres;
>  	xfs_agnumber_t		oagcount;
>  	struct xfs_trans	*tp;
>  	struct aghdr_init_data	id = {};
>  
>  	nb = in->newblocks;
> -	if (nb < mp->m_sb.sb_dblocks)
> -		return -EINVAL;
> -	if ((error = xfs_sb_validate_fsb_count(&mp->m_sb, nb)))
> +	if (nb == mp->m_sb.sb_dblocks)
> +		return 0;

It looks like the caller already does this check.

> +
> +	error = xfs_sb_validate_fsb_count(&mp->m_sb, nb);
> +	if (error)
>  		return error;
> -	error = xfs_buf_read_uncached(mp->m_ddev_targp,
> +
> +	if (nb > mp->m_sb.sb_dblocks) {
> +		error = xfs_buf_read_uncached(mp->m_ddev_targp,
>  				XFS_FSB_TO_BB(mp, nb) - XFS_FSS_TO_BB(mp, 1),
>  				XFS_FSS_TO_BB(mp, 1), 0, &bp, NULL);
> -	if (error)
> -		return error;
> -	xfs_buf_relse(bp);
> +		if (error)
> +			return error;
> +		xfs_buf_relse(bp);
> +	}
>  
>  	nb_div = nb;
>  	nb_mod = do_div(nb_div, mp->m_sb.sb_agblocks);
> @@ -115,10 +120,15 @@ xfs_growfs_data_private(
>  	if (nb_mod && nb_mod < XFS_MIN_AG_BLOCKS) {
>  		nagcount--;
>  		nb = (xfs_rfsblock_t)nagcount * mp->m_sb.sb_agblocks;
> -		if (nb < mp->m_sb.sb_dblocks)
> -			return -EINVAL;
>  	}
>  	delta = nb - mp->m_sb.sb_dblocks;
> +	/*
> +	 * XFS doesn't really support single-AG filesystems, so do not
> +	 * permit callers to remove the filesystem's second and last AG.
> +	 */
> +	if (delta < 0 && nagcount < 2)
> +		return -EINVAL;
> +

What if the filesystem is already single AG? Unless I'm missing
something, we already have a check a bit further down that prevents
removal of AGs in the first place.

Otherwise looks reasonable..

Brian

>  	oagcount = mp->m_sb.sb_agcount;
>  
>  	/* allocate the new per-ag structures */
> @@ -126,15 +136,22 @@ xfs_growfs_data_private(
>  		error = xfs_initialize_perag(mp, nagcount, &nagimax);
>  		if (error)
>  			return error;
> +	} else if (nagcount < oagcount) {
> +		/* TODO: shrinking the entire AGs hasn't yet completed */
> +		return -EINVAL;
>  	}
>  
>  	error = xfs_trans_alloc(mp, &M_RES(mp)->tr_growdata,
> -			XFS_GROWFS_SPACE_RES(mp), 0, XFS_TRANS_RESERVE, &tp);
> +			(delta > 0 ? XFS_GROWFS_SPACE_RES(mp) : -delta), 0,
> +			XFS_TRANS_RESERVE, &tp);
>  	if (error)
>  		return error;
>  
> -	error = xfs_resizefs_init_new_ags(tp, &id, oagcount, nagcount,
> -					  delta, &lastag_resetagres);
> +	if (delta > 0)
> +		error = xfs_resizefs_init_new_ags(tp, &id, oagcount, nagcount,
> +						  delta, &lastag_resetagres);
> +	else
> +		error = xfs_ag_shrink_space(mp, &tp, nagcount - 1, -delta);
>  	if (error)
>  		goto out_trans_cancel;
>  
> @@ -145,7 +162,7 @@ xfs_growfs_data_private(
>  	 */
>  	if (nagcount > oagcount)
>  		xfs_trans_mod_sb(tp, XFS_TRANS_SB_AGCOUNT, nagcount - oagcount);
> -	if (delta > 0)
> +	if (delta)
>  		xfs_trans_mod_sb(tp, XFS_TRANS_SB_DBLOCKS, delta);
>  	if (id.nfree)
>  		xfs_trans_mod_sb(tp, XFS_TRANS_SB_FDBLOCKS, id.nfree);
> @@ -169,28 +186,29 @@ xfs_growfs_data_private(
>  	xfs_set_low_space_thresholds(mp);
>  	mp->m_alloc_set_aside = xfs_alloc_set_aside(mp);
>  
> -	/*
> -	 * If we expanded the last AG, free the per-AG reservation
> -	 * so we can reinitialize it with the new size.
> -	 */
> -	if (lastag_resetagres) {
> -		struct xfs_perag	*pag;
> -
> -		pag = xfs_perag_get(mp, id.agno);
> -		error = xfs_ag_resv_free(pag);
> -		xfs_perag_put(pag);
> -		if (error)
> -			return error;
> +	if (delta > 0) {
> +		/*
> +		 * If we expanded the last AG, free the per-AG reservation
> +		 * so we can reinitialize it with the new size.
> +		 */
> +		if (lastag_resetagres) {
> +			struct xfs_perag	*pag;
> +
> +			pag = xfs_perag_get(mp, id.agno);
> +			error = xfs_ag_resv_free(pag);
> +			xfs_perag_put(pag);
> +			if (error)
> +				return error;
> +		}
> +		/*
> +		 * Reserve AG metadata blocks. ENOSPC here does not mean there
> +		 * was a growfs failure, just that there still isn't space for
> +		 * new user data after the grow has been run.
> +		 */
> +		error = xfs_fs_reserve_ag_blocks(mp);
> +		if (error == -ENOSPC)
> +			error = 0;
>  	}
> -
> -	/*
> -	 * Reserve AG metadata blocks. ENOSPC here does not mean there was a
> -	 * growfs failure, just that there still isn't space for new user data
> -	 * after the grow has been run.
> -	 */
> -	error = xfs_fs_reserve_ag_blocks(mp);
> -	if (error == -ENOSPC)
> -		error = 0;
>  	return error;
>  
>  out_trans_cancel:
> diff --git a/fs/xfs/xfs_trans.c b/fs/xfs/xfs_trans.c
> index 44f72c09c203..d047f5f26cc0 100644
> --- a/fs/xfs/xfs_trans.c
> +++ b/fs/xfs/xfs_trans.c
> @@ -434,7 +434,6 @@ xfs_trans_mod_sb(
>  		tp->t_res_frextents_delta += delta;
>  		break;
>  	case XFS_TRANS_SB_DBLOCKS:
> -		ASSERT(delta > 0);
>  		tp->t_dblocks_delta += delta;
>  		break;
>  	case XFS_TRANS_SB_AGCOUNT:
> -- 
> 2.27.0
>
Gao Xiang March 22, 2021, 12:07 p.m. UTC | #2
On Mon, Mar 22, 2021 at 07:30:40AM -0400, Brian Foster wrote:
> On Fri, Mar 05, 2021 at 10:57:02AM +0800, Gao Xiang wrote:
> > As the first step of shrinking, this attempts to enable shrinking
> > unused space in the last allocation group by fixing up freespace
> > btree, agi, agf and adjusting super block and use a helper
> > xfs_ag_shrink_space() to fixup the last AG.
> > 
> > This can be all done in one transaction for now, so I think no
> > additional protection is needed.
> > 
> > Reviewed-by: Darrick J. Wong <djwong@kernel.org>
> > Signed-off-by: Gao Xiang <hsiangkao@redhat.com>
> > ---
> >  fs/xfs/xfs_fsops.c | 88 ++++++++++++++++++++++++++++------------------
> >  fs/xfs/xfs_trans.c |  1 -
> >  2 files changed, 53 insertions(+), 36 deletions(-)
> > 
> > diff --git a/fs/xfs/xfs_fsops.c b/fs/xfs/xfs_fsops.c
> > index fc9e799b2ae3..71cba61a451c 100644
> > --- a/fs/xfs/xfs_fsops.c
> > +++ b/fs/xfs/xfs_fsops.c
> > @@ -91,23 +91,28 @@ xfs_growfs_data_private(
> >  	xfs_agnumber_t		nagcount;
> >  	xfs_agnumber_t		nagimax = 0;
> >  	xfs_rfsblock_t		nb, nb_div, nb_mod;
> > -	xfs_rfsblock_t		delta;
> > +	int64_t			delta;
> >  	bool			lastag_resetagres;
> >  	xfs_agnumber_t		oagcount;
> >  	struct xfs_trans	*tp;
> >  	struct aghdr_init_data	id = {};
> >  
> >  	nb = in->newblocks;
> > -	if (nb < mp->m_sb.sb_dblocks)
> > -		return -EINVAL;
> > -	if ((error = xfs_sb_validate_fsb_count(&mp->m_sb, nb)))
> > +	if (nb == mp->m_sb.sb_dblocks)
> > +		return 0;
> 
> It looks like the caller already does this check.

yeah, will remove it. Thanks for pointing out.

> 
> > +
> > +	error = xfs_sb_validate_fsb_count(&mp->m_sb, nb);
> > +	if (error)
> >  		return error;
> > -	error = xfs_buf_read_uncached(mp->m_ddev_targp,
> > +
> > +	if (nb > mp->m_sb.sb_dblocks) {
> > +		error = xfs_buf_read_uncached(mp->m_ddev_targp,
> >  				XFS_FSB_TO_BB(mp, nb) - XFS_FSS_TO_BB(mp, 1),
> >  				XFS_FSS_TO_BB(mp, 1), 0, &bp, NULL);
> > -	if (error)
> > -		return error;
> > -	xfs_buf_relse(bp);
> > +		if (error)
> > +			return error;
> > +		xfs_buf_relse(bp);
> > +	}
> >  
> >  	nb_div = nb;
> >  	nb_mod = do_div(nb_div, mp->m_sb.sb_agblocks);
> > @@ -115,10 +120,15 @@ xfs_growfs_data_private(
> >  	if (nb_mod && nb_mod < XFS_MIN_AG_BLOCKS) {
> >  		nagcount--;
> >  		nb = (xfs_rfsblock_t)nagcount * mp->m_sb.sb_agblocks;
> > -		if (nb < mp->m_sb.sb_dblocks)
> > -			return -EINVAL;
> >  	}
> >  	delta = nb - mp->m_sb.sb_dblocks;
> > +	/*
> > +	 * XFS doesn't really support single-AG filesystems, so do not
> > +	 * permit callers to remove the filesystem's second and last AG.
> > +	 */
> > +	if (delta < 0 && nagcount < 2)
> > +		return -EINVAL;
> > +
> 
> What if the filesystem is already single AG? Unless I'm missing
> something, we already have a check a bit further down that prevents
> removal of AGs in the first place.

I think it tends to forbid (return -EINVAL) shrinking the filesystem with
a single AG only? Am I missing something?

Thanks,
Gao Xiang

> 
> Otherwise looks reasonable..
> 
> Brian
> 
> >  	oagcount = mp->m_sb.sb_agcount;
> >  
> >  	/* allocate the new per-ag structures */
> > @@ -126,15 +136,22 @@ xfs_growfs_data_private(
> >  		error = xfs_initialize_perag(mp, nagcount, &nagimax);
> >  		if (error)
> >  			return error;
> > +	} else if (nagcount < oagcount) {
> > +		/* TODO: shrinking the entire AGs hasn't yet completed */
> > +		return -EINVAL;
> >  	}
> >  
> >  	error = xfs_trans_alloc(mp, &M_RES(mp)->tr_growdata,
> > -			XFS_GROWFS_SPACE_RES(mp), 0, XFS_TRANS_RESERVE, &tp);
> > +			(delta > 0 ? XFS_GROWFS_SPACE_RES(mp) : -delta), 0,
> > +			XFS_TRANS_RESERVE, &tp);
> >  	if (error)
> >  		return error;
> >  
> > -	error = xfs_resizefs_init_new_ags(tp, &id, oagcount, nagcount,
> > -					  delta, &lastag_resetagres);
> > +	if (delta > 0)
> > +		error = xfs_resizefs_init_new_ags(tp, &id, oagcount, nagcount,
> > +						  delta, &lastag_resetagres);
> > +	else
> > +		error = xfs_ag_shrink_space(mp, &tp, nagcount - 1, -delta);
> >  	if (error)
> >  		goto out_trans_cancel;
> >  
> > @@ -145,7 +162,7 @@ xfs_growfs_data_private(
> >  	 */
> >  	if (nagcount > oagcount)
> >  		xfs_trans_mod_sb(tp, XFS_TRANS_SB_AGCOUNT, nagcount - oagcount);
> > -	if (delta > 0)
> > +	if (delta)
> >  		xfs_trans_mod_sb(tp, XFS_TRANS_SB_DBLOCKS, delta);
> >  	if (id.nfree)
> >  		xfs_trans_mod_sb(tp, XFS_TRANS_SB_FDBLOCKS, id.nfree);
> > @@ -169,28 +186,29 @@ xfs_growfs_data_private(
> >  	xfs_set_low_space_thresholds(mp);
> >  	mp->m_alloc_set_aside = xfs_alloc_set_aside(mp);
> >  
> > -	/*
> > -	 * If we expanded the last AG, free the per-AG reservation
> > -	 * so we can reinitialize it with the new size.
> > -	 */
> > -	if (lastag_resetagres) {
> > -		struct xfs_perag	*pag;
> > -
> > -		pag = xfs_perag_get(mp, id.agno);
> > -		error = xfs_ag_resv_free(pag);
> > -		xfs_perag_put(pag);
> > -		if (error)
> > -			return error;
> > +	if (delta > 0) {
> > +		/*
> > +		 * If we expanded the last AG, free the per-AG reservation
> > +		 * so we can reinitialize it with the new size.
> > +		 */
> > +		if (lastag_resetagres) {
> > +			struct xfs_perag	*pag;
> > +
> > +			pag = xfs_perag_get(mp, id.agno);
> > +			error = xfs_ag_resv_free(pag);
> > +			xfs_perag_put(pag);
> > +			if (error)
> > +				return error;
> > +		}
> > +		/*
> > +		 * Reserve AG metadata blocks. ENOSPC here does not mean there
> > +		 * was a growfs failure, just that there still isn't space for
> > +		 * new user data after the grow has been run.
> > +		 */
> > +		error = xfs_fs_reserve_ag_blocks(mp);
> > +		if (error == -ENOSPC)
> > +			error = 0;
> >  	}
> > -
> > -	/*
> > -	 * Reserve AG metadata blocks. ENOSPC here does not mean there was a
> > -	 * growfs failure, just that there still isn't space for new user data
> > -	 * after the grow has been run.
> > -	 */
> > -	error = xfs_fs_reserve_ag_blocks(mp);
> > -	if (error == -ENOSPC)
> > -		error = 0;
> >  	return error;
> >  
> >  out_trans_cancel:
> > diff --git a/fs/xfs/xfs_trans.c b/fs/xfs/xfs_trans.c
> > index 44f72c09c203..d047f5f26cc0 100644
> > --- a/fs/xfs/xfs_trans.c
> > +++ b/fs/xfs/xfs_trans.c
> > @@ -434,7 +434,6 @@ xfs_trans_mod_sb(
> >  		tp->t_res_frextents_delta += delta;
> >  		break;
> >  	case XFS_TRANS_SB_DBLOCKS:
> > -		ASSERT(delta > 0);
> >  		tp->t_dblocks_delta += delta;
> >  		break;
> >  	case XFS_TRANS_SB_AGCOUNT:
> > -- 
> > 2.27.0
> > 
>
Brian Foster March 22, 2021, 12:26 p.m. UTC | #3
On Mon, Mar 22, 2021 at 08:07:22PM +0800, Gao Xiang wrote:
> On Mon, Mar 22, 2021 at 07:30:40AM -0400, Brian Foster wrote:
> > On Fri, Mar 05, 2021 at 10:57:02AM +0800, Gao Xiang wrote:
> > > As the first step of shrinking, this attempts to enable shrinking
> > > unused space in the last allocation group by fixing up freespace
> > > btree, agi, agf and adjusting super block and use a helper
> > > xfs_ag_shrink_space() to fixup the last AG.
> > > 
> > > This can be all done in one transaction for now, so I think no
> > > additional protection is needed.
> > > 
> > > Reviewed-by: Darrick J. Wong <djwong@kernel.org>
> > > Signed-off-by: Gao Xiang <hsiangkao@redhat.com>
> > > ---
> > >  fs/xfs/xfs_fsops.c | 88 ++++++++++++++++++++++++++++------------------
> > >  fs/xfs/xfs_trans.c |  1 -
> > >  2 files changed, 53 insertions(+), 36 deletions(-)
> > > 
> > > diff --git a/fs/xfs/xfs_fsops.c b/fs/xfs/xfs_fsops.c
> > > index fc9e799b2ae3..71cba61a451c 100644
> > > --- a/fs/xfs/xfs_fsops.c
> > > +++ b/fs/xfs/xfs_fsops.c
...
> > > @@ -115,10 +120,15 @@ xfs_growfs_data_private(
> > >  	if (nb_mod && nb_mod < XFS_MIN_AG_BLOCKS) {
> > >  		nagcount--;
> > >  		nb = (xfs_rfsblock_t)nagcount * mp->m_sb.sb_agblocks;
> > > -		if (nb < mp->m_sb.sb_dblocks)
> > > -			return -EINVAL;
> > >  	}
> > >  	delta = nb - mp->m_sb.sb_dblocks;
> > > +	/*
> > > +	 * XFS doesn't really support single-AG filesystems, so do not
> > > +	 * permit callers to remove the filesystem's second and last AG.
> > > +	 */
> > > +	if (delta < 0 && nagcount < 2)
> > > +		return -EINVAL;
> > > +
> > 
> > What if the filesystem is already single AG? Unless I'm missing
> > something, we already have a check a bit further down that prevents
> > removal of AGs in the first place.
> 
> I think it tends to forbid (return -EINVAL) shrinking the filesystem with
> a single AG only? Am I missing something?
> 

My assumption was this check means one can't shrink a filesystem that is
already agcount == 1. The comment refers to preventing shrink from
causing an agcount == 1 fs. What is the intent?

Brian

> Thanks,
> Gao Xiang
> 
> > 
> > Otherwise looks reasonable..
> > 
> > Brian
> > 
> > >  	oagcount = mp->m_sb.sb_agcount;
> > >  
> > >  	/* allocate the new per-ag structures */
> > > @@ -126,15 +136,22 @@ xfs_growfs_data_private(
> > >  		error = xfs_initialize_perag(mp, nagcount, &nagimax);
> > >  		if (error)
> > >  			return error;
> > > +	} else if (nagcount < oagcount) {
> > > +		/* TODO: shrinking the entire AGs hasn't yet completed */
> > > +		return -EINVAL;
> > >  	}
> > >  
> > >  	error = xfs_trans_alloc(mp, &M_RES(mp)->tr_growdata,
> > > -			XFS_GROWFS_SPACE_RES(mp), 0, XFS_TRANS_RESERVE, &tp);
> > > +			(delta > 0 ? XFS_GROWFS_SPACE_RES(mp) : -delta), 0,
> > > +			XFS_TRANS_RESERVE, &tp);
> > >  	if (error)
> > >  		return error;
> > >  
> > > -	error = xfs_resizefs_init_new_ags(tp, &id, oagcount, nagcount,
> > > -					  delta, &lastag_resetagres);
> > > +	if (delta > 0)
> > > +		error = xfs_resizefs_init_new_ags(tp, &id, oagcount, nagcount,
> > > +						  delta, &lastag_resetagres);
> > > +	else
> > > +		error = xfs_ag_shrink_space(mp, &tp, nagcount - 1, -delta);
> > >  	if (error)
> > >  		goto out_trans_cancel;
> > >  
> > > @@ -145,7 +162,7 @@ xfs_growfs_data_private(
> > >  	 */
> > >  	if (nagcount > oagcount)
> > >  		xfs_trans_mod_sb(tp, XFS_TRANS_SB_AGCOUNT, nagcount - oagcount);
> > > -	if (delta > 0)
> > > +	if (delta)
> > >  		xfs_trans_mod_sb(tp, XFS_TRANS_SB_DBLOCKS, delta);
> > >  	if (id.nfree)
> > >  		xfs_trans_mod_sb(tp, XFS_TRANS_SB_FDBLOCKS, id.nfree);
> > > @@ -169,28 +186,29 @@ xfs_growfs_data_private(
> > >  	xfs_set_low_space_thresholds(mp);
> > >  	mp->m_alloc_set_aside = xfs_alloc_set_aside(mp);
> > >  
> > > -	/*
> > > -	 * If we expanded the last AG, free the per-AG reservation
> > > -	 * so we can reinitialize it with the new size.
> > > -	 */
> > > -	if (lastag_resetagres) {
> > > -		struct xfs_perag	*pag;
> > > -
> > > -		pag = xfs_perag_get(mp, id.agno);
> > > -		error = xfs_ag_resv_free(pag);
> > > -		xfs_perag_put(pag);
> > > -		if (error)
> > > -			return error;
> > > +	if (delta > 0) {
> > > +		/*
> > > +		 * If we expanded the last AG, free the per-AG reservation
> > > +		 * so we can reinitialize it with the new size.
> > > +		 */
> > > +		if (lastag_resetagres) {
> > > +			struct xfs_perag	*pag;
> > > +
> > > +			pag = xfs_perag_get(mp, id.agno);
> > > +			error = xfs_ag_resv_free(pag);
> > > +			xfs_perag_put(pag);
> > > +			if (error)
> > > +				return error;
> > > +		}
> > > +		/*
> > > +		 * Reserve AG metadata blocks. ENOSPC here does not mean there
> > > +		 * was a growfs failure, just that there still isn't space for
> > > +		 * new user data after the grow has been run.
> > > +		 */
> > > +		error = xfs_fs_reserve_ag_blocks(mp);
> > > +		if (error == -ENOSPC)
> > > +			error = 0;
> > >  	}
> > > -
> > > -	/*
> > > -	 * Reserve AG metadata blocks. ENOSPC here does not mean there was a
> > > -	 * growfs failure, just that there still isn't space for new user data
> > > -	 * after the grow has been run.
> > > -	 */
> > > -	error = xfs_fs_reserve_ag_blocks(mp);
> > > -	if (error == -ENOSPC)
> > > -		error = 0;
> > >  	return error;
> > >  
> > >  out_trans_cancel:
> > > diff --git a/fs/xfs/xfs_trans.c b/fs/xfs/xfs_trans.c
> > > index 44f72c09c203..d047f5f26cc0 100644
> > > --- a/fs/xfs/xfs_trans.c
> > > +++ b/fs/xfs/xfs_trans.c
> > > @@ -434,7 +434,6 @@ xfs_trans_mod_sb(
> > >  		tp->t_res_frextents_delta += delta;
> > >  		break;
> > >  	case XFS_TRANS_SB_DBLOCKS:
> > > -		ASSERT(delta > 0);
> > >  		tp->t_dblocks_delta += delta;
> > >  		break;
> > >  	case XFS_TRANS_SB_AGCOUNT:
> > > -- 
> > > 2.27.0
> > > 
> > 
>
Gao Xiang March 22, 2021, 12:36 p.m. UTC | #4
On Mon, Mar 22, 2021 at 08:26:41AM -0400, Brian Foster wrote:
> On Mon, Mar 22, 2021 at 08:07:22PM +0800, Gao Xiang wrote:
> > On Mon, Mar 22, 2021 at 07:30:40AM -0400, Brian Foster wrote:
> > > On Fri, Mar 05, 2021 at 10:57:02AM +0800, Gao Xiang wrote:
> > > > As the first step of shrinking, this attempts to enable shrinking
> > > > unused space in the last allocation group by fixing up freespace
> > > > btree, agi, agf and adjusting super block and use a helper
> > > > xfs_ag_shrink_space() to fixup the last AG.
> > > > 
> > > > This can be all done in one transaction for now, so I think no
> > > > additional protection is needed.
> > > > 
> > > > Reviewed-by: Darrick J. Wong <djwong@kernel.org>
> > > > Signed-off-by: Gao Xiang <hsiangkao@redhat.com>
> > > > ---
> > > >  fs/xfs/xfs_fsops.c | 88 ++++++++++++++++++++++++++++------------------
> > > >  fs/xfs/xfs_trans.c |  1 -
> > > >  2 files changed, 53 insertions(+), 36 deletions(-)
> > > > 
> > > > diff --git a/fs/xfs/xfs_fsops.c b/fs/xfs/xfs_fsops.c
> > > > index fc9e799b2ae3..71cba61a451c 100644
> > > > --- a/fs/xfs/xfs_fsops.c
> > > > +++ b/fs/xfs/xfs_fsops.c
> ...
> > > > @@ -115,10 +120,15 @@ xfs_growfs_data_private(
> > > >  	if (nb_mod && nb_mod < XFS_MIN_AG_BLOCKS) {
> > > >  		nagcount--;
> > > >  		nb = (xfs_rfsblock_t)nagcount * mp->m_sb.sb_agblocks;
> > > > -		if (nb < mp->m_sb.sb_dblocks)
> > > > -			return -EINVAL;
> > > >  	}
> > > >  	delta = nb - mp->m_sb.sb_dblocks;
> > > > +	/*
> > > > +	 * XFS doesn't really support single-AG filesystems, so do not
> > > > +	 * permit callers to remove the filesystem's second and last AG.
> > > > +	 */
> > > > +	if (delta < 0 && nagcount < 2)
> > > > +		return -EINVAL;
> > > > +
> > > 
> > > What if the filesystem is already single AG? Unless I'm missing
> > > something, we already have a check a bit further down that prevents
> > > removal of AGs in the first place.
> > 
> > I think it tends to forbid (return -EINVAL) shrinking the filesystem with
> > a single AG only? Am I missing something?
> > 
> 
> My assumption was this check means one can't shrink a filesystem that is
> already agcount == 1. The comment refers to preventing shrink from
> causing an agcount == 1 fs. What is the intent?

I think it means the latter -- preventing shrink from causing an agcount == 1
fs. since nagcount (new agcount) <= 1?

Actually, I'm not good at English, if comments need to be improved, please
kindly point out. Thank you very much!

Thanks,
Gao Xiang

> 
> Brian
> 
> > Thanks,
> > Gao Xiang
> > 
> > > 
> > > Otherwise looks reasonable..
> > > 
> > > Brian
> > > 
> > > >  	oagcount = mp->m_sb.sb_agcount;
> > > >  
> > > >  	/* allocate the new per-ag structures */
> > > > @@ -126,15 +136,22 @@ xfs_growfs_data_private(
> > > >  		error = xfs_initialize_perag(mp, nagcount, &nagimax);
> > > >  		if (error)
> > > >  			return error;
> > > > +	} else if (nagcount < oagcount) {
> > > > +		/* TODO: shrinking the entire AGs hasn't yet completed */
> > > > +		return -EINVAL;
> > > >  	}
> > > >  
> > > >  	error = xfs_trans_alloc(mp, &M_RES(mp)->tr_growdata,
> > > > -			XFS_GROWFS_SPACE_RES(mp), 0, XFS_TRANS_RESERVE, &tp);
> > > > +			(delta > 0 ? XFS_GROWFS_SPACE_RES(mp) : -delta), 0,
> > > > +			XFS_TRANS_RESERVE, &tp);
> > > >  	if (error)
> > > >  		return error;
> > > >  
> > > > -	error = xfs_resizefs_init_new_ags(tp, &id, oagcount, nagcount,
> > > > -					  delta, &lastag_resetagres);
> > > > +	if (delta > 0)
> > > > +		error = xfs_resizefs_init_new_ags(tp, &id, oagcount, nagcount,
> > > > +						  delta, &lastag_resetagres);
> > > > +	else
> > > > +		error = xfs_ag_shrink_space(mp, &tp, nagcount - 1, -delta);
> > > >  	if (error)
> > > >  		goto out_trans_cancel;
> > > >  
> > > > @@ -145,7 +162,7 @@ xfs_growfs_data_private(
> > > >  	 */
> > > >  	if (nagcount > oagcount)
> > > >  		xfs_trans_mod_sb(tp, XFS_TRANS_SB_AGCOUNT, nagcount - oagcount);
> > > > -	if (delta > 0)
> > > > +	if (delta)
> > > >  		xfs_trans_mod_sb(tp, XFS_TRANS_SB_DBLOCKS, delta);
> > > >  	if (id.nfree)
> > > >  		xfs_trans_mod_sb(tp, XFS_TRANS_SB_FDBLOCKS, id.nfree);
> > > > @@ -169,28 +186,29 @@ xfs_growfs_data_private(
> > > >  	xfs_set_low_space_thresholds(mp);
> > > >  	mp->m_alloc_set_aside = xfs_alloc_set_aside(mp);
> > > >  
> > > > -	/*
> > > > -	 * If we expanded the last AG, free the per-AG reservation
> > > > -	 * so we can reinitialize it with the new size.
> > > > -	 */
> > > > -	if (lastag_resetagres) {
> > > > -		struct xfs_perag	*pag;
> > > > -
> > > > -		pag = xfs_perag_get(mp, id.agno);
> > > > -		error = xfs_ag_resv_free(pag);
> > > > -		xfs_perag_put(pag);
> > > > -		if (error)
> > > > -			return error;
> > > > +	if (delta > 0) {
> > > > +		/*
> > > > +		 * If we expanded the last AG, free the per-AG reservation
> > > > +		 * so we can reinitialize it with the new size.
> > > > +		 */
> > > > +		if (lastag_resetagres) {
> > > > +			struct xfs_perag	*pag;
> > > > +
> > > > +			pag = xfs_perag_get(mp, id.agno);
> > > > +			error = xfs_ag_resv_free(pag);
> > > > +			xfs_perag_put(pag);
> > > > +			if (error)
> > > > +				return error;
> > > > +		}
> > > > +		/*
> > > > +		 * Reserve AG metadata blocks. ENOSPC here does not mean there
> > > > +		 * was a growfs failure, just that there still isn't space for
> > > > +		 * new user data after the grow has been run.
> > > > +		 */
> > > > +		error = xfs_fs_reserve_ag_blocks(mp);
> > > > +		if (error == -ENOSPC)
> > > > +			error = 0;
> > > >  	}
> > > > -
> > > > -	/*
> > > > -	 * Reserve AG metadata blocks. ENOSPC here does not mean there was a
> > > > -	 * growfs failure, just that there still isn't space for new user data
> > > > -	 * after the grow has been run.
> > > > -	 */
> > > > -	error = xfs_fs_reserve_ag_blocks(mp);
> > > > -	if (error == -ENOSPC)
> > > > -		error = 0;
> > > >  	return error;
> > > >  
> > > >  out_trans_cancel:
> > > > diff --git a/fs/xfs/xfs_trans.c b/fs/xfs/xfs_trans.c
> > > > index 44f72c09c203..d047f5f26cc0 100644
> > > > --- a/fs/xfs/xfs_trans.c
> > > > +++ b/fs/xfs/xfs_trans.c
> > > > @@ -434,7 +434,6 @@ xfs_trans_mod_sb(
> > > >  		tp->t_res_frextents_delta += delta;
> > > >  		break;
> > > >  	case XFS_TRANS_SB_DBLOCKS:
> > > > -		ASSERT(delta > 0);
> > > >  		tp->t_dblocks_delta += delta;
> > > >  		break;
> > > >  	case XFS_TRANS_SB_AGCOUNT:
> > > > -- 
> > > > 2.27.0
> > > > 
> > > 
> > 
>
Brian Foster March 22, 2021, 12:43 p.m. UTC | #5
On Mon, Mar 22, 2021 at 08:36:52PM +0800, Gao Xiang wrote:
> On Mon, Mar 22, 2021 at 08:26:41AM -0400, Brian Foster wrote:
> > On Mon, Mar 22, 2021 at 08:07:22PM +0800, Gao Xiang wrote:
> > > On Mon, Mar 22, 2021 at 07:30:40AM -0400, Brian Foster wrote:
> > > > On Fri, Mar 05, 2021 at 10:57:02AM +0800, Gao Xiang wrote:
> > > > > As the first step of shrinking, this attempts to enable shrinking
> > > > > unused space in the last allocation group by fixing up freespace
> > > > > btree, agi, agf and adjusting super block and use a helper
> > > > > xfs_ag_shrink_space() to fixup the last AG.
> > > > > 
> > > > > This can be all done in one transaction for now, so I think no
> > > > > additional protection is needed.
> > > > > 
> > > > > Reviewed-by: Darrick J. Wong <djwong@kernel.org>
> > > > > Signed-off-by: Gao Xiang <hsiangkao@redhat.com>
> > > > > ---
> > > > >  fs/xfs/xfs_fsops.c | 88 ++++++++++++++++++++++++++++------------------
> > > > >  fs/xfs/xfs_trans.c |  1 -
> > > > >  2 files changed, 53 insertions(+), 36 deletions(-)
> > > > > 
> > > > > diff --git a/fs/xfs/xfs_fsops.c b/fs/xfs/xfs_fsops.c
> > > > > index fc9e799b2ae3..71cba61a451c 100644
> > > > > --- a/fs/xfs/xfs_fsops.c
> > > > > +++ b/fs/xfs/xfs_fsops.c
> > ...
> > > > > @@ -115,10 +120,15 @@ xfs_growfs_data_private(
> > > > >  	if (nb_mod && nb_mod < XFS_MIN_AG_BLOCKS) {
> > > > >  		nagcount--;
> > > > >  		nb = (xfs_rfsblock_t)nagcount * mp->m_sb.sb_agblocks;
> > > > > -		if (nb < mp->m_sb.sb_dblocks)
> > > > > -			return -EINVAL;
> > > > >  	}
> > > > >  	delta = nb - mp->m_sb.sb_dblocks;
> > > > > +	/*
> > > > > +	 * XFS doesn't really support single-AG filesystems, so do not
> > > > > +	 * permit callers to remove the filesystem's second and last AG.
> > > > > +	 */
> > > > > +	if (delta < 0 && nagcount < 2)
> > > > > +		return -EINVAL;
> > > > > +
> > > > 
> > > > What if the filesystem is already single AG? Unless I'm missing
> > > > something, we already have a check a bit further down that prevents
> > > > removal of AGs in the first place.
> > > 
> > > I think it tends to forbid (return -EINVAL) shrinking the filesystem with
> > > a single AG only? Am I missing something?
> > > 
> > 
> > My assumption was this check means one can't shrink a filesystem that is
> > already agcount == 1. The comment refers to preventing shrink from
> > causing an agcount == 1 fs. What is the intent?
> 
> I think it means the latter -- preventing shrink from causing an agcount == 1
> fs. since nagcount (new agcount) <= 1?
> 

Right, so that leads to my question... does this check also fail a
shrink on an fs that is already agcount == 1? If so, why? I know
technically it's not a supported configuration, but mkfs allows it.

Brian

> Actually, I'm not good at English, if comments need to be improved, please
> kindly point out. Thank you very much!
> 
> Thanks,
> Gao Xiang
> 
> > 
> > Brian
> > 
> > > Thanks,
> > > Gao Xiang
> > > 
> > > > 
> > > > Otherwise looks reasonable..
> > > > 
> > > > Brian
> > > > 
> > > > >  	oagcount = mp->m_sb.sb_agcount;
> > > > >  
> > > > >  	/* allocate the new per-ag structures */
> > > > > @@ -126,15 +136,22 @@ xfs_growfs_data_private(
> > > > >  		error = xfs_initialize_perag(mp, nagcount, &nagimax);
> > > > >  		if (error)
> > > > >  			return error;
> > > > > +	} else if (nagcount < oagcount) {
> > > > > +		/* TODO: shrinking the entire AGs hasn't yet completed */
> > > > > +		return -EINVAL;
> > > > >  	}
> > > > >  
> > > > >  	error = xfs_trans_alloc(mp, &M_RES(mp)->tr_growdata,
> > > > > -			XFS_GROWFS_SPACE_RES(mp), 0, XFS_TRANS_RESERVE, &tp);
> > > > > +			(delta > 0 ? XFS_GROWFS_SPACE_RES(mp) : -delta), 0,
> > > > > +			XFS_TRANS_RESERVE, &tp);
> > > > >  	if (error)
> > > > >  		return error;
> > > > >  
> > > > > -	error = xfs_resizefs_init_new_ags(tp, &id, oagcount, nagcount,
> > > > > -					  delta, &lastag_resetagres);
> > > > > +	if (delta > 0)
> > > > > +		error = xfs_resizefs_init_new_ags(tp, &id, oagcount, nagcount,
> > > > > +						  delta, &lastag_resetagres);
> > > > > +	else
> > > > > +		error = xfs_ag_shrink_space(mp, &tp, nagcount - 1, -delta);
> > > > >  	if (error)
> > > > >  		goto out_trans_cancel;
> > > > >  
> > > > > @@ -145,7 +162,7 @@ xfs_growfs_data_private(
> > > > >  	 */
> > > > >  	if (nagcount > oagcount)
> > > > >  		xfs_trans_mod_sb(tp, XFS_TRANS_SB_AGCOUNT, nagcount - oagcount);
> > > > > -	if (delta > 0)
> > > > > +	if (delta)
> > > > >  		xfs_trans_mod_sb(tp, XFS_TRANS_SB_DBLOCKS, delta);
> > > > >  	if (id.nfree)
> > > > >  		xfs_trans_mod_sb(tp, XFS_TRANS_SB_FDBLOCKS, id.nfree);
> > > > > @@ -169,28 +186,29 @@ xfs_growfs_data_private(
> > > > >  	xfs_set_low_space_thresholds(mp);
> > > > >  	mp->m_alloc_set_aside = xfs_alloc_set_aside(mp);
> > > > >  
> > > > > -	/*
> > > > > -	 * If we expanded the last AG, free the per-AG reservation
> > > > > -	 * so we can reinitialize it with the new size.
> > > > > -	 */
> > > > > -	if (lastag_resetagres) {
> > > > > -		struct xfs_perag	*pag;
> > > > > -
> > > > > -		pag = xfs_perag_get(mp, id.agno);
> > > > > -		error = xfs_ag_resv_free(pag);
> > > > > -		xfs_perag_put(pag);
> > > > > -		if (error)
> > > > > -			return error;
> > > > > +	if (delta > 0) {
> > > > > +		/*
> > > > > +		 * If we expanded the last AG, free the per-AG reservation
> > > > > +		 * so we can reinitialize it with the new size.
> > > > > +		 */
> > > > > +		if (lastag_resetagres) {
> > > > > +			struct xfs_perag	*pag;
> > > > > +
> > > > > +			pag = xfs_perag_get(mp, id.agno);
> > > > > +			error = xfs_ag_resv_free(pag);
> > > > > +			xfs_perag_put(pag);
> > > > > +			if (error)
> > > > > +				return error;
> > > > > +		}
> > > > > +		/*
> > > > > +		 * Reserve AG metadata blocks. ENOSPC here does not mean there
> > > > > +		 * was a growfs failure, just that there still isn't space for
> > > > > +		 * new user data after the grow has been run.
> > > > > +		 */
> > > > > +		error = xfs_fs_reserve_ag_blocks(mp);
> > > > > +		if (error == -ENOSPC)
> > > > > +			error = 0;
> > > > >  	}
> > > > > -
> > > > > -	/*
> > > > > -	 * Reserve AG metadata blocks. ENOSPC here does not mean there was a
> > > > > -	 * growfs failure, just that there still isn't space for new user data
> > > > > -	 * after the grow has been run.
> > > > > -	 */
> > > > > -	error = xfs_fs_reserve_ag_blocks(mp);
> > > > > -	if (error == -ENOSPC)
> > > > > -		error = 0;
> > > > >  	return error;
> > > > >  
> > > > >  out_trans_cancel:
> > > > > diff --git a/fs/xfs/xfs_trans.c b/fs/xfs/xfs_trans.c
> > > > > index 44f72c09c203..d047f5f26cc0 100644
> > > > > --- a/fs/xfs/xfs_trans.c
> > > > > +++ b/fs/xfs/xfs_trans.c
> > > > > @@ -434,7 +434,6 @@ xfs_trans_mod_sb(
> > > > >  		tp->t_res_frextents_delta += delta;
> > > > >  		break;
> > > > >  	case XFS_TRANS_SB_DBLOCKS:
> > > > > -		ASSERT(delta > 0);
> > > > >  		tp->t_dblocks_delta += delta;
> > > > >  		break;
> > > > >  	case XFS_TRANS_SB_AGCOUNT:
> > > > > -- 
> > > > > 2.27.0
> > > > > 
> > > > 
> > > 
> > 
>
Gao Xiang March 22, 2021, 12:50 p.m. UTC | #6
On Mon, Mar 22, 2021 at 08:43:19AM -0400, Brian Foster wrote:
> On Mon, Mar 22, 2021 at 08:36:52PM +0800, Gao Xiang wrote:
> > On Mon, Mar 22, 2021 at 08:26:41AM -0400, Brian Foster wrote:
> > > On Mon, Mar 22, 2021 at 08:07:22PM +0800, Gao Xiang wrote:
> > > > On Mon, Mar 22, 2021 at 07:30:40AM -0400, Brian Foster wrote:
> > > > > On Fri, Mar 05, 2021 at 10:57:02AM +0800, Gao Xiang wrote:
> > > > > > As the first step of shrinking, this attempts to enable shrinking
> > > > > > unused space in the last allocation group by fixing up freespace
> > > > > > btree, agi, agf and adjusting super block and use a helper
> > > > > > xfs_ag_shrink_space() to fixup the last AG.
> > > > > > 
> > > > > > This can be all done in one transaction for now, so I think no
> > > > > > additional protection is needed.
> > > > > > 
> > > > > > Reviewed-by: Darrick J. Wong <djwong@kernel.org>
> > > > > > Signed-off-by: Gao Xiang <hsiangkao@redhat.com>
> > > > > > ---
> > > > > >  fs/xfs/xfs_fsops.c | 88 ++++++++++++++++++++++++++++------------------
> > > > > >  fs/xfs/xfs_trans.c |  1 -
> > > > > >  2 files changed, 53 insertions(+), 36 deletions(-)
> > > > > > 
> > > > > > diff --git a/fs/xfs/xfs_fsops.c b/fs/xfs/xfs_fsops.c
> > > > > > index fc9e799b2ae3..71cba61a451c 100644
> > > > > > --- a/fs/xfs/xfs_fsops.c
> > > > > > +++ b/fs/xfs/xfs_fsops.c
> > > ...
> > > > > > @@ -115,10 +120,15 @@ xfs_growfs_data_private(
> > > > > >  	if (nb_mod && nb_mod < XFS_MIN_AG_BLOCKS) {
> > > > > >  		nagcount--;
> > > > > >  		nb = (xfs_rfsblock_t)nagcount * mp->m_sb.sb_agblocks;
> > > > > > -		if (nb < mp->m_sb.sb_dblocks)
> > > > > > -			return -EINVAL;
> > > > > >  	}
> > > > > >  	delta = nb - mp->m_sb.sb_dblocks;
> > > > > > +	/*
> > > > > > +	 * XFS doesn't really support single-AG filesystems, so do not
> > > > > > +	 * permit callers to remove the filesystem's second and last AG.
> > > > > > +	 */
> > > > > > +	if (delta < 0 && nagcount < 2)
> > > > > > +		return -EINVAL;
> > > > > > +
> > > > > 
> > > > > What if the filesystem is already single AG? Unless I'm missing
> > > > > something, we already have a check a bit further down that prevents
> > > > > removal of AGs in the first place.
> > > > 
> > > > I think it tends to forbid (return -EINVAL) shrinking the filesystem with
> > > > a single AG only? Am I missing something?
> > > > 
> > > 
> > > My assumption was this check means one can't shrink a filesystem that is
> > > already agcount == 1. The comment refers to preventing shrink from
> > > causing an agcount == 1 fs. What is the intent?
> > 
> > I think it means the latter -- preventing shrink from causing an agcount == 1
> > fs. since nagcount (new agcount) <= 1?
> > 
> 
> Right, so that leads to my question... does this check also fail a
> shrink on an fs that is already agcount == 1? If so, why? I know
> technically it's not a supported configuration, but mkfs allows it.

Ah, I'm not sure if Darrick would like to forbid agcount == 1 shrinking
functionitity completely, see the previous comment:
https://lore.kernel.org/r/20201014160633.GD9832@magnolia/

(please ignore the modification at that time, since it was buggy...)

Thanks,
Gao Xiang

> 
> Brian
> 
> > Actually, I'm not good at English, if comments need to be improved, please
> > kindly point out. Thank you very much!
> > 
> > Thanks,
> > Gao Xiang
> > 
> > > 
> > > Brian
> > > 
> > > > Thanks,
> > > > Gao Xiang
> > > > 
> > > > > 
> > > > > Otherwise looks reasonable..
> > > > > 
> > > > > Brian
> > > > > 
> > > > > >  	oagcount = mp->m_sb.sb_agcount;
> > > > > >  
> > > > > >  	/* allocate the new per-ag structures */
> > > > > > @@ -126,15 +136,22 @@ xfs_growfs_data_private(
> > > > > >  		error = xfs_initialize_perag(mp, nagcount, &nagimax);
> > > > > >  		if (error)
> > > > > >  			return error;
> > > > > > +	} else if (nagcount < oagcount) {
> > > > > > +		/* TODO: shrinking the entire AGs hasn't yet completed */
> > > > > > +		return -EINVAL;
> > > > > >  	}
> > > > > >  
> > > > > >  	error = xfs_trans_alloc(mp, &M_RES(mp)->tr_growdata,
> > > > > > -			XFS_GROWFS_SPACE_RES(mp), 0, XFS_TRANS_RESERVE, &tp);
> > > > > > +			(delta > 0 ? XFS_GROWFS_SPACE_RES(mp) : -delta), 0,
> > > > > > +			XFS_TRANS_RESERVE, &tp);
> > > > > >  	if (error)
> > > > > >  		return error;
> > > > > >  
> > > > > > -	error = xfs_resizefs_init_new_ags(tp, &id, oagcount, nagcount,
> > > > > > -					  delta, &lastag_resetagres);
> > > > > > +	if (delta > 0)
> > > > > > +		error = xfs_resizefs_init_new_ags(tp, &id, oagcount, nagcount,
> > > > > > +						  delta, &lastag_resetagres);
> > > > > > +	else
> > > > > > +		error = xfs_ag_shrink_space(mp, &tp, nagcount - 1, -delta);
> > > > > >  	if (error)
> > > > > >  		goto out_trans_cancel;
> > > > > >  
> > > > > > @@ -145,7 +162,7 @@ xfs_growfs_data_private(
> > > > > >  	 */
> > > > > >  	if (nagcount > oagcount)
> > > > > >  		xfs_trans_mod_sb(tp, XFS_TRANS_SB_AGCOUNT, nagcount - oagcount);
> > > > > > -	if (delta > 0)
> > > > > > +	if (delta)
> > > > > >  		xfs_trans_mod_sb(tp, XFS_TRANS_SB_DBLOCKS, delta);
> > > > > >  	if (id.nfree)
> > > > > >  		xfs_trans_mod_sb(tp, XFS_TRANS_SB_FDBLOCKS, id.nfree);
> > > > > > @@ -169,28 +186,29 @@ xfs_growfs_data_private(
> > > > > >  	xfs_set_low_space_thresholds(mp);
> > > > > >  	mp->m_alloc_set_aside = xfs_alloc_set_aside(mp);
> > > > > >  
> > > > > > -	/*
> > > > > > -	 * If we expanded the last AG, free the per-AG reservation
> > > > > > -	 * so we can reinitialize it with the new size.
> > > > > > -	 */
> > > > > > -	if (lastag_resetagres) {
> > > > > > -		struct xfs_perag	*pag;
> > > > > > -
> > > > > > -		pag = xfs_perag_get(mp, id.agno);
> > > > > > -		error = xfs_ag_resv_free(pag);
> > > > > > -		xfs_perag_put(pag);
> > > > > > -		if (error)
> > > > > > -			return error;
> > > > > > +	if (delta > 0) {
> > > > > > +		/*
> > > > > > +		 * If we expanded the last AG, free the per-AG reservation
> > > > > > +		 * so we can reinitialize it with the new size.
> > > > > > +		 */
> > > > > > +		if (lastag_resetagres) {
> > > > > > +			struct xfs_perag	*pag;
> > > > > > +
> > > > > > +			pag = xfs_perag_get(mp, id.agno);
> > > > > > +			error = xfs_ag_resv_free(pag);
> > > > > > +			xfs_perag_put(pag);
> > > > > > +			if (error)
> > > > > > +				return error;
> > > > > > +		}
> > > > > > +		/*
> > > > > > +		 * Reserve AG metadata blocks. ENOSPC here does not mean there
> > > > > > +		 * was a growfs failure, just that there still isn't space for
> > > > > > +		 * new user data after the grow has been run.
> > > > > > +		 */
> > > > > > +		error = xfs_fs_reserve_ag_blocks(mp);
> > > > > > +		if (error == -ENOSPC)
> > > > > > +			error = 0;
> > > > > >  	}
> > > > > > -
> > > > > > -	/*
> > > > > > -	 * Reserve AG metadata blocks. ENOSPC here does not mean there was a
> > > > > > -	 * growfs failure, just that there still isn't space for new user data
> > > > > > -	 * after the grow has been run.
> > > > > > -	 */
> > > > > > -	error = xfs_fs_reserve_ag_blocks(mp);
> > > > > > -	if (error == -ENOSPC)
> > > > > > -		error = 0;
> > > > > >  	return error;
> > > > > >  
> > > > > >  out_trans_cancel:
> > > > > > diff --git a/fs/xfs/xfs_trans.c b/fs/xfs/xfs_trans.c
> > > > > > index 44f72c09c203..d047f5f26cc0 100644
> > > > > > --- a/fs/xfs/xfs_trans.c
> > > > > > +++ b/fs/xfs/xfs_trans.c
> > > > > > @@ -434,7 +434,6 @@ xfs_trans_mod_sb(
> > > > > >  		tp->t_res_frextents_delta += delta;
> > > > > >  		break;
> > > > > >  	case XFS_TRANS_SB_DBLOCKS:
> > > > > > -		ASSERT(delta > 0);
> > > > > >  		tp->t_dblocks_delta += delta;
> > > > > >  		break;
> > > > > >  	case XFS_TRANS_SB_AGCOUNT:
> > > > > > -- 
> > > > > > 2.27.0
> > > > > > 
> > > > > 
> > > > 
> > > 
> > 
>
Darrick J. Wong March 22, 2021, 4:42 p.m. UTC | #7
On Mon, Mar 22, 2021 at 08:50:28PM +0800, Gao Xiang wrote:
> On Mon, Mar 22, 2021 at 08:43:19AM -0400, Brian Foster wrote:
> > On Mon, Mar 22, 2021 at 08:36:52PM +0800, Gao Xiang wrote:
> > > On Mon, Mar 22, 2021 at 08:26:41AM -0400, Brian Foster wrote:
> > > > On Mon, Mar 22, 2021 at 08:07:22PM +0800, Gao Xiang wrote:
> > > > > On Mon, Mar 22, 2021 at 07:30:40AM -0400, Brian Foster wrote:
> > > > > > On Fri, Mar 05, 2021 at 10:57:02AM +0800, Gao Xiang wrote:
> > > > > > > As the first step of shrinking, this attempts to enable shrinking
> > > > > > > unused space in the last allocation group by fixing up freespace
> > > > > > > btree, agi, agf and adjusting super block and use a helper
> > > > > > > xfs_ag_shrink_space() to fixup the last AG.
> > > > > > > 
> > > > > > > This can be all done in one transaction for now, so I think no
> > > > > > > additional protection is needed.
> > > > > > > 
> > > > > > > Reviewed-by: Darrick J. Wong <djwong@kernel.org>
> > > > > > > Signed-off-by: Gao Xiang <hsiangkao@redhat.com>
> > > > > > > ---
> > > > > > >  fs/xfs/xfs_fsops.c | 88 ++++++++++++++++++++++++++++------------------
> > > > > > >  fs/xfs/xfs_trans.c |  1 -
> > > > > > >  2 files changed, 53 insertions(+), 36 deletions(-)
> > > > > > > 
> > > > > > > diff --git a/fs/xfs/xfs_fsops.c b/fs/xfs/xfs_fsops.c
> > > > > > > index fc9e799b2ae3..71cba61a451c 100644
> > > > > > > --- a/fs/xfs/xfs_fsops.c
> > > > > > > +++ b/fs/xfs/xfs_fsops.c
> > > > ...
> > > > > > > @@ -115,10 +120,15 @@ xfs_growfs_data_private(
> > > > > > >  	if (nb_mod && nb_mod < XFS_MIN_AG_BLOCKS) {
> > > > > > >  		nagcount--;
> > > > > > >  		nb = (xfs_rfsblock_t)nagcount * mp->m_sb.sb_agblocks;
> > > > > > > -		if (nb < mp->m_sb.sb_dblocks)
> > > > > > > -			return -EINVAL;
> > > > > > >  	}
> > > > > > >  	delta = nb - mp->m_sb.sb_dblocks;
> > > > > > > +	/*
> > > > > > > +	 * XFS doesn't really support single-AG filesystems, so do not
> > > > > > > +	 * permit callers to remove the filesystem's second and last AG.
> > > > > > > +	 */
> > > > > > > +	if (delta < 0 && nagcount < 2)
> > > > > > > +		return -EINVAL;
> > > > > > > +
> > > > > > 
> > > > > > What if the filesystem is already single AG? Unless I'm missing
> > > > > > something, we already have a check a bit further down that prevents
> > > > > > removal of AGs in the first place.
> > > > > 
> > > > > I think it tends to forbid (return -EINVAL) shrinking the filesystem with
> > > > > a single AG only? Am I missing something?
> > > > > 
> > > > 
> > > > My assumption was this check means one can't shrink a filesystem that is
> > > > already agcount == 1. The comment refers to preventing shrink from
> > > > causing an agcount == 1 fs. What is the intent?

Both of those things.

> > > 
> > > I think it means the latter -- preventing shrink from causing an agcount == 1
> > > fs. since nagcount (new agcount) <= 1?
> > > 
> > 
> > Right, so that leads to my question... does this check also fail a
> > shrink on an fs that is already agcount == 1? If so, why? I know
> > technically it's not a supported configuration, but mkfs allows it.
> 
> Ah, I'm not sure if Darrick would like to forbid agcount == 1 shrinking
> functionitity completely, see the previous comment:
> https://lore.kernel.org/r/20201014160633.GD9832@magnolia/
> 
> (please ignore the modification at that time, since it was buggy...)

Given the confusion I propose a new comment:

	/*
	 * Reject filesystems with a single AG because they are not
	 * supported, and reject a shrink operation that would cause a
	 * filesystem to become unsupported.
	 */
	if (delta < 0 && nagcount < 2)
		return -EINVAL;

--D

> 
> Thanks,
> Gao Xiang
> 
> > 
> > Brian
> > 
> > > Actually, I'm not good at English, if comments need to be improved, please
> > > kindly point out. Thank you very much!
> > > 
> > > Thanks,
> > > Gao Xiang
> > > 
> > > > 
> > > > Brian
> > > > 
> > > > > Thanks,
> > > > > Gao Xiang
> > > > > 
> > > > > > 
> > > > > > Otherwise looks reasonable..
> > > > > > 
> > > > > > Brian
> > > > > > 
> > > > > > >  	oagcount = mp->m_sb.sb_agcount;
> > > > > > >  
> > > > > > >  	/* allocate the new per-ag structures */
> > > > > > > @@ -126,15 +136,22 @@ xfs_growfs_data_private(
> > > > > > >  		error = xfs_initialize_perag(mp, nagcount, &nagimax);
> > > > > > >  		if (error)
> > > > > > >  			return error;
> > > > > > > +	} else if (nagcount < oagcount) {
> > > > > > > +		/* TODO: shrinking the entire AGs hasn't yet completed */
> > > > > > > +		return -EINVAL;
> > > > > > >  	}
> > > > > > >  
> > > > > > >  	error = xfs_trans_alloc(mp, &M_RES(mp)->tr_growdata,
> > > > > > > -			XFS_GROWFS_SPACE_RES(mp), 0, XFS_TRANS_RESERVE, &tp);
> > > > > > > +			(delta > 0 ? XFS_GROWFS_SPACE_RES(mp) : -delta), 0,
> > > > > > > +			XFS_TRANS_RESERVE, &tp);
> > > > > > >  	if (error)
> > > > > > >  		return error;
> > > > > > >  
> > > > > > > -	error = xfs_resizefs_init_new_ags(tp, &id, oagcount, nagcount,
> > > > > > > -					  delta, &lastag_resetagres);
> > > > > > > +	if (delta > 0)
> > > > > > > +		error = xfs_resizefs_init_new_ags(tp, &id, oagcount, nagcount,
> > > > > > > +						  delta, &lastag_resetagres);
> > > > > > > +	else
> > > > > > > +		error = xfs_ag_shrink_space(mp, &tp, nagcount - 1, -delta);
> > > > > > >  	if (error)
> > > > > > >  		goto out_trans_cancel;
> > > > > > >  
> > > > > > > @@ -145,7 +162,7 @@ xfs_growfs_data_private(
> > > > > > >  	 */
> > > > > > >  	if (nagcount > oagcount)
> > > > > > >  		xfs_trans_mod_sb(tp, XFS_TRANS_SB_AGCOUNT, nagcount - oagcount);
> > > > > > > -	if (delta > 0)
> > > > > > > +	if (delta)
> > > > > > >  		xfs_trans_mod_sb(tp, XFS_TRANS_SB_DBLOCKS, delta);
> > > > > > >  	if (id.nfree)
> > > > > > >  		xfs_trans_mod_sb(tp, XFS_TRANS_SB_FDBLOCKS, id.nfree);
> > > > > > > @@ -169,28 +186,29 @@ xfs_growfs_data_private(
> > > > > > >  	xfs_set_low_space_thresholds(mp);
> > > > > > >  	mp->m_alloc_set_aside = xfs_alloc_set_aside(mp);
> > > > > > >  
> > > > > > > -	/*
> > > > > > > -	 * If we expanded the last AG, free the per-AG reservation
> > > > > > > -	 * so we can reinitialize it with the new size.
> > > > > > > -	 */
> > > > > > > -	if (lastag_resetagres) {
> > > > > > > -		struct xfs_perag	*pag;
> > > > > > > -
> > > > > > > -		pag = xfs_perag_get(mp, id.agno);
> > > > > > > -		error = xfs_ag_resv_free(pag);
> > > > > > > -		xfs_perag_put(pag);
> > > > > > > -		if (error)
> > > > > > > -			return error;
> > > > > > > +	if (delta > 0) {
> > > > > > > +		/*
> > > > > > > +		 * If we expanded the last AG, free the per-AG reservation
> > > > > > > +		 * so we can reinitialize it with the new size.
> > > > > > > +		 */
> > > > > > > +		if (lastag_resetagres) {
> > > > > > > +			struct xfs_perag	*pag;
> > > > > > > +
> > > > > > > +			pag = xfs_perag_get(mp, id.agno);
> > > > > > > +			error = xfs_ag_resv_free(pag);
> > > > > > > +			xfs_perag_put(pag);
> > > > > > > +			if (error)
> > > > > > > +				return error;
> > > > > > > +		}
> > > > > > > +		/*
> > > > > > > +		 * Reserve AG metadata blocks. ENOSPC here does not mean there
> > > > > > > +		 * was a growfs failure, just that there still isn't space for
> > > > > > > +		 * new user data after the grow has been run.
> > > > > > > +		 */
> > > > > > > +		error = xfs_fs_reserve_ag_blocks(mp);
> > > > > > > +		if (error == -ENOSPC)
> > > > > > > +			error = 0;
> > > > > > >  	}
> > > > > > > -
> > > > > > > -	/*
> > > > > > > -	 * Reserve AG metadata blocks. ENOSPC here does not mean there was a
> > > > > > > -	 * growfs failure, just that there still isn't space for new user data
> > > > > > > -	 * after the grow has been run.
> > > > > > > -	 */
> > > > > > > -	error = xfs_fs_reserve_ag_blocks(mp);
> > > > > > > -	if (error == -ENOSPC)
> > > > > > > -		error = 0;
> > > > > > >  	return error;
> > > > > > >  
> > > > > > >  out_trans_cancel:
> > > > > > > diff --git a/fs/xfs/xfs_trans.c b/fs/xfs/xfs_trans.c
> > > > > > > index 44f72c09c203..d047f5f26cc0 100644
> > > > > > > --- a/fs/xfs/xfs_trans.c
> > > > > > > +++ b/fs/xfs/xfs_trans.c
> > > > > > > @@ -434,7 +434,6 @@ xfs_trans_mod_sb(
> > > > > > >  		tp->t_res_frextents_delta += delta;
> > > > > > >  		break;
> > > > > > >  	case XFS_TRANS_SB_DBLOCKS:
> > > > > > > -		ASSERT(delta > 0);
> > > > > > >  		tp->t_dblocks_delta += delta;
> > > > > > >  		break;
> > > > > > >  	case XFS_TRANS_SB_AGCOUNT:
> > > > > > > -- 
> > > > > > > 2.27.0
> > > > > > > 
> > > > > > 
> > > > > 
> > > > 
> > > 
> > 
>
Gao Xiang March 23, 2021, 1:15 a.m. UTC | #8
On Mon, Mar 22, 2021 at 09:42:55AM -0700, Darrick J. Wong wrote:
> On Mon, Mar 22, 2021 at 08:50:28PM +0800, Gao Xiang wrote:
> > On Mon, Mar 22, 2021 at 08:43:19AM -0400, Brian Foster wrote:
> > > On Mon, Mar 22, 2021 at 08:36:52PM +0800, Gao Xiang wrote:
> > > > On Mon, Mar 22, 2021 at 08:26:41AM -0400, Brian Foster wrote:
> > > > > On Mon, Mar 22, 2021 at 08:07:22PM +0800, Gao Xiang wrote:
> > > > > > On Mon, Mar 22, 2021 at 07:30:40AM -0400, Brian Foster wrote:
> > > > > > > On Fri, Mar 05, 2021 at 10:57:02AM +0800, Gao Xiang wrote:
> > > > > > > > As the first step of shrinking, this attempts to enable shrinking
> > > > > > > > unused space in the last allocation group by fixing up freespace
> > > > > > > > btree, agi, agf and adjusting super block and use a helper
> > > > > > > > xfs_ag_shrink_space() to fixup the last AG.
> > > > > > > > 
> > > > > > > > This can be all done in one transaction for now, so I think no
> > > > > > > > additional protection is needed.
> > > > > > > > 
> > > > > > > > Reviewed-by: Darrick J. Wong <djwong@kernel.org>
> > > > > > > > Signed-off-by: Gao Xiang <hsiangkao@redhat.com>
> > > > > > > > ---
> > > > > > > >  fs/xfs/xfs_fsops.c | 88 ++++++++++++++++++++++++++++------------------
> > > > > > > >  fs/xfs/xfs_trans.c |  1 -
> > > > > > > >  2 files changed, 53 insertions(+), 36 deletions(-)
> > > > > > > > 
> > > > > > > > diff --git a/fs/xfs/xfs_fsops.c b/fs/xfs/xfs_fsops.c
> > > > > > > > index fc9e799b2ae3..71cba61a451c 100644
> > > > > > > > --- a/fs/xfs/xfs_fsops.c
> > > > > > > > +++ b/fs/xfs/xfs_fsops.c
> > > > > ...
> > > > > > > > @@ -115,10 +120,15 @@ xfs_growfs_data_private(
> > > > > > > >  	if (nb_mod && nb_mod < XFS_MIN_AG_BLOCKS) {
> > > > > > > >  		nagcount--;
> > > > > > > >  		nb = (xfs_rfsblock_t)nagcount * mp->m_sb.sb_agblocks;
> > > > > > > > -		if (nb < mp->m_sb.sb_dblocks)
> > > > > > > > -			return -EINVAL;
> > > > > > > >  	}
> > > > > > > >  	delta = nb - mp->m_sb.sb_dblocks;
> > > > > > > > +	/*
> > > > > > > > +	 * XFS doesn't really support single-AG filesystems, so do not
> > > > > > > > +	 * permit callers to remove the filesystem's second and last AG.
> > > > > > > > +	 */
> > > > > > > > +	if (delta < 0 && nagcount < 2)
> > > > > > > > +		return -EINVAL;
> > > > > > > > +
> > > > > > > 
> > > > > > > What if the filesystem is already single AG? Unless I'm missing
> > > > > > > something, we already have a check a bit further down that prevents
> > > > > > > removal of AGs in the first place.
> > > > > > 
> > > > > > I think it tends to forbid (return -EINVAL) shrinking the filesystem with
> > > > > > a single AG only? Am I missing something?
> > > > > > 
> > > > > 
> > > > > My assumption was this check means one can't shrink a filesystem that is
> > > > > already agcount == 1. The comment refers to preventing shrink from
> > > > > causing an agcount == 1 fs. What is the intent?
> 
> Both of those things.
> 
> > > > 
> > > > I think it means the latter -- preventing shrink from causing an agcount == 1
> > > > fs. since nagcount (new agcount) <= 1?
> > > > 
> > > 
> > > Right, so that leads to my question... does this check also fail a
> > > shrink on an fs that is already agcount == 1? If so, why? I know
> > > technically it's not a supported configuration, but mkfs allows it.
> > 
> > Ah, I'm not sure if Darrick would like to forbid agcount == 1 shrinking
> > functionitity completely, see the previous comment:
> > https://lore.kernel.org/r/20201014160633.GD9832@magnolia/
> > 
> > (please ignore the modification at that time, since it was buggy...)
> 
> Given the confusion I propose a new comment:
> 
> 	/*
> 	 * Reject filesystems with a single AG because they are not
> 	 * supported, and reject a shrink operation that would cause a
> 	 * filesystem to become unsupported.
> 	 */
> 	if (delta < 0 && nagcount < 2)
> 		return -EINVAL;
> 

ok, will update this comment. thanks for your suggestion!

Thanks,
Gao Xiang

> --D
diff mbox series

Patch

diff --git a/fs/xfs/xfs_fsops.c b/fs/xfs/xfs_fsops.c
index fc9e799b2ae3..71cba61a451c 100644
--- a/fs/xfs/xfs_fsops.c
+++ b/fs/xfs/xfs_fsops.c
@@ -91,23 +91,28 @@  xfs_growfs_data_private(
 	xfs_agnumber_t		nagcount;
 	xfs_agnumber_t		nagimax = 0;
 	xfs_rfsblock_t		nb, nb_div, nb_mod;
-	xfs_rfsblock_t		delta;
+	int64_t			delta;
 	bool			lastag_resetagres;
 	xfs_agnumber_t		oagcount;
 	struct xfs_trans	*tp;
 	struct aghdr_init_data	id = {};
 
 	nb = in->newblocks;
-	if (nb < mp->m_sb.sb_dblocks)
-		return -EINVAL;
-	if ((error = xfs_sb_validate_fsb_count(&mp->m_sb, nb)))
+	if (nb == mp->m_sb.sb_dblocks)
+		return 0;
+
+	error = xfs_sb_validate_fsb_count(&mp->m_sb, nb);
+	if (error)
 		return error;
-	error = xfs_buf_read_uncached(mp->m_ddev_targp,
+
+	if (nb > mp->m_sb.sb_dblocks) {
+		error = xfs_buf_read_uncached(mp->m_ddev_targp,
 				XFS_FSB_TO_BB(mp, nb) - XFS_FSS_TO_BB(mp, 1),
 				XFS_FSS_TO_BB(mp, 1), 0, &bp, NULL);
-	if (error)
-		return error;
-	xfs_buf_relse(bp);
+		if (error)
+			return error;
+		xfs_buf_relse(bp);
+	}
 
 	nb_div = nb;
 	nb_mod = do_div(nb_div, mp->m_sb.sb_agblocks);
@@ -115,10 +120,15 @@  xfs_growfs_data_private(
 	if (nb_mod && nb_mod < XFS_MIN_AG_BLOCKS) {
 		nagcount--;
 		nb = (xfs_rfsblock_t)nagcount * mp->m_sb.sb_agblocks;
-		if (nb < mp->m_sb.sb_dblocks)
-			return -EINVAL;
 	}
 	delta = nb - mp->m_sb.sb_dblocks;
+	/*
+	 * XFS doesn't really support single-AG filesystems, so do not
+	 * permit callers to remove the filesystem's second and last AG.
+	 */
+	if (delta < 0 && nagcount < 2)
+		return -EINVAL;
+
 	oagcount = mp->m_sb.sb_agcount;
 
 	/* allocate the new per-ag structures */
@@ -126,15 +136,22 @@  xfs_growfs_data_private(
 		error = xfs_initialize_perag(mp, nagcount, &nagimax);
 		if (error)
 			return error;
+	} else if (nagcount < oagcount) {
+		/* TODO: shrinking the entire AGs hasn't yet completed */
+		return -EINVAL;
 	}
 
 	error = xfs_trans_alloc(mp, &M_RES(mp)->tr_growdata,
-			XFS_GROWFS_SPACE_RES(mp), 0, XFS_TRANS_RESERVE, &tp);
+			(delta > 0 ? XFS_GROWFS_SPACE_RES(mp) : -delta), 0,
+			XFS_TRANS_RESERVE, &tp);
 	if (error)
 		return error;
 
-	error = xfs_resizefs_init_new_ags(tp, &id, oagcount, nagcount,
-					  delta, &lastag_resetagres);
+	if (delta > 0)
+		error = xfs_resizefs_init_new_ags(tp, &id, oagcount, nagcount,
+						  delta, &lastag_resetagres);
+	else
+		error = xfs_ag_shrink_space(mp, &tp, nagcount - 1, -delta);
 	if (error)
 		goto out_trans_cancel;
 
@@ -145,7 +162,7 @@  xfs_growfs_data_private(
 	 */
 	if (nagcount > oagcount)
 		xfs_trans_mod_sb(tp, XFS_TRANS_SB_AGCOUNT, nagcount - oagcount);
-	if (delta > 0)
+	if (delta)
 		xfs_trans_mod_sb(tp, XFS_TRANS_SB_DBLOCKS, delta);
 	if (id.nfree)
 		xfs_trans_mod_sb(tp, XFS_TRANS_SB_FDBLOCKS, id.nfree);
@@ -169,28 +186,29 @@  xfs_growfs_data_private(
 	xfs_set_low_space_thresholds(mp);
 	mp->m_alloc_set_aside = xfs_alloc_set_aside(mp);
 
-	/*
-	 * If we expanded the last AG, free the per-AG reservation
-	 * so we can reinitialize it with the new size.
-	 */
-	if (lastag_resetagres) {
-		struct xfs_perag	*pag;
-
-		pag = xfs_perag_get(mp, id.agno);
-		error = xfs_ag_resv_free(pag);
-		xfs_perag_put(pag);
-		if (error)
-			return error;
+	if (delta > 0) {
+		/*
+		 * If we expanded the last AG, free the per-AG reservation
+		 * so we can reinitialize it with the new size.
+		 */
+		if (lastag_resetagres) {
+			struct xfs_perag	*pag;
+
+			pag = xfs_perag_get(mp, id.agno);
+			error = xfs_ag_resv_free(pag);
+			xfs_perag_put(pag);
+			if (error)
+				return error;
+		}
+		/*
+		 * Reserve AG metadata blocks. ENOSPC here does not mean there
+		 * was a growfs failure, just that there still isn't space for
+		 * new user data after the grow has been run.
+		 */
+		error = xfs_fs_reserve_ag_blocks(mp);
+		if (error == -ENOSPC)
+			error = 0;
 	}
-
-	/*
-	 * Reserve AG metadata blocks. ENOSPC here does not mean there was a
-	 * growfs failure, just that there still isn't space for new user data
-	 * after the grow has been run.
-	 */
-	error = xfs_fs_reserve_ag_blocks(mp);
-	if (error == -ENOSPC)
-		error = 0;
 	return error;
 
 out_trans_cancel:
diff --git a/fs/xfs/xfs_trans.c b/fs/xfs/xfs_trans.c
index 44f72c09c203..d047f5f26cc0 100644
--- a/fs/xfs/xfs_trans.c
+++ b/fs/xfs/xfs_trans.c
@@ -434,7 +434,6 @@  xfs_trans_mod_sb(
 		tp->t_res_frextents_delta += delta;
 		break;
 	case XFS_TRANS_SB_DBLOCKS:
-		ASSERT(delta > 0);
 		tp->t_dblocks_delta += delta;
 		break;
 	case XFS_TRANS_SB_AGCOUNT: