{
        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;
                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);
 
                 * 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);
                                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;
        }
 
        /*
                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
                }
        }
 
- 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);
 }
 
 /*
        struct xfs_mount        *mp,
        struct xfs_growfs_data  *in)
 {
+       xfs_agnumber_t          oagcount;
        int                     error = 0;
 
        if (!capable(CAP_SYS_ADMIN))
                        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)
        } 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