Commit fb2fc172 authored by Gao Xiang's avatar Gao Xiang Committed by Darrick J. Wong

xfs: support shrinking unused space in the last AG

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: default avatarDarrick J. Wong <djwong@kernel.org>
Signed-off-by: default avatarGao Xiang <hsiangkao@redhat.com>
Signed-off-by: default avatarDarrick J. Wong <djwong@kernel.org>
parent 46141dc8
...@@ -91,23 +91,25 @@ xfs_growfs_data_private( ...@@ -91,23 +91,25 @@ xfs_growfs_data_private(
xfs_agnumber_t nagcount; xfs_agnumber_t nagcount;
xfs_agnumber_t nagimax = 0; xfs_agnumber_t nagimax = 0;
xfs_rfsblock_t nb, nb_div, nb_mod; xfs_rfsblock_t nb, nb_div, nb_mod;
xfs_rfsblock_t delta; int64_t delta;
bool lastag_extended; bool lastag_extended;
xfs_agnumber_t oagcount; xfs_agnumber_t oagcount;
struct xfs_trans *tp; struct xfs_trans *tp;
struct aghdr_init_data id = {}; struct aghdr_init_data id = {};
nb = in->newblocks; nb = in->newblocks;
if (nb < mp->m_sb.sb_dblocks) error = xfs_sb_validate_fsb_count(&mp->m_sb, nb);
return -EINVAL; if (error)
if ((error = xfs_sb_validate_fsb_count(&mp->m_sb, nb)))
return 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_FSB_TO_BB(mp, nb) - XFS_FSS_TO_BB(mp, 1),
XFS_FSS_TO_BB(mp, 1), 0, &bp, NULL); XFS_FSS_TO_BB(mp, 1), 0, &bp, NULL);
if (error) if (error)
return error; return error;
xfs_buf_relse(bp); xfs_buf_relse(bp);
}
nb_div = nb; nb_div = nb;
nb_mod = do_div(nb_div, mp->m_sb.sb_agblocks); nb_mod = do_div(nb_div, mp->m_sb.sb_agblocks);
...@@ -115,10 +117,16 @@ xfs_growfs_data_private( ...@@ -115,10 +117,16 @@ xfs_growfs_data_private(
if (nb_mod && nb_mod < XFS_MIN_AG_BLOCKS) { if (nb_mod && nb_mod < XFS_MIN_AG_BLOCKS) {
nagcount--; nagcount--;
nb = (xfs_rfsblock_t)nagcount * mp->m_sb.sb_agblocks; 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; delta = nb - mp->m_sb.sb_dblocks;
/*
* 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;
oagcount = mp->m_sb.sb_agcount; oagcount = mp->m_sb.sb_agcount;
/* allocate the new per-ag structures */ /* allocate the new per-ag structures */
...@@ -126,15 +134,31 @@ xfs_growfs_data_private( ...@@ -126,15 +134,31 @@ xfs_growfs_data_private(
error = xfs_initialize_perag(mp, nagcount, &nagimax); error = xfs_initialize_perag(mp, nagcount, &nagimax);
if (error) if (error)
return 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, 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) if (error)
return error; return error;
error = xfs_resizefs_init_new_ags(tp, &id, oagcount, nagcount, if (delta > 0) {
delta, &lastag_extended); error = xfs_resizefs_init_new_ags(tp, &id, oagcount, nagcount,
delta, &lastag_extended);
} else {
static struct ratelimit_state shrink_warning = \
RATELIMIT_STATE_INIT("shrink_warning", 86400 * HZ, 1);
ratelimit_set_flags(&shrink_warning, RATELIMIT_MSG_ON_RELEASE);
if (__ratelimit(&shrink_warning))
xfs_alert(mp,
"EXPERIMENTAL online shrink feature in use. Use at your own risk!");
error = xfs_ag_shrink_space(mp, &tp, nagcount - 1, -delta);
}
if (error) if (error)
goto out_trans_cancel; goto out_trans_cancel;
...@@ -169,28 +193,29 @@ xfs_growfs_data_private( ...@@ -169,28 +193,29 @@ xfs_growfs_data_private(
xfs_set_low_space_thresholds(mp); xfs_set_low_space_thresholds(mp);
mp->m_alloc_set_aside = xfs_alloc_set_aside(mp); mp->m_alloc_set_aside = xfs_alloc_set_aside(mp);
/* if (delta > 0) {
* If we expanded the last AG, free the per-AG reservation /*
* so we can reinitialize it with the new size. * If we expanded the last AG, free the per-AG reservation
*/ * so we can reinitialize it with the new size.
if (lastag_extended) { */
struct xfs_perag *pag; if (lastag_extended) {
struct xfs_perag *pag;
pag = xfs_perag_get(mp, id.agno);
error = xfs_ag_resv_free(pag); pag = xfs_perag_get(mp, id.agno);
xfs_perag_put(pag); error = xfs_ag_resv_free(pag);
if (error) xfs_perag_put(pag);
return error; 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; return error;
out_trans_cancel: out_trans_cancel:
......
...@@ -436,7 +436,6 @@ xfs_trans_mod_sb( ...@@ -436,7 +436,6 @@ xfs_trans_mod_sb(
tp->t_res_frextents_delta += delta; tp->t_res_frextents_delta += delta;
break; break;
case XFS_TRANS_SB_DBLOCKS: case XFS_TRANS_SB_DBLOCKS:
ASSERT(delta > 0);
tp->t_dblocks_delta += delta; tp->t_dblocks_delta += delta;
break; break;
case XFS_TRANS_SB_AGCOUNT: case XFS_TRANS_SB_AGCOUNT:
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment