summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--fs/xfs/xfs_fsops.c159
1 files changed, 92 insertions, 67 deletions
diff --git a/fs/xfs/xfs_fsops.c b/fs/xfs/xfs_fsops.c
index 4e66d390e2a4..a718bb002cf4 100644
--- a/fs/xfs/xfs_fsops.c
+++ b/fs/xfs/xfs_fsops.c
@@ -422,9 +422,8 @@ xfs_growfs_data_private(
{
xfs_agf_t *agf;
xfs_agi_t *agi;
- xfs_agnumber_t agno;
xfs_buf_t *bp;
- int error, saved_error = 0;
+ int error;
xfs_agnumber_t nagcount;
xfs_agnumber_t nagimax = 0;
xfs_rfsblock_t nb, nb_mod;
@@ -496,12 +495,12 @@ xfs_growfs_data_private(
error = xfs_grow_ag_headers(mp, &id);
if (error) {
xfs_buf_delwri_cancel(&id.buffer_list);
- goto error0;
+ goto out_trans_cancel;
}
}
error = xfs_buf_delwri_submit(&id.buffer_list);
if (error)
- goto error0;
+ goto out_trans_cancel;
xfs_trans_agblocks_delta(tp, id.nfree);
@@ -515,22 +514,23 @@ xfs_growfs_data_private(
* Change the agi length.
*/
error = xfs_ialloc_read_agi(mp, tp, id.agno, &bp);
- if (error) {
- goto error0;
- }
+ if (error)
+ goto out_trans_cancel;
+
ASSERT(bp);
agi = XFS_BUF_TO_AGI(bp);
be32_add_cpu(&agi->agi_length, new);
ASSERT(nagcount == oagcount ||
be32_to_cpu(agi->agi_length) == mp->m_sb.sb_agblocks);
xfs_ialloc_log_agi(tp, bp, XFS_AGI_LENGTH);
+
/*
* Change agf length.
*/
error = xfs_alloc_read_agf(mp, tp, id.agno, 0, &bp);
- if (error) {
- goto error0;
- }
+ if (error)
+ goto out_trans_cancel;
+
ASSERT(bp);
agf = XFS_BUF_TO_AGF(bp);
be32_add_cpu(&agf->agf_length, new);
@@ -550,13 +550,13 @@ xfs_growfs_data_private(
be32_to_cpu(agf->agf_length) - new,
new, &oinfo);
if (error)
- goto error0;
+ goto out_trans_cancel;
error = xfs_free_extent(tp,
XFS_AGB_TO_FSB(mp, id.agno,
be32_to_cpu(agf->agf_length) - new),
new, &oinfo, XFS_AG_RESV_NONE);
if (error)
- goto error0;
+ goto out_trans_cancel;
}
/*
@@ -593,16 +593,86 @@ xfs_growfs_data_private(
error = xfs_ag_resv_free(pag);
xfs_perag_put(pag);
if (error)
- goto out;
+ return error;
}
- /* Reserve AG metadata blocks. */
+ /*
+ * 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 && error != -ENOSPC)
- goto out;
+ if (error == -ENOSPC)
+ error = 0;
+ return error;
+
+out_trans_cancel:
+ xfs_trans_cancel(tp);
+ return error;
+}
+
+static int
+xfs_growfs_log_private(
+ xfs_mount_t *mp, /* mount point for filesystem */
+ xfs_growfs_log_t *in) /* growfs log input struct */
+{
+ xfs_extlen_t nb;
+
+ nb = in->newblocks;
+ if (nb < XFS_MIN_LOG_BLOCKS || nb < XFS_B_TO_FSB(mp, XFS_MIN_LOG_BYTES))
+ return -EINVAL;
+ if (nb == mp->m_sb.sb_logblocks &&
+ in->isint == (mp->m_sb.sb_logstart != 0))
+ return -EINVAL;
+ /*
+ * Moving the log is hard, need new interfaces to sync
+ * the log first, hold off all activity while moving it.
+ * Can have shorter or longer log in the same space,
+ * or transform internal to external log or vice versa.
+ */
+ return -ENOSYS;
+}
+
+static int
+xfs_growfs_imaxpct(
+ struct xfs_mount *mp,
+ __u32 imaxpct)
+{
+ struct xfs_trans *tp;
+ int dpct;
+ int error;
+
+ if (imaxpct > 100)
+ return -EINVAL;
+
+ error = xfs_trans_alloc(mp, &M_RES(mp)->tr_growdata,
+ XFS_GROWFS_SPACE_RES(mp), 0, XFS_TRANS_RESERVE, &tp);
+ if (error)
+ return error;
+
+ dpct = imaxpct - mp->m_sb.sb_imax_pct;
+ xfs_trans_mod_sb(tp, XFS_TRANS_SB_IMAXPCT, dpct);
+ xfs_trans_set_sync(tp);
+ return xfs_trans_commit(tp);
+}
+
+/*
+ * After a grow operation, we need to update all the secondary superblocks
+ * to match the new state of the primary. Read/init the superblocks and update
+ * them appropriately.
+ */
+static int
+xfs_growfs_update_superblocks(
+ struct xfs_mount *mp,
+ xfs_agnumber_t oagcount)
+{
+ struct xfs_buf *bp;
+ xfs_agnumber_t agno;
+ int saved_error = 0;
+ int error = 0;
/* update secondary superblocks. */
- for (agno = 1; agno < nagcount; agno++) {
+ for (agno = 1; agno < mp->m_sb.sb_agcount; agno++) {
error = 0;
/*
* new secondary superblocks need to be zeroed, not read from
@@ -652,57 +722,7 @@ xfs_growfs_data_private(
}
}
- out:
return saved_error ? saved_error : error;
-
- error0:
- xfs_trans_cancel(tp);
- return error;
-}
-
-static int
-xfs_growfs_log_private(
- xfs_mount_t *mp, /* mount point for filesystem */
- xfs_growfs_log_t *in) /* growfs log input struct */
-{
- xfs_extlen_t nb;
-
- nb = in->newblocks;
- if (nb < XFS_MIN_LOG_BLOCKS || nb < XFS_B_TO_FSB(mp, XFS_MIN_LOG_BYTES))
- return -EINVAL;
- if (nb == mp->m_sb.sb_logblocks &&
- in->isint == (mp->m_sb.sb_logstart != 0))
- return -EINVAL;
- /*
- * Moving the log is hard, need new interfaces to sync
- * the log first, hold off all activity while moving it.
- * Can have shorter or longer log in the same space,
- * or transform internal to external log or vice versa.
- */
- return -ENOSYS;
-}
-
-static int
-xfs_growfs_imaxpct(
- struct xfs_mount *mp,
- __u32 imaxpct)
-{
- struct xfs_trans *tp;
- int64_t dpct;
- int error;
-
- if (imaxpct > 100)
- return -EINVAL;
-
- error = xfs_trans_alloc(mp, &M_RES(mp)->tr_growdata,
- XFS_GROWFS_SPACE_RES(mp), 0, XFS_TRANS_RESERVE, &tp);
- if (error)
- return error;
-
- dpct = (int64_t)imaxpct - mp->m_sb.sb_imax_pct;
- xfs_trans_mod_sb(tp, XFS_TRANS_SB_IMAXPCT, dpct);
- xfs_trans_set_sync(tp);
- return xfs_trans_commit(tp);
}
/*
@@ -715,6 +735,7 @@ xfs_growfs_data(
struct xfs_mount *mp,
struct xfs_growfs_data *in)
{
+ xfs_agnumber_t oagcount;
int error = 0;
if (!capable(CAP_SYS_ADMIN))
@@ -729,6 +750,7 @@ xfs_growfs_data(
goto out_error;
}
+ oagcount = mp->m_sb.sb_agcount;
if (in->newblocks != mp->m_sb.sb_dblocks) {
error = xfs_growfs_data_private(mp, in);
if (error)
@@ -743,6 +765,9 @@ xfs_growfs_data(
} else
mp->m_maxicount = 0;
+ /* Update secondary superblocks now the physical grow has completed */
+ error = xfs_growfs_update_superblocks(mp, oagcount);
+
out_error:
/*
* Increment the generation unconditionally, the error could be from