All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 0/10 v3] xfs: refactor and tablise growfs
@ 2018-05-14  4:18 Dave Chinner
  2018-05-14  4:18 ` [PATCH 01/10] xfs: one-shot cached buffers Dave Chinner
                   ` (9 more replies)
  0 siblings, 10 replies; 17+ messages in thread
From: Dave Chinner @ 2018-05-14  4:18 UTC (permalink / raw)
  To: linux-xfs

THis is the third version of the growfs tablisation patchset,
addressing the changes that Darrick raised. I've reposted the
entire patchset because changes in early patches cascaded throw
several others and it was just easier to repost the lot.

Version 3:
- fix agno variable to use xfs_agnumber_t
- change ag header table to use expanded definitions
- dropped numrecs from tables as they are always either zero or hard
  coded by type specific block initialisers.

Version 2:
- rebase on current for-next
- add oneshot cached buffer support
- clean up more of the typedef usage.
- move id->nfree calc to per-ag hdr init function from the freespace
  btree root initialisation.
- only use id->type and id->numrecs for the generic btree root block
  init calls.
- only call xfs_growfs_data_private() if the size is actually being
  changed.
- make sure we still update secondary superblocks if physical grow
  fails with ENOSPC.
- change secondary superblock updates to use cached one-shot
  buffers.
- moved the core code to libxfs so it can be shared easily with
  userspace.

Version 1:
https://marc.info/?l=linux-xfs&m=151746732826277&w=2



^ permalink raw reply	[flat|nested] 17+ messages in thread

* [PATCH 01/10] xfs: one-shot cached buffers
  2018-05-14  4:18 [PATCH 0/10 v3] xfs: refactor and tablise growfs Dave Chinner
@ 2018-05-14  4:18 ` Dave Chinner
  2018-05-14  4:18 ` [PATCH 02/10] xfs: factor out AG header initialisation from growfs core Dave Chinner
                   ` (8 subsequent siblings)
  9 siblings, 0 replies; 17+ messages in thread
From: Dave Chinner @ 2018-05-14  4:18 UTC (permalink / raw)
  To: linux-xfs

From: Dave Chinner <dchinner@redhat.com>

For the new growfs work, we want to ensure that we serialise
secondary superblock updates with other operations (e.g. scrub)
correctly, but we don't want to cache the buffers for long term
reuse. We need cached buffers for serialisation, however.

To solve this, introduce a "oneshot" buffer which will be marshalled
through the cache but then released once the last current reference
goes away. If the buffer is already cached, then we ignore the
"one-shot" behaviour and leave the buffer in the state it was prior
to the one-shot command being run. This means we don't perturb
either the working set or existing cached buffer state by a one-shot
operation.

Signed-Off-By: Dave Chinner <dchinner@redhat.com>
Reviewed-by: Darrick J. Wong <darrick.wong@oracle.com>
---
 fs/xfs/xfs_buf.h | 12 ++++++++++++
 1 file changed, 12 insertions(+)

diff --git a/fs/xfs/xfs_buf.h b/fs/xfs/xfs_buf.h
index 830e2f6c064a..f5f2b71c2fde 100644
--- a/fs/xfs/xfs_buf.h
+++ b/fs/xfs/xfs_buf.h
@@ -347,6 +347,18 @@ extern void xfs_buf_terminate(void);
 
 void xfs_buf_set_ref(struct xfs_buf *bp, int lru_ref);
 
+/*
+ * If the buffer is already on the LRU, do nothing. Otherwise set the buffer
+ * up with a reference count of 0 so it will be tossed from the cache when
+ * released.
+ */
+static inline void xfs_buf_oneshot(struct xfs_buf *bp)
+{
+	if (!list_empty(&bp->b_lru) || atomic_read(&bp->b_lru_ref) > 1)
+		return;
+	atomic_set(&bp->b_lru_ref, 0);
+}
+
 static inline int xfs_buf_ispinned(struct xfs_buf *bp)
 {
 	return atomic_read(&bp->b_pin_count);
-- 
2.17.0


^ permalink raw reply related	[flat|nested] 17+ messages in thread

* [PATCH 02/10] xfs: factor out AG header initialisation from growfs core
  2018-05-14  4:18 [PATCH 0/10 v3] xfs: refactor and tablise growfs Dave Chinner
  2018-05-14  4:18 ` [PATCH 01/10] xfs: one-shot cached buffers Dave Chinner
@ 2018-05-14  4:18 ` Dave Chinner
  2018-05-14 19:28   ` Darrick J. Wong
  2018-05-14  4:18 ` [PATCH 03/10] xfs: convert growfs AG header init to use buffer lists Dave Chinner
                   ` (7 subsequent siblings)
  9 siblings, 1 reply; 17+ messages in thread
From: Dave Chinner @ 2018-05-14  4:18 UTC (permalink / raw)
  To: linux-xfs

From: Dave Chinner <dchinner@redhat.com>

The intialisation of new AG headers is mostly common with the
userspace mkfs code and growfs in the kernel, so start factoring it
out so we can move it to libxfs and use it in both places.

Signed-Off-By: Dave Chinner <dchinner@redhat.com>
Reviewed-by: Brian Foster <bfoster@redhat.com>
---
 fs/xfs/xfs_fsops.c | 637 +++++++++++++++++++++++----------------------
 1 file changed, 331 insertions(+), 306 deletions(-)

diff --git a/fs/xfs/xfs_fsops.c b/fs/xfs/xfs_fsops.c
index 523792768080..391d1938a6c8 100644
--- a/fs/xfs/xfs_fsops.c
+++ b/fs/xfs/xfs_fsops.c
@@ -71,20 +71,344 @@ xfs_growfs_get_hdr_buf(
 	return bp;
 }
 
+/*
+ * Write new AG headers to disk. Non-transactional, but written
+ * synchronously so they are completed prior to the growfs transaction
+ * being logged.
+ */
+static int
+xfs_grow_ag_headers(
+	struct xfs_mount	*mp,
+	xfs_agnumber_t		agno,
+	xfs_extlen_t		agsize,
+	xfs_rfsblock_t		*nfree)
+{
+	struct xfs_agf		*agf;
+	struct xfs_agi		*agi;
+	struct xfs_agfl		*agfl;
+	__be32			*agfl_bno;
+	xfs_alloc_rec_t		*arec;
+	struct xfs_buf		*bp;
+	int			bucket;
+	xfs_extlen_t		tmpsize;
+	int			error = 0;
+
+	/*
+	 * AG freespace header block
+	 */
+	bp = xfs_growfs_get_hdr_buf(mp,
+			XFS_AG_DADDR(mp, agno, XFS_AGF_DADDR(mp)),
+			XFS_FSS_TO_BB(mp, 1), 0,
+			&xfs_agf_buf_ops);
+	if (!bp) {
+		error = -ENOMEM;
+		goto out_error;
+	}
+
+	agf = XFS_BUF_TO_AGF(bp);
+	agf->agf_magicnum = cpu_to_be32(XFS_AGF_MAGIC);
+	agf->agf_versionnum = cpu_to_be32(XFS_AGF_VERSION);
+	agf->agf_seqno = cpu_to_be32(agno);
+	agf->agf_length = cpu_to_be32(agsize);
+	agf->agf_roots[XFS_BTNUM_BNOi] = cpu_to_be32(XFS_BNO_BLOCK(mp));
+	agf->agf_roots[XFS_BTNUM_CNTi] = cpu_to_be32(XFS_CNT_BLOCK(mp));
+	agf->agf_levels[XFS_BTNUM_BNOi] = cpu_to_be32(1);
+	agf->agf_levels[XFS_BTNUM_CNTi] = cpu_to_be32(1);
+	if (xfs_sb_version_hasrmapbt(&mp->m_sb)) {
+		agf->agf_roots[XFS_BTNUM_RMAPi] =
+					cpu_to_be32(XFS_RMAP_BLOCK(mp));
+		agf->agf_levels[XFS_BTNUM_RMAPi] = cpu_to_be32(1);
+		agf->agf_rmap_blocks = cpu_to_be32(1);
+	}
+
+	agf->agf_flfirst = cpu_to_be32(1);
+	agf->agf_fllast = 0;
+	agf->agf_flcount = 0;
+	tmpsize = agsize - mp->m_ag_prealloc_blocks;
+	agf->agf_freeblks = cpu_to_be32(tmpsize);
+	agf->agf_longest = cpu_to_be32(tmpsize);
+	if (xfs_sb_version_hascrc(&mp->m_sb))
+		uuid_copy(&agf->agf_uuid, &mp->m_sb.sb_meta_uuid);
+	if (xfs_sb_version_hasreflink(&mp->m_sb)) {
+		agf->agf_refcount_root = cpu_to_be32(
+				xfs_refc_block(mp));
+		agf->agf_refcount_level = cpu_to_be32(1);
+		agf->agf_refcount_blocks = cpu_to_be32(1);
+	}
+
+	error = xfs_bwrite(bp);
+	xfs_buf_relse(bp);
+	if (error)
+		goto out_error;
+
+	/*
+	 * AG freelist header block
+	 */
+	bp = xfs_growfs_get_hdr_buf(mp,
+			XFS_AG_DADDR(mp, agno, XFS_AGFL_DADDR(mp)),
+			XFS_FSS_TO_BB(mp, 1), 0,
+			&xfs_agfl_buf_ops);
+	if (!bp) {
+		error = -ENOMEM;
+		goto out_error;
+	}
+
+	agfl = XFS_BUF_TO_AGFL(bp);
+	if (xfs_sb_version_hascrc(&mp->m_sb)) {
+		agfl->agfl_magicnum = cpu_to_be32(XFS_AGFL_MAGIC);
+		agfl->agfl_seqno = cpu_to_be32(agno);
+		uuid_copy(&agfl->agfl_uuid, &mp->m_sb.sb_meta_uuid);
+	}
+
+	agfl_bno = XFS_BUF_TO_AGFL_BNO(mp, bp);
+	for (bucket = 0; bucket < xfs_agfl_size(mp); bucket++)
+		agfl_bno[bucket] = cpu_to_be32(NULLAGBLOCK);
+
+	error = xfs_bwrite(bp);
+	xfs_buf_relse(bp);
+	if (error)
+		goto out_error;
+
+	/*
+	 * AG inode header block
+	 */
+	bp = xfs_growfs_get_hdr_buf(mp,
+			XFS_AG_DADDR(mp, agno, XFS_AGI_DADDR(mp)),
+			XFS_FSS_TO_BB(mp, 1), 0,
+			&xfs_agi_buf_ops);
+	if (!bp) {
+		error = -ENOMEM;
+		goto out_error;
+	}
+
+	agi = XFS_BUF_TO_AGI(bp);
+	agi->agi_magicnum = cpu_to_be32(XFS_AGI_MAGIC);
+	agi->agi_versionnum = cpu_to_be32(XFS_AGI_VERSION);
+	agi->agi_seqno = cpu_to_be32(agno);
+	agi->agi_length = cpu_to_be32(agsize);
+	agi->agi_count = 0;
+	agi->agi_root = cpu_to_be32(XFS_IBT_BLOCK(mp));
+	agi->agi_level = cpu_to_be32(1);
+	agi->agi_freecount = 0;
+	agi->agi_newino = cpu_to_be32(NULLAGINO);
+	agi->agi_dirino = cpu_to_be32(NULLAGINO);
+	if (xfs_sb_version_hascrc(&mp->m_sb))
+		uuid_copy(&agi->agi_uuid, &mp->m_sb.sb_meta_uuid);
+	if (xfs_sb_version_hasfinobt(&mp->m_sb)) {
+		agi->agi_free_root = cpu_to_be32(XFS_FIBT_BLOCK(mp));
+		agi->agi_free_level = cpu_to_be32(1);
+	}
+	for (bucket = 0; bucket < XFS_AGI_UNLINKED_BUCKETS; bucket++)
+		agi->agi_unlinked[bucket] = cpu_to_be32(NULLAGINO);
+
+	error = xfs_bwrite(bp);
+	xfs_buf_relse(bp);
+	if (error)
+		goto out_error;
+
+	/*
+	 * BNO btree root block
+	 */
+	bp = xfs_growfs_get_hdr_buf(mp,
+			XFS_AGB_TO_DADDR(mp, agno, XFS_BNO_BLOCK(mp)),
+			BTOBB(mp->m_sb.sb_blocksize), 0,
+			&xfs_allocbt_buf_ops);
+
+	if (!bp) {
+		error = -ENOMEM;
+		goto out_error;
+	}
+
+	xfs_btree_init_block(mp, bp, XFS_BTNUM_BNO, 0, 1, agno, 0);
+
+	arec = XFS_ALLOC_REC_ADDR(mp, XFS_BUF_TO_BLOCK(bp), 1);
+	arec->ar_startblock = cpu_to_be32(mp->m_ag_prealloc_blocks);
+	arec->ar_blockcount = cpu_to_be32(
+		agsize - be32_to_cpu(arec->ar_startblock));
+
+	error = xfs_bwrite(bp);
+	xfs_buf_relse(bp);
+	if (error)
+		goto out_error;
+
+	/*
+	 * CNT btree root block
+	 */
+	bp = xfs_growfs_get_hdr_buf(mp,
+			XFS_AGB_TO_DADDR(mp, agno, XFS_CNT_BLOCK(mp)),
+			BTOBB(mp->m_sb.sb_blocksize), 0,
+			&xfs_allocbt_buf_ops);
+	if (!bp) {
+		error = -ENOMEM;
+		goto out_error;
+	}
+
+	xfs_btree_init_block(mp, bp, XFS_BTNUM_CNT, 0, 1, agno, 0);
+
+	arec = XFS_ALLOC_REC_ADDR(mp, XFS_BUF_TO_BLOCK(bp), 1);
+	arec->ar_startblock = cpu_to_be32(mp->m_ag_prealloc_blocks);
+	arec->ar_blockcount = cpu_to_be32(
+		agsize - be32_to_cpu(arec->ar_startblock));
+	*nfree += be32_to_cpu(arec->ar_blockcount);
+
+	error = xfs_bwrite(bp);
+	xfs_buf_relse(bp);
+	if (error)
+		goto out_error;
+
+	/* RMAP btree root block */
+	if (xfs_sb_version_hasrmapbt(&mp->m_sb)) {
+		struct xfs_rmap_rec	*rrec;
+		struct xfs_btree_block	*block;
+
+		bp = xfs_growfs_get_hdr_buf(mp,
+			XFS_AGB_TO_DADDR(mp, agno, XFS_RMAP_BLOCK(mp)),
+			BTOBB(mp->m_sb.sb_blocksize), 0,
+			&xfs_rmapbt_buf_ops);
+		if (!bp) {
+			error = -ENOMEM;
+			goto out_error;
+		}
+
+		xfs_btree_init_block(mp, bp, XFS_BTNUM_RMAP, 0, 0,
+					agno, 0);
+		block = XFS_BUF_TO_BLOCK(bp);
+
+
+		/*
+		 * mark the AG header regions as static metadata The BNO
+		 * btree block is the first block after the headers, so
+		 * it's location defines the size of region the static
+		 * metadata consumes.
+		 *
+		 * Note: unlike mkfs, we never have to account for log
+		 * space when growing the data regions
+		 */
+		rrec = XFS_RMAP_REC_ADDR(block, 1);
+		rrec->rm_startblock = 0;
+		rrec->rm_blockcount = cpu_to_be32(XFS_BNO_BLOCK(mp));
+		rrec->rm_owner = cpu_to_be64(XFS_RMAP_OWN_FS);
+		rrec->rm_offset = 0;
+		be16_add_cpu(&block->bb_numrecs, 1);
+
+		/* account freespace btree root blocks */
+		rrec = XFS_RMAP_REC_ADDR(block, 2);
+		rrec->rm_startblock = cpu_to_be32(XFS_BNO_BLOCK(mp));
+		rrec->rm_blockcount = cpu_to_be32(2);
+		rrec->rm_owner = cpu_to_be64(XFS_RMAP_OWN_AG);
+		rrec->rm_offset = 0;
+		be16_add_cpu(&block->bb_numrecs, 1);
+
+		/* account inode btree root blocks */
+		rrec = XFS_RMAP_REC_ADDR(block, 3);
+		rrec->rm_startblock = cpu_to_be32(XFS_IBT_BLOCK(mp));
+		rrec->rm_blockcount = cpu_to_be32(XFS_RMAP_BLOCK(mp) -
+						XFS_IBT_BLOCK(mp));
+		rrec->rm_owner = cpu_to_be64(XFS_RMAP_OWN_INOBT);
+		rrec->rm_offset = 0;
+		be16_add_cpu(&block->bb_numrecs, 1);
+
+		/* account for rmap btree root */
+		rrec = XFS_RMAP_REC_ADDR(block, 4);
+		rrec->rm_startblock = cpu_to_be32(XFS_RMAP_BLOCK(mp));
+		rrec->rm_blockcount = cpu_to_be32(1);
+		rrec->rm_owner = cpu_to_be64(XFS_RMAP_OWN_AG);
+		rrec->rm_offset = 0;
+		be16_add_cpu(&block->bb_numrecs, 1);
+
+		/* account for refc btree root */
+		if (xfs_sb_version_hasreflink(&mp->m_sb)) {
+			rrec = XFS_RMAP_REC_ADDR(block, 5);
+			rrec->rm_startblock = cpu_to_be32(xfs_refc_block(mp));
+			rrec->rm_blockcount = cpu_to_be32(1);
+			rrec->rm_owner = cpu_to_be64(XFS_RMAP_OWN_REFC);
+			rrec->rm_offset = 0;
+			be16_add_cpu(&block->bb_numrecs, 1);
+		}
+
+		error = xfs_bwrite(bp);
+		xfs_buf_relse(bp);
+		if (error)
+			goto out_error;
+	}
+
+	/*
+	 * INO btree root block
+	 */
+	bp = xfs_growfs_get_hdr_buf(mp,
+			XFS_AGB_TO_DADDR(mp, agno, XFS_IBT_BLOCK(mp)),
+			BTOBB(mp->m_sb.sb_blocksize), 0,
+			&xfs_inobt_buf_ops);
+	if (!bp) {
+		error = -ENOMEM;
+		goto out_error;
+	}
+
+	xfs_btree_init_block(mp, bp, XFS_BTNUM_INO , 0, 0, agno, 0);
+
+	error = xfs_bwrite(bp);
+	xfs_buf_relse(bp);
+	if (error)
+		goto out_error;
+
+	/*
+	 * FINO btree root block
+	 */
+	if (xfs_sb_version_hasfinobt(&mp->m_sb)) {
+		bp = xfs_growfs_get_hdr_buf(mp,
+			XFS_AGB_TO_DADDR(mp, agno, XFS_FIBT_BLOCK(mp)),
+			BTOBB(mp->m_sb.sb_blocksize), 0,
+			&xfs_inobt_buf_ops);
+		if (!bp) {
+			error = -ENOMEM;
+			goto out_error;
+		}
+
+		xfs_btree_init_block(mp, bp, XFS_BTNUM_FINO,
+					     0, 0, agno, 0);
+
+		error = xfs_bwrite(bp);
+		xfs_buf_relse(bp);
+		if (error)
+			goto out_error;
+	}
+
+	/*
+	 * refcount btree root block
+	 */
+	if (xfs_sb_version_hasreflink(&mp->m_sb)) {
+		bp = xfs_growfs_get_hdr_buf(mp,
+			XFS_AGB_TO_DADDR(mp, agno, xfs_refc_block(mp)),
+			BTOBB(mp->m_sb.sb_blocksize), 0,
+			&xfs_refcountbt_buf_ops);
+		if (!bp) {
+			error = -ENOMEM;
+			goto out_error;
+		}
+
+		xfs_btree_init_block(mp, bp, XFS_BTNUM_REFC,
+				     0, 0, agno, 0);
+
+		error = xfs_bwrite(bp);
+		xfs_buf_relse(bp);
+		if (error)
+			goto out_error;
+	}
+
+out_error:
+	return error;
+}
+
 static int
 xfs_growfs_data_private(
 	xfs_mount_t		*mp,		/* mount point for filesystem */
 	xfs_growfs_data_t	*in)		/* growfs data input struct */
 {
 	xfs_agf_t		*agf;
-	struct xfs_agfl		*agfl;
 	xfs_agi_t		*agi;
 	xfs_agnumber_t		agno;
 	xfs_extlen_t		agsize;
-	xfs_extlen_t		tmpsize;
-	xfs_alloc_rec_t		*arec;
 	xfs_buf_t		*bp;
-	int			bucket;
 	int			dpct;
 	int			error, saved_error = 0;
 	xfs_agnumber_t		nagcount;
@@ -141,318 +465,19 @@ xfs_growfs_data_private(
 	 */
 	nfree = 0;
 	for (agno = nagcount - 1; agno >= oagcount; agno--, new -= agsize) {
-		__be32	*agfl_bno;
-
-		/*
-		 * AG freespace header block
-		 */
-		bp = xfs_growfs_get_hdr_buf(mp,
-				XFS_AG_DADDR(mp, agno, XFS_AGF_DADDR(mp)),
-				XFS_FSS_TO_BB(mp, 1), 0,
-				&xfs_agf_buf_ops);
-		if (!bp) {
-			error = -ENOMEM;
-			goto error0;
-		}
 
-		agf = XFS_BUF_TO_AGF(bp);
-		agf->agf_magicnum = cpu_to_be32(XFS_AGF_MAGIC);
-		agf->agf_versionnum = cpu_to_be32(XFS_AGF_VERSION);
-		agf->agf_seqno = cpu_to_be32(agno);
 		if (agno == nagcount - 1)
-			agsize =
-				nb -
+			agsize = nb -
 				(agno * (xfs_rfsblock_t)mp->m_sb.sb_agblocks);
 		else
 			agsize = mp->m_sb.sb_agblocks;
-		agf->agf_length = cpu_to_be32(agsize);
-		agf->agf_roots[XFS_BTNUM_BNOi] = cpu_to_be32(XFS_BNO_BLOCK(mp));
-		agf->agf_roots[XFS_BTNUM_CNTi] = cpu_to_be32(XFS_CNT_BLOCK(mp));
-		agf->agf_levels[XFS_BTNUM_BNOi] = cpu_to_be32(1);
-		agf->agf_levels[XFS_BTNUM_CNTi] = cpu_to_be32(1);
-		if (xfs_sb_version_hasrmapbt(&mp->m_sb)) {
-			agf->agf_roots[XFS_BTNUM_RMAPi] =
-						cpu_to_be32(XFS_RMAP_BLOCK(mp));
-			agf->agf_levels[XFS_BTNUM_RMAPi] = cpu_to_be32(1);
-			agf->agf_rmap_blocks = cpu_to_be32(1);
-		}
-
-		agf->agf_flfirst = cpu_to_be32(1);
-		agf->agf_fllast = 0;
-		agf->agf_flcount = 0;
-		tmpsize = agsize - mp->m_ag_prealloc_blocks;
-		agf->agf_freeblks = cpu_to_be32(tmpsize);
-		agf->agf_longest = cpu_to_be32(tmpsize);
-		if (xfs_sb_version_hascrc(&mp->m_sb))
-			uuid_copy(&agf->agf_uuid, &mp->m_sb.sb_meta_uuid);
-		if (xfs_sb_version_hasreflink(&mp->m_sb)) {
-			agf->agf_refcount_root = cpu_to_be32(
-					xfs_refc_block(mp));
-			agf->agf_refcount_level = cpu_to_be32(1);
-			agf->agf_refcount_blocks = cpu_to_be32(1);
-		}
-
-		error = xfs_bwrite(bp);
-		xfs_buf_relse(bp);
-		if (error)
-			goto error0;
-
-		/*
-		 * AG freelist header block
-		 */
-		bp = xfs_growfs_get_hdr_buf(mp,
-				XFS_AG_DADDR(mp, agno, XFS_AGFL_DADDR(mp)),
-				XFS_FSS_TO_BB(mp, 1), 0,
-				&xfs_agfl_buf_ops);
-		if (!bp) {
-			error = -ENOMEM;
-			goto error0;
-		}
-
-		agfl = XFS_BUF_TO_AGFL(bp);
-		if (xfs_sb_version_hascrc(&mp->m_sb)) {
-			agfl->agfl_magicnum = cpu_to_be32(XFS_AGFL_MAGIC);
-			agfl->agfl_seqno = cpu_to_be32(agno);
-			uuid_copy(&agfl->agfl_uuid, &mp->m_sb.sb_meta_uuid);
-		}
-
-		agfl_bno = XFS_BUF_TO_AGFL_BNO(mp, bp);
-		for (bucket = 0; bucket < xfs_agfl_size(mp); bucket++)
-			agfl_bno[bucket] = cpu_to_be32(NULLAGBLOCK);
 
-		error = xfs_bwrite(bp);
-		xfs_buf_relse(bp);
+		error = xfs_grow_ag_headers(mp, agno, agsize, &nfree);
 		if (error)
 			goto error0;
-
-		/*
-		 * AG inode header block
-		 */
-		bp = xfs_growfs_get_hdr_buf(mp,
-				XFS_AG_DADDR(mp, agno, XFS_AGI_DADDR(mp)),
-				XFS_FSS_TO_BB(mp, 1), 0,
-				&xfs_agi_buf_ops);
-		if (!bp) {
-			error = -ENOMEM;
-			goto error0;
-		}
-
-		agi = XFS_BUF_TO_AGI(bp);
-		agi->agi_magicnum = cpu_to_be32(XFS_AGI_MAGIC);
-		agi->agi_versionnum = cpu_to_be32(XFS_AGI_VERSION);
-		agi->agi_seqno = cpu_to_be32(agno);
-		agi->agi_length = cpu_to_be32(agsize);
-		agi->agi_count = 0;
-		agi->agi_root = cpu_to_be32(XFS_IBT_BLOCK(mp));
-		agi->agi_level = cpu_to_be32(1);
-		agi->agi_freecount = 0;
-		agi->agi_newino = cpu_to_be32(NULLAGINO);
-		agi->agi_dirino = cpu_to_be32(NULLAGINO);
-		if (xfs_sb_version_hascrc(&mp->m_sb))
-			uuid_copy(&agi->agi_uuid, &mp->m_sb.sb_meta_uuid);
-		if (xfs_sb_version_hasfinobt(&mp->m_sb)) {
-			agi->agi_free_root = cpu_to_be32(XFS_FIBT_BLOCK(mp));
-			agi->agi_free_level = cpu_to_be32(1);
-		}
-		for (bucket = 0; bucket < XFS_AGI_UNLINKED_BUCKETS; bucket++)
-			agi->agi_unlinked[bucket] = cpu_to_be32(NULLAGINO);
-
-		error = xfs_bwrite(bp);
-		xfs_buf_relse(bp);
-		if (error)
-			goto error0;
-
-		/*
-		 * BNO btree root block
-		 */
-		bp = xfs_growfs_get_hdr_buf(mp,
-				XFS_AGB_TO_DADDR(mp, agno, XFS_BNO_BLOCK(mp)),
-				BTOBB(mp->m_sb.sb_blocksize), 0,
-				&xfs_allocbt_buf_ops);
-
-		if (!bp) {
-			error = -ENOMEM;
-			goto error0;
-		}
-
-		xfs_btree_init_block(mp, bp, XFS_BTNUM_BNO, 0, 1, agno, 0);
-
-		arec = XFS_ALLOC_REC_ADDR(mp, XFS_BUF_TO_BLOCK(bp), 1);
-		arec->ar_startblock = cpu_to_be32(mp->m_ag_prealloc_blocks);
-		arec->ar_blockcount = cpu_to_be32(
-			agsize - be32_to_cpu(arec->ar_startblock));
-
-		error = xfs_bwrite(bp);
-		xfs_buf_relse(bp);
-		if (error)
-			goto error0;
-
-		/*
-		 * CNT btree root block
-		 */
-		bp = xfs_growfs_get_hdr_buf(mp,
-				XFS_AGB_TO_DADDR(mp, agno, XFS_CNT_BLOCK(mp)),
-				BTOBB(mp->m_sb.sb_blocksize), 0,
-				&xfs_allocbt_buf_ops);
-		if (!bp) {
-			error = -ENOMEM;
-			goto error0;
-		}
-
-		xfs_btree_init_block(mp, bp, XFS_BTNUM_CNT, 0, 1, agno, 0);
-
-		arec = XFS_ALLOC_REC_ADDR(mp, XFS_BUF_TO_BLOCK(bp), 1);
-		arec->ar_startblock = cpu_to_be32(mp->m_ag_prealloc_blocks);
-		arec->ar_blockcount = cpu_to_be32(
-			agsize - be32_to_cpu(arec->ar_startblock));
-		nfree += be32_to_cpu(arec->ar_blockcount);
-
-		error = xfs_bwrite(bp);
-		xfs_buf_relse(bp);
-		if (error)
-			goto error0;
-
-		/* RMAP btree root block */
-		if (xfs_sb_version_hasrmapbt(&mp->m_sb)) {
-			struct xfs_rmap_rec	*rrec;
-			struct xfs_btree_block	*block;
-
-			bp = xfs_growfs_get_hdr_buf(mp,
-				XFS_AGB_TO_DADDR(mp, agno, XFS_RMAP_BLOCK(mp)),
-				BTOBB(mp->m_sb.sb_blocksize), 0,
-				&xfs_rmapbt_buf_ops);
-			if (!bp) {
-				error = -ENOMEM;
-				goto error0;
-			}
-
-			xfs_btree_init_block(mp, bp, XFS_BTNUM_RMAP, 0, 0,
-						agno, 0);
-			block = XFS_BUF_TO_BLOCK(bp);
-
-
-			/*
-			 * mark the AG header regions as static metadata The BNO
-			 * btree block is the first block after the headers, so
-			 * it's location defines the size of region the static
-			 * metadata consumes.
-			 *
-			 * Note: unlike mkfs, we never have to account for log
-			 * space when growing the data regions
-			 */
-			rrec = XFS_RMAP_REC_ADDR(block, 1);
-			rrec->rm_startblock = 0;
-			rrec->rm_blockcount = cpu_to_be32(XFS_BNO_BLOCK(mp));
-			rrec->rm_owner = cpu_to_be64(XFS_RMAP_OWN_FS);
-			rrec->rm_offset = 0;
-			be16_add_cpu(&block->bb_numrecs, 1);
-
-			/* account freespace btree root blocks */
-			rrec = XFS_RMAP_REC_ADDR(block, 2);
-			rrec->rm_startblock = cpu_to_be32(XFS_BNO_BLOCK(mp));
-			rrec->rm_blockcount = cpu_to_be32(2);
-			rrec->rm_owner = cpu_to_be64(XFS_RMAP_OWN_AG);
-			rrec->rm_offset = 0;
-			be16_add_cpu(&block->bb_numrecs, 1);
-
-			/* account inode btree root blocks */
-			rrec = XFS_RMAP_REC_ADDR(block, 3);
-			rrec->rm_startblock = cpu_to_be32(XFS_IBT_BLOCK(mp));
-			rrec->rm_blockcount = cpu_to_be32(XFS_RMAP_BLOCK(mp) -
-							XFS_IBT_BLOCK(mp));
-			rrec->rm_owner = cpu_to_be64(XFS_RMAP_OWN_INOBT);
-			rrec->rm_offset = 0;
-			be16_add_cpu(&block->bb_numrecs, 1);
-
-			/* account for rmap btree root */
-			rrec = XFS_RMAP_REC_ADDR(block, 4);
-			rrec->rm_startblock = cpu_to_be32(XFS_RMAP_BLOCK(mp));
-			rrec->rm_blockcount = cpu_to_be32(1);
-			rrec->rm_owner = cpu_to_be64(XFS_RMAP_OWN_AG);
-			rrec->rm_offset = 0;
-			be16_add_cpu(&block->bb_numrecs, 1);
-
-			/* account for refc btree root */
-			if (xfs_sb_version_hasreflink(&mp->m_sb)) {
-				rrec = XFS_RMAP_REC_ADDR(block, 5);
-				rrec->rm_startblock = cpu_to_be32(
-						xfs_refc_block(mp));
-				rrec->rm_blockcount = cpu_to_be32(1);
-				rrec->rm_owner = cpu_to_be64(XFS_RMAP_OWN_REFC);
-				rrec->rm_offset = 0;
-				be16_add_cpu(&block->bb_numrecs, 1);
-			}
-
-			error = xfs_bwrite(bp);
-			xfs_buf_relse(bp);
-			if (error)
-				goto error0;
-		}
-
-		/*
-		 * INO btree root block
-		 */
-		bp = xfs_growfs_get_hdr_buf(mp,
-				XFS_AGB_TO_DADDR(mp, agno, XFS_IBT_BLOCK(mp)),
-				BTOBB(mp->m_sb.sb_blocksize), 0,
-				&xfs_inobt_buf_ops);
-		if (!bp) {
-			error = -ENOMEM;
-			goto error0;
-		}
-
-		xfs_btree_init_block(mp, bp, XFS_BTNUM_INO , 0, 0, agno, 0);
-
-		error = xfs_bwrite(bp);
-		xfs_buf_relse(bp);
-		if (error)
-			goto error0;
-
-		/*
-		 * FINO btree root block
-		 */
-		if (xfs_sb_version_hasfinobt(&mp->m_sb)) {
-			bp = xfs_growfs_get_hdr_buf(mp,
-				XFS_AGB_TO_DADDR(mp, agno, XFS_FIBT_BLOCK(mp)),
-				BTOBB(mp->m_sb.sb_blocksize), 0,
-				&xfs_inobt_buf_ops);
-			if (!bp) {
-				error = -ENOMEM;
-				goto error0;
-			}
-
-			xfs_btree_init_block(mp, bp, XFS_BTNUM_FINO,
-						     0, 0, agno, 0);
-
-			error = xfs_bwrite(bp);
-			xfs_buf_relse(bp);
-			if (error)
-				goto error0;
-		}
-
-		/*
-		 * refcount btree root block
-		 */
-		if (xfs_sb_version_hasreflink(&mp->m_sb)) {
-			bp = xfs_growfs_get_hdr_buf(mp,
-				XFS_AGB_TO_DADDR(mp, agno, xfs_refc_block(mp)),
-				BTOBB(mp->m_sb.sb_blocksize), 0,
-				&xfs_refcountbt_buf_ops);
-			if (!bp) {
-				error = -ENOMEM;
-				goto error0;
-			}
-
-			xfs_btree_init_block(mp, bp, XFS_BTNUM_REFC,
-					     0, 0, agno, 0);
-
-			error = xfs_bwrite(bp);
-			xfs_buf_relse(bp);
-			if (error)
-				goto error0;
-		}
 	}
 	xfs_trans_agblocks_delta(tp, nfree);
+
 	/*
 	 * There are new blocks in the old last a.g.
 	 */
-- 
2.17.0


^ permalink raw reply related	[flat|nested] 17+ messages in thread

* [PATCH 03/10] xfs: convert growfs AG header init to use buffer lists
  2018-05-14  4:18 [PATCH 0/10 v3] xfs: refactor and tablise growfs Dave Chinner
  2018-05-14  4:18 ` [PATCH 01/10] xfs: one-shot cached buffers Dave Chinner
  2018-05-14  4:18 ` [PATCH 02/10] xfs: factor out AG header initialisation from growfs core Dave Chinner
@ 2018-05-14  4:18 ` Dave Chinner
  2018-05-14  4:18 ` [PATCH 04/10] xfs: factor ag btree root block initialisation Dave Chinner
                   ` (6 subsequent siblings)
  9 siblings, 0 replies; 17+ messages in thread
From: Dave Chinner @ 2018-05-14  4:18 UTC (permalink / raw)
  To: linux-xfs

From: Dave Chinner <dchinner@redhat.com>

We currently write all new AG headers synchronously, which can be
slow for large grow operations. All we really need to do is ensure
all the headers are on disk before we run the growfs transaction, so
convert this to a buffer list and a delayed write operation. We
block waiting for the delayed write buffer submission to complete,
so this will fulfill the requirement to have all the buffers written
correctly before proceeding.

Signed-Off-By: Dave Chinner <dchinner@redhat.com>
Reviewed-by: Brian Foster <bfoster@redhat.com>
Reviewed-by: Darrick J. Wong <darrick.wong@oracle.com>
---
 fs/xfs/xfs_fsops.c | 74 +++++++++++++++++++++-------------------------
 1 file changed, 33 insertions(+), 41 deletions(-)

diff --git a/fs/xfs/xfs_fsops.c b/fs/xfs/xfs_fsops.c
index 391d1938a6c8..4b560caaf397 100644
--- a/fs/xfs/xfs_fsops.c
+++ b/fs/xfs/xfs_fsops.c
@@ -81,7 +81,8 @@ xfs_grow_ag_headers(
 	struct xfs_mount	*mp,
 	xfs_agnumber_t		agno,
 	xfs_extlen_t		agsize,
-	xfs_rfsblock_t		*nfree)
+	xfs_rfsblock_t		*nfree,
+	struct list_head	*buffer_list)
 {
 	struct xfs_agf		*agf;
 	struct xfs_agi		*agi;
@@ -135,11 +136,8 @@ xfs_grow_ag_headers(
 		agf->agf_refcount_level = cpu_to_be32(1);
 		agf->agf_refcount_blocks = cpu_to_be32(1);
 	}
-
-	error = xfs_bwrite(bp);
+	xfs_buf_delwri_queue(bp, buffer_list);
 	xfs_buf_relse(bp);
-	if (error)
-		goto out_error;
 
 	/*
 	 * AG freelist header block
@@ -164,10 +162,8 @@ xfs_grow_ag_headers(
 	for (bucket = 0; bucket < xfs_agfl_size(mp); bucket++)
 		agfl_bno[bucket] = cpu_to_be32(NULLAGBLOCK);
 
-	error = xfs_bwrite(bp);
+	xfs_buf_delwri_queue(bp, buffer_list);
 	xfs_buf_relse(bp);
-	if (error)
-		goto out_error;
 
 	/*
 	 * AG inode header block
@@ -201,10 +197,8 @@ xfs_grow_ag_headers(
 	for (bucket = 0; bucket < XFS_AGI_UNLINKED_BUCKETS; bucket++)
 		agi->agi_unlinked[bucket] = cpu_to_be32(NULLAGINO);
 
-	error = xfs_bwrite(bp);
+	xfs_buf_delwri_queue(bp, buffer_list);
 	xfs_buf_relse(bp);
-	if (error)
-		goto out_error;
 
 	/*
 	 * BNO btree root block
@@ -226,10 +220,8 @@ xfs_grow_ag_headers(
 	arec->ar_blockcount = cpu_to_be32(
 		agsize - be32_to_cpu(arec->ar_startblock));
 
-	error = xfs_bwrite(bp);
+	xfs_buf_delwri_queue(bp, buffer_list);
 	xfs_buf_relse(bp);
-	if (error)
-		goto out_error;
 
 	/*
 	 * CNT btree root block
@@ -251,10 +243,8 @@ xfs_grow_ag_headers(
 		agsize - be32_to_cpu(arec->ar_startblock));
 	*nfree += be32_to_cpu(arec->ar_blockcount);
 
-	error = xfs_bwrite(bp);
+	xfs_buf_delwri_queue(bp, buffer_list);
 	xfs_buf_relse(bp);
-	if (error)
-		goto out_error;
 
 	/* RMAP btree root block */
 	if (xfs_sb_version_hasrmapbt(&mp->m_sb)) {
@@ -326,10 +316,8 @@ xfs_grow_ag_headers(
 			be16_add_cpu(&block->bb_numrecs, 1);
 		}
 
-		error = xfs_bwrite(bp);
+		xfs_buf_delwri_queue(bp, buffer_list);
 		xfs_buf_relse(bp);
-		if (error)
-			goto out_error;
 	}
 
 	/*
@@ -345,11 +333,8 @@ xfs_grow_ag_headers(
 	}
 
 	xfs_btree_init_block(mp, bp, XFS_BTNUM_INO , 0, 0, agno, 0);
-
-	error = xfs_bwrite(bp);
+	xfs_buf_delwri_queue(bp, buffer_list);
 	xfs_buf_relse(bp);
-	if (error)
-		goto out_error;
 
 	/*
 	 * FINO btree root block
@@ -364,13 +349,9 @@ xfs_grow_ag_headers(
 			goto out_error;
 		}
 
-		xfs_btree_init_block(mp, bp, XFS_BTNUM_FINO,
-					     0, 0, agno, 0);
-
-		error = xfs_bwrite(bp);
+		xfs_btree_init_block(mp, bp, XFS_BTNUM_FINO, 0, 0, agno, 0);
+		xfs_buf_delwri_queue(bp, buffer_list);
 		xfs_buf_relse(bp);
-		if (error)
-			goto out_error;
 	}
 
 	/*
@@ -386,13 +367,9 @@ xfs_grow_ag_headers(
 			goto out_error;
 		}
 
-		xfs_btree_init_block(mp, bp, XFS_BTNUM_REFC,
-				     0, 0, agno, 0);
-
-		error = xfs_bwrite(bp);
+		xfs_btree_init_block(mp, bp, XFS_BTNUM_REFC, 0, 0, agno, 0);
+		xfs_buf_delwri_queue(bp, buffer_list);
 		xfs_buf_relse(bp);
-		if (error)
-			goto out_error;
 	}
 
 out_error:
@@ -419,6 +396,7 @@ xfs_growfs_data_private(
 	xfs_agnumber_t		oagcount;
 	int			pct;
 	xfs_trans_t		*tp;
+	LIST_HEAD		(buffer_list);
 
 	nb = in->newblocks;
 	pct = in->imaxpct;
@@ -459,9 +437,16 @@ xfs_growfs_data_private(
 		return error;
 
 	/*
-	 * Write new AG headers to disk. Non-transactional, but written
-	 * synchronously so they are completed prior to the growfs transaction
-	 * being logged.
+	 * Write new AG headers to disk. Non-transactional, but need to be
+	 * written and completed prior to the growfs transaction being logged.
+	 * To do this, we use a delayed write buffer list and wait for
+	 * submission and IO completion of the list as a whole. This allows the
+	 * IO subsystem to merge all the AG headers in a single AG into a single
+	 * IO and hide most of the latency of the IO from us.
+	 *
+	 * This also means that if we get an error whilst building the buffer
+	 * list to write, we can cancel the entire list without having written
+	 * anything.
 	 */
 	nfree = 0;
 	for (agno = nagcount - 1; agno >= oagcount; agno--, new -= agsize) {
@@ -472,10 +457,17 @@ xfs_growfs_data_private(
 		else
 			agsize = mp->m_sb.sb_agblocks;
 
-		error = xfs_grow_ag_headers(mp, agno, agsize, &nfree);
-		if (error)
+		error = xfs_grow_ag_headers(mp, agno, agsize, &nfree,
+					    &buffer_list);
+		if (error) {
+			xfs_buf_delwri_cancel(&buffer_list);
 			goto error0;
+		}
 	}
+	error = xfs_buf_delwri_submit(&buffer_list);
+	if (error)
+		goto error0;
+
 	xfs_trans_agblocks_delta(tp, nfree);
 
 	/*
-- 
2.17.0


^ permalink raw reply related	[flat|nested] 17+ messages in thread

* [PATCH 04/10] xfs: factor ag btree root block initialisation
  2018-05-14  4:18 [PATCH 0/10 v3] xfs: refactor and tablise growfs Dave Chinner
                   ` (2 preceding siblings ...)
  2018-05-14  4:18 ` [PATCH 03/10] xfs: convert growfs AG header init to use buffer lists Dave Chinner
@ 2018-05-14  4:18 ` Dave Chinner
  2018-05-14 19:25   ` Darrick J. Wong
  2018-05-14  4:18 ` [PATCH 05/10] xfs: turn ag header initialisation into a table driven operation Dave Chinner
                   ` (5 subsequent siblings)
  9 siblings, 1 reply; 17+ messages in thread
From: Dave Chinner @ 2018-05-14  4:18 UTC (permalink / raw)
  To: linux-xfs

From: Dave Chinner <dchinner@redhat.com>

Cookie cutter code, easily factored.

Signed-Off-By: Dave Chinner <dchinner@redhat.com>
---
 fs/xfs/xfs_fsops.c | 481 ++++++++++++++++++++++++---------------------
 1 file changed, 259 insertions(+), 222 deletions(-)

diff --git a/fs/xfs/xfs_fsops.c b/fs/xfs/xfs_fsops.c
index 4b560caaf397..ec3c51ee8134 100644
--- a/fs/xfs/xfs_fsops.c
+++ b/fs/xfs/xfs_fsops.c
@@ -71,46 +71,140 @@ xfs_growfs_get_hdr_buf(
 	return bp;
 }
 
+struct aghdr_init_data {
+	/* per ag data */
+	xfs_agnumber_t		agno;		/* ag to init */
+	xfs_extlen_t		agsize;		/* new AG size */
+	struct list_head	buffer_list;	/* buffer writeback list */
+	xfs_rfsblock_t		nfree;		/* cumulative new free space */
+
+	/* per header data */
+	xfs_daddr_t		daddr;		/* header location */
+	size_t			numblks;	/* size of header */
+	xfs_btnum_t		type;		/* type of btree root block */
+};
+
 /*
- * Write new AG headers to disk. Non-transactional, but written
- * synchronously so they are completed prior to the growfs transaction
- * being logged.
+ * Generic btree root block init function
  */
-static int
-xfs_grow_ag_headers(
+static void
+xfs_btroot_init(
 	struct xfs_mount	*mp,
-	xfs_agnumber_t		agno,
-	xfs_extlen_t		agsize,
-	xfs_rfsblock_t		*nfree,
-	struct list_head	*buffer_list)
+	struct xfs_buf		*bp,
+	struct aghdr_init_data	*id)
 {
-	struct xfs_agf		*agf;
-	struct xfs_agi		*agi;
-	struct xfs_agfl		*agfl;
-	__be32			*agfl_bno;
-	xfs_alloc_rec_t		*arec;
-	struct xfs_buf		*bp;
-	int			bucket;
-	xfs_extlen_t		tmpsize;
-	int			error = 0;
+	xfs_btree_init_block(mp, bp, id->type, 0, 0, id->agno, 0);
+}
+
+/*
+ * Alloc btree root block init functions
+ */
+static void
+xfs_bnoroot_init(
+	struct xfs_mount	*mp,
+	struct xfs_buf		*bp,
+	struct aghdr_init_data	*id)
+{
+	struct xfs_alloc_rec	*arec;
+
+	xfs_btree_init_block(mp, bp, XFS_BTNUM_BNO, 0, 1, id->agno, 0);
+	arec = XFS_ALLOC_REC_ADDR(mp, XFS_BUF_TO_BLOCK(bp), 1);
+	arec->ar_startblock = cpu_to_be32(mp->m_ag_prealloc_blocks);
+	arec->ar_blockcount = cpu_to_be32(id->agsize -
+					  be32_to_cpu(arec->ar_startblock));
+}
+
+static void
+xfs_cntroot_init(
+	struct xfs_mount	*mp,
+	struct xfs_buf		*bp,
+	struct aghdr_init_data	*id)
+{
+	struct xfs_alloc_rec	*arec;
+
+	xfs_btree_init_block(mp, bp, XFS_BTNUM_CNT, 0, 1, id->agno, 0);
+	arec = XFS_ALLOC_REC_ADDR(mp, XFS_BUF_TO_BLOCK(bp), 1);
+	arec->ar_startblock = cpu_to_be32(mp->m_ag_prealloc_blocks);
+	arec->ar_blockcount = cpu_to_be32(id->agsize -
+					  be32_to_cpu(arec->ar_startblock));
+}
+
+/*
+ * Reverse map root block init
+ */
+static void
+xfs_rmaproot_init(
+	struct xfs_mount	*mp,
+	struct xfs_buf		*bp,
+	struct aghdr_init_data	*id)
+{
+	struct xfs_btree_block	*block = XFS_BUF_TO_BLOCK(bp);
+	struct xfs_rmap_rec	*rrec;
+
+	xfs_btree_init_block(mp, bp, XFS_BTNUM_RMAP, 0, 4, id->agno, 0);
 
 	/*
-	 * AG freespace header block
+	 * mark the AG header regions as static metadata The BNO
+	 * btree block is the first block after the headers, so
+	 * it's location defines the size of region the static
+	 * metadata consumes.
+	 *
+	 * Note: unlike mkfs, we never have to account for log
+	 * space when growing the data regions
 	 */
-	bp = xfs_growfs_get_hdr_buf(mp,
-			XFS_AG_DADDR(mp, agno, XFS_AGF_DADDR(mp)),
-			XFS_FSS_TO_BB(mp, 1), 0,
-			&xfs_agf_buf_ops);
-	if (!bp) {
-		error = -ENOMEM;
-		goto out_error;
+	rrec = XFS_RMAP_REC_ADDR(block, 1);
+	rrec->rm_startblock = 0;
+	rrec->rm_blockcount = cpu_to_be32(XFS_BNO_BLOCK(mp));
+	rrec->rm_owner = cpu_to_be64(XFS_RMAP_OWN_FS);
+	rrec->rm_offset = 0;
+
+	/* account freespace btree root blocks */
+	rrec = XFS_RMAP_REC_ADDR(block, 2);
+	rrec->rm_startblock = cpu_to_be32(XFS_BNO_BLOCK(mp));
+	rrec->rm_blockcount = cpu_to_be32(2);
+	rrec->rm_owner = cpu_to_be64(XFS_RMAP_OWN_AG);
+	rrec->rm_offset = 0;
+
+	/* account inode btree root blocks */
+	rrec = XFS_RMAP_REC_ADDR(block, 3);
+	rrec->rm_startblock = cpu_to_be32(XFS_IBT_BLOCK(mp));
+	rrec->rm_blockcount = cpu_to_be32(XFS_RMAP_BLOCK(mp) -
+					  XFS_IBT_BLOCK(mp));
+	rrec->rm_owner = cpu_to_be64(XFS_RMAP_OWN_INOBT);
+	rrec->rm_offset = 0;
+
+	/* account for rmap btree root */
+	rrec = XFS_RMAP_REC_ADDR(block, 4);
+	rrec->rm_startblock = cpu_to_be32(XFS_RMAP_BLOCK(mp));
+	rrec->rm_blockcount = cpu_to_be32(1);
+	rrec->rm_owner = cpu_to_be64(XFS_RMAP_OWN_AG);
+	rrec->rm_offset = 0;
+
+	/* account for refc btree root */
+	if (xfs_sb_version_hasreflink(&mp->m_sb)) {
+		rrec = XFS_RMAP_REC_ADDR(block, 5);
+		rrec->rm_startblock = cpu_to_be32(xfs_refc_block(mp));
+		rrec->rm_blockcount = cpu_to_be32(1);
+		rrec->rm_owner = cpu_to_be64(XFS_RMAP_OWN_REFC);
+		rrec->rm_offset = 0;
+		be16_add_cpu(&block->bb_numrecs, 1);
 	}
+}
+
+
+static void
+xfs_agfblock_init(
+	struct xfs_mount	*mp,
+	struct xfs_buf		*bp,
+	struct aghdr_init_data	*id)
+{
+	struct xfs_agf		*agf = XFS_BUF_TO_AGF(bp);
+	xfs_extlen_t		tmpsize;
 
-	agf = XFS_BUF_TO_AGF(bp);
 	agf->agf_magicnum = cpu_to_be32(XFS_AGF_MAGIC);
 	agf->agf_versionnum = cpu_to_be32(XFS_AGF_VERSION);
-	agf->agf_seqno = cpu_to_be32(agno);
-	agf->agf_length = cpu_to_be32(agsize);
+	agf->agf_seqno = cpu_to_be32(id->agno);
+	agf->agf_length = cpu_to_be32(id->agsize);
 	agf->agf_roots[XFS_BTNUM_BNOi] = cpu_to_be32(XFS_BNO_BLOCK(mp));
 	agf->agf_roots[XFS_BTNUM_CNTi] = cpu_to_be32(XFS_CNT_BLOCK(mp));
 	agf->agf_levels[XFS_BTNUM_BNOi] = cpu_to_be32(1);
@@ -125,7 +219,7 @@ xfs_grow_ag_headers(
 	agf->agf_flfirst = cpu_to_be32(1);
 	agf->agf_fllast = 0;
 	agf->agf_flcount = 0;
-	tmpsize = agsize - mp->m_ag_prealloc_blocks;
+	tmpsize = id->agsize - mp->m_ag_prealloc_blocks;
 	agf->agf_freeblks = cpu_to_be32(tmpsize);
 	agf->agf_longest = cpu_to_be32(tmpsize);
 	if (xfs_sb_version_hascrc(&mp->m_sb))
@@ -136,52 +230,42 @@ xfs_grow_ag_headers(
 		agf->agf_refcount_level = cpu_to_be32(1);
 		agf->agf_refcount_blocks = cpu_to_be32(1);
 	}
-	xfs_buf_delwri_queue(bp, buffer_list);
-	xfs_buf_relse(bp);
+}
 
-	/*
-	 * AG freelist header block
-	 */
-	bp = xfs_growfs_get_hdr_buf(mp,
-			XFS_AG_DADDR(mp, agno, XFS_AGFL_DADDR(mp)),
-			XFS_FSS_TO_BB(mp, 1), 0,
-			&xfs_agfl_buf_ops);
-	if (!bp) {
-		error = -ENOMEM;
-		goto out_error;
-	}
+static void
+xfs_agflblock_init(
+	struct xfs_mount	*mp,
+	struct xfs_buf		*bp,
+	struct aghdr_init_data	*id)
+{
+	struct xfs_agfl		*agfl = XFS_BUF_TO_AGFL(bp);
+	__be32			*agfl_bno;
+	int			bucket;
 
-	agfl = XFS_BUF_TO_AGFL(bp);
 	if (xfs_sb_version_hascrc(&mp->m_sb)) {
 		agfl->agfl_magicnum = cpu_to_be32(XFS_AGFL_MAGIC);
-		agfl->agfl_seqno = cpu_to_be32(agno);
+		agfl->agfl_seqno = cpu_to_be32(id->agno);
 		uuid_copy(&agfl->agfl_uuid, &mp->m_sb.sb_meta_uuid);
 	}
 
 	agfl_bno = XFS_BUF_TO_AGFL_BNO(mp, bp);
 	for (bucket = 0; bucket < xfs_agfl_size(mp); bucket++)
 		agfl_bno[bucket] = cpu_to_be32(NULLAGBLOCK);
+}
 
-	xfs_buf_delwri_queue(bp, buffer_list);
-	xfs_buf_relse(bp);
-
-	/*
-	 * AG inode header block
-	 */
-	bp = xfs_growfs_get_hdr_buf(mp,
-			XFS_AG_DADDR(mp, agno, XFS_AGI_DADDR(mp)),
-			XFS_FSS_TO_BB(mp, 1), 0,
-			&xfs_agi_buf_ops);
-	if (!bp) {
-		error = -ENOMEM;
-		goto out_error;
-	}
+static void
+xfs_agiblock_init(
+	struct xfs_mount	*mp,
+	struct xfs_buf		*bp,
+	struct aghdr_init_data	*id)
+{
+	struct xfs_agi		*agi = XFS_BUF_TO_AGI(bp);
+	int			bucket;
 
-	agi = XFS_BUF_TO_AGI(bp);
 	agi->agi_magicnum = cpu_to_be32(XFS_AGI_MAGIC);
 	agi->agi_versionnum = cpu_to_be32(XFS_AGI_VERSION);
-	agi->agi_seqno = cpu_to_be32(agno);
-	agi->agi_length = cpu_to_be32(agsize);
+	agi->agi_seqno = cpu_to_be32(id->agno);
+	agi->agi_length = cpu_to_be32(id->agsize);
 	agi->agi_count = 0;
 	agi->agi_root = cpu_to_be32(XFS_IBT_BLOCK(mp));
 	agi->agi_level = cpu_to_be32(1);
@@ -196,180 +280,133 @@ xfs_grow_ag_headers(
 	}
 	for (bucket = 0; bucket < XFS_AGI_UNLINKED_BUCKETS; bucket++)
 		agi->agi_unlinked[bucket] = cpu_to_be32(NULLAGINO);
+}
 
-	xfs_buf_delwri_queue(bp, buffer_list);
-	xfs_buf_relse(bp);
-
-	/*
-	 * BNO btree root block
-	 */
-	bp = xfs_growfs_get_hdr_buf(mp,
-			XFS_AGB_TO_DADDR(mp, agno, XFS_BNO_BLOCK(mp)),
-			BTOBB(mp->m_sb.sb_blocksize), 0,
-			&xfs_allocbt_buf_ops);
+static int
+xfs_growfs_init_aghdr(
+	struct xfs_mount	*mp,
+	struct aghdr_init_data	*id,
+	void			(*work)(struct xfs_mount *, struct xfs_buf *,
+					struct aghdr_init_data *),
+	const struct xfs_buf_ops *ops)
 
-	if (!bp) {
-		error = -ENOMEM;
-		goto out_error;
-	}
+{
+	struct xfs_buf		*bp;
 
-	xfs_btree_init_block(mp, bp, XFS_BTNUM_BNO, 0, 1, agno, 0);
+	bp = xfs_growfs_get_hdr_buf(mp, id->daddr, id->numblks, 0, ops);
+	if (!bp)
+		return -ENOMEM;
 
-	arec = XFS_ALLOC_REC_ADDR(mp, XFS_BUF_TO_BLOCK(bp), 1);
-	arec->ar_startblock = cpu_to_be32(mp->m_ag_prealloc_blocks);
-	arec->ar_blockcount = cpu_to_be32(
-		agsize - be32_to_cpu(arec->ar_startblock));
+	(*work)(mp, bp, id);
 
-	xfs_buf_delwri_queue(bp, buffer_list);
+	xfs_buf_delwri_queue(bp, &id->buffer_list);
 	xfs_buf_relse(bp);
+	return 0;
+}
 
-	/*
-	 * CNT btree root block
-	 */
-	bp = xfs_growfs_get_hdr_buf(mp,
-			XFS_AGB_TO_DADDR(mp, agno, XFS_CNT_BLOCK(mp)),
-			BTOBB(mp->m_sb.sb_blocksize), 0,
-			&xfs_allocbt_buf_ops);
-	if (!bp) {
-		error = -ENOMEM;
-		goto out_error;
-	}
-
-	xfs_btree_init_block(mp, bp, XFS_BTNUM_CNT, 0, 1, agno, 0);
+/*
+ * Write new AG headers to disk. Non-transactional, but written
+ * synchronously so they are completed prior to the growfs transaction
+ * being logged.
+ */
+static int
+xfs_grow_ag_headers(
+	struct xfs_mount	*mp,
+	struct aghdr_init_data	*id)
 
-	arec = XFS_ALLOC_REC_ADDR(mp, XFS_BUF_TO_BLOCK(bp), 1);
-	arec->ar_startblock = cpu_to_be32(mp->m_ag_prealloc_blocks);
-	arec->ar_blockcount = cpu_to_be32(
-		agsize - be32_to_cpu(arec->ar_startblock));
-	*nfree += be32_to_cpu(arec->ar_blockcount);
+{
+	int			error = 0;
 
-	xfs_buf_delwri_queue(bp, buffer_list);
-	xfs_buf_relse(bp);
+	/* Account for AG free space in new AG */
+	id->nfree += id->agsize - mp->m_ag_prealloc_blocks;
 
-	/* RMAP btree root block */
-	if (xfs_sb_version_hasrmapbt(&mp->m_sb)) {
-		struct xfs_rmap_rec	*rrec;
-		struct xfs_btree_block	*block;
-
-		bp = xfs_growfs_get_hdr_buf(mp,
-			XFS_AGB_TO_DADDR(mp, agno, XFS_RMAP_BLOCK(mp)),
-			BTOBB(mp->m_sb.sb_blocksize), 0,
-			&xfs_rmapbt_buf_ops);
-		if (!bp) {
-			error = -ENOMEM;
-			goto out_error;
-		}
+	/* AG freespace header block */
+	id->daddr = XFS_AG_DADDR(mp, id->agno, XFS_AGF_DADDR(mp));
+	id->numblks = XFS_FSS_TO_BB(mp, 1);
+	error = xfs_growfs_init_aghdr(mp, id, xfs_agfblock_init,
+					&xfs_agf_buf_ops);
+	if (error)
+		goto out_error;
 
-		xfs_btree_init_block(mp, bp, XFS_BTNUM_RMAP, 0, 0,
-					agno, 0);
-		block = XFS_BUF_TO_BLOCK(bp);
+	/* AG freelist header block */
+	id->daddr = XFS_AG_DADDR(mp, id->agno, XFS_AGFL_DADDR(mp));
+	id->numblks = XFS_FSS_TO_BB(mp, 1);
+	error = xfs_growfs_init_aghdr(mp, id, xfs_agflblock_init,
+					&xfs_agfl_buf_ops);
+	if (error)
+		goto out_error;
 
+	/* AG inode header block */
+	id->daddr = XFS_AG_DADDR(mp, id->agno, XFS_AGI_DADDR(mp));
+	id->numblks = XFS_FSS_TO_BB(mp, 1);
+	error = xfs_growfs_init_aghdr(mp, id, xfs_agiblock_init,
+					&xfs_agi_buf_ops);
+	if (error)
+		goto out_error;
 
-		/*
-		 * mark the AG header regions as static metadata The BNO
-		 * btree block is the first block after the headers, so
-		 * it's location defines the size of region the static
-		 * metadata consumes.
-		 *
-		 * Note: unlike mkfs, we never have to account for log
-		 * space when growing the data regions
-		 */
-		rrec = XFS_RMAP_REC_ADDR(block, 1);
-		rrec->rm_startblock = 0;
-		rrec->rm_blockcount = cpu_to_be32(XFS_BNO_BLOCK(mp));
-		rrec->rm_owner = cpu_to_be64(XFS_RMAP_OWN_FS);
-		rrec->rm_offset = 0;
-		be16_add_cpu(&block->bb_numrecs, 1);
 
-		/* account freespace btree root blocks */
-		rrec = XFS_RMAP_REC_ADDR(block, 2);
-		rrec->rm_startblock = cpu_to_be32(XFS_BNO_BLOCK(mp));
-		rrec->rm_blockcount = cpu_to_be32(2);
-		rrec->rm_owner = cpu_to_be64(XFS_RMAP_OWN_AG);
-		rrec->rm_offset = 0;
-		be16_add_cpu(&block->bb_numrecs, 1);
+	/* BNO btree root block */
+	id->daddr = XFS_AGB_TO_DADDR(mp, id->agno, XFS_BNO_BLOCK(mp));
+	id->numblks = BTOBB(mp->m_sb.sb_blocksize);
+	error = xfs_growfs_init_aghdr(mp, id, xfs_bnoroot_init,
+				   &xfs_allocbt_buf_ops);
+	if (error)
+		goto out_error;
 
-		/* account inode btree root blocks */
-		rrec = XFS_RMAP_REC_ADDR(block, 3);
-		rrec->rm_startblock = cpu_to_be32(XFS_IBT_BLOCK(mp));
-		rrec->rm_blockcount = cpu_to_be32(XFS_RMAP_BLOCK(mp) -
-						XFS_IBT_BLOCK(mp));
-		rrec->rm_owner = cpu_to_be64(XFS_RMAP_OWN_INOBT);
-		rrec->rm_offset = 0;
-		be16_add_cpu(&block->bb_numrecs, 1);
 
-		/* account for rmap btree root */
-		rrec = XFS_RMAP_REC_ADDR(block, 4);
-		rrec->rm_startblock = cpu_to_be32(XFS_RMAP_BLOCK(mp));
-		rrec->rm_blockcount = cpu_to_be32(1);
-		rrec->rm_owner = cpu_to_be64(XFS_RMAP_OWN_AG);
-		rrec->rm_offset = 0;
-		be16_add_cpu(&block->bb_numrecs, 1);
+	/* CNT btree root block */
+	id->daddr = XFS_AGB_TO_DADDR(mp, id->agno, XFS_CNT_BLOCK(mp));
+	id->numblks = BTOBB(mp->m_sb.sb_blocksize);
+	error = xfs_growfs_init_aghdr(mp, id, xfs_cntroot_init,
+				   &xfs_allocbt_buf_ops);
+	if (error)
+		goto out_error;
 
-		/* account for refc btree root */
-		if (xfs_sb_version_hasreflink(&mp->m_sb)) {
-			rrec = XFS_RMAP_REC_ADDR(block, 5);
-			rrec->rm_startblock = cpu_to_be32(xfs_refc_block(mp));
-			rrec->rm_blockcount = cpu_to_be32(1);
-			rrec->rm_owner = cpu_to_be64(XFS_RMAP_OWN_REFC);
-			rrec->rm_offset = 0;
-			be16_add_cpu(&block->bb_numrecs, 1);
-		}
+	/* RMAP btree root block */
+	if (xfs_sb_version_hasrmapbt(&mp->m_sb)) {
+		id->daddr = XFS_AGB_TO_DADDR(mp, id->agno, XFS_RMAP_BLOCK(mp));
+		id->numblks = BTOBB(mp->m_sb.sb_blocksize);
+		error = xfs_growfs_init_aghdr(mp, id, xfs_rmaproot_init,
+					   &xfs_rmapbt_buf_ops);
+		if (error)
+			goto out_error;
 
-		xfs_buf_delwri_queue(bp, buffer_list);
-		xfs_buf_relse(bp);
 	}
 
-	/*
-	 * INO btree root block
-	 */
-	bp = xfs_growfs_get_hdr_buf(mp,
-			XFS_AGB_TO_DADDR(mp, agno, XFS_IBT_BLOCK(mp)),
-			BTOBB(mp->m_sb.sb_blocksize), 0,
-			&xfs_inobt_buf_ops);
-	if (!bp) {
-		error = -ENOMEM;
+	/* INO btree root block */
+	id->daddr = XFS_AGB_TO_DADDR(mp, id->agno, XFS_IBT_BLOCK(mp));
+	id->numblks = BTOBB(mp->m_sb.sb_blocksize);
+	id->type = XFS_BTNUM_INO;
+	error = xfs_growfs_init_aghdr(mp, id, xfs_btroot_init,
+				   &xfs_inobt_buf_ops);
+	if (error)
 		goto out_error;
-	}
 
-	xfs_btree_init_block(mp, bp, XFS_BTNUM_INO , 0, 0, agno, 0);
-	xfs_buf_delwri_queue(bp, buffer_list);
-	xfs_buf_relse(bp);
 
 	/*
 	 * FINO btree root block
 	 */
 	if (xfs_sb_version_hasfinobt(&mp->m_sb)) {
-		bp = xfs_growfs_get_hdr_buf(mp,
-			XFS_AGB_TO_DADDR(mp, agno, XFS_FIBT_BLOCK(mp)),
-			BTOBB(mp->m_sb.sb_blocksize), 0,
-			&xfs_inobt_buf_ops);
-		if (!bp) {
-			error = -ENOMEM;
+		id->daddr = XFS_AGB_TO_DADDR(mp, id->agno, XFS_FIBT_BLOCK(mp));
+		id->numblks = BTOBB(mp->m_sb.sb_blocksize);
+		id->type = XFS_BTNUM_FINO;
+		error = xfs_growfs_init_aghdr(mp, id, xfs_btroot_init,
+					   &xfs_inobt_buf_ops);
+		if (error)
 			goto out_error;
-		}
-
-		xfs_btree_init_block(mp, bp, XFS_BTNUM_FINO, 0, 0, agno, 0);
-		xfs_buf_delwri_queue(bp, buffer_list);
-		xfs_buf_relse(bp);
 	}
 
 	/*
 	 * refcount btree root block
 	 */
 	if (xfs_sb_version_hasreflink(&mp->m_sb)) {
-		bp = xfs_growfs_get_hdr_buf(mp,
-			XFS_AGB_TO_DADDR(mp, agno, xfs_refc_block(mp)),
-			BTOBB(mp->m_sb.sb_blocksize), 0,
-			&xfs_refcountbt_buf_ops);
-		if (!bp) {
-			error = -ENOMEM;
+		id->daddr = XFS_AGB_TO_DADDR(mp, id->agno, xfs_refc_block(mp));
+		id->numblks = BTOBB(mp->m_sb.sb_blocksize);
+		id->type = XFS_BTNUM_REFC;
+		error = xfs_growfs_init_aghdr(mp, id, xfs_btroot_init,
+					   &xfs_refcountbt_buf_ops);
+		if (error)
 			goto out_error;
-		}
-
-		xfs_btree_init_block(mp, bp, XFS_BTNUM_REFC, 0, 0, agno, 0);
-		xfs_buf_delwri_queue(bp, buffer_list);
-		xfs_buf_relse(bp);
 	}
 
 out_error:
@@ -384,7 +421,6 @@ xfs_growfs_data_private(
 	xfs_agf_t		*agf;
 	xfs_agi_t		*agi;
 	xfs_agnumber_t		agno;
-	xfs_extlen_t		agsize;
 	xfs_buf_t		*bp;
 	int			dpct;
 	int			error, saved_error = 0;
@@ -392,11 +428,11 @@ xfs_growfs_data_private(
 	xfs_agnumber_t		nagimax = 0;
 	xfs_rfsblock_t		nb, nb_mod;
 	xfs_rfsblock_t		new;
-	xfs_rfsblock_t		nfree;
 	xfs_agnumber_t		oagcount;
 	int			pct;
 	xfs_trans_t		*tp;
 	LIST_HEAD		(buffer_list);
+	struct aghdr_init_data	id = {};
 
 	nb = in->newblocks;
 	pct = in->imaxpct;
@@ -448,27 +484,28 @@ xfs_growfs_data_private(
 	 * list to write, we can cancel the entire list without having written
 	 * anything.
 	 */
-	nfree = 0;
-	for (agno = nagcount - 1; agno >= oagcount; agno--, new -= agsize) {
-
-		if (agno == nagcount - 1)
-			agsize = nb -
-				(agno * (xfs_rfsblock_t)mp->m_sb.sb_agblocks);
+	INIT_LIST_HEAD(&id.buffer_list);
+	for (id.agno = nagcount - 1;
+	     id.agno >= oagcount;
+	     id.agno--, new -= id.agsize) {
+
+		if (id.agno == nagcount - 1)
+			id.agsize = nb -
+				(id.agno * (xfs_rfsblock_t)mp->m_sb.sb_agblocks);
 		else
-			agsize = mp->m_sb.sb_agblocks;
+			id.agsize = mp->m_sb.sb_agblocks;
 
-		error = xfs_grow_ag_headers(mp, agno, agsize, &nfree,
-					    &buffer_list);
+		error = xfs_grow_ag_headers(mp, &id);
 		if (error) {
-			xfs_buf_delwri_cancel(&buffer_list);
+			xfs_buf_delwri_cancel(&id.buffer_list);
 			goto error0;
 		}
 	}
-	error = xfs_buf_delwri_submit(&buffer_list);
+	error = xfs_buf_delwri_submit(&id.buffer_list);
 	if (error)
 		goto error0;
 
-	xfs_trans_agblocks_delta(tp, nfree);
+	xfs_trans_agblocks_delta(tp, id.nfree);
 
 	/*
 	 * There are new blocks in the old last a.g.
@@ -479,7 +516,7 @@ xfs_growfs_data_private(
 		/*
 		 * Change the agi length.
 		 */
-		error = xfs_ialloc_read_agi(mp, tp, agno, &bp);
+		error = xfs_ialloc_read_agi(mp, tp, id.agno, &bp);
 		if (error) {
 			goto error0;
 		}
@@ -492,7 +529,7 @@ xfs_growfs_data_private(
 		/*
 		 * Change agf length.
 		 */
-		error = xfs_alloc_read_agf(mp, tp, agno, 0, &bp);
+		error = xfs_alloc_read_agf(mp, tp, id.agno, 0, &bp);
 		if (error) {
 			goto error0;
 		}
@@ -511,13 +548,13 @@ xfs_growfs_data_private(
 		 * this doesn't actually exist in the rmap btree.
 		 */
 		xfs_rmap_ag_owner(&oinfo, XFS_RMAP_OWN_NULL);
-		error = xfs_rmap_free(tp, bp, agno,
+		error = xfs_rmap_free(tp, bp, id.agno,
 				be32_to_cpu(agf->agf_length) - new,
 				new, &oinfo);
 		if (error)
 			goto error0;
 		error = xfs_free_extent(tp,
-				XFS_AGB_TO_FSB(mp, agno,
+				XFS_AGB_TO_FSB(mp, id.agno,
 					be32_to_cpu(agf->agf_length) - new),
 				new, &oinfo, XFS_AG_RESV_NONE);
 		if (error)
@@ -534,8 +571,8 @@ xfs_growfs_data_private(
 	if (nb > mp->m_sb.sb_dblocks)
 		xfs_trans_mod_sb(tp, XFS_TRANS_SB_DBLOCKS,
 				 nb - mp->m_sb.sb_dblocks);
-	if (nfree)
-		xfs_trans_mod_sb(tp, XFS_TRANS_SB_FDBLOCKS, nfree);
+	if (id.nfree)
+		xfs_trans_mod_sb(tp, XFS_TRANS_SB_FDBLOCKS, id.nfree);
 	if (dpct)
 		xfs_trans_mod_sb(tp, XFS_TRANS_SB_IMAXPCT, dpct);
 	xfs_trans_set_sync(tp);
@@ -562,7 +599,7 @@ xfs_growfs_data_private(
 	if (new) {
 		struct xfs_perag	*pag;
 
-		pag = xfs_perag_get(mp, agno);
+		pag = xfs_perag_get(mp, id.agno);
 		error = xfs_ag_resv_free(pag);
 		xfs_perag_put(pag);
 		if (error)
-- 
2.17.0


^ permalink raw reply related	[flat|nested] 17+ messages in thread

* [PATCH 05/10] xfs: turn ag header initialisation into a table driven operation
  2018-05-14  4:18 [PATCH 0/10 v3] xfs: refactor and tablise growfs Dave Chinner
                   ` (3 preceding siblings ...)
  2018-05-14  4:18 ` [PATCH 04/10] xfs: factor ag btree root block initialisation Dave Chinner
@ 2018-05-14  4:18 ` Dave Chinner
  2018-05-14 19:27   ` Darrick J. Wong
  2018-05-14  4:19 ` [PATCH 06/10] xfs: make imaxpct changes in growfs separate Dave Chinner
                   ` (4 subsequent siblings)
  9 siblings, 1 reply; 17+ messages in thread
From: Dave Chinner @ 2018-05-14  4:18 UTC (permalink / raw)
  To: linux-xfs

From: Dave Chinner <dchinner@redhat.com>

There's still more cookie cutter code in setting up each AG header.
Separate all the variables into a simple structure and iterate a
table of header definitions to initialise everything.

Signed-Off-By: Dave Chinner <dchinner@redhat.com>
Reviewed-by: Brian Foster <bfoster@redhat.com>
---
 fs/xfs/xfs_fsops.c | 182 +++++++++++++++++++++++----------------------
 1 file changed, 92 insertions(+), 90 deletions(-)

diff --git a/fs/xfs/xfs_fsops.c b/fs/xfs/xfs_fsops.c
index ec3c51ee8134..fb283d2cf42b 100644
--- a/fs/xfs/xfs_fsops.c
+++ b/fs/xfs/xfs_fsops.c
@@ -282,12 +282,13 @@ xfs_agiblock_init(
 		agi->agi_unlinked[bucket] = cpu_to_be32(NULLAGINO);
 }
 
+typedef void (*aghdr_init_work_f)(struct xfs_mount *mp, struct xfs_buf *bp,
+				  struct aghdr_init_data *id);
 static int
 xfs_growfs_init_aghdr(
 	struct xfs_mount	*mp,
 	struct aghdr_init_data	*id,
-	void			(*work)(struct xfs_mount *, struct xfs_buf *,
-					struct aghdr_init_data *),
+	aghdr_init_work_f	work,
 	const struct xfs_buf_ops *ops)
 
 {
@@ -304,6 +305,15 @@ xfs_growfs_init_aghdr(
 	return 0;
 }
 
+struct xfs_aghdr_grow_data {
+	xfs_daddr_t		daddr;
+	size_t			numblks;
+	const struct xfs_buf_ops *ops;
+	aghdr_init_work_f	work;
+	xfs_btnum_t		type;
+	bool			need_init;
+};
+
 /*
  * Write new AG headers to disk. Non-transactional, but written
  * synchronously so they are completed prior to the growfs transaction
@@ -315,101 +325,93 @@ xfs_grow_ag_headers(
 	struct aghdr_init_data	*id)
 
 {
+	struct xfs_aghdr_grow_data aghdr_data[] = {
+	{ /* AGF */
+		.daddr = XFS_AG_DADDR(mp, id->agno, XFS_AGF_DADDR(mp)),
+		.numblks = XFS_FSS_TO_BB(mp, 1),
+		.ops = &xfs_agf_buf_ops,
+		.work = &xfs_agfblock_init,
+		.need_init = true
+	},
+	{ /* AGFL */
+		.daddr = XFS_AG_DADDR(mp, id->agno, XFS_AGFL_DADDR(mp)),
+		.numblks = XFS_FSS_TO_BB(mp, 1),
+		.ops = &xfs_agfl_buf_ops,
+		.work = &xfs_agflblock_init,
+		.need_init = true
+	},
+	{ /* AGI */
+		.daddr = XFS_AG_DADDR(mp, id->agno, XFS_AGI_DADDR(mp)),
+		.numblks = XFS_FSS_TO_BB(mp, 1),
+		.ops = &xfs_agi_buf_ops,
+		.work = &xfs_agiblock_init,
+		.need_init = true
+	},
+	{ /* BNO root block */
+		.daddr = XFS_AGB_TO_DADDR(mp, id->agno, XFS_BNO_BLOCK(mp)),
+		.numblks = BTOBB(mp->m_sb.sb_blocksize),
+		.ops = &xfs_allocbt_buf_ops,
+		.work = &xfs_bnoroot_init,
+		.need_init = true
+	},
+	{ /* CNT root block */
+		.daddr = XFS_AGB_TO_DADDR(mp, id->agno, XFS_CNT_BLOCK(mp)),
+		.numblks = BTOBB(mp->m_sb.sb_blocksize),
+		.ops = &xfs_allocbt_buf_ops,
+		.work = &xfs_cntroot_init,
+		.need_init = true
+	},
+	{ /* INO root block */
+		.daddr = XFS_AGB_TO_DADDR(mp, id->agno, XFS_IBT_BLOCK(mp)),
+		.numblks = BTOBB(mp->m_sb.sb_blocksize),
+		.ops = &xfs_inobt_buf_ops,
+		.work = &xfs_btroot_init,
+		.type = XFS_BTNUM_INO,
+		.need_init = true
+	},
+	{ /* FINO root block */
+		.daddr = XFS_AGB_TO_DADDR(mp, id->agno, XFS_FIBT_BLOCK(mp)),
+		.numblks = BTOBB(mp->m_sb.sb_blocksize),
+		.ops = &xfs_inobt_buf_ops,
+		.work = &xfs_btroot_init,
+		.type = XFS_BTNUM_FINO,
+		.need_init =  xfs_sb_version_hasfinobt(&mp->m_sb)
+	},
+	{ /* RMAP root block */
+		.daddr = XFS_AGB_TO_DADDR(mp, id->agno, XFS_RMAP_BLOCK(mp)),
+		.numblks = BTOBB(mp->m_sb.sb_blocksize),
+		.ops = &xfs_rmapbt_buf_ops,
+		.work = &xfs_rmaproot_init,
+		.need_init = xfs_sb_version_hasrmapbt(&mp->m_sb)
+	},
+	{ /* REFC root block */
+		.daddr = XFS_AGB_TO_DADDR(mp, id->agno, xfs_refc_block(mp)),
+		.numblks = BTOBB(mp->m_sb.sb_blocksize),
+		.ops = &xfs_refcountbt_buf_ops,
+		.work = &xfs_btroot_init,
+		.type = XFS_BTNUM_REFC,
+		.need_init = xfs_sb_version_hasreflink(&mp->m_sb)
+	},
+	{ /* NULL terminating block */
+		.daddr = XFS_BUF_DADDR_NULL,
+	}
+	};
+	struct  xfs_aghdr_grow_data *dp;
 	int			error = 0;
 
 	/* Account for AG free space in new AG */
 	id->nfree += id->agsize - mp->m_ag_prealloc_blocks;
+	for (dp = &aghdr_data[0]; dp->daddr != XFS_BUF_DADDR_NULL; dp++) {
+		if (!dp->need_init)
+			continue;
 
-	/* AG freespace header block */
-	id->daddr = XFS_AG_DADDR(mp, id->agno, XFS_AGF_DADDR(mp));
-	id->numblks = XFS_FSS_TO_BB(mp, 1);
-	error = xfs_growfs_init_aghdr(mp, id, xfs_agfblock_init,
-					&xfs_agf_buf_ops);
-	if (error)
-		goto out_error;
-
-	/* AG freelist header block */
-	id->daddr = XFS_AG_DADDR(mp, id->agno, XFS_AGFL_DADDR(mp));
-	id->numblks = XFS_FSS_TO_BB(mp, 1);
-	error = xfs_growfs_init_aghdr(mp, id, xfs_agflblock_init,
-					&xfs_agfl_buf_ops);
-	if (error)
-		goto out_error;
-
-	/* AG inode header block */
-	id->daddr = XFS_AG_DADDR(mp, id->agno, XFS_AGI_DADDR(mp));
-	id->numblks = XFS_FSS_TO_BB(mp, 1);
-	error = xfs_growfs_init_aghdr(mp, id, xfs_agiblock_init,
-					&xfs_agi_buf_ops);
-	if (error)
-		goto out_error;
-
-
-	/* BNO btree root block */
-	id->daddr = XFS_AGB_TO_DADDR(mp, id->agno, XFS_BNO_BLOCK(mp));
-	id->numblks = BTOBB(mp->m_sb.sb_blocksize);
-	error = xfs_growfs_init_aghdr(mp, id, xfs_bnoroot_init,
-				   &xfs_allocbt_buf_ops);
-	if (error)
-		goto out_error;
-
-
-	/* CNT btree root block */
-	id->daddr = XFS_AGB_TO_DADDR(mp, id->agno, XFS_CNT_BLOCK(mp));
-	id->numblks = BTOBB(mp->m_sb.sb_blocksize);
-	error = xfs_growfs_init_aghdr(mp, id, xfs_cntroot_init,
-				   &xfs_allocbt_buf_ops);
-	if (error)
-		goto out_error;
-
-	/* RMAP btree root block */
-	if (xfs_sb_version_hasrmapbt(&mp->m_sb)) {
-		id->daddr = XFS_AGB_TO_DADDR(mp, id->agno, XFS_RMAP_BLOCK(mp));
-		id->numblks = BTOBB(mp->m_sb.sb_blocksize);
-		error = xfs_growfs_init_aghdr(mp, id, xfs_rmaproot_init,
-					   &xfs_rmapbt_buf_ops);
-		if (error)
-			goto out_error;
-
-	}
-
-	/* INO btree root block */
-	id->daddr = XFS_AGB_TO_DADDR(mp, id->agno, XFS_IBT_BLOCK(mp));
-	id->numblks = BTOBB(mp->m_sb.sb_blocksize);
-	id->type = XFS_BTNUM_INO;
-	error = xfs_growfs_init_aghdr(mp, id, xfs_btroot_init,
-				   &xfs_inobt_buf_ops);
-	if (error)
-		goto out_error;
-
-
-	/*
-	 * FINO btree root block
-	 */
-	if (xfs_sb_version_hasfinobt(&mp->m_sb)) {
-		id->daddr = XFS_AGB_TO_DADDR(mp, id->agno, XFS_FIBT_BLOCK(mp));
-		id->numblks = BTOBB(mp->m_sb.sb_blocksize);
-		id->type = XFS_BTNUM_FINO;
-		error = xfs_growfs_init_aghdr(mp, id, xfs_btroot_init,
-					   &xfs_inobt_buf_ops);
-		if (error)
-			goto out_error;
-	}
-
-	/*
-	 * refcount btree root block
-	 */
-	if (xfs_sb_version_hasreflink(&mp->m_sb)) {
-		id->daddr = XFS_AGB_TO_DADDR(mp, id->agno, xfs_refc_block(mp));
-		id->numblks = BTOBB(mp->m_sb.sb_blocksize);
-		id->type = XFS_BTNUM_REFC;
-		error = xfs_growfs_init_aghdr(mp, id, xfs_btroot_init,
-					   &xfs_refcountbt_buf_ops);
+		id->daddr = dp->daddr;
+		id->numblks = dp->numblks;
+		id->type = dp->type;
+		error = xfs_growfs_init_aghdr(mp, id, dp->work, dp->ops);
 		if (error)
-			goto out_error;
+			break;
 	}
-
-out_error:
 	return error;
 }
 
-- 
2.17.0


^ permalink raw reply related	[flat|nested] 17+ messages in thread

* [PATCH 06/10] xfs: make imaxpct changes in growfs separate
  2018-05-14  4:18 [PATCH 0/10 v3] xfs: refactor and tablise growfs Dave Chinner
                   ` (4 preceding siblings ...)
  2018-05-14  4:18 ` [PATCH 05/10] xfs: turn ag header initialisation into a table driven operation Dave Chinner
@ 2018-05-14  4:19 ` Dave Chinner
  2018-05-14  4:19 ` [PATCH 07/10] xfs: separate secondary sb update in growfs Dave Chinner
                   ` (3 subsequent siblings)
  9 siblings, 0 replies; 17+ messages in thread
From: Dave Chinner @ 2018-05-14  4:19 UTC (permalink / raw)
  To: linux-xfs

From: Dave Chinner <dchinner@redhat.com>

When growfs changes the imaxpct value of the filesystem, it runs
through all the "change size" growfs code, whether it needs to or
not. Separate out changing imaxpct into it's own function and
transaction to simplify the rest of the growfs code.

Signed-Off-By: Dave Chinner <dchinner@redhat.com>
Reviewed-by: Darrick J. Wong <darrick.wong@oracle.com>
---
 fs/xfs/xfs_fsops.c | 69 +++++++++++++++++++++++++++++++++-------------
 1 file changed, 50 insertions(+), 19 deletions(-)

diff --git a/fs/xfs/xfs_fsops.c b/fs/xfs/xfs_fsops.c
index fb283d2cf42b..4e66d390e2a4 100644
--- a/fs/xfs/xfs_fsops.c
+++ b/fs/xfs/xfs_fsops.c
@@ -424,25 +424,21 @@ xfs_growfs_data_private(
 	xfs_agi_t		*agi;
 	xfs_agnumber_t		agno;
 	xfs_buf_t		*bp;
-	int			dpct;
 	int			error, saved_error = 0;
 	xfs_agnumber_t		nagcount;
 	xfs_agnumber_t		nagimax = 0;
 	xfs_rfsblock_t		nb, nb_mod;
 	xfs_rfsblock_t		new;
 	xfs_agnumber_t		oagcount;
-	int			pct;
 	xfs_trans_t		*tp;
 	LIST_HEAD		(buffer_list);
 	struct aghdr_init_data	id = {};
 
 	nb = in->newblocks;
-	pct = in->imaxpct;
-	if (nb < mp->m_sb.sb_dblocks || pct < 0 || pct > 100)
+	if (nb < mp->m_sb.sb_dblocks)
 		return -EINVAL;
 	if ((error = xfs_sb_validate_fsb_count(&mp->m_sb, nb)))
 		return error;
-	dpct = pct - mp->m_sb.sb_imax_pct;
 	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);
@@ -575,8 +571,6 @@ xfs_growfs_data_private(
 				 nb - mp->m_sb.sb_dblocks);
 	if (id.nfree)
 		xfs_trans_mod_sb(tp, XFS_TRANS_SB_FDBLOCKS, id.nfree);
-	if (dpct)
-		xfs_trans_mod_sb(tp, XFS_TRANS_SB_IMAXPCT, dpct);
 	xfs_trans_set_sync(tp);
 	error = xfs_trans_commit(tp);
 	if (error)
@@ -585,12 +579,6 @@ xfs_growfs_data_private(
 	/* New allocation groups fully initialized, so update mount struct */
 	if (nagimax)
 		mp->m_maxagi = nagimax;
-	if (mp->m_sb.sb_imax_pct) {
-		uint64_t icount = mp->m_sb.sb_dblocks * mp->m_sb.sb_imax_pct;
-		do_div(icount, 100);
-		mp->m_maxicount = icount << mp->m_sb.sb_inopblog;
-	} else
-		mp->m_maxicount = 0;
 	xfs_set_low_space_thresholds(mp);
 	mp->m_alloc_set_aside = xfs_alloc_set_aside(mp);
 
@@ -694,25 +682,68 @@ xfs_growfs_log_private(
 	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);
+}
+
 /*
  * protected versions of growfs function acquire and release locks on the mount
  * point - exported through ioctls: XFS_IOC_FSGROWFSDATA, XFS_IOC_FSGROWFSLOG,
  * XFS_IOC_FSGROWFSRT
  */
-
-
 int
 xfs_growfs_data(
-	xfs_mount_t		*mp,
-	xfs_growfs_data_t	*in)
+	struct xfs_mount	*mp,
+	struct xfs_growfs_data	*in)
 {
-	int error;
+	int			error = 0;
 
 	if (!capable(CAP_SYS_ADMIN))
 		return -EPERM;
 	if (!mutex_trylock(&mp->m_growlock))
 		return -EWOULDBLOCK;
-	error = xfs_growfs_data_private(mp, in);
+
+	/* update imaxpct separately to the physical grow of the filesystem */
+	if (in->imaxpct != mp->m_sb.sb_imax_pct) {
+		error = xfs_growfs_imaxpct(mp, in->imaxpct);
+		if (error)
+			goto out_error;
+	}
+
+	if (in->newblocks != mp->m_sb.sb_dblocks) {
+		error = xfs_growfs_data_private(mp, in);
+		if (error)
+			goto out_error;
+	}
+
+	/* Post growfs calculations needed to reflect new state in operations */
+	if (mp->m_sb.sb_imax_pct) {
+		uint64_t icount = mp->m_sb.sb_dblocks * mp->m_sb.sb_imax_pct;
+		do_div(icount, 100);
+		mp->m_maxicount = icount << mp->m_sb.sb_inopblog;
+	} else
+		mp->m_maxicount = 0;
+
+out_error:
 	/*
 	 * Increment the generation unconditionally, the error could be from
 	 * updating the secondary superblocks, in which case the new size
-- 
2.17.0


^ permalink raw reply related	[flat|nested] 17+ messages in thread

* [PATCH 07/10] xfs: separate secondary sb update in growfs
  2018-05-14  4:18 [PATCH 0/10 v3] xfs: refactor and tablise growfs Dave Chinner
                   ` (5 preceding siblings ...)
  2018-05-14  4:19 ` [PATCH 06/10] xfs: make imaxpct changes in growfs separate Dave Chinner
@ 2018-05-14  4:19 ` Dave Chinner
  2018-05-14  4:19 ` [PATCH 08/10] xfs: rework secondary superblock updates " Dave Chinner
                   ` (2 subsequent siblings)
  9 siblings, 0 replies; 17+ messages in thread
From: Dave Chinner @ 2018-05-14  4:19 UTC (permalink / raw)
  To: linux-xfs

From: Dave Chinner <dchinner@redhat.com>

This happens after all the transactions to update the superblock
occur, and errors need to be handled slightly differently. Seperate
out the code into it's own function, and clean up the error goto
stack in the core growfs code as it is now much simpler.

Signed-Off-By: Dave Chinner <dchinner@redhat.com>
Reviewed-by: Darrick J. Wong <darrick.wong@oracle.com>
---
 fs/xfs/xfs_fsops.c | 159 ++++++++++++++++++++++++++-------------------
 1 file 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
-- 
2.17.0


^ permalink raw reply related	[flat|nested] 17+ messages in thread

* [PATCH 08/10] xfs: rework secondary superblock updates in growfs
  2018-05-14  4:18 [PATCH 0/10 v3] xfs: refactor and tablise growfs Dave Chinner
                   ` (6 preceding siblings ...)
  2018-05-14  4:19 ` [PATCH 07/10] xfs: separate secondary sb update in growfs Dave Chinner
@ 2018-05-14  4:19 ` Dave Chinner
  2018-05-14  4:19 ` [PATCH 09/10] xfs: move growfs core to libxfs Dave Chinner
  2018-05-14  4:19 ` [PATCH 10/10] xfs: factor the ag length extension code into libxfs Dave Chinner
  9 siblings, 0 replies; 17+ messages in thread
From: Dave Chinner @ 2018-05-14  4:19 UTC (permalink / raw)
  To: linux-xfs

From: Dave Chinner <dchinner@redhat.com>

Right now we wait until we've committed changes to the primary
superblock before we initialise any of the new secondary
superblocks. This means that if we have any write errors for new
secondary superblocks we end up with garbage in place rather than
zeros or even an "in progress" superblock to indicate a grow
operation is being done.

To ensure we can write the secondary superblocks, initialise them
earlier in the same loop that initialises the AG headers. We stamp
the new secondary superblocks here with the old geometry, but set
the "sb_inprogress" field to indicate that updates are being done to
the superblock so they cannot be used.  This will result in the
secondary superblock fields being updated or triggering errors that
will abort the grow before we commit any permanent changes.

This also means we can change the update mechanism of the secondary
superblocks.  We know that we are going to wholly overwrite the
information in the struct xfs_sb in the buffer, so there's no point
reading it from disk. Just allocate an uncached buffer, zero it in
memory, stamp the new superblock structure in it and write it out.
If we fail to write it out, then we'll leave the existing sb (old or
new w/ inprogress) on disk for repair to deal with later.

Signed-Off-By: Dave Chinner <dchinner@redhat.com>
Reviewed-by: Darrick J. Wong <darrick.wong@oracle.com>
---
 fs/xfs/xfs_fsops.c | 102 +++++++++++++++++++++++++++++----------------
 1 file changed, 65 insertions(+), 37 deletions(-)

diff --git a/fs/xfs/xfs_fsops.c b/fs/xfs/xfs_fsops.c
index a718bb002cf4..ca3cf3f6239a 100644
--- a/fs/xfs/xfs_fsops.c
+++ b/fs/xfs/xfs_fsops.c
@@ -191,6 +191,25 @@ xfs_rmaproot_init(
 	}
 }
 
+/*
+ * Initialise new secondary superblocks with the pre-grow geometry, but mark
+ * them as "in progress" so we know they haven't yet been activated. This will
+ * get cleared when the update with the new geometry information is done after
+ * changes to the primary are committed. This isn't strictly necessary, but we
+ * get it for free with the delayed buffer write lists and it means we can tell
+ * if a grow operation didn't complete properly after the fact.
+ */
+static void
+xfs_sbblock_init(
+	struct xfs_mount	*mp,
+	struct xfs_buf		*bp,
+	struct aghdr_init_data	*id)
+{
+	struct xfs_dsb		*dsb = XFS_BUF_TO_SBP(bp);
+
+	xfs_sb_to_disk(dsb, &mp->m_sb);
+	dsb->sb_inprogress = 1;
+}
 
 static void
 xfs_agfblock_init(
@@ -326,6 +345,13 @@ xfs_grow_ag_headers(
 
 {
 	struct xfs_aghdr_grow_data aghdr_data[] = {
+	{ /* SB */
+		.daddr = XFS_AG_DADDR(mp, id->agno, XFS_SB_DADDR),
+		.numblks = XFS_FSS_TO_BB(mp, 1),
+		.ops = &xfs_sb_buf_ops,
+		.work = &xfs_sbblock_init,
+		.need_init = true
+	},
 	{ /* AGF */
 		.daddr = XFS_AG_DADDR(mp, id->agno, XFS_AGF_DADDR(mp)),
 		.numblks = XFS_FSS_TO_BB(mp, 1),
@@ -658,43 +684,30 @@ xfs_growfs_imaxpct(
 
 /*
  * 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.
+ * to match the new state of the primary. Because we are completely overwriting
+ * all the existing fields in the secondary superblock buffers, there is no need
+ * to read them in from disk. Just get a new buffer, stamp it and write it.
+ *
+ * The sb buffers need to be cached here so that we serialise against scrub
+ * scanning secondary superblocks, but we don't want to keep it in memory once
+ * it is written so we mark it as a one-shot buffer.
  */
 static int
 xfs_growfs_update_superblocks(
-	struct xfs_mount	*mp,
-	xfs_agnumber_t		oagcount)
+	struct xfs_mount	*mp)
 {
-	struct xfs_buf		*bp;
 	xfs_agnumber_t		agno;
 	int			saved_error = 0;
 	int			error = 0;
+	LIST_HEAD		(buffer_list);
 
 	/* update secondary superblocks. */
 	for (agno = 1; agno < mp->m_sb.sb_agcount; agno++) {
-		error = 0;
-		/*
-		 * new secondary superblocks need to be zeroed, not read from
-		 * disk as the contents of the new area we are growing into is
-		 * completely unknown.
-		 */
-		if (agno < oagcount) {
-			error = xfs_trans_read_buf(mp, NULL, mp->m_ddev_targp,
-				  XFS_AGB_TO_DADDR(mp, agno, XFS_SB_BLOCK(mp)),
-				  XFS_FSS_TO_BB(mp, 1), 0, &bp,
-				  &xfs_sb_buf_ops);
-		} else {
-			bp = xfs_trans_get_buf(NULL, mp->m_ddev_targp,
-				  XFS_AGB_TO_DADDR(mp, agno, XFS_SB_BLOCK(mp)),
-				  XFS_FSS_TO_BB(mp, 1), 0);
-			if (bp) {
-				bp->b_ops = &xfs_sb_buf_ops;
-				xfs_buf_zero(bp, 0, BBTOB(bp->b_length));
-			} else
-				error = -ENOMEM;
-		}
+		struct xfs_buf		*bp;
 
+		bp = xfs_buf_get(mp->m_ddev_targp,
+				 XFS_AG_DADDR(mp, agno, XFS_SB_DADDR),
+				 XFS_FSS_TO_BB(mp, 1), 0);
 		/*
 		 * If we get an error reading or writing alternate superblocks,
 		 * continue.  xfs_repair chooses the "best" superblock based
@@ -702,25 +715,42 @@ xfs_growfs_update_superblocks(
 		 * superblocks un-updated than updated, and xfs_repair may
 		 * pick them over the properly-updated primary.
 		 */
-		if (error) {
+		if (!bp) {
 			xfs_warn(mp,
-		"error %d reading secondary superblock for ag %d",
-				error, agno);
-			saved_error = error;
+		"error allocating secondary superblock for ag %d",
+				agno);
+			if (!saved_error)
+				saved_error = -ENOMEM;
 			continue;
 		}
-		xfs_sb_to_disk(XFS_BUF_TO_SBP(bp), &mp->m_sb);
 
-		error = xfs_bwrite(bp);
+		bp->b_ops = &xfs_sb_buf_ops;
+		xfs_buf_oneshot(bp);
+		xfs_buf_zero(bp, 0, BBTOB(bp->b_length));
+		xfs_sb_to_disk(XFS_BUF_TO_SBP(bp), &mp->m_sb);
+		xfs_buf_delwri_queue(bp, &buffer_list);
 		xfs_buf_relse(bp);
+
+		/* don't hold too many buffers at once */
+		if (agno % 16)
+			continue;
+
+		error = xfs_buf_delwri_submit(&buffer_list);
 		if (error) {
 			xfs_warn(mp,
-		"write error %d updating secondary superblock for ag %d",
+		"write error %d updating a secondary superblock near ag %d",
 				error, agno);
-			saved_error = error;
+			if (!saved_error)
+				saved_error = error;
 			continue;
 		}
 	}
+	error = xfs_buf_delwri_submit(&buffer_list);
+	if (error) {
+		xfs_warn(mp,
+		"write error %d updating a secondary superblock near ag %d",
+			error, agno);
+	}
 
 	return saved_error ? saved_error : error;
 }
@@ -735,7 +765,6 @@ xfs_growfs_data(
 	struct xfs_mount	*mp,
 	struct xfs_growfs_data	*in)
 {
-	xfs_agnumber_t		oagcount;
 	int			error = 0;
 
 	if (!capable(CAP_SYS_ADMIN))
@@ -750,7 +779,6 @@ 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)
@@ -766,7 +794,7 @@ xfs_growfs_data(
 		mp->m_maxicount = 0;
 
 	/* Update secondary superblocks now the physical grow has completed */
-	error = xfs_growfs_update_superblocks(mp, oagcount);
+	error = xfs_growfs_update_superblocks(mp);
 
 out_error:
 	/*
-- 
2.17.0


^ permalink raw reply related	[flat|nested] 17+ messages in thread

* [PATCH 09/10] xfs: move growfs core to libxfs
  2018-05-14  4:18 [PATCH 0/10 v3] xfs: refactor and tablise growfs Dave Chinner
                   ` (7 preceding siblings ...)
  2018-05-14  4:19 ` [PATCH 08/10] xfs: rework secondary superblock updates " Dave Chinner
@ 2018-05-14  4:19 ` Dave Chinner
  2018-05-14  4:19 ` [PATCH 10/10] xfs: factor the ag length extension code into libxfs Dave Chinner
  9 siblings, 0 replies; 17+ messages in thread
From: Dave Chinner @ 2018-05-14  4:19 UTC (permalink / raw)
  To: linux-xfs

From: Dave Chinner <dchinner@redhat.com>

So it can be shared with userspace (e.g. mkfs) easily.

Signed-Off-By: Dave Chinner <dchinner@redhat.com>
Reviewed-by: Darrick J. Wong <darrick.wong@oracle.com>
---
 fs/xfs/Makefile        |   1 +
 fs/xfs/libxfs/xfs_ag.c | 404 ++++++++++++++++++++++++++++++++++
 fs/xfs/libxfs/xfs_ag.h |  25 +++
 fs/xfs/libxfs/xfs_sb.c |  73 +++++++
 fs/xfs/libxfs/xfs_sb.h |   9 +
 fs/xfs/xfs_fsops.c     | 479 +----------------------------------------
 6 files changed, 516 insertions(+), 475 deletions(-)
 create mode 100644 fs/xfs/libxfs/xfs_ag.c
 create mode 100644 fs/xfs/libxfs/xfs_ag.h

diff --git a/fs/xfs/Makefile b/fs/xfs/Makefile
index 7ceb41a9786a..762477208322 100644
--- a/fs/xfs/Makefile
+++ b/fs/xfs/Makefile
@@ -28,6 +28,7 @@ xfs-y				+= xfs_trace.o
 
 # build the libxfs code first
 xfs-y				+= $(addprefix libxfs/, \
+				   xfs_ag.o \
 				   xfs_alloc.o \
 				   xfs_alloc_btree.o \
 				   xfs_attr.o \
diff --git a/fs/xfs/libxfs/xfs_ag.c b/fs/xfs/libxfs/xfs_ag.c
new file mode 100644
index 000000000000..5baa22c07095
--- /dev/null
+++ b/fs/xfs/libxfs/xfs_ag.c
@@ -0,0 +1,404 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2000-2005 Silicon Graphics, Inc.
+ * Copyright (c) 2018 Red Hat, Inc.
+ * All rights reserved.
+ */
+
+#include "xfs.h"
+#include "xfs_fs.h"
+#include "xfs_shared.h"
+#include "xfs_format.h"
+#include "xfs_trans_resv.h"
+#include "xfs_sb.h"
+#include "xfs_mount.h"
+#include "xfs_btree.h"
+#include "xfs_alloc_btree.h"
+#include "xfs_rmap_btree.h"
+#include "xfs_alloc.h"
+#include "xfs_rmap.h"
+#include "xfs_ag.h"
+
+static struct xfs_buf *
+xfs_get_aghdr_buf(
+	struct xfs_mount	*mp,
+	xfs_daddr_t		blkno,
+	size_t			numblks,
+	int			flags,
+	const struct xfs_buf_ops *ops)
+{
+	struct xfs_buf		*bp;
+
+	bp = xfs_buf_get_uncached(mp->m_ddev_targp, numblks, flags);
+	if (!bp)
+		return NULL;
+
+	xfs_buf_zero(bp, 0, BBTOB(bp->b_length));
+	bp->b_bn = blkno;
+	bp->b_maps[0].bm_bn = blkno;
+	bp->b_ops = ops;
+
+	return bp;
+}
+
+/*
+ * Generic btree root block init function
+ */
+static void
+xfs_btroot_init(
+	struct xfs_mount	*mp,
+	struct xfs_buf		*bp,
+	struct aghdr_init_data	*id)
+{
+	xfs_btree_init_block(mp, bp, id->type, 0, 0, id->agno, 0);
+}
+
+/*
+ * Alloc btree root block init functions
+ */
+static void
+xfs_bnoroot_init(
+	struct xfs_mount	*mp,
+	struct xfs_buf		*bp,
+	struct aghdr_init_data	*id)
+{
+	struct xfs_alloc_rec	*arec;
+
+	xfs_btree_init_block(mp, bp, XFS_BTNUM_BNO, 0, 1, id->agno, 0);
+	arec = XFS_ALLOC_REC_ADDR(mp, XFS_BUF_TO_BLOCK(bp), 1);
+	arec->ar_startblock = cpu_to_be32(mp->m_ag_prealloc_blocks);
+	arec->ar_blockcount = cpu_to_be32(id->agsize -
+					  be32_to_cpu(arec->ar_startblock));
+}
+
+static void
+xfs_cntroot_init(
+	struct xfs_mount	*mp,
+	struct xfs_buf		*bp,
+	struct aghdr_init_data	*id)
+{
+	struct xfs_alloc_rec	*arec;
+
+	xfs_btree_init_block(mp, bp, XFS_BTNUM_CNT, 0, 1, id->agno, 0);
+	arec = XFS_ALLOC_REC_ADDR(mp, XFS_BUF_TO_BLOCK(bp), 1);
+	arec->ar_startblock = cpu_to_be32(mp->m_ag_prealloc_blocks);
+	arec->ar_blockcount = cpu_to_be32(id->agsize -
+					  be32_to_cpu(arec->ar_startblock));
+}
+
+/*
+ * Reverse map root block init
+ */
+static void
+xfs_rmaproot_init(
+	struct xfs_mount	*mp,
+	struct xfs_buf		*bp,
+	struct aghdr_init_data	*id)
+{
+	struct xfs_btree_block	*block = XFS_BUF_TO_BLOCK(bp);
+	struct xfs_rmap_rec	*rrec;
+
+	xfs_btree_init_block(mp, bp, XFS_BTNUM_RMAP, 0, 4, id->agno, 0);
+
+	/*
+	 * mark the AG header regions as static metadata The BNO
+	 * btree block is the first block after the headers, so
+	 * it's location defines the size of region the static
+	 * metadata consumes.
+	 *
+	 * Note: unlike mkfs, we never have to account for log
+	 * space when growing the data regions
+	 */
+	rrec = XFS_RMAP_REC_ADDR(block, 1);
+	rrec->rm_startblock = 0;
+	rrec->rm_blockcount = cpu_to_be32(XFS_BNO_BLOCK(mp));
+	rrec->rm_owner = cpu_to_be64(XFS_RMAP_OWN_FS);
+	rrec->rm_offset = 0;
+
+	/* account freespace btree root blocks */
+	rrec = XFS_RMAP_REC_ADDR(block, 2);
+	rrec->rm_startblock = cpu_to_be32(XFS_BNO_BLOCK(mp));
+	rrec->rm_blockcount = cpu_to_be32(2);
+	rrec->rm_owner = cpu_to_be64(XFS_RMAP_OWN_AG);
+	rrec->rm_offset = 0;
+
+	/* account inode btree root blocks */
+	rrec = XFS_RMAP_REC_ADDR(block, 3);
+	rrec->rm_startblock = cpu_to_be32(XFS_IBT_BLOCK(mp));
+	rrec->rm_blockcount = cpu_to_be32(XFS_RMAP_BLOCK(mp) -
+					  XFS_IBT_BLOCK(mp));
+	rrec->rm_owner = cpu_to_be64(XFS_RMAP_OWN_INOBT);
+	rrec->rm_offset = 0;
+
+	/* account for rmap btree root */
+	rrec = XFS_RMAP_REC_ADDR(block, 4);
+	rrec->rm_startblock = cpu_to_be32(XFS_RMAP_BLOCK(mp));
+	rrec->rm_blockcount = cpu_to_be32(1);
+	rrec->rm_owner = cpu_to_be64(XFS_RMAP_OWN_AG);
+	rrec->rm_offset = 0;
+
+	/* account for refc btree root */
+	if (xfs_sb_version_hasreflink(&mp->m_sb)) {
+		rrec = XFS_RMAP_REC_ADDR(block, 5);
+		rrec->rm_startblock = cpu_to_be32(xfs_refc_block(mp));
+		rrec->rm_blockcount = cpu_to_be32(1);
+		rrec->rm_owner = cpu_to_be64(XFS_RMAP_OWN_REFC);
+		rrec->rm_offset = 0;
+		be16_add_cpu(&block->bb_numrecs, 1);
+	}
+}
+
+/*
+ * Initialise new secondary superblocks with the pre-grow geometry, but mark
+ * them as "in progress" so we know they haven't yet been activated. This will
+ * get cleared when the update with the new geometry information is done after
+ * changes to the primary are committed. This isn't strictly necessary, but we
+ * get it for free with the delayed buffer write lists and it means we can tell
+ * if a grow operation didn't complete properly after the fact.
+ */
+static void
+xfs_sbblock_init(
+	struct xfs_mount	*mp,
+	struct xfs_buf		*bp,
+	struct aghdr_init_data	*id)
+{
+	struct xfs_dsb		*dsb = XFS_BUF_TO_SBP(bp);
+
+	xfs_sb_to_disk(dsb, &mp->m_sb);
+	dsb->sb_inprogress = 1;
+}
+
+static void
+xfs_agfblock_init(
+	struct xfs_mount	*mp,
+	struct xfs_buf		*bp,
+	struct aghdr_init_data	*id)
+{
+	struct xfs_agf		*agf = XFS_BUF_TO_AGF(bp);
+	xfs_extlen_t		tmpsize;
+
+	agf->agf_magicnum = cpu_to_be32(XFS_AGF_MAGIC);
+	agf->agf_versionnum = cpu_to_be32(XFS_AGF_VERSION);
+	agf->agf_seqno = cpu_to_be32(id->agno);
+	agf->agf_length = cpu_to_be32(id->agsize);
+	agf->agf_roots[XFS_BTNUM_BNOi] = cpu_to_be32(XFS_BNO_BLOCK(mp));
+	agf->agf_roots[XFS_BTNUM_CNTi] = cpu_to_be32(XFS_CNT_BLOCK(mp));
+	agf->agf_levels[XFS_BTNUM_BNOi] = cpu_to_be32(1);
+	agf->agf_levels[XFS_BTNUM_CNTi] = cpu_to_be32(1);
+	if (xfs_sb_version_hasrmapbt(&mp->m_sb)) {
+		agf->agf_roots[XFS_BTNUM_RMAPi] =
+					cpu_to_be32(XFS_RMAP_BLOCK(mp));
+		agf->agf_levels[XFS_BTNUM_RMAPi] = cpu_to_be32(1);
+		agf->agf_rmap_blocks = cpu_to_be32(1);
+	}
+
+	agf->agf_flfirst = cpu_to_be32(1);
+	agf->agf_fllast = 0;
+	agf->agf_flcount = 0;
+	tmpsize = id->agsize - mp->m_ag_prealloc_blocks;
+	agf->agf_freeblks = cpu_to_be32(tmpsize);
+	agf->agf_longest = cpu_to_be32(tmpsize);
+	if (xfs_sb_version_hascrc(&mp->m_sb))
+		uuid_copy(&agf->agf_uuid, &mp->m_sb.sb_meta_uuid);
+	if (xfs_sb_version_hasreflink(&mp->m_sb)) {
+		agf->agf_refcount_root = cpu_to_be32(
+				xfs_refc_block(mp));
+		agf->agf_refcount_level = cpu_to_be32(1);
+		agf->agf_refcount_blocks = cpu_to_be32(1);
+	}
+}
+
+static void
+xfs_agflblock_init(
+	struct xfs_mount	*mp,
+	struct xfs_buf		*bp,
+	struct aghdr_init_data	*id)
+{
+	struct xfs_agfl		*agfl = XFS_BUF_TO_AGFL(bp);
+	__be32			*agfl_bno;
+	int			bucket;
+
+	if (xfs_sb_version_hascrc(&mp->m_sb)) {
+		agfl->agfl_magicnum = cpu_to_be32(XFS_AGFL_MAGIC);
+		agfl->agfl_seqno = cpu_to_be32(id->agno);
+		uuid_copy(&agfl->agfl_uuid, &mp->m_sb.sb_meta_uuid);
+	}
+
+	agfl_bno = XFS_BUF_TO_AGFL_BNO(mp, bp);
+	for (bucket = 0; bucket < xfs_agfl_size(mp); bucket++)
+		agfl_bno[bucket] = cpu_to_be32(NULLAGBLOCK);
+}
+
+static void
+xfs_agiblock_init(
+	struct xfs_mount	*mp,
+	struct xfs_buf		*bp,
+	struct aghdr_init_data	*id)
+{
+	struct xfs_agi		*agi = XFS_BUF_TO_AGI(bp);
+	int			bucket;
+
+	agi->agi_magicnum = cpu_to_be32(XFS_AGI_MAGIC);
+	agi->agi_versionnum = cpu_to_be32(XFS_AGI_VERSION);
+	agi->agi_seqno = cpu_to_be32(id->agno);
+	agi->agi_length = cpu_to_be32(id->agsize);
+	agi->agi_count = 0;
+	agi->agi_root = cpu_to_be32(XFS_IBT_BLOCK(mp));
+	agi->agi_level = cpu_to_be32(1);
+	agi->agi_freecount = 0;
+	agi->agi_newino = cpu_to_be32(NULLAGINO);
+	agi->agi_dirino = cpu_to_be32(NULLAGINO);
+	if (xfs_sb_version_hascrc(&mp->m_sb))
+		uuid_copy(&agi->agi_uuid, &mp->m_sb.sb_meta_uuid);
+	if (xfs_sb_version_hasfinobt(&mp->m_sb)) {
+		agi->agi_free_root = cpu_to_be32(XFS_FIBT_BLOCK(mp));
+		agi->agi_free_level = cpu_to_be32(1);
+	}
+	for (bucket = 0; bucket < XFS_AGI_UNLINKED_BUCKETS; bucket++)
+		agi->agi_unlinked[bucket] = cpu_to_be32(NULLAGINO);
+}
+
+typedef void (*aghdr_init_work_f)(struct xfs_mount *mp, struct xfs_buf *bp,
+				  struct aghdr_init_data *id);
+static int
+xfs_ag_init_hdr(
+	struct xfs_mount	*mp,
+	struct aghdr_init_data	*id,
+	aghdr_init_work_f	work,
+	const struct xfs_buf_ops *ops)
+
+{
+	struct xfs_buf		*bp;
+
+	bp = xfs_get_aghdr_buf(mp, id->daddr, id->numblks, 0, ops);
+	if (!bp)
+		return -ENOMEM;
+
+	(*work)(mp, bp, id);
+
+	xfs_buf_delwri_queue(bp, &id->buffer_list);
+	xfs_buf_relse(bp);
+	return 0;
+}
+
+struct xfs_aghdr_grow_data {
+	xfs_daddr_t		daddr;
+	size_t			numblks;
+	const struct xfs_buf_ops *ops;
+	aghdr_init_work_f	work;
+	xfs_btnum_t		type;
+	bool			need_init;
+};
+
+/*
+ * Prepare new AG headers to be written to disk. We use uncached buffers here,
+ * as it is assumed these new AG headers are currently beyond the currently
+ * valid filesystem address space. Using cached buffers would trip over EOFS
+ * corruption detection alogrithms in the buffer cache lookup routines.
+ *
+ * This is a non-transactional function, but the prepared buffers are added to a
+ * delayed write buffer list supplied by the caller so they can submit them to
+ * disk and wait on them as required.
+ */
+int
+xfs_ag_init_headers(
+	struct xfs_mount	*mp,
+	struct aghdr_init_data	*id)
+
+{
+	struct xfs_aghdr_grow_data aghdr_data[] = {
+	{ /* SB */
+		.daddr = XFS_AG_DADDR(mp, id->agno, XFS_SB_DADDR),
+		.numblks = XFS_FSS_TO_BB(mp, 1),
+		.ops = &xfs_sb_buf_ops,
+		.work = &xfs_sbblock_init,
+		.need_init = true
+	},
+	{ /* AGF */
+		.daddr = XFS_AG_DADDR(mp, id->agno, XFS_AGF_DADDR(mp)),
+		.numblks = XFS_FSS_TO_BB(mp, 1),
+		.ops = &xfs_agf_buf_ops,
+		.work = &xfs_agfblock_init,
+		.need_init = true
+	},
+	{ /* AGFL */
+		.daddr = XFS_AG_DADDR(mp, id->agno, XFS_AGFL_DADDR(mp)),
+		.numblks = XFS_FSS_TO_BB(mp, 1),
+		.ops = &xfs_agfl_buf_ops,
+		.work = &xfs_agflblock_init,
+		.need_init = true
+	},
+	{ /* AGI */
+		.daddr = XFS_AG_DADDR(mp, id->agno, XFS_AGI_DADDR(mp)),
+		.numblks = XFS_FSS_TO_BB(mp, 1),
+		.ops = &xfs_agi_buf_ops,
+		.work = &xfs_agiblock_init,
+		.need_init = true
+	},
+	{ /* BNO root block */
+		.daddr = XFS_AGB_TO_DADDR(mp, id->agno, XFS_BNO_BLOCK(mp)),
+		.numblks = BTOBB(mp->m_sb.sb_blocksize),
+		.ops = &xfs_allocbt_buf_ops,
+		.work = &xfs_bnoroot_init,
+		.need_init = true
+	},
+	{ /* CNT root block */
+		.daddr = XFS_AGB_TO_DADDR(mp, id->agno, XFS_CNT_BLOCK(mp)),
+		.numblks = BTOBB(mp->m_sb.sb_blocksize),
+		.ops = &xfs_allocbt_buf_ops,
+		.work = &xfs_cntroot_init,
+		.need_init = true
+	},
+	{ /* INO root block */
+		.daddr = XFS_AGB_TO_DADDR(mp, id->agno, XFS_IBT_BLOCK(mp)),
+		.numblks = BTOBB(mp->m_sb.sb_blocksize),
+		.ops = &xfs_inobt_buf_ops,
+		.work = &xfs_btroot_init,
+		.type = XFS_BTNUM_INO,
+		.need_init = true
+	},
+	{ /* FINO root block */
+		.daddr = XFS_AGB_TO_DADDR(mp, id->agno, XFS_FIBT_BLOCK(mp)),
+		.numblks = BTOBB(mp->m_sb.sb_blocksize),
+		.ops = &xfs_inobt_buf_ops,
+		.work = &xfs_btroot_init,
+		.type = XFS_BTNUM_FINO,
+		.need_init =  xfs_sb_version_hasfinobt(&mp->m_sb)
+	},
+	{ /* RMAP root block */
+		.daddr = XFS_AGB_TO_DADDR(mp, id->agno, XFS_RMAP_BLOCK(mp)),
+		.numblks = BTOBB(mp->m_sb.sb_blocksize),
+		.ops = &xfs_rmapbt_buf_ops,
+		.work = &xfs_rmaproot_init,
+		.need_init = xfs_sb_version_hasrmapbt(&mp->m_sb)
+	},
+	{ /* REFC root block */
+		.daddr = XFS_AGB_TO_DADDR(mp, id->agno, xfs_refc_block(mp)),
+		.numblks = BTOBB(mp->m_sb.sb_blocksize),
+		.ops = &xfs_refcountbt_buf_ops,
+		.work = &xfs_btroot_init,
+		.type = XFS_BTNUM_REFC,
+		.need_init = xfs_sb_version_hasreflink(&mp->m_sb)
+	},
+	{ /* NULL terminating block */
+		.daddr = XFS_BUF_DADDR_NULL,
+	}
+	};
+	struct  xfs_aghdr_grow_data *dp;
+	int			error = 0;
+
+	/* Account for AG free space in new AG */
+	id->nfree += id->agsize - mp->m_ag_prealloc_blocks;
+	for (dp = &aghdr_data[0]; dp->daddr != XFS_BUF_DADDR_NULL; dp++) {
+		if (!dp->need_init)
+			continue;
+
+		id->daddr = dp->daddr;
+		id->numblks = dp->numblks;
+		id->type = dp->type;
+		error = xfs_ag_init_hdr(mp, id, dp->work, dp->ops);
+		if (error)
+			break;
+	}
+	return error;
+}
diff --git a/fs/xfs/libxfs/xfs_ag.h b/fs/xfs/libxfs/xfs_ag.h
new file mode 100644
index 000000000000..69f2fd4be0ea
--- /dev/null
+++ b/fs/xfs/libxfs/xfs_ag.h
@@ -0,0 +1,25 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2018 Red Hat, Inc.
+ * All rights reserved.
+ */
+
+#ifndef __LIBXFS_AG_H
+#define __LIBXFS_AG_H 1
+
+struct aghdr_init_data {
+	/* per ag data */
+	xfs_agblock_t		agno;		/* ag to init */
+	xfs_extlen_t		agsize;		/* new AG size */
+	struct list_head	buffer_list;	/* buffer writeback list */
+	xfs_rfsblock_t		nfree;		/* cumulative new free space */
+
+	/* per header data */
+	xfs_daddr_t		daddr;		/* header location */
+	size_t			numblks;	/* size of header */
+	xfs_btnum_t		type;		/* type of btree root block */
+};
+
+int xfs_ag_init_headers( struct xfs_mount *mp, struct aghdr_init_data *id);
+
+#endif /* __LIBXFS_AG_H */
diff --git a/fs/xfs/libxfs/xfs_sb.c b/fs/xfs/libxfs/xfs_sb.c
index d9b94bd5f689..75b2fee6f99a 100644
--- a/fs/xfs/libxfs/xfs_sb.c
+++ b/fs/xfs/libxfs/xfs_sb.c
@@ -888,6 +888,79 @@ xfs_sync_sb(
 	return xfs_trans_commit(tp);
 }
 
+/*
+ * Update all the secondary superblocks to match the new state of the primary.
+ * Because we are completely overwriting all the existing fields in the
+ * secondary superblock buffers, there is no need to read them in from disk.
+ * Just get a new buffer, stamp it and write it.
+ *
+ * The sb buffers need to be cached here so that we serialise against other
+ * operations that access the secondary superblocks, but we don't want to keep
+ * them in memory once it is written so we mark it as a one-shot buffer.
+ */
+int
+xfs_update_secondary_sbs(
+	struct xfs_mount	*mp)
+{
+	xfs_agnumber_t		agno;
+	int			saved_error = 0;
+	int			error = 0;
+	LIST_HEAD		(buffer_list);
+
+	/* update secondary superblocks. */
+	for (agno = 1; agno < mp->m_sb.sb_agcount; agno++) {
+		struct xfs_buf		*bp;
+
+		bp = xfs_buf_get(mp->m_ddev_targp,
+				 XFS_AG_DADDR(mp, agno, XFS_SB_DADDR),
+				 XFS_FSS_TO_BB(mp, 1), 0);
+		/*
+		 * If we get an error reading or writing alternate superblocks,
+		 * continue.  xfs_repair chooses the "best" superblock based
+		 * on most matches; if we break early, we'll leave more
+		 * superblocks un-updated than updated, and xfs_repair may
+		 * pick them over the properly-updated primary.
+		 */
+		if (!bp) {
+			xfs_warn(mp,
+		"error allocating secondary superblock for ag %d",
+				agno);
+			if (!saved_error)
+				saved_error = -ENOMEM;
+			continue;
+		}
+
+		bp->b_ops = &xfs_sb_buf_ops;
+		xfs_buf_oneshot(bp);
+		xfs_buf_zero(bp, 0, BBTOB(bp->b_length));
+		xfs_sb_to_disk(XFS_BUF_TO_SBP(bp), &mp->m_sb);
+		xfs_buf_delwri_queue(bp, &buffer_list);
+		xfs_buf_relse(bp);
+
+		/* don't hold too many buffers at once */
+		if (agno % 16)
+			continue;
+
+		error = xfs_buf_delwri_submit(&buffer_list);
+		if (error) {
+			xfs_warn(mp,
+		"write error %d updating a secondary superblock near ag %d",
+				error, agno);
+			if (!saved_error)
+				saved_error = error;
+			continue;
+		}
+	}
+	error = xfs_buf_delwri_submit(&buffer_list);
+	if (error) {
+		xfs_warn(mp,
+		"write error %d updating a secondary superblock near ag %d",
+			error, agno);
+	}
+
+	return saved_error ? saved_error : error;
+}
+
 int
 xfs_fs_geometry(
 	struct xfs_sb		*sbp,
diff --git a/fs/xfs/libxfs/xfs_sb.h b/fs/xfs/libxfs/xfs_sb.h
index 63dcd2a1a657..bb0196b56fa5 100644
--- a/fs/xfs/libxfs/xfs_sb.h
+++ b/fs/xfs/libxfs/xfs_sb.h
@@ -18,6 +18,13 @@
 #ifndef __XFS_SB_H__
 #define	__XFS_SB_H__
 
+struct xfs_mount;
+struct xfs_sb;
+struct xfs_dsb;
+struct xfs_trans;
+struct xfs_fsop_geom;
+struct xfs_perag;
+
 /*
  * perag get/put wrappers for ref counting
  */
@@ -34,6 +41,8 @@ extern void	xfs_sb_from_disk(struct xfs_sb *to, struct xfs_dsb *from);
 extern void	xfs_sb_to_disk(struct xfs_dsb *to, struct xfs_sb *from);
 extern void	xfs_sb_quota_from_disk(struct xfs_sb *sbp);
 
+extern int	xfs_update_secondary_sbs(struct xfs_mount *mp);
+
 #define XFS_FS_GEOM_MAX_STRUCT_VER	(4)
 extern int	xfs_fs_geometry(struct xfs_sb *sbp, struct xfs_fsop_geom *geo,
 				int struct_version);
diff --git a/fs/xfs/xfs_fsops.c b/fs/xfs/xfs_fsops.c
index ca3cf3f6239a..c5087b79bcea 100644
--- a/fs/xfs/xfs_fsops.c
+++ b/fs/xfs/xfs_fsops.c
@@ -24,11 +24,7 @@
 #include "xfs_sb.h"
 #include "xfs_mount.h"
 #include "xfs_defer.h"
-#include "xfs_da_format.h"
-#include "xfs_da_btree.h"
-#include "xfs_inode.h"
 #include "xfs_trans.h"
-#include "xfs_inode_item.h"
 #include "xfs_error.h"
 #include "xfs_btree.h"
 #include "xfs_alloc_btree.h"
@@ -36,411 +32,17 @@
 #include "xfs_rmap_btree.h"
 #include "xfs_ialloc.h"
 #include "xfs_fsops.h"
-#include "xfs_itable.h"
 #include "xfs_trans_space.h"
 #include "xfs_rtalloc.h"
 #include "xfs_trace.h"
 #include "xfs_log.h"
-#include "xfs_filestream.h"
 #include "xfs_rmap.h"
+#include "xfs_ag.h"
 #include "xfs_ag_resv.h"
 
 /*
- * File system operations
- */
-
-static struct xfs_buf *
-xfs_growfs_get_hdr_buf(
-	struct xfs_mount	*mp,
-	xfs_daddr_t		blkno,
-	size_t			numblks,
-	int			flags,
-	const struct xfs_buf_ops *ops)
-{
-	struct xfs_buf		*bp;
-
-	bp = xfs_buf_get_uncached(mp->m_ddev_targp, numblks, flags);
-	if (!bp)
-		return NULL;
-
-	xfs_buf_zero(bp, 0, BBTOB(bp->b_length));
-	bp->b_bn = blkno;
-	bp->b_maps[0].bm_bn = blkno;
-	bp->b_ops = ops;
-
-	return bp;
-}
-
-struct aghdr_init_data {
-	/* per ag data */
-	xfs_agnumber_t		agno;		/* ag to init */
-	xfs_extlen_t		agsize;		/* new AG size */
-	struct list_head	buffer_list;	/* buffer writeback list */
-	xfs_rfsblock_t		nfree;		/* cumulative new free space */
-
-	/* per header data */
-	xfs_daddr_t		daddr;		/* header location */
-	size_t			numblks;	/* size of header */
-	xfs_btnum_t		type;		/* type of btree root block */
-};
-
-/*
- * Generic btree root block init function
- */
-static void
-xfs_btroot_init(
-	struct xfs_mount	*mp,
-	struct xfs_buf		*bp,
-	struct aghdr_init_data	*id)
-{
-	xfs_btree_init_block(mp, bp, id->type, 0, 0, id->agno, 0);
-}
-
-/*
- * Alloc btree root block init functions
- */
-static void
-xfs_bnoroot_init(
-	struct xfs_mount	*mp,
-	struct xfs_buf		*bp,
-	struct aghdr_init_data	*id)
-{
-	struct xfs_alloc_rec	*arec;
-
-	xfs_btree_init_block(mp, bp, XFS_BTNUM_BNO, 0, 1, id->agno, 0);
-	arec = XFS_ALLOC_REC_ADDR(mp, XFS_BUF_TO_BLOCK(bp), 1);
-	arec->ar_startblock = cpu_to_be32(mp->m_ag_prealloc_blocks);
-	arec->ar_blockcount = cpu_to_be32(id->agsize -
-					  be32_to_cpu(arec->ar_startblock));
-}
-
-static void
-xfs_cntroot_init(
-	struct xfs_mount	*mp,
-	struct xfs_buf		*bp,
-	struct aghdr_init_data	*id)
-{
-	struct xfs_alloc_rec	*arec;
-
-	xfs_btree_init_block(mp, bp, XFS_BTNUM_CNT, 0, 1, id->agno, 0);
-	arec = XFS_ALLOC_REC_ADDR(mp, XFS_BUF_TO_BLOCK(bp), 1);
-	arec->ar_startblock = cpu_to_be32(mp->m_ag_prealloc_blocks);
-	arec->ar_blockcount = cpu_to_be32(id->agsize -
-					  be32_to_cpu(arec->ar_startblock));
-}
-
-/*
- * Reverse map root block init
- */
-static void
-xfs_rmaproot_init(
-	struct xfs_mount	*mp,
-	struct xfs_buf		*bp,
-	struct aghdr_init_data	*id)
-{
-	struct xfs_btree_block	*block = XFS_BUF_TO_BLOCK(bp);
-	struct xfs_rmap_rec	*rrec;
-
-	xfs_btree_init_block(mp, bp, XFS_BTNUM_RMAP, 0, 4, id->agno, 0);
-
-	/*
-	 * mark the AG header regions as static metadata The BNO
-	 * btree block is the first block after the headers, so
-	 * it's location defines the size of region the static
-	 * metadata consumes.
-	 *
-	 * Note: unlike mkfs, we never have to account for log
-	 * space when growing the data regions
-	 */
-	rrec = XFS_RMAP_REC_ADDR(block, 1);
-	rrec->rm_startblock = 0;
-	rrec->rm_blockcount = cpu_to_be32(XFS_BNO_BLOCK(mp));
-	rrec->rm_owner = cpu_to_be64(XFS_RMAP_OWN_FS);
-	rrec->rm_offset = 0;
-
-	/* account freespace btree root blocks */
-	rrec = XFS_RMAP_REC_ADDR(block, 2);
-	rrec->rm_startblock = cpu_to_be32(XFS_BNO_BLOCK(mp));
-	rrec->rm_blockcount = cpu_to_be32(2);
-	rrec->rm_owner = cpu_to_be64(XFS_RMAP_OWN_AG);
-	rrec->rm_offset = 0;
-
-	/* account inode btree root blocks */
-	rrec = XFS_RMAP_REC_ADDR(block, 3);
-	rrec->rm_startblock = cpu_to_be32(XFS_IBT_BLOCK(mp));
-	rrec->rm_blockcount = cpu_to_be32(XFS_RMAP_BLOCK(mp) -
-					  XFS_IBT_BLOCK(mp));
-	rrec->rm_owner = cpu_to_be64(XFS_RMAP_OWN_INOBT);
-	rrec->rm_offset = 0;
-
-	/* account for rmap btree root */
-	rrec = XFS_RMAP_REC_ADDR(block, 4);
-	rrec->rm_startblock = cpu_to_be32(XFS_RMAP_BLOCK(mp));
-	rrec->rm_blockcount = cpu_to_be32(1);
-	rrec->rm_owner = cpu_to_be64(XFS_RMAP_OWN_AG);
-	rrec->rm_offset = 0;
-
-	/* account for refc btree root */
-	if (xfs_sb_version_hasreflink(&mp->m_sb)) {
-		rrec = XFS_RMAP_REC_ADDR(block, 5);
-		rrec->rm_startblock = cpu_to_be32(xfs_refc_block(mp));
-		rrec->rm_blockcount = cpu_to_be32(1);
-		rrec->rm_owner = cpu_to_be64(XFS_RMAP_OWN_REFC);
-		rrec->rm_offset = 0;
-		be16_add_cpu(&block->bb_numrecs, 1);
-	}
-}
-
-/*
- * Initialise new secondary superblocks with the pre-grow geometry, but mark
- * them as "in progress" so we know they haven't yet been activated. This will
- * get cleared when the update with the new geometry information is done after
- * changes to the primary are committed. This isn't strictly necessary, but we
- * get it for free with the delayed buffer write lists and it means we can tell
- * if a grow operation didn't complete properly after the fact.
- */
-static void
-xfs_sbblock_init(
-	struct xfs_mount	*mp,
-	struct xfs_buf		*bp,
-	struct aghdr_init_data	*id)
-{
-	struct xfs_dsb		*dsb = XFS_BUF_TO_SBP(bp);
-
-	xfs_sb_to_disk(dsb, &mp->m_sb);
-	dsb->sb_inprogress = 1;
-}
-
-static void
-xfs_agfblock_init(
-	struct xfs_mount	*mp,
-	struct xfs_buf		*bp,
-	struct aghdr_init_data	*id)
-{
-	struct xfs_agf		*agf = XFS_BUF_TO_AGF(bp);
-	xfs_extlen_t		tmpsize;
-
-	agf->agf_magicnum = cpu_to_be32(XFS_AGF_MAGIC);
-	agf->agf_versionnum = cpu_to_be32(XFS_AGF_VERSION);
-	agf->agf_seqno = cpu_to_be32(id->agno);
-	agf->agf_length = cpu_to_be32(id->agsize);
-	agf->agf_roots[XFS_BTNUM_BNOi] = cpu_to_be32(XFS_BNO_BLOCK(mp));
-	agf->agf_roots[XFS_BTNUM_CNTi] = cpu_to_be32(XFS_CNT_BLOCK(mp));
-	agf->agf_levels[XFS_BTNUM_BNOi] = cpu_to_be32(1);
-	agf->agf_levels[XFS_BTNUM_CNTi] = cpu_to_be32(1);
-	if (xfs_sb_version_hasrmapbt(&mp->m_sb)) {
-		agf->agf_roots[XFS_BTNUM_RMAPi] =
-					cpu_to_be32(XFS_RMAP_BLOCK(mp));
-		agf->agf_levels[XFS_BTNUM_RMAPi] = cpu_to_be32(1);
-		agf->agf_rmap_blocks = cpu_to_be32(1);
-	}
-
-	agf->agf_flfirst = cpu_to_be32(1);
-	agf->agf_fllast = 0;
-	agf->agf_flcount = 0;
-	tmpsize = id->agsize - mp->m_ag_prealloc_blocks;
-	agf->agf_freeblks = cpu_to_be32(tmpsize);
-	agf->agf_longest = cpu_to_be32(tmpsize);
-	if (xfs_sb_version_hascrc(&mp->m_sb))
-		uuid_copy(&agf->agf_uuid, &mp->m_sb.sb_meta_uuid);
-	if (xfs_sb_version_hasreflink(&mp->m_sb)) {
-		agf->agf_refcount_root = cpu_to_be32(
-				xfs_refc_block(mp));
-		agf->agf_refcount_level = cpu_to_be32(1);
-		agf->agf_refcount_blocks = cpu_to_be32(1);
-	}
-}
-
-static void
-xfs_agflblock_init(
-	struct xfs_mount	*mp,
-	struct xfs_buf		*bp,
-	struct aghdr_init_data	*id)
-{
-	struct xfs_agfl		*agfl = XFS_BUF_TO_AGFL(bp);
-	__be32			*agfl_bno;
-	int			bucket;
-
-	if (xfs_sb_version_hascrc(&mp->m_sb)) {
-		agfl->agfl_magicnum = cpu_to_be32(XFS_AGFL_MAGIC);
-		agfl->agfl_seqno = cpu_to_be32(id->agno);
-		uuid_copy(&agfl->agfl_uuid, &mp->m_sb.sb_meta_uuid);
-	}
-
-	agfl_bno = XFS_BUF_TO_AGFL_BNO(mp, bp);
-	for (bucket = 0; bucket < xfs_agfl_size(mp); bucket++)
-		agfl_bno[bucket] = cpu_to_be32(NULLAGBLOCK);
-}
-
-static void
-xfs_agiblock_init(
-	struct xfs_mount	*mp,
-	struct xfs_buf		*bp,
-	struct aghdr_init_data	*id)
-{
-	struct xfs_agi		*agi = XFS_BUF_TO_AGI(bp);
-	int			bucket;
-
-	agi->agi_magicnum = cpu_to_be32(XFS_AGI_MAGIC);
-	agi->agi_versionnum = cpu_to_be32(XFS_AGI_VERSION);
-	agi->agi_seqno = cpu_to_be32(id->agno);
-	agi->agi_length = cpu_to_be32(id->agsize);
-	agi->agi_count = 0;
-	agi->agi_root = cpu_to_be32(XFS_IBT_BLOCK(mp));
-	agi->agi_level = cpu_to_be32(1);
-	agi->agi_freecount = 0;
-	agi->agi_newino = cpu_to_be32(NULLAGINO);
-	agi->agi_dirino = cpu_to_be32(NULLAGINO);
-	if (xfs_sb_version_hascrc(&mp->m_sb))
-		uuid_copy(&agi->agi_uuid, &mp->m_sb.sb_meta_uuid);
-	if (xfs_sb_version_hasfinobt(&mp->m_sb)) {
-		agi->agi_free_root = cpu_to_be32(XFS_FIBT_BLOCK(mp));
-		agi->agi_free_level = cpu_to_be32(1);
-	}
-	for (bucket = 0; bucket < XFS_AGI_UNLINKED_BUCKETS; bucket++)
-		agi->agi_unlinked[bucket] = cpu_to_be32(NULLAGINO);
-}
-
-typedef void (*aghdr_init_work_f)(struct xfs_mount *mp, struct xfs_buf *bp,
-				  struct aghdr_init_data *id);
-static int
-xfs_growfs_init_aghdr(
-	struct xfs_mount	*mp,
-	struct aghdr_init_data	*id,
-	aghdr_init_work_f	work,
-	const struct xfs_buf_ops *ops)
-
-{
-	struct xfs_buf		*bp;
-
-	bp = xfs_growfs_get_hdr_buf(mp, id->daddr, id->numblks, 0, ops);
-	if (!bp)
-		return -ENOMEM;
-
-	(*work)(mp, bp, id);
-
-	xfs_buf_delwri_queue(bp, &id->buffer_list);
-	xfs_buf_relse(bp);
-	return 0;
-}
-
-struct xfs_aghdr_grow_data {
-	xfs_daddr_t		daddr;
-	size_t			numblks;
-	const struct xfs_buf_ops *ops;
-	aghdr_init_work_f	work;
-	xfs_btnum_t		type;
-	bool			need_init;
-};
-
-/*
- * Write new AG headers to disk. Non-transactional, but written
- * synchronously so they are completed prior to the growfs transaction
- * being logged.
+ * growfs operations
  */
-static int
-xfs_grow_ag_headers(
-	struct xfs_mount	*mp,
-	struct aghdr_init_data	*id)
-
-{
-	struct xfs_aghdr_grow_data aghdr_data[] = {
-	{ /* SB */
-		.daddr = XFS_AG_DADDR(mp, id->agno, XFS_SB_DADDR),
-		.numblks = XFS_FSS_TO_BB(mp, 1),
-		.ops = &xfs_sb_buf_ops,
-		.work = &xfs_sbblock_init,
-		.need_init = true
-	},
-	{ /* AGF */
-		.daddr = XFS_AG_DADDR(mp, id->agno, XFS_AGF_DADDR(mp)),
-		.numblks = XFS_FSS_TO_BB(mp, 1),
-		.ops = &xfs_agf_buf_ops,
-		.work = &xfs_agfblock_init,
-		.need_init = true
-	},
-	{ /* AGFL */
-		.daddr = XFS_AG_DADDR(mp, id->agno, XFS_AGFL_DADDR(mp)),
-		.numblks = XFS_FSS_TO_BB(mp, 1),
-		.ops = &xfs_agfl_buf_ops,
-		.work = &xfs_agflblock_init,
-		.need_init = true
-	},
-	{ /* AGI */
-		.daddr = XFS_AG_DADDR(mp, id->agno, XFS_AGI_DADDR(mp)),
-		.numblks = XFS_FSS_TO_BB(mp, 1),
-		.ops = &xfs_agi_buf_ops,
-		.work = &xfs_agiblock_init,
-		.need_init = true
-	},
-	{ /* BNO root block */
-		.daddr = XFS_AGB_TO_DADDR(mp, id->agno, XFS_BNO_BLOCK(mp)),
-		.numblks = BTOBB(mp->m_sb.sb_blocksize),
-		.ops = &xfs_allocbt_buf_ops,
-		.work = &xfs_bnoroot_init,
-		.need_init = true
-	},
-	{ /* CNT root block */
-		.daddr = XFS_AGB_TO_DADDR(mp, id->agno, XFS_CNT_BLOCK(mp)),
-		.numblks = BTOBB(mp->m_sb.sb_blocksize),
-		.ops = &xfs_allocbt_buf_ops,
-		.work = &xfs_cntroot_init,
-		.need_init = true
-	},
-	{ /* INO root block */
-		.daddr = XFS_AGB_TO_DADDR(mp, id->agno, XFS_IBT_BLOCK(mp)),
-		.numblks = BTOBB(mp->m_sb.sb_blocksize),
-		.ops = &xfs_inobt_buf_ops,
-		.work = &xfs_btroot_init,
-		.type = XFS_BTNUM_INO,
-		.need_init = true
-	},
-	{ /* FINO root block */
-		.daddr = XFS_AGB_TO_DADDR(mp, id->agno, XFS_FIBT_BLOCK(mp)),
-		.numblks = BTOBB(mp->m_sb.sb_blocksize),
-		.ops = &xfs_inobt_buf_ops,
-		.work = &xfs_btroot_init,
-		.type = XFS_BTNUM_FINO,
-		.need_init =  xfs_sb_version_hasfinobt(&mp->m_sb)
-	},
-	{ /* RMAP root block */
-		.daddr = XFS_AGB_TO_DADDR(mp, id->agno, XFS_RMAP_BLOCK(mp)),
-		.numblks = BTOBB(mp->m_sb.sb_blocksize),
-		.ops = &xfs_rmapbt_buf_ops,
-		.work = &xfs_rmaproot_init,
-		.need_init = xfs_sb_version_hasrmapbt(&mp->m_sb)
-	},
-	{ /* REFC root block */
-		.daddr = XFS_AGB_TO_DADDR(mp, id->agno, xfs_refc_block(mp)),
-		.numblks = BTOBB(mp->m_sb.sb_blocksize),
-		.ops = &xfs_refcountbt_buf_ops,
-		.work = &xfs_btroot_init,
-		.type = XFS_BTNUM_REFC,
-		.need_init = xfs_sb_version_hasreflink(&mp->m_sb)
-	},
-	{ /* NULL terminating block */
-		.daddr = XFS_BUF_DADDR_NULL,
-	}
-	};
-	struct  xfs_aghdr_grow_data *dp;
-	int			error = 0;
-
-	/* Account for AG free space in new AG */
-	id->nfree += id->agsize - mp->m_ag_prealloc_blocks;
-	for (dp = &aghdr_data[0]; dp->daddr != XFS_BUF_DADDR_NULL; dp++) {
-		if (!dp->need_init)
-			continue;
-
-		id->daddr = dp->daddr;
-		id->numblks = dp->numblks;
-		id->type = dp->type;
-		error = xfs_growfs_init_aghdr(mp, id, dp->work, dp->ops);
-		if (error)
-			break;
-	}
-	return error;
-}
-
 static int
 xfs_growfs_data_private(
 	xfs_mount_t		*mp,		/* mount point for filesystem */
@@ -518,7 +120,7 @@ xfs_growfs_data_private(
 		else
 			id.agsize = mp->m_sb.sb_agblocks;
 
-		error = xfs_grow_ag_headers(mp, &id);
+		error = xfs_ag_init_headers(mp, &id);
 		if (error) {
 			xfs_buf_delwri_cancel(&id.buffer_list);
 			goto out_trans_cancel;
@@ -682,79 +284,6 @@ xfs_growfs_imaxpct(
 	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. Because we are completely overwriting
- * all the existing fields in the secondary superblock buffers, there is no need
- * to read them in from disk. Just get a new buffer, stamp it and write it.
- *
- * The sb buffers need to be cached here so that we serialise against scrub
- * scanning secondary superblocks, but we don't want to keep it in memory once
- * it is written so we mark it as a one-shot buffer.
- */
-static int
-xfs_growfs_update_superblocks(
-	struct xfs_mount	*mp)
-{
-	xfs_agnumber_t		agno;
-	int			saved_error = 0;
-	int			error = 0;
-	LIST_HEAD		(buffer_list);
-
-	/* update secondary superblocks. */
-	for (agno = 1; agno < mp->m_sb.sb_agcount; agno++) {
-		struct xfs_buf		*bp;
-
-		bp = xfs_buf_get(mp->m_ddev_targp,
-				 XFS_AG_DADDR(mp, agno, XFS_SB_DADDR),
-				 XFS_FSS_TO_BB(mp, 1), 0);
-		/*
-		 * If we get an error reading or writing alternate superblocks,
-		 * continue.  xfs_repair chooses the "best" superblock based
-		 * on most matches; if we break early, we'll leave more
-		 * superblocks un-updated than updated, and xfs_repair may
-		 * pick them over the properly-updated primary.
-		 */
-		if (!bp) {
-			xfs_warn(mp,
-		"error allocating secondary superblock for ag %d",
-				agno);
-			if (!saved_error)
-				saved_error = -ENOMEM;
-			continue;
-		}
-
-		bp->b_ops = &xfs_sb_buf_ops;
-		xfs_buf_oneshot(bp);
-		xfs_buf_zero(bp, 0, BBTOB(bp->b_length));
-		xfs_sb_to_disk(XFS_BUF_TO_SBP(bp), &mp->m_sb);
-		xfs_buf_delwri_queue(bp, &buffer_list);
-		xfs_buf_relse(bp);
-
-		/* don't hold too many buffers at once */
-		if (agno % 16)
-			continue;
-
-		error = xfs_buf_delwri_submit(&buffer_list);
-		if (error) {
-			xfs_warn(mp,
-		"write error %d updating a secondary superblock near ag %d",
-				error, agno);
-			if (!saved_error)
-				saved_error = error;
-			continue;
-		}
-	}
-	error = xfs_buf_delwri_submit(&buffer_list);
-	if (error) {
-		xfs_warn(mp,
-		"write error %d updating a secondary superblock near ag %d",
-			error, agno);
-	}
-
-	return saved_error ? saved_error : error;
-}
-
 /*
  * protected versions of growfs function acquire and release locks on the mount
  * point - exported through ioctls: XFS_IOC_FSGROWFSDATA, XFS_IOC_FSGROWFSLOG,
@@ -794,7 +323,7 @@ xfs_growfs_data(
 		mp->m_maxicount = 0;
 
 	/* Update secondary superblocks now the physical grow has completed */
-	error = xfs_growfs_update_superblocks(mp);
+	error = xfs_update_secondary_sbs(mp);
 
 out_error:
 	/*
-- 
2.17.0


^ permalink raw reply related	[flat|nested] 17+ messages in thread

* [PATCH 10/10] xfs: factor the ag length extension code into libxfs
  2018-05-14  4:18 [PATCH 0/10 v3] xfs: refactor and tablise growfs Dave Chinner
                   ` (8 preceding siblings ...)
  2018-05-14  4:19 ` [PATCH 09/10] xfs: move growfs core to libxfs Dave Chinner
@ 2018-05-14  4:19 ` Dave Chinner
  9 siblings, 0 replies; 17+ messages in thread
From: Dave Chinner @ 2018-05-14  4:19 UTC (permalink / raw)
  To: linux-xfs

From: Dave Chinner <dchinner@redhat.com>

Growfs currently manually codes the extension of the last AG in a
filesytem during the growfs process. Factor that out of the growfs
code and move it into libxfs along with teh rest of the AG header
modification code.

Signed-Off-By: Dave Chinner <dchinner@redhat.com>
Reviewed-by: Darrick J. Wong <darrick.wong@oracle.com>
---
 fs/xfs/libxfs/xfs_ag.c | 60 ++++++++++++++++++++++++++++++++++++++++++
 fs/xfs/libxfs/xfs_ag.h |  7 ++++-
 fs/xfs/xfs_fsops.c     | 58 ++--------------------------------------
 3 files changed, 68 insertions(+), 57 deletions(-)

diff --git a/fs/xfs/libxfs/xfs_ag.c b/fs/xfs/libxfs/xfs_ag.c
index 5baa22c07095..9345802c99f7 100644
--- a/fs/xfs/libxfs/xfs_ag.c
+++ b/fs/xfs/libxfs/xfs_ag.c
@@ -16,6 +16,7 @@
 #include "xfs_alloc_btree.h"
 #include "xfs_rmap_btree.h"
 #include "xfs_alloc.h"
+#include "xfs_ialloc.h"
 #include "xfs_rmap.h"
 #include "xfs_ag.h"
 
@@ -402,3 +403,62 @@ xfs_ag_init_headers(
 	}
 	return error;
 }
+
+/*
+ * Extent the AG indicated by the @id by the length passed in
+ */
+int
+xfs_ag_extend_space(
+	struct xfs_mount	*mp,
+	struct xfs_trans	*tp,
+	struct aghdr_init_data	*id,
+	xfs_extlen_t		len)
+{
+	struct xfs_owner_info	oinfo;
+	struct xfs_buf		*bp;
+	struct xfs_agi		*agi;
+	struct xfs_agf		*agf;
+	int			error;
+
+	/*
+	 * Change the agi length.
+	 */
+	error = xfs_ialloc_read_agi(mp, tp, id->agno, &bp);
+	if (error)
+		return error;
+
+	agi = XFS_BUF_TO_AGI(bp);
+	be32_add_cpu(&agi->agi_length, len);
+	ASSERT(id->agno == mp->m_sb.sb_agcount - 1 ||
+	       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)
+		return error;
+
+	agf = XFS_BUF_TO_AGF(bp);
+	be32_add_cpu(&agf->agf_length, len);
+	ASSERT(agf->agf_length == agi->agi_length);
+	xfs_alloc_log_agf(tp, bp, XFS_AGF_LENGTH);
+
+	/*
+	 * Free the new space.
+	 *
+	 * XFS_RMAP_OWN_NULL is used here to tell the rmap btree that
+	 * this doesn't actually exist in the rmap btree.
+	 */
+	xfs_rmap_ag_owner(&oinfo, XFS_RMAP_OWN_NULL);
+	error = xfs_rmap_free(tp, bp, id->agno,
+				be32_to_cpu(agf->agf_length) - len,
+				len, &oinfo);
+	if (error)
+		return error;
+
+	return  xfs_free_extent(tp, XFS_AGB_TO_FSB(mp, id->agno,
+					be32_to_cpu(agf->agf_length) - len),
+				len, &oinfo, XFS_AG_RESV_NONE);
+}
diff --git a/fs/xfs/libxfs/xfs_ag.h b/fs/xfs/libxfs/xfs_ag.h
index 69f2fd4be0ea..412702e23f61 100644
--- a/fs/xfs/libxfs/xfs_ag.h
+++ b/fs/xfs/libxfs/xfs_ag.h
@@ -7,6 +7,9 @@
 #ifndef __LIBXFS_AG_H
 #define __LIBXFS_AG_H 1
 
+struct xfs_mount;
+struct xfs_trans;
+
 struct aghdr_init_data {
 	/* per ag data */
 	xfs_agblock_t		agno;		/* ag to init */
@@ -20,6 +23,8 @@ struct aghdr_init_data {
 	xfs_btnum_t		type;		/* type of btree root block */
 };
 
-int xfs_ag_init_headers( struct xfs_mount *mp, struct aghdr_init_data *id);
+int xfs_ag_init_headers(struct xfs_mount *mp, struct aghdr_init_data *id);
+int xfs_ag_extend_space(struct xfs_mount *mp, struct xfs_trans *tp,
+			struct aghdr_init_data *id, xfs_extlen_t len);
 
 #endif /* __LIBXFS_AG_H */
diff --git a/fs/xfs/xfs_fsops.c b/fs/xfs/xfs_fsops.c
index c5087b79bcea..bc7ef18da243 100644
--- a/fs/xfs/xfs_fsops.c
+++ b/fs/xfs/xfs_fsops.c
@@ -27,16 +27,12 @@
 #include "xfs_trans.h"
 #include "xfs_error.h"
 #include "xfs_btree.h"
-#include "xfs_alloc_btree.h"
 #include "xfs_alloc.h"
-#include "xfs_rmap_btree.h"
-#include "xfs_ialloc.h"
 #include "xfs_fsops.h"
 #include "xfs_trans_space.h"
 #include "xfs_rtalloc.h"
 #include "xfs_trace.h"
 #include "xfs_log.h"
-#include "xfs_rmap.h"
 #include "xfs_ag.h"
 #include "xfs_ag_resv.h"
 
@@ -48,8 +44,6 @@ xfs_growfs_data_private(
 	xfs_mount_t		*mp,		/* mount point for filesystem */
 	xfs_growfs_data_t	*in)		/* growfs data input struct */
 {
-	xfs_agf_t		*agf;
-	xfs_agi_t		*agi;
 	xfs_buf_t		*bp;
 	int			error;
 	xfs_agnumber_t		nagcount;
@@ -132,57 +126,9 @@ xfs_growfs_data_private(
 
 	xfs_trans_agblocks_delta(tp, id.nfree);
 
-	/*
-	 * There are new blocks in the old last a.g.
-	 */
+	/* If there are new blocks in the old last AG, extend it. */
 	if (new) {
-		struct xfs_owner_info	oinfo;
-
-		/*
-		 * Change the agi length.
-		 */
-		error = xfs_ialloc_read_agi(mp, tp, id.agno, &bp);
-		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 out_trans_cancel;
-
-		ASSERT(bp);
-		agf = XFS_BUF_TO_AGF(bp);
-		be32_add_cpu(&agf->agf_length, new);
-		ASSERT(be32_to_cpu(agf->agf_length) ==
-		       be32_to_cpu(agi->agi_length));
-
-		xfs_alloc_log_agf(tp, bp, XFS_AGF_LENGTH);
-
-		/*
-		 * Free the new space.
-		 *
-		 * XFS_RMAP_OWN_NULL is used here to tell the rmap btree that
-		 * this doesn't actually exist in the rmap btree.
-		 */
-		xfs_rmap_ag_owner(&oinfo, XFS_RMAP_OWN_NULL);
-		error = xfs_rmap_free(tp, bp, id.agno,
-				be32_to_cpu(agf->agf_length) - new,
-				new, &oinfo);
-		if (error)
-			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);
+		error = xfs_ag_extend_space(mp, tp, &id, new);
 		if (error)
 			goto out_trans_cancel;
 	}
-- 
2.17.0


^ permalink raw reply related	[flat|nested] 17+ messages in thread

* Re: [PATCH 04/10] xfs: factor ag btree root block initialisation
  2018-05-14  4:18 ` [PATCH 04/10] xfs: factor ag btree root block initialisation Dave Chinner
@ 2018-05-14 19:25   ` Darrick J. Wong
  0 siblings, 0 replies; 17+ messages in thread
From: Darrick J. Wong @ 2018-05-14 19:25 UTC (permalink / raw)
  To: Dave Chinner; +Cc: linux-xfs

On Mon, May 14, 2018 at 02:18:58PM +1000, Dave Chinner wrote:
> From: Dave Chinner <dchinner@redhat.com>
> 
> Cookie cutter code, easily factored.
> 
> Signed-Off-By: Dave Chinner <dchinner@redhat.com>

Looks ok,
Reviewed-by: Darrick J. Wong <darrick.wong@oracle.com>

--D

> ---
>  fs/xfs/xfs_fsops.c | 481 ++++++++++++++++++++++++---------------------
>  1 file changed, 259 insertions(+), 222 deletions(-)
> 
> diff --git a/fs/xfs/xfs_fsops.c b/fs/xfs/xfs_fsops.c
> index 4b560caaf397..ec3c51ee8134 100644
> --- a/fs/xfs/xfs_fsops.c
> +++ b/fs/xfs/xfs_fsops.c
> @@ -71,46 +71,140 @@ xfs_growfs_get_hdr_buf(
>  	return bp;
>  }
>  
> +struct aghdr_init_data {
> +	/* per ag data */
> +	xfs_agnumber_t		agno;		/* ag to init */
> +	xfs_extlen_t		agsize;		/* new AG size */
> +	struct list_head	buffer_list;	/* buffer writeback list */
> +	xfs_rfsblock_t		nfree;		/* cumulative new free space */
> +
> +	/* per header data */
> +	xfs_daddr_t		daddr;		/* header location */
> +	size_t			numblks;	/* size of header */
> +	xfs_btnum_t		type;		/* type of btree root block */
> +};
> +
>  /*
> - * Write new AG headers to disk. Non-transactional, but written
> - * synchronously so they are completed prior to the growfs transaction
> - * being logged.
> + * Generic btree root block init function
>   */
> -static int
> -xfs_grow_ag_headers(
> +static void
> +xfs_btroot_init(
>  	struct xfs_mount	*mp,
> -	xfs_agnumber_t		agno,
> -	xfs_extlen_t		agsize,
> -	xfs_rfsblock_t		*nfree,
> -	struct list_head	*buffer_list)
> +	struct xfs_buf		*bp,
> +	struct aghdr_init_data	*id)
>  {
> -	struct xfs_agf		*agf;
> -	struct xfs_agi		*agi;
> -	struct xfs_agfl		*agfl;
> -	__be32			*agfl_bno;
> -	xfs_alloc_rec_t		*arec;
> -	struct xfs_buf		*bp;
> -	int			bucket;
> -	xfs_extlen_t		tmpsize;
> -	int			error = 0;
> +	xfs_btree_init_block(mp, bp, id->type, 0, 0, id->agno, 0);
> +}
> +
> +/*
> + * Alloc btree root block init functions
> + */
> +static void
> +xfs_bnoroot_init(
> +	struct xfs_mount	*mp,
> +	struct xfs_buf		*bp,
> +	struct aghdr_init_data	*id)
> +{
> +	struct xfs_alloc_rec	*arec;
> +
> +	xfs_btree_init_block(mp, bp, XFS_BTNUM_BNO, 0, 1, id->agno, 0);
> +	arec = XFS_ALLOC_REC_ADDR(mp, XFS_BUF_TO_BLOCK(bp), 1);
> +	arec->ar_startblock = cpu_to_be32(mp->m_ag_prealloc_blocks);
> +	arec->ar_blockcount = cpu_to_be32(id->agsize -
> +					  be32_to_cpu(arec->ar_startblock));
> +}
> +
> +static void
> +xfs_cntroot_init(
> +	struct xfs_mount	*mp,
> +	struct xfs_buf		*bp,
> +	struct aghdr_init_data	*id)
> +{
> +	struct xfs_alloc_rec	*arec;
> +
> +	xfs_btree_init_block(mp, bp, XFS_BTNUM_CNT, 0, 1, id->agno, 0);
> +	arec = XFS_ALLOC_REC_ADDR(mp, XFS_BUF_TO_BLOCK(bp), 1);
> +	arec->ar_startblock = cpu_to_be32(mp->m_ag_prealloc_blocks);
> +	arec->ar_blockcount = cpu_to_be32(id->agsize -
> +					  be32_to_cpu(arec->ar_startblock));
> +}
> +
> +/*
> + * Reverse map root block init
> + */
> +static void
> +xfs_rmaproot_init(
> +	struct xfs_mount	*mp,
> +	struct xfs_buf		*bp,
> +	struct aghdr_init_data	*id)
> +{
> +	struct xfs_btree_block	*block = XFS_BUF_TO_BLOCK(bp);
> +	struct xfs_rmap_rec	*rrec;
> +
> +	xfs_btree_init_block(mp, bp, XFS_BTNUM_RMAP, 0, 4, id->agno, 0);
>  
>  	/*
> -	 * AG freespace header block
> +	 * mark the AG header regions as static metadata The BNO
> +	 * btree block is the first block after the headers, so
> +	 * it's location defines the size of region the static
> +	 * metadata consumes.
> +	 *
> +	 * Note: unlike mkfs, we never have to account for log
> +	 * space when growing the data regions
>  	 */
> -	bp = xfs_growfs_get_hdr_buf(mp,
> -			XFS_AG_DADDR(mp, agno, XFS_AGF_DADDR(mp)),
> -			XFS_FSS_TO_BB(mp, 1), 0,
> -			&xfs_agf_buf_ops);
> -	if (!bp) {
> -		error = -ENOMEM;
> -		goto out_error;
> +	rrec = XFS_RMAP_REC_ADDR(block, 1);
> +	rrec->rm_startblock = 0;
> +	rrec->rm_blockcount = cpu_to_be32(XFS_BNO_BLOCK(mp));
> +	rrec->rm_owner = cpu_to_be64(XFS_RMAP_OWN_FS);
> +	rrec->rm_offset = 0;
> +
> +	/* account freespace btree root blocks */
> +	rrec = XFS_RMAP_REC_ADDR(block, 2);
> +	rrec->rm_startblock = cpu_to_be32(XFS_BNO_BLOCK(mp));
> +	rrec->rm_blockcount = cpu_to_be32(2);
> +	rrec->rm_owner = cpu_to_be64(XFS_RMAP_OWN_AG);
> +	rrec->rm_offset = 0;
> +
> +	/* account inode btree root blocks */
> +	rrec = XFS_RMAP_REC_ADDR(block, 3);
> +	rrec->rm_startblock = cpu_to_be32(XFS_IBT_BLOCK(mp));
> +	rrec->rm_blockcount = cpu_to_be32(XFS_RMAP_BLOCK(mp) -
> +					  XFS_IBT_BLOCK(mp));
> +	rrec->rm_owner = cpu_to_be64(XFS_RMAP_OWN_INOBT);
> +	rrec->rm_offset = 0;
> +
> +	/* account for rmap btree root */
> +	rrec = XFS_RMAP_REC_ADDR(block, 4);
> +	rrec->rm_startblock = cpu_to_be32(XFS_RMAP_BLOCK(mp));
> +	rrec->rm_blockcount = cpu_to_be32(1);
> +	rrec->rm_owner = cpu_to_be64(XFS_RMAP_OWN_AG);
> +	rrec->rm_offset = 0;
> +
> +	/* account for refc btree root */
> +	if (xfs_sb_version_hasreflink(&mp->m_sb)) {
> +		rrec = XFS_RMAP_REC_ADDR(block, 5);
> +		rrec->rm_startblock = cpu_to_be32(xfs_refc_block(mp));
> +		rrec->rm_blockcount = cpu_to_be32(1);
> +		rrec->rm_owner = cpu_to_be64(XFS_RMAP_OWN_REFC);
> +		rrec->rm_offset = 0;
> +		be16_add_cpu(&block->bb_numrecs, 1);
>  	}
> +}
> +
> +
> +static void
> +xfs_agfblock_init(
> +	struct xfs_mount	*mp,
> +	struct xfs_buf		*bp,
> +	struct aghdr_init_data	*id)
> +{
> +	struct xfs_agf		*agf = XFS_BUF_TO_AGF(bp);
> +	xfs_extlen_t		tmpsize;
>  
> -	agf = XFS_BUF_TO_AGF(bp);
>  	agf->agf_magicnum = cpu_to_be32(XFS_AGF_MAGIC);
>  	agf->agf_versionnum = cpu_to_be32(XFS_AGF_VERSION);
> -	agf->agf_seqno = cpu_to_be32(agno);
> -	agf->agf_length = cpu_to_be32(agsize);
> +	agf->agf_seqno = cpu_to_be32(id->agno);
> +	agf->agf_length = cpu_to_be32(id->agsize);
>  	agf->agf_roots[XFS_BTNUM_BNOi] = cpu_to_be32(XFS_BNO_BLOCK(mp));
>  	agf->agf_roots[XFS_BTNUM_CNTi] = cpu_to_be32(XFS_CNT_BLOCK(mp));
>  	agf->agf_levels[XFS_BTNUM_BNOi] = cpu_to_be32(1);
> @@ -125,7 +219,7 @@ xfs_grow_ag_headers(
>  	agf->agf_flfirst = cpu_to_be32(1);
>  	agf->agf_fllast = 0;
>  	agf->agf_flcount = 0;
> -	tmpsize = agsize - mp->m_ag_prealloc_blocks;
> +	tmpsize = id->agsize - mp->m_ag_prealloc_blocks;
>  	agf->agf_freeblks = cpu_to_be32(tmpsize);
>  	agf->agf_longest = cpu_to_be32(tmpsize);
>  	if (xfs_sb_version_hascrc(&mp->m_sb))
> @@ -136,52 +230,42 @@ xfs_grow_ag_headers(
>  		agf->agf_refcount_level = cpu_to_be32(1);
>  		agf->agf_refcount_blocks = cpu_to_be32(1);
>  	}
> -	xfs_buf_delwri_queue(bp, buffer_list);
> -	xfs_buf_relse(bp);
> +}
>  
> -	/*
> -	 * AG freelist header block
> -	 */
> -	bp = xfs_growfs_get_hdr_buf(mp,
> -			XFS_AG_DADDR(mp, agno, XFS_AGFL_DADDR(mp)),
> -			XFS_FSS_TO_BB(mp, 1), 0,
> -			&xfs_agfl_buf_ops);
> -	if (!bp) {
> -		error = -ENOMEM;
> -		goto out_error;
> -	}
> +static void
> +xfs_agflblock_init(
> +	struct xfs_mount	*mp,
> +	struct xfs_buf		*bp,
> +	struct aghdr_init_data	*id)
> +{
> +	struct xfs_agfl		*agfl = XFS_BUF_TO_AGFL(bp);
> +	__be32			*agfl_bno;
> +	int			bucket;
>  
> -	agfl = XFS_BUF_TO_AGFL(bp);
>  	if (xfs_sb_version_hascrc(&mp->m_sb)) {
>  		agfl->agfl_magicnum = cpu_to_be32(XFS_AGFL_MAGIC);
> -		agfl->agfl_seqno = cpu_to_be32(agno);
> +		agfl->agfl_seqno = cpu_to_be32(id->agno);
>  		uuid_copy(&agfl->agfl_uuid, &mp->m_sb.sb_meta_uuid);
>  	}
>  
>  	agfl_bno = XFS_BUF_TO_AGFL_BNO(mp, bp);
>  	for (bucket = 0; bucket < xfs_agfl_size(mp); bucket++)
>  		agfl_bno[bucket] = cpu_to_be32(NULLAGBLOCK);
> +}
>  
> -	xfs_buf_delwri_queue(bp, buffer_list);
> -	xfs_buf_relse(bp);
> -
> -	/*
> -	 * AG inode header block
> -	 */
> -	bp = xfs_growfs_get_hdr_buf(mp,
> -			XFS_AG_DADDR(mp, agno, XFS_AGI_DADDR(mp)),
> -			XFS_FSS_TO_BB(mp, 1), 0,
> -			&xfs_agi_buf_ops);
> -	if (!bp) {
> -		error = -ENOMEM;
> -		goto out_error;
> -	}
> +static void
> +xfs_agiblock_init(
> +	struct xfs_mount	*mp,
> +	struct xfs_buf		*bp,
> +	struct aghdr_init_data	*id)
> +{
> +	struct xfs_agi		*agi = XFS_BUF_TO_AGI(bp);
> +	int			bucket;
>  
> -	agi = XFS_BUF_TO_AGI(bp);
>  	agi->agi_magicnum = cpu_to_be32(XFS_AGI_MAGIC);
>  	agi->agi_versionnum = cpu_to_be32(XFS_AGI_VERSION);
> -	agi->agi_seqno = cpu_to_be32(agno);
> -	agi->agi_length = cpu_to_be32(agsize);
> +	agi->agi_seqno = cpu_to_be32(id->agno);
> +	agi->agi_length = cpu_to_be32(id->agsize);
>  	agi->agi_count = 0;
>  	agi->agi_root = cpu_to_be32(XFS_IBT_BLOCK(mp));
>  	agi->agi_level = cpu_to_be32(1);
> @@ -196,180 +280,133 @@ xfs_grow_ag_headers(
>  	}
>  	for (bucket = 0; bucket < XFS_AGI_UNLINKED_BUCKETS; bucket++)
>  		agi->agi_unlinked[bucket] = cpu_to_be32(NULLAGINO);
> +}
>  
> -	xfs_buf_delwri_queue(bp, buffer_list);
> -	xfs_buf_relse(bp);
> -
> -	/*
> -	 * BNO btree root block
> -	 */
> -	bp = xfs_growfs_get_hdr_buf(mp,
> -			XFS_AGB_TO_DADDR(mp, agno, XFS_BNO_BLOCK(mp)),
> -			BTOBB(mp->m_sb.sb_blocksize), 0,
> -			&xfs_allocbt_buf_ops);
> +static int
> +xfs_growfs_init_aghdr(
> +	struct xfs_mount	*mp,
> +	struct aghdr_init_data	*id,
> +	void			(*work)(struct xfs_mount *, struct xfs_buf *,
> +					struct aghdr_init_data *),
> +	const struct xfs_buf_ops *ops)
>  
> -	if (!bp) {
> -		error = -ENOMEM;
> -		goto out_error;
> -	}
> +{
> +	struct xfs_buf		*bp;
>  
> -	xfs_btree_init_block(mp, bp, XFS_BTNUM_BNO, 0, 1, agno, 0);
> +	bp = xfs_growfs_get_hdr_buf(mp, id->daddr, id->numblks, 0, ops);
> +	if (!bp)
> +		return -ENOMEM;
>  
> -	arec = XFS_ALLOC_REC_ADDR(mp, XFS_BUF_TO_BLOCK(bp), 1);
> -	arec->ar_startblock = cpu_to_be32(mp->m_ag_prealloc_blocks);
> -	arec->ar_blockcount = cpu_to_be32(
> -		agsize - be32_to_cpu(arec->ar_startblock));
> +	(*work)(mp, bp, id);
>  
> -	xfs_buf_delwri_queue(bp, buffer_list);
> +	xfs_buf_delwri_queue(bp, &id->buffer_list);
>  	xfs_buf_relse(bp);
> +	return 0;
> +}
>  
> -	/*
> -	 * CNT btree root block
> -	 */
> -	bp = xfs_growfs_get_hdr_buf(mp,
> -			XFS_AGB_TO_DADDR(mp, agno, XFS_CNT_BLOCK(mp)),
> -			BTOBB(mp->m_sb.sb_blocksize), 0,
> -			&xfs_allocbt_buf_ops);
> -	if (!bp) {
> -		error = -ENOMEM;
> -		goto out_error;
> -	}
> -
> -	xfs_btree_init_block(mp, bp, XFS_BTNUM_CNT, 0, 1, agno, 0);
> +/*
> + * Write new AG headers to disk. Non-transactional, but written
> + * synchronously so they are completed prior to the growfs transaction
> + * being logged.
> + */
> +static int
> +xfs_grow_ag_headers(
> +	struct xfs_mount	*mp,
> +	struct aghdr_init_data	*id)
>  
> -	arec = XFS_ALLOC_REC_ADDR(mp, XFS_BUF_TO_BLOCK(bp), 1);
> -	arec->ar_startblock = cpu_to_be32(mp->m_ag_prealloc_blocks);
> -	arec->ar_blockcount = cpu_to_be32(
> -		agsize - be32_to_cpu(arec->ar_startblock));
> -	*nfree += be32_to_cpu(arec->ar_blockcount);
> +{
> +	int			error = 0;
>  
> -	xfs_buf_delwri_queue(bp, buffer_list);
> -	xfs_buf_relse(bp);
> +	/* Account for AG free space in new AG */
> +	id->nfree += id->agsize - mp->m_ag_prealloc_blocks;
>  
> -	/* RMAP btree root block */
> -	if (xfs_sb_version_hasrmapbt(&mp->m_sb)) {
> -		struct xfs_rmap_rec	*rrec;
> -		struct xfs_btree_block	*block;
> -
> -		bp = xfs_growfs_get_hdr_buf(mp,
> -			XFS_AGB_TO_DADDR(mp, agno, XFS_RMAP_BLOCK(mp)),
> -			BTOBB(mp->m_sb.sb_blocksize), 0,
> -			&xfs_rmapbt_buf_ops);
> -		if (!bp) {
> -			error = -ENOMEM;
> -			goto out_error;
> -		}
> +	/* AG freespace header block */
> +	id->daddr = XFS_AG_DADDR(mp, id->agno, XFS_AGF_DADDR(mp));
> +	id->numblks = XFS_FSS_TO_BB(mp, 1);
> +	error = xfs_growfs_init_aghdr(mp, id, xfs_agfblock_init,
> +					&xfs_agf_buf_ops);
> +	if (error)
> +		goto out_error;
>  
> -		xfs_btree_init_block(mp, bp, XFS_BTNUM_RMAP, 0, 0,
> -					agno, 0);
> -		block = XFS_BUF_TO_BLOCK(bp);
> +	/* AG freelist header block */
> +	id->daddr = XFS_AG_DADDR(mp, id->agno, XFS_AGFL_DADDR(mp));
> +	id->numblks = XFS_FSS_TO_BB(mp, 1);
> +	error = xfs_growfs_init_aghdr(mp, id, xfs_agflblock_init,
> +					&xfs_agfl_buf_ops);
> +	if (error)
> +		goto out_error;
>  
> +	/* AG inode header block */
> +	id->daddr = XFS_AG_DADDR(mp, id->agno, XFS_AGI_DADDR(mp));
> +	id->numblks = XFS_FSS_TO_BB(mp, 1);
> +	error = xfs_growfs_init_aghdr(mp, id, xfs_agiblock_init,
> +					&xfs_agi_buf_ops);
> +	if (error)
> +		goto out_error;
>  
> -		/*
> -		 * mark the AG header regions as static metadata The BNO
> -		 * btree block is the first block after the headers, so
> -		 * it's location defines the size of region the static
> -		 * metadata consumes.
> -		 *
> -		 * Note: unlike mkfs, we never have to account for log
> -		 * space when growing the data regions
> -		 */
> -		rrec = XFS_RMAP_REC_ADDR(block, 1);
> -		rrec->rm_startblock = 0;
> -		rrec->rm_blockcount = cpu_to_be32(XFS_BNO_BLOCK(mp));
> -		rrec->rm_owner = cpu_to_be64(XFS_RMAP_OWN_FS);
> -		rrec->rm_offset = 0;
> -		be16_add_cpu(&block->bb_numrecs, 1);
>  
> -		/* account freespace btree root blocks */
> -		rrec = XFS_RMAP_REC_ADDR(block, 2);
> -		rrec->rm_startblock = cpu_to_be32(XFS_BNO_BLOCK(mp));
> -		rrec->rm_blockcount = cpu_to_be32(2);
> -		rrec->rm_owner = cpu_to_be64(XFS_RMAP_OWN_AG);
> -		rrec->rm_offset = 0;
> -		be16_add_cpu(&block->bb_numrecs, 1);
> +	/* BNO btree root block */
> +	id->daddr = XFS_AGB_TO_DADDR(mp, id->agno, XFS_BNO_BLOCK(mp));
> +	id->numblks = BTOBB(mp->m_sb.sb_blocksize);
> +	error = xfs_growfs_init_aghdr(mp, id, xfs_bnoroot_init,
> +				   &xfs_allocbt_buf_ops);
> +	if (error)
> +		goto out_error;
>  
> -		/* account inode btree root blocks */
> -		rrec = XFS_RMAP_REC_ADDR(block, 3);
> -		rrec->rm_startblock = cpu_to_be32(XFS_IBT_BLOCK(mp));
> -		rrec->rm_blockcount = cpu_to_be32(XFS_RMAP_BLOCK(mp) -
> -						XFS_IBT_BLOCK(mp));
> -		rrec->rm_owner = cpu_to_be64(XFS_RMAP_OWN_INOBT);
> -		rrec->rm_offset = 0;
> -		be16_add_cpu(&block->bb_numrecs, 1);
>  
> -		/* account for rmap btree root */
> -		rrec = XFS_RMAP_REC_ADDR(block, 4);
> -		rrec->rm_startblock = cpu_to_be32(XFS_RMAP_BLOCK(mp));
> -		rrec->rm_blockcount = cpu_to_be32(1);
> -		rrec->rm_owner = cpu_to_be64(XFS_RMAP_OWN_AG);
> -		rrec->rm_offset = 0;
> -		be16_add_cpu(&block->bb_numrecs, 1);
> +	/* CNT btree root block */
> +	id->daddr = XFS_AGB_TO_DADDR(mp, id->agno, XFS_CNT_BLOCK(mp));
> +	id->numblks = BTOBB(mp->m_sb.sb_blocksize);
> +	error = xfs_growfs_init_aghdr(mp, id, xfs_cntroot_init,
> +				   &xfs_allocbt_buf_ops);
> +	if (error)
> +		goto out_error;
>  
> -		/* account for refc btree root */
> -		if (xfs_sb_version_hasreflink(&mp->m_sb)) {
> -			rrec = XFS_RMAP_REC_ADDR(block, 5);
> -			rrec->rm_startblock = cpu_to_be32(xfs_refc_block(mp));
> -			rrec->rm_blockcount = cpu_to_be32(1);
> -			rrec->rm_owner = cpu_to_be64(XFS_RMAP_OWN_REFC);
> -			rrec->rm_offset = 0;
> -			be16_add_cpu(&block->bb_numrecs, 1);
> -		}
> +	/* RMAP btree root block */
> +	if (xfs_sb_version_hasrmapbt(&mp->m_sb)) {
> +		id->daddr = XFS_AGB_TO_DADDR(mp, id->agno, XFS_RMAP_BLOCK(mp));
> +		id->numblks = BTOBB(mp->m_sb.sb_blocksize);
> +		error = xfs_growfs_init_aghdr(mp, id, xfs_rmaproot_init,
> +					   &xfs_rmapbt_buf_ops);
> +		if (error)
> +			goto out_error;
>  
> -		xfs_buf_delwri_queue(bp, buffer_list);
> -		xfs_buf_relse(bp);
>  	}
>  
> -	/*
> -	 * INO btree root block
> -	 */
> -	bp = xfs_growfs_get_hdr_buf(mp,
> -			XFS_AGB_TO_DADDR(mp, agno, XFS_IBT_BLOCK(mp)),
> -			BTOBB(mp->m_sb.sb_blocksize), 0,
> -			&xfs_inobt_buf_ops);
> -	if (!bp) {
> -		error = -ENOMEM;
> +	/* INO btree root block */
> +	id->daddr = XFS_AGB_TO_DADDR(mp, id->agno, XFS_IBT_BLOCK(mp));
> +	id->numblks = BTOBB(mp->m_sb.sb_blocksize);
> +	id->type = XFS_BTNUM_INO;
> +	error = xfs_growfs_init_aghdr(mp, id, xfs_btroot_init,
> +				   &xfs_inobt_buf_ops);
> +	if (error)
>  		goto out_error;
> -	}
>  
> -	xfs_btree_init_block(mp, bp, XFS_BTNUM_INO , 0, 0, agno, 0);
> -	xfs_buf_delwri_queue(bp, buffer_list);
> -	xfs_buf_relse(bp);
>  
>  	/*
>  	 * FINO btree root block
>  	 */
>  	if (xfs_sb_version_hasfinobt(&mp->m_sb)) {
> -		bp = xfs_growfs_get_hdr_buf(mp,
> -			XFS_AGB_TO_DADDR(mp, agno, XFS_FIBT_BLOCK(mp)),
> -			BTOBB(mp->m_sb.sb_blocksize), 0,
> -			&xfs_inobt_buf_ops);
> -		if (!bp) {
> -			error = -ENOMEM;
> +		id->daddr = XFS_AGB_TO_DADDR(mp, id->agno, XFS_FIBT_BLOCK(mp));
> +		id->numblks = BTOBB(mp->m_sb.sb_blocksize);
> +		id->type = XFS_BTNUM_FINO;
> +		error = xfs_growfs_init_aghdr(mp, id, xfs_btroot_init,
> +					   &xfs_inobt_buf_ops);
> +		if (error)
>  			goto out_error;
> -		}
> -
> -		xfs_btree_init_block(mp, bp, XFS_BTNUM_FINO, 0, 0, agno, 0);
> -		xfs_buf_delwri_queue(bp, buffer_list);
> -		xfs_buf_relse(bp);
>  	}
>  
>  	/*
>  	 * refcount btree root block
>  	 */
>  	if (xfs_sb_version_hasreflink(&mp->m_sb)) {
> -		bp = xfs_growfs_get_hdr_buf(mp,
> -			XFS_AGB_TO_DADDR(mp, agno, xfs_refc_block(mp)),
> -			BTOBB(mp->m_sb.sb_blocksize), 0,
> -			&xfs_refcountbt_buf_ops);
> -		if (!bp) {
> -			error = -ENOMEM;
> +		id->daddr = XFS_AGB_TO_DADDR(mp, id->agno, xfs_refc_block(mp));
> +		id->numblks = BTOBB(mp->m_sb.sb_blocksize);
> +		id->type = XFS_BTNUM_REFC;
> +		error = xfs_growfs_init_aghdr(mp, id, xfs_btroot_init,
> +					   &xfs_refcountbt_buf_ops);
> +		if (error)
>  			goto out_error;
> -		}
> -
> -		xfs_btree_init_block(mp, bp, XFS_BTNUM_REFC, 0, 0, agno, 0);
> -		xfs_buf_delwri_queue(bp, buffer_list);
> -		xfs_buf_relse(bp);
>  	}
>  
>  out_error:
> @@ -384,7 +421,6 @@ xfs_growfs_data_private(
>  	xfs_agf_t		*agf;
>  	xfs_agi_t		*agi;
>  	xfs_agnumber_t		agno;
> -	xfs_extlen_t		agsize;
>  	xfs_buf_t		*bp;
>  	int			dpct;
>  	int			error, saved_error = 0;
> @@ -392,11 +428,11 @@ xfs_growfs_data_private(
>  	xfs_agnumber_t		nagimax = 0;
>  	xfs_rfsblock_t		nb, nb_mod;
>  	xfs_rfsblock_t		new;
> -	xfs_rfsblock_t		nfree;
>  	xfs_agnumber_t		oagcount;
>  	int			pct;
>  	xfs_trans_t		*tp;
>  	LIST_HEAD		(buffer_list);
> +	struct aghdr_init_data	id = {};
>  
>  	nb = in->newblocks;
>  	pct = in->imaxpct;
> @@ -448,27 +484,28 @@ xfs_growfs_data_private(
>  	 * list to write, we can cancel the entire list without having written
>  	 * anything.
>  	 */
> -	nfree = 0;
> -	for (agno = nagcount - 1; agno >= oagcount; agno--, new -= agsize) {
> -
> -		if (agno == nagcount - 1)
> -			agsize = nb -
> -				(agno * (xfs_rfsblock_t)mp->m_sb.sb_agblocks);
> +	INIT_LIST_HEAD(&id.buffer_list);
> +	for (id.agno = nagcount - 1;
> +	     id.agno >= oagcount;
> +	     id.agno--, new -= id.agsize) {
> +
> +		if (id.agno == nagcount - 1)
> +			id.agsize = nb -
> +				(id.agno * (xfs_rfsblock_t)mp->m_sb.sb_agblocks);
>  		else
> -			agsize = mp->m_sb.sb_agblocks;
> +			id.agsize = mp->m_sb.sb_agblocks;
>  
> -		error = xfs_grow_ag_headers(mp, agno, agsize, &nfree,
> -					    &buffer_list);
> +		error = xfs_grow_ag_headers(mp, &id);
>  		if (error) {
> -			xfs_buf_delwri_cancel(&buffer_list);
> +			xfs_buf_delwri_cancel(&id.buffer_list);
>  			goto error0;
>  		}
>  	}
> -	error = xfs_buf_delwri_submit(&buffer_list);
> +	error = xfs_buf_delwri_submit(&id.buffer_list);
>  	if (error)
>  		goto error0;
>  
> -	xfs_trans_agblocks_delta(tp, nfree);
> +	xfs_trans_agblocks_delta(tp, id.nfree);
>  
>  	/*
>  	 * There are new blocks in the old last a.g.
> @@ -479,7 +516,7 @@ xfs_growfs_data_private(
>  		/*
>  		 * Change the agi length.
>  		 */
> -		error = xfs_ialloc_read_agi(mp, tp, agno, &bp);
> +		error = xfs_ialloc_read_agi(mp, tp, id.agno, &bp);
>  		if (error) {
>  			goto error0;
>  		}
> @@ -492,7 +529,7 @@ xfs_growfs_data_private(
>  		/*
>  		 * Change agf length.
>  		 */
> -		error = xfs_alloc_read_agf(mp, tp, agno, 0, &bp);
> +		error = xfs_alloc_read_agf(mp, tp, id.agno, 0, &bp);
>  		if (error) {
>  			goto error0;
>  		}
> @@ -511,13 +548,13 @@ xfs_growfs_data_private(
>  		 * this doesn't actually exist in the rmap btree.
>  		 */
>  		xfs_rmap_ag_owner(&oinfo, XFS_RMAP_OWN_NULL);
> -		error = xfs_rmap_free(tp, bp, agno,
> +		error = xfs_rmap_free(tp, bp, id.agno,
>  				be32_to_cpu(agf->agf_length) - new,
>  				new, &oinfo);
>  		if (error)
>  			goto error0;
>  		error = xfs_free_extent(tp,
> -				XFS_AGB_TO_FSB(mp, agno,
> +				XFS_AGB_TO_FSB(mp, id.agno,
>  					be32_to_cpu(agf->agf_length) - new),
>  				new, &oinfo, XFS_AG_RESV_NONE);
>  		if (error)
> @@ -534,8 +571,8 @@ xfs_growfs_data_private(
>  	if (nb > mp->m_sb.sb_dblocks)
>  		xfs_trans_mod_sb(tp, XFS_TRANS_SB_DBLOCKS,
>  				 nb - mp->m_sb.sb_dblocks);
> -	if (nfree)
> -		xfs_trans_mod_sb(tp, XFS_TRANS_SB_FDBLOCKS, nfree);
> +	if (id.nfree)
> +		xfs_trans_mod_sb(tp, XFS_TRANS_SB_FDBLOCKS, id.nfree);
>  	if (dpct)
>  		xfs_trans_mod_sb(tp, XFS_TRANS_SB_IMAXPCT, dpct);
>  	xfs_trans_set_sync(tp);
> @@ -562,7 +599,7 @@ xfs_growfs_data_private(
>  	if (new) {
>  		struct xfs_perag	*pag;
>  
> -		pag = xfs_perag_get(mp, agno);
> +		pag = xfs_perag_get(mp, id.agno);
>  		error = xfs_ag_resv_free(pag);
>  		xfs_perag_put(pag);
>  		if (error)
> -- 
> 2.17.0
> 
> --
> To unsubscribe from this list: send the line "unsubscribe linux-xfs" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [PATCH 05/10] xfs: turn ag header initialisation into a table driven operation
  2018-05-14  4:18 ` [PATCH 05/10] xfs: turn ag header initialisation into a table driven operation Dave Chinner
@ 2018-05-14 19:27   ` Darrick J. Wong
  0 siblings, 0 replies; 17+ messages in thread
From: Darrick J. Wong @ 2018-05-14 19:27 UTC (permalink / raw)
  To: Dave Chinner; +Cc: linux-xfs

On Mon, May 14, 2018 at 02:18:59PM +1000, Dave Chinner wrote:
> From: Dave Chinner <dchinner@redhat.com>
> 
> There's still more cookie cutter code in setting up each AG header.
> Separate all the variables into a simple structure and iterate a
> table of header definitions to initialise everything.
> 
> Signed-Off-By: Dave Chinner <dchinner@redhat.com>
> Reviewed-by: Brian Foster <bfoster@redhat.com>
> ---
>  fs/xfs/xfs_fsops.c | 182 +++++++++++++++++++++++----------------------
>  1 file changed, 92 insertions(+), 90 deletions(-)
> 
> diff --git a/fs/xfs/xfs_fsops.c b/fs/xfs/xfs_fsops.c
> index ec3c51ee8134..fb283d2cf42b 100644
> --- a/fs/xfs/xfs_fsops.c
> +++ b/fs/xfs/xfs_fsops.c
> @@ -282,12 +282,13 @@ xfs_agiblock_init(
>  		agi->agi_unlinked[bucket] = cpu_to_be32(NULLAGINO);
>  }
>  
> +typedef void (*aghdr_init_work_f)(struct xfs_mount *mp, struct xfs_buf *bp,
> +				  struct aghdr_init_data *id);
>  static int
>  xfs_growfs_init_aghdr(
>  	struct xfs_mount	*mp,
>  	struct aghdr_init_data	*id,
> -	void			(*work)(struct xfs_mount *, struct xfs_buf *,
> -					struct aghdr_init_data *),
> +	aghdr_init_work_f	work,
>  	const struct xfs_buf_ops *ops)
>  
>  {
> @@ -304,6 +305,15 @@ xfs_growfs_init_aghdr(
>  	return 0;
>  }
>  
> +struct xfs_aghdr_grow_data {
> +	xfs_daddr_t		daddr;
> +	size_t			numblks;
> +	const struct xfs_buf_ops *ops;
> +	aghdr_init_work_f	work;
> +	xfs_btnum_t		type;
> +	bool			need_init;
> +};
> +
>  /*
>   * Write new AG headers to disk. Non-transactional, but written
>   * synchronously so they are completed prior to the growfs transaction
> @@ -315,101 +325,93 @@ xfs_grow_ag_headers(
>  	struct aghdr_init_data	*id)
>  
>  {
> +	struct xfs_aghdr_grow_data aghdr_data[] = {
> +	{ /* AGF */
> +		.daddr = XFS_AG_DADDR(mp, id->agno, XFS_AGF_DADDR(mp)),
> +		.numblks = XFS_FSS_TO_BB(mp, 1),
> +		.ops = &xfs_agf_buf_ops,
> +		.work = &xfs_agfblock_init,
> +		.need_init = true
> +	},
> +	{ /* AGFL */
> +		.daddr = XFS_AG_DADDR(mp, id->agno, XFS_AGFL_DADDR(mp)),
> +		.numblks = XFS_FSS_TO_BB(mp, 1),
> +		.ops = &xfs_agfl_buf_ops,
> +		.work = &xfs_agflblock_init,
> +		.need_init = true
> +	},
> +	{ /* AGI */
> +		.daddr = XFS_AG_DADDR(mp, id->agno, XFS_AGI_DADDR(mp)),
> +		.numblks = XFS_FSS_TO_BB(mp, 1),
> +		.ops = &xfs_agi_buf_ops,
> +		.work = &xfs_agiblock_init,
> +		.need_init = true
> +	},
> +	{ /* BNO root block */
> +		.daddr = XFS_AGB_TO_DADDR(mp, id->agno, XFS_BNO_BLOCK(mp)),
> +		.numblks = BTOBB(mp->m_sb.sb_blocksize),
> +		.ops = &xfs_allocbt_buf_ops,
> +		.work = &xfs_bnoroot_init,
> +		.need_init = true
> +	},
> +	{ /* CNT root block */
> +		.daddr = XFS_AGB_TO_DADDR(mp, id->agno, XFS_CNT_BLOCK(mp)),
> +		.numblks = BTOBB(mp->m_sb.sb_blocksize),
> +		.ops = &xfs_allocbt_buf_ops,
> +		.work = &xfs_cntroot_init,
> +		.need_init = true
> +	},
> +	{ /* INO root block */
> +		.daddr = XFS_AGB_TO_DADDR(mp, id->agno, XFS_IBT_BLOCK(mp)),
> +		.numblks = BTOBB(mp->m_sb.sb_blocksize),
> +		.ops = &xfs_inobt_buf_ops,
> +		.work = &xfs_btroot_init,
> +		.type = XFS_BTNUM_INO,
> +		.need_init = true
> +	},
> +	{ /* FINO root block */
> +		.daddr = XFS_AGB_TO_DADDR(mp, id->agno, XFS_FIBT_BLOCK(mp)),
> +		.numblks = BTOBB(mp->m_sb.sb_blocksize),
> +		.ops = &xfs_inobt_buf_ops,
> +		.work = &xfs_btroot_init,
> +		.type = XFS_BTNUM_FINO,
> +		.need_init =  xfs_sb_version_hasfinobt(&mp->m_sb)
> +	},
> +	{ /* RMAP root block */
> +		.daddr = XFS_AGB_TO_DADDR(mp, id->agno, XFS_RMAP_BLOCK(mp)),
> +		.numblks = BTOBB(mp->m_sb.sb_blocksize),
> +		.ops = &xfs_rmapbt_buf_ops,
> +		.work = &xfs_rmaproot_init,
> +		.need_init = xfs_sb_version_hasrmapbt(&mp->m_sb)
> +	},
> +	{ /* REFC root block */
> +		.daddr = XFS_AGB_TO_DADDR(mp, id->agno, xfs_refc_block(mp)),
> +		.numblks = BTOBB(mp->m_sb.sb_blocksize),
> +		.ops = &xfs_refcountbt_buf_ops,
> +		.work = &xfs_btroot_init,
> +		.type = XFS_BTNUM_REFC,
> +		.need_init = xfs_sb_version_hasreflink(&mp->m_sb)

Muuuuch easier to read,
Reviewed-by: Darrick J. Wong <darrick.wong@oracle.com>

--D


> +	},
> +	{ /* NULL terminating block */
> +		.daddr = XFS_BUF_DADDR_NULL,
> +	}
> +	};
> +	struct  xfs_aghdr_grow_data *dp;
>  	int			error = 0;
>  
>  	/* Account for AG free space in new AG */
>  	id->nfree += id->agsize - mp->m_ag_prealloc_blocks;
> +	for (dp = &aghdr_data[0]; dp->daddr != XFS_BUF_DADDR_NULL; dp++) {
> +		if (!dp->need_init)
> +			continue;
>  
> -	/* AG freespace header block */
> -	id->daddr = XFS_AG_DADDR(mp, id->agno, XFS_AGF_DADDR(mp));
> -	id->numblks = XFS_FSS_TO_BB(mp, 1);
> -	error = xfs_growfs_init_aghdr(mp, id, xfs_agfblock_init,
> -					&xfs_agf_buf_ops);
> -	if (error)
> -		goto out_error;
> -
> -	/* AG freelist header block */
> -	id->daddr = XFS_AG_DADDR(mp, id->agno, XFS_AGFL_DADDR(mp));
> -	id->numblks = XFS_FSS_TO_BB(mp, 1);
> -	error = xfs_growfs_init_aghdr(mp, id, xfs_agflblock_init,
> -					&xfs_agfl_buf_ops);
> -	if (error)
> -		goto out_error;
> -
> -	/* AG inode header block */
> -	id->daddr = XFS_AG_DADDR(mp, id->agno, XFS_AGI_DADDR(mp));
> -	id->numblks = XFS_FSS_TO_BB(mp, 1);
> -	error = xfs_growfs_init_aghdr(mp, id, xfs_agiblock_init,
> -					&xfs_agi_buf_ops);
> -	if (error)
> -		goto out_error;
> -
> -
> -	/* BNO btree root block */
> -	id->daddr = XFS_AGB_TO_DADDR(mp, id->agno, XFS_BNO_BLOCK(mp));
> -	id->numblks = BTOBB(mp->m_sb.sb_blocksize);
> -	error = xfs_growfs_init_aghdr(mp, id, xfs_bnoroot_init,
> -				   &xfs_allocbt_buf_ops);
> -	if (error)
> -		goto out_error;
> -
> -
> -	/* CNT btree root block */
> -	id->daddr = XFS_AGB_TO_DADDR(mp, id->agno, XFS_CNT_BLOCK(mp));
> -	id->numblks = BTOBB(mp->m_sb.sb_blocksize);
> -	error = xfs_growfs_init_aghdr(mp, id, xfs_cntroot_init,
> -				   &xfs_allocbt_buf_ops);
> -	if (error)
> -		goto out_error;
> -
> -	/* RMAP btree root block */
> -	if (xfs_sb_version_hasrmapbt(&mp->m_sb)) {
> -		id->daddr = XFS_AGB_TO_DADDR(mp, id->agno, XFS_RMAP_BLOCK(mp));
> -		id->numblks = BTOBB(mp->m_sb.sb_blocksize);
> -		error = xfs_growfs_init_aghdr(mp, id, xfs_rmaproot_init,
> -					   &xfs_rmapbt_buf_ops);
> -		if (error)
> -			goto out_error;
> -
> -	}
> -
> -	/* INO btree root block */
> -	id->daddr = XFS_AGB_TO_DADDR(mp, id->agno, XFS_IBT_BLOCK(mp));
> -	id->numblks = BTOBB(mp->m_sb.sb_blocksize);
> -	id->type = XFS_BTNUM_INO;
> -	error = xfs_growfs_init_aghdr(mp, id, xfs_btroot_init,
> -				   &xfs_inobt_buf_ops);
> -	if (error)
> -		goto out_error;
> -
> -
> -	/*
> -	 * FINO btree root block
> -	 */
> -	if (xfs_sb_version_hasfinobt(&mp->m_sb)) {
> -		id->daddr = XFS_AGB_TO_DADDR(mp, id->agno, XFS_FIBT_BLOCK(mp));
> -		id->numblks = BTOBB(mp->m_sb.sb_blocksize);
> -		id->type = XFS_BTNUM_FINO;
> -		error = xfs_growfs_init_aghdr(mp, id, xfs_btroot_init,
> -					   &xfs_inobt_buf_ops);
> -		if (error)
> -			goto out_error;
> -	}
> -
> -	/*
> -	 * refcount btree root block
> -	 */
> -	if (xfs_sb_version_hasreflink(&mp->m_sb)) {
> -		id->daddr = XFS_AGB_TO_DADDR(mp, id->agno, xfs_refc_block(mp));
> -		id->numblks = BTOBB(mp->m_sb.sb_blocksize);
> -		id->type = XFS_BTNUM_REFC;
> -		error = xfs_growfs_init_aghdr(mp, id, xfs_btroot_init,
> -					   &xfs_refcountbt_buf_ops);
> +		id->daddr = dp->daddr;
> +		id->numblks = dp->numblks;
> +		id->type = dp->type;
> +		error = xfs_growfs_init_aghdr(mp, id, dp->work, dp->ops);
>  		if (error)
> -			goto out_error;
> +			break;
>  	}
> -
> -out_error:
>  	return error;
>  }
>  
> -- 
> 2.17.0
> 
> --
> To unsubscribe from this list: send the line "unsubscribe linux-xfs" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [PATCH 02/10] xfs: factor out AG header initialisation from growfs core
  2018-05-14  4:18 ` [PATCH 02/10] xfs: factor out AG header initialisation from growfs core Dave Chinner
@ 2018-05-14 19:28   ` Darrick J. Wong
  0 siblings, 0 replies; 17+ messages in thread
From: Darrick J. Wong @ 2018-05-14 19:28 UTC (permalink / raw)
  To: Dave Chinner; +Cc: linux-xfs

On Mon, May 14, 2018 at 02:18:56PM +1000, Dave Chinner wrote:
> From: Dave Chinner <dchinner@redhat.com>
> 
> The intialisation of new AG headers is mostly common with the
> userspace mkfs code and growfs in the kernel, so start factoring it
> out so we can move it to libxfs and use it in both places.
> 
> Signed-Off-By: Dave Chinner <dchinner@redhat.com>
> Reviewed-by: Brian Foster <bfoster@redhat.com>

Looks ok,
Reviewed-by: Darrick J. Wong <darrick.wong@oracle.com>

--D

> ---
>  fs/xfs/xfs_fsops.c | 637 +++++++++++++++++++++++----------------------
>  1 file changed, 331 insertions(+), 306 deletions(-)
> 
> diff --git a/fs/xfs/xfs_fsops.c b/fs/xfs/xfs_fsops.c
> index 523792768080..391d1938a6c8 100644
> --- a/fs/xfs/xfs_fsops.c
> +++ b/fs/xfs/xfs_fsops.c
> @@ -71,20 +71,344 @@ xfs_growfs_get_hdr_buf(
>  	return bp;
>  }
>  
> +/*
> + * Write new AG headers to disk. Non-transactional, but written
> + * synchronously so they are completed prior to the growfs transaction
> + * being logged.
> + */
> +static int
> +xfs_grow_ag_headers(
> +	struct xfs_mount	*mp,
> +	xfs_agnumber_t		agno,
> +	xfs_extlen_t		agsize,
> +	xfs_rfsblock_t		*nfree)
> +{
> +	struct xfs_agf		*agf;
> +	struct xfs_agi		*agi;
> +	struct xfs_agfl		*agfl;
> +	__be32			*agfl_bno;
> +	xfs_alloc_rec_t		*arec;
> +	struct xfs_buf		*bp;
> +	int			bucket;
> +	xfs_extlen_t		tmpsize;
> +	int			error = 0;
> +
> +	/*
> +	 * AG freespace header block
> +	 */
> +	bp = xfs_growfs_get_hdr_buf(mp,
> +			XFS_AG_DADDR(mp, agno, XFS_AGF_DADDR(mp)),
> +			XFS_FSS_TO_BB(mp, 1), 0,
> +			&xfs_agf_buf_ops);
> +	if (!bp) {
> +		error = -ENOMEM;
> +		goto out_error;
> +	}
> +
> +	agf = XFS_BUF_TO_AGF(bp);
> +	agf->agf_magicnum = cpu_to_be32(XFS_AGF_MAGIC);
> +	agf->agf_versionnum = cpu_to_be32(XFS_AGF_VERSION);
> +	agf->agf_seqno = cpu_to_be32(agno);
> +	agf->agf_length = cpu_to_be32(agsize);
> +	agf->agf_roots[XFS_BTNUM_BNOi] = cpu_to_be32(XFS_BNO_BLOCK(mp));
> +	agf->agf_roots[XFS_BTNUM_CNTi] = cpu_to_be32(XFS_CNT_BLOCK(mp));
> +	agf->agf_levels[XFS_BTNUM_BNOi] = cpu_to_be32(1);
> +	agf->agf_levels[XFS_BTNUM_CNTi] = cpu_to_be32(1);
> +	if (xfs_sb_version_hasrmapbt(&mp->m_sb)) {
> +		agf->agf_roots[XFS_BTNUM_RMAPi] =
> +					cpu_to_be32(XFS_RMAP_BLOCK(mp));
> +		agf->agf_levels[XFS_BTNUM_RMAPi] = cpu_to_be32(1);
> +		agf->agf_rmap_blocks = cpu_to_be32(1);
> +	}
> +
> +	agf->agf_flfirst = cpu_to_be32(1);
> +	agf->agf_fllast = 0;
> +	agf->agf_flcount = 0;
> +	tmpsize = agsize - mp->m_ag_prealloc_blocks;
> +	agf->agf_freeblks = cpu_to_be32(tmpsize);
> +	agf->agf_longest = cpu_to_be32(tmpsize);
> +	if (xfs_sb_version_hascrc(&mp->m_sb))
> +		uuid_copy(&agf->agf_uuid, &mp->m_sb.sb_meta_uuid);
> +	if (xfs_sb_version_hasreflink(&mp->m_sb)) {
> +		agf->agf_refcount_root = cpu_to_be32(
> +				xfs_refc_block(mp));
> +		agf->agf_refcount_level = cpu_to_be32(1);
> +		agf->agf_refcount_blocks = cpu_to_be32(1);
> +	}
> +
> +	error = xfs_bwrite(bp);
> +	xfs_buf_relse(bp);
> +	if (error)
> +		goto out_error;
> +
> +	/*
> +	 * AG freelist header block
> +	 */
> +	bp = xfs_growfs_get_hdr_buf(mp,
> +			XFS_AG_DADDR(mp, agno, XFS_AGFL_DADDR(mp)),
> +			XFS_FSS_TO_BB(mp, 1), 0,
> +			&xfs_agfl_buf_ops);
> +	if (!bp) {
> +		error = -ENOMEM;
> +		goto out_error;
> +	}
> +
> +	agfl = XFS_BUF_TO_AGFL(bp);
> +	if (xfs_sb_version_hascrc(&mp->m_sb)) {
> +		agfl->agfl_magicnum = cpu_to_be32(XFS_AGFL_MAGIC);
> +		agfl->agfl_seqno = cpu_to_be32(agno);
> +		uuid_copy(&agfl->agfl_uuid, &mp->m_sb.sb_meta_uuid);
> +	}
> +
> +	agfl_bno = XFS_BUF_TO_AGFL_BNO(mp, bp);
> +	for (bucket = 0; bucket < xfs_agfl_size(mp); bucket++)
> +		agfl_bno[bucket] = cpu_to_be32(NULLAGBLOCK);
> +
> +	error = xfs_bwrite(bp);
> +	xfs_buf_relse(bp);
> +	if (error)
> +		goto out_error;
> +
> +	/*
> +	 * AG inode header block
> +	 */
> +	bp = xfs_growfs_get_hdr_buf(mp,
> +			XFS_AG_DADDR(mp, agno, XFS_AGI_DADDR(mp)),
> +			XFS_FSS_TO_BB(mp, 1), 0,
> +			&xfs_agi_buf_ops);
> +	if (!bp) {
> +		error = -ENOMEM;
> +		goto out_error;
> +	}
> +
> +	agi = XFS_BUF_TO_AGI(bp);
> +	agi->agi_magicnum = cpu_to_be32(XFS_AGI_MAGIC);
> +	agi->agi_versionnum = cpu_to_be32(XFS_AGI_VERSION);
> +	agi->agi_seqno = cpu_to_be32(agno);
> +	agi->agi_length = cpu_to_be32(agsize);
> +	agi->agi_count = 0;
> +	agi->agi_root = cpu_to_be32(XFS_IBT_BLOCK(mp));
> +	agi->agi_level = cpu_to_be32(1);
> +	agi->agi_freecount = 0;
> +	agi->agi_newino = cpu_to_be32(NULLAGINO);
> +	agi->agi_dirino = cpu_to_be32(NULLAGINO);
> +	if (xfs_sb_version_hascrc(&mp->m_sb))
> +		uuid_copy(&agi->agi_uuid, &mp->m_sb.sb_meta_uuid);
> +	if (xfs_sb_version_hasfinobt(&mp->m_sb)) {
> +		agi->agi_free_root = cpu_to_be32(XFS_FIBT_BLOCK(mp));
> +		agi->agi_free_level = cpu_to_be32(1);
> +	}
> +	for (bucket = 0; bucket < XFS_AGI_UNLINKED_BUCKETS; bucket++)
> +		agi->agi_unlinked[bucket] = cpu_to_be32(NULLAGINO);
> +
> +	error = xfs_bwrite(bp);
> +	xfs_buf_relse(bp);
> +	if (error)
> +		goto out_error;
> +
> +	/*
> +	 * BNO btree root block
> +	 */
> +	bp = xfs_growfs_get_hdr_buf(mp,
> +			XFS_AGB_TO_DADDR(mp, agno, XFS_BNO_BLOCK(mp)),
> +			BTOBB(mp->m_sb.sb_blocksize), 0,
> +			&xfs_allocbt_buf_ops);
> +
> +	if (!bp) {
> +		error = -ENOMEM;
> +		goto out_error;
> +	}
> +
> +	xfs_btree_init_block(mp, bp, XFS_BTNUM_BNO, 0, 1, agno, 0);
> +
> +	arec = XFS_ALLOC_REC_ADDR(mp, XFS_BUF_TO_BLOCK(bp), 1);
> +	arec->ar_startblock = cpu_to_be32(mp->m_ag_prealloc_blocks);
> +	arec->ar_blockcount = cpu_to_be32(
> +		agsize - be32_to_cpu(arec->ar_startblock));
> +
> +	error = xfs_bwrite(bp);
> +	xfs_buf_relse(bp);
> +	if (error)
> +		goto out_error;
> +
> +	/*
> +	 * CNT btree root block
> +	 */
> +	bp = xfs_growfs_get_hdr_buf(mp,
> +			XFS_AGB_TO_DADDR(mp, agno, XFS_CNT_BLOCK(mp)),
> +			BTOBB(mp->m_sb.sb_blocksize), 0,
> +			&xfs_allocbt_buf_ops);
> +	if (!bp) {
> +		error = -ENOMEM;
> +		goto out_error;
> +	}
> +
> +	xfs_btree_init_block(mp, bp, XFS_BTNUM_CNT, 0, 1, agno, 0);
> +
> +	arec = XFS_ALLOC_REC_ADDR(mp, XFS_BUF_TO_BLOCK(bp), 1);
> +	arec->ar_startblock = cpu_to_be32(mp->m_ag_prealloc_blocks);
> +	arec->ar_blockcount = cpu_to_be32(
> +		agsize - be32_to_cpu(arec->ar_startblock));
> +	*nfree += be32_to_cpu(arec->ar_blockcount);
> +
> +	error = xfs_bwrite(bp);
> +	xfs_buf_relse(bp);
> +	if (error)
> +		goto out_error;
> +
> +	/* RMAP btree root block */
> +	if (xfs_sb_version_hasrmapbt(&mp->m_sb)) {
> +		struct xfs_rmap_rec	*rrec;
> +		struct xfs_btree_block	*block;
> +
> +		bp = xfs_growfs_get_hdr_buf(mp,
> +			XFS_AGB_TO_DADDR(mp, agno, XFS_RMAP_BLOCK(mp)),
> +			BTOBB(mp->m_sb.sb_blocksize), 0,
> +			&xfs_rmapbt_buf_ops);
> +		if (!bp) {
> +			error = -ENOMEM;
> +			goto out_error;
> +		}
> +
> +		xfs_btree_init_block(mp, bp, XFS_BTNUM_RMAP, 0, 0,
> +					agno, 0);
> +		block = XFS_BUF_TO_BLOCK(bp);
> +
> +
> +		/*
> +		 * mark the AG header regions as static metadata The BNO
> +		 * btree block is the first block after the headers, so
> +		 * it's location defines the size of region the static
> +		 * metadata consumes.
> +		 *
> +		 * Note: unlike mkfs, we never have to account for log
> +		 * space when growing the data regions
> +		 */
> +		rrec = XFS_RMAP_REC_ADDR(block, 1);
> +		rrec->rm_startblock = 0;
> +		rrec->rm_blockcount = cpu_to_be32(XFS_BNO_BLOCK(mp));
> +		rrec->rm_owner = cpu_to_be64(XFS_RMAP_OWN_FS);
> +		rrec->rm_offset = 0;
> +		be16_add_cpu(&block->bb_numrecs, 1);
> +
> +		/* account freespace btree root blocks */
> +		rrec = XFS_RMAP_REC_ADDR(block, 2);
> +		rrec->rm_startblock = cpu_to_be32(XFS_BNO_BLOCK(mp));
> +		rrec->rm_blockcount = cpu_to_be32(2);
> +		rrec->rm_owner = cpu_to_be64(XFS_RMAP_OWN_AG);
> +		rrec->rm_offset = 0;
> +		be16_add_cpu(&block->bb_numrecs, 1);
> +
> +		/* account inode btree root blocks */
> +		rrec = XFS_RMAP_REC_ADDR(block, 3);
> +		rrec->rm_startblock = cpu_to_be32(XFS_IBT_BLOCK(mp));
> +		rrec->rm_blockcount = cpu_to_be32(XFS_RMAP_BLOCK(mp) -
> +						XFS_IBT_BLOCK(mp));
> +		rrec->rm_owner = cpu_to_be64(XFS_RMAP_OWN_INOBT);
> +		rrec->rm_offset = 0;
> +		be16_add_cpu(&block->bb_numrecs, 1);
> +
> +		/* account for rmap btree root */
> +		rrec = XFS_RMAP_REC_ADDR(block, 4);
> +		rrec->rm_startblock = cpu_to_be32(XFS_RMAP_BLOCK(mp));
> +		rrec->rm_blockcount = cpu_to_be32(1);
> +		rrec->rm_owner = cpu_to_be64(XFS_RMAP_OWN_AG);
> +		rrec->rm_offset = 0;
> +		be16_add_cpu(&block->bb_numrecs, 1);
> +
> +		/* account for refc btree root */
> +		if (xfs_sb_version_hasreflink(&mp->m_sb)) {
> +			rrec = XFS_RMAP_REC_ADDR(block, 5);
> +			rrec->rm_startblock = cpu_to_be32(xfs_refc_block(mp));
> +			rrec->rm_blockcount = cpu_to_be32(1);
> +			rrec->rm_owner = cpu_to_be64(XFS_RMAP_OWN_REFC);
> +			rrec->rm_offset = 0;
> +			be16_add_cpu(&block->bb_numrecs, 1);
> +		}
> +
> +		error = xfs_bwrite(bp);
> +		xfs_buf_relse(bp);
> +		if (error)
> +			goto out_error;
> +	}
> +
> +	/*
> +	 * INO btree root block
> +	 */
> +	bp = xfs_growfs_get_hdr_buf(mp,
> +			XFS_AGB_TO_DADDR(mp, agno, XFS_IBT_BLOCK(mp)),
> +			BTOBB(mp->m_sb.sb_blocksize), 0,
> +			&xfs_inobt_buf_ops);
> +	if (!bp) {
> +		error = -ENOMEM;
> +		goto out_error;
> +	}
> +
> +	xfs_btree_init_block(mp, bp, XFS_BTNUM_INO , 0, 0, agno, 0);
> +
> +	error = xfs_bwrite(bp);
> +	xfs_buf_relse(bp);
> +	if (error)
> +		goto out_error;
> +
> +	/*
> +	 * FINO btree root block
> +	 */
> +	if (xfs_sb_version_hasfinobt(&mp->m_sb)) {
> +		bp = xfs_growfs_get_hdr_buf(mp,
> +			XFS_AGB_TO_DADDR(mp, agno, XFS_FIBT_BLOCK(mp)),
> +			BTOBB(mp->m_sb.sb_blocksize), 0,
> +			&xfs_inobt_buf_ops);
> +		if (!bp) {
> +			error = -ENOMEM;
> +			goto out_error;
> +		}
> +
> +		xfs_btree_init_block(mp, bp, XFS_BTNUM_FINO,
> +					     0, 0, agno, 0);
> +
> +		error = xfs_bwrite(bp);
> +		xfs_buf_relse(bp);
> +		if (error)
> +			goto out_error;
> +	}
> +
> +	/*
> +	 * refcount btree root block
> +	 */
> +	if (xfs_sb_version_hasreflink(&mp->m_sb)) {
> +		bp = xfs_growfs_get_hdr_buf(mp,
> +			XFS_AGB_TO_DADDR(mp, agno, xfs_refc_block(mp)),
> +			BTOBB(mp->m_sb.sb_blocksize), 0,
> +			&xfs_refcountbt_buf_ops);
> +		if (!bp) {
> +			error = -ENOMEM;
> +			goto out_error;
> +		}
> +
> +		xfs_btree_init_block(mp, bp, XFS_BTNUM_REFC,
> +				     0, 0, agno, 0);
> +
> +		error = xfs_bwrite(bp);
> +		xfs_buf_relse(bp);
> +		if (error)
> +			goto out_error;
> +	}
> +
> +out_error:
> +	return error;
> +}
> +
>  static int
>  xfs_growfs_data_private(
>  	xfs_mount_t		*mp,		/* mount point for filesystem */
>  	xfs_growfs_data_t	*in)		/* growfs data input struct */
>  {
>  	xfs_agf_t		*agf;
> -	struct xfs_agfl		*agfl;
>  	xfs_agi_t		*agi;
>  	xfs_agnumber_t		agno;
>  	xfs_extlen_t		agsize;
> -	xfs_extlen_t		tmpsize;
> -	xfs_alloc_rec_t		*arec;
>  	xfs_buf_t		*bp;
> -	int			bucket;
>  	int			dpct;
>  	int			error, saved_error = 0;
>  	xfs_agnumber_t		nagcount;
> @@ -141,318 +465,19 @@ xfs_growfs_data_private(
>  	 */
>  	nfree = 0;
>  	for (agno = nagcount - 1; agno >= oagcount; agno--, new -= agsize) {
> -		__be32	*agfl_bno;
> -
> -		/*
> -		 * AG freespace header block
> -		 */
> -		bp = xfs_growfs_get_hdr_buf(mp,
> -				XFS_AG_DADDR(mp, agno, XFS_AGF_DADDR(mp)),
> -				XFS_FSS_TO_BB(mp, 1), 0,
> -				&xfs_agf_buf_ops);
> -		if (!bp) {
> -			error = -ENOMEM;
> -			goto error0;
> -		}
>  
> -		agf = XFS_BUF_TO_AGF(bp);
> -		agf->agf_magicnum = cpu_to_be32(XFS_AGF_MAGIC);
> -		agf->agf_versionnum = cpu_to_be32(XFS_AGF_VERSION);
> -		agf->agf_seqno = cpu_to_be32(agno);
>  		if (agno == nagcount - 1)
> -			agsize =
> -				nb -
> +			agsize = nb -
>  				(agno * (xfs_rfsblock_t)mp->m_sb.sb_agblocks);
>  		else
>  			agsize = mp->m_sb.sb_agblocks;
> -		agf->agf_length = cpu_to_be32(agsize);
> -		agf->agf_roots[XFS_BTNUM_BNOi] = cpu_to_be32(XFS_BNO_BLOCK(mp));
> -		agf->agf_roots[XFS_BTNUM_CNTi] = cpu_to_be32(XFS_CNT_BLOCK(mp));
> -		agf->agf_levels[XFS_BTNUM_BNOi] = cpu_to_be32(1);
> -		agf->agf_levels[XFS_BTNUM_CNTi] = cpu_to_be32(1);
> -		if (xfs_sb_version_hasrmapbt(&mp->m_sb)) {
> -			agf->agf_roots[XFS_BTNUM_RMAPi] =
> -						cpu_to_be32(XFS_RMAP_BLOCK(mp));
> -			agf->agf_levels[XFS_BTNUM_RMAPi] = cpu_to_be32(1);
> -			agf->agf_rmap_blocks = cpu_to_be32(1);
> -		}
> -
> -		agf->agf_flfirst = cpu_to_be32(1);
> -		agf->agf_fllast = 0;
> -		agf->agf_flcount = 0;
> -		tmpsize = agsize - mp->m_ag_prealloc_blocks;
> -		agf->agf_freeblks = cpu_to_be32(tmpsize);
> -		agf->agf_longest = cpu_to_be32(tmpsize);
> -		if (xfs_sb_version_hascrc(&mp->m_sb))
> -			uuid_copy(&agf->agf_uuid, &mp->m_sb.sb_meta_uuid);
> -		if (xfs_sb_version_hasreflink(&mp->m_sb)) {
> -			agf->agf_refcount_root = cpu_to_be32(
> -					xfs_refc_block(mp));
> -			agf->agf_refcount_level = cpu_to_be32(1);
> -			agf->agf_refcount_blocks = cpu_to_be32(1);
> -		}
> -
> -		error = xfs_bwrite(bp);
> -		xfs_buf_relse(bp);
> -		if (error)
> -			goto error0;
> -
> -		/*
> -		 * AG freelist header block
> -		 */
> -		bp = xfs_growfs_get_hdr_buf(mp,
> -				XFS_AG_DADDR(mp, agno, XFS_AGFL_DADDR(mp)),
> -				XFS_FSS_TO_BB(mp, 1), 0,
> -				&xfs_agfl_buf_ops);
> -		if (!bp) {
> -			error = -ENOMEM;
> -			goto error0;
> -		}
> -
> -		agfl = XFS_BUF_TO_AGFL(bp);
> -		if (xfs_sb_version_hascrc(&mp->m_sb)) {
> -			agfl->agfl_magicnum = cpu_to_be32(XFS_AGFL_MAGIC);
> -			agfl->agfl_seqno = cpu_to_be32(agno);
> -			uuid_copy(&agfl->agfl_uuid, &mp->m_sb.sb_meta_uuid);
> -		}
> -
> -		agfl_bno = XFS_BUF_TO_AGFL_BNO(mp, bp);
> -		for (bucket = 0; bucket < xfs_agfl_size(mp); bucket++)
> -			agfl_bno[bucket] = cpu_to_be32(NULLAGBLOCK);
>  
> -		error = xfs_bwrite(bp);
> -		xfs_buf_relse(bp);
> +		error = xfs_grow_ag_headers(mp, agno, agsize, &nfree);
>  		if (error)
>  			goto error0;
> -
> -		/*
> -		 * AG inode header block
> -		 */
> -		bp = xfs_growfs_get_hdr_buf(mp,
> -				XFS_AG_DADDR(mp, agno, XFS_AGI_DADDR(mp)),
> -				XFS_FSS_TO_BB(mp, 1), 0,
> -				&xfs_agi_buf_ops);
> -		if (!bp) {
> -			error = -ENOMEM;
> -			goto error0;
> -		}
> -
> -		agi = XFS_BUF_TO_AGI(bp);
> -		agi->agi_magicnum = cpu_to_be32(XFS_AGI_MAGIC);
> -		agi->agi_versionnum = cpu_to_be32(XFS_AGI_VERSION);
> -		agi->agi_seqno = cpu_to_be32(agno);
> -		agi->agi_length = cpu_to_be32(agsize);
> -		agi->agi_count = 0;
> -		agi->agi_root = cpu_to_be32(XFS_IBT_BLOCK(mp));
> -		agi->agi_level = cpu_to_be32(1);
> -		agi->agi_freecount = 0;
> -		agi->agi_newino = cpu_to_be32(NULLAGINO);
> -		agi->agi_dirino = cpu_to_be32(NULLAGINO);
> -		if (xfs_sb_version_hascrc(&mp->m_sb))
> -			uuid_copy(&agi->agi_uuid, &mp->m_sb.sb_meta_uuid);
> -		if (xfs_sb_version_hasfinobt(&mp->m_sb)) {
> -			agi->agi_free_root = cpu_to_be32(XFS_FIBT_BLOCK(mp));
> -			agi->agi_free_level = cpu_to_be32(1);
> -		}
> -		for (bucket = 0; bucket < XFS_AGI_UNLINKED_BUCKETS; bucket++)
> -			agi->agi_unlinked[bucket] = cpu_to_be32(NULLAGINO);
> -
> -		error = xfs_bwrite(bp);
> -		xfs_buf_relse(bp);
> -		if (error)
> -			goto error0;
> -
> -		/*
> -		 * BNO btree root block
> -		 */
> -		bp = xfs_growfs_get_hdr_buf(mp,
> -				XFS_AGB_TO_DADDR(mp, agno, XFS_BNO_BLOCK(mp)),
> -				BTOBB(mp->m_sb.sb_blocksize), 0,
> -				&xfs_allocbt_buf_ops);
> -
> -		if (!bp) {
> -			error = -ENOMEM;
> -			goto error0;
> -		}
> -
> -		xfs_btree_init_block(mp, bp, XFS_BTNUM_BNO, 0, 1, agno, 0);
> -
> -		arec = XFS_ALLOC_REC_ADDR(mp, XFS_BUF_TO_BLOCK(bp), 1);
> -		arec->ar_startblock = cpu_to_be32(mp->m_ag_prealloc_blocks);
> -		arec->ar_blockcount = cpu_to_be32(
> -			agsize - be32_to_cpu(arec->ar_startblock));
> -
> -		error = xfs_bwrite(bp);
> -		xfs_buf_relse(bp);
> -		if (error)
> -			goto error0;
> -
> -		/*
> -		 * CNT btree root block
> -		 */
> -		bp = xfs_growfs_get_hdr_buf(mp,
> -				XFS_AGB_TO_DADDR(mp, agno, XFS_CNT_BLOCK(mp)),
> -				BTOBB(mp->m_sb.sb_blocksize), 0,
> -				&xfs_allocbt_buf_ops);
> -		if (!bp) {
> -			error = -ENOMEM;
> -			goto error0;
> -		}
> -
> -		xfs_btree_init_block(mp, bp, XFS_BTNUM_CNT, 0, 1, agno, 0);
> -
> -		arec = XFS_ALLOC_REC_ADDR(mp, XFS_BUF_TO_BLOCK(bp), 1);
> -		arec->ar_startblock = cpu_to_be32(mp->m_ag_prealloc_blocks);
> -		arec->ar_blockcount = cpu_to_be32(
> -			agsize - be32_to_cpu(arec->ar_startblock));
> -		nfree += be32_to_cpu(arec->ar_blockcount);
> -
> -		error = xfs_bwrite(bp);
> -		xfs_buf_relse(bp);
> -		if (error)
> -			goto error0;
> -
> -		/* RMAP btree root block */
> -		if (xfs_sb_version_hasrmapbt(&mp->m_sb)) {
> -			struct xfs_rmap_rec	*rrec;
> -			struct xfs_btree_block	*block;
> -
> -			bp = xfs_growfs_get_hdr_buf(mp,
> -				XFS_AGB_TO_DADDR(mp, agno, XFS_RMAP_BLOCK(mp)),
> -				BTOBB(mp->m_sb.sb_blocksize), 0,
> -				&xfs_rmapbt_buf_ops);
> -			if (!bp) {
> -				error = -ENOMEM;
> -				goto error0;
> -			}
> -
> -			xfs_btree_init_block(mp, bp, XFS_BTNUM_RMAP, 0, 0,
> -						agno, 0);
> -			block = XFS_BUF_TO_BLOCK(bp);
> -
> -
> -			/*
> -			 * mark the AG header regions as static metadata The BNO
> -			 * btree block is the first block after the headers, so
> -			 * it's location defines the size of region the static
> -			 * metadata consumes.
> -			 *
> -			 * Note: unlike mkfs, we never have to account for log
> -			 * space when growing the data regions
> -			 */
> -			rrec = XFS_RMAP_REC_ADDR(block, 1);
> -			rrec->rm_startblock = 0;
> -			rrec->rm_blockcount = cpu_to_be32(XFS_BNO_BLOCK(mp));
> -			rrec->rm_owner = cpu_to_be64(XFS_RMAP_OWN_FS);
> -			rrec->rm_offset = 0;
> -			be16_add_cpu(&block->bb_numrecs, 1);
> -
> -			/* account freespace btree root blocks */
> -			rrec = XFS_RMAP_REC_ADDR(block, 2);
> -			rrec->rm_startblock = cpu_to_be32(XFS_BNO_BLOCK(mp));
> -			rrec->rm_blockcount = cpu_to_be32(2);
> -			rrec->rm_owner = cpu_to_be64(XFS_RMAP_OWN_AG);
> -			rrec->rm_offset = 0;
> -			be16_add_cpu(&block->bb_numrecs, 1);
> -
> -			/* account inode btree root blocks */
> -			rrec = XFS_RMAP_REC_ADDR(block, 3);
> -			rrec->rm_startblock = cpu_to_be32(XFS_IBT_BLOCK(mp));
> -			rrec->rm_blockcount = cpu_to_be32(XFS_RMAP_BLOCK(mp) -
> -							XFS_IBT_BLOCK(mp));
> -			rrec->rm_owner = cpu_to_be64(XFS_RMAP_OWN_INOBT);
> -			rrec->rm_offset = 0;
> -			be16_add_cpu(&block->bb_numrecs, 1);
> -
> -			/* account for rmap btree root */
> -			rrec = XFS_RMAP_REC_ADDR(block, 4);
> -			rrec->rm_startblock = cpu_to_be32(XFS_RMAP_BLOCK(mp));
> -			rrec->rm_blockcount = cpu_to_be32(1);
> -			rrec->rm_owner = cpu_to_be64(XFS_RMAP_OWN_AG);
> -			rrec->rm_offset = 0;
> -			be16_add_cpu(&block->bb_numrecs, 1);
> -
> -			/* account for refc btree root */
> -			if (xfs_sb_version_hasreflink(&mp->m_sb)) {
> -				rrec = XFS_RMAP_REC_ADDR(block, 5);
> -				rrec->rm_startblock = cpu_to_be32(
> -						xfs_refc_block(mp));
> -				rrec->rm_blockcount = cpu_to_be32(1);
> -				rrec->rm_owner = cpu_to_be64(XFS_RMAP_OWN_REFC);
> -				rrec->rm_offset = 0;
> -				be16_add_cpu(&block->bb_numrecs, 1);
> -			}
> -
> -			error = xfs_bwrite(bp);
> -			xfs_buf_relse(bp);
> -			if (error)
> -				goto error0;
> -		}
> -
> -		/*
> -		 * INO btree root block
> -		 */
> -		bp = xfs_growfs_get_hdr_buf(mp,
> -				XFS_AGB_TO_DADDR(mp, agno, XFS_IBT_BLOCK(mp)),
> -				BTOBB(mp->m_sb.sb_blocksize), 0,
> -				&xfs_inobt_buf_ops);
> -		if (!bp) {
> -			error = -ENOMEM;
> -			goto error0;
> -		}
> -
> -		xfs_btree_init_block(mp, bp, XFS_BTNUM_INO , 0, 0, agno, 0);
> -
> -		error = xfs_bwrite(bp);
> -		xfs_buf_relse(bp);
> -		if (error)
> -			goto error0;
> -
> -		/*
> -		 * FINO btree root block
> -		 */
> -		if (xfs_sb_version_hasfinobt(&mp->m_sb)) {
> -			bp = xfs_growfs_get_hdr_buf(mp,
> -				XFS_AGB_TO_DADDR(mp, agno, XFS_FIBT_BLOCK(mp)),
> -				BTOBB(mp->m_sb.sb_blocksize), 0,
> -				&xfs_inobt_buf_ops);
> -			if (!bp) {
> -				error = -ENOMEM;
> -				goto error0;
> -			}
> -
> -			xfs_btree_init_block(mp, bp, XFS_BTNUM_FINO,
> -						     0, 0, agno, 0);
> -
> -			error = xfs_bwrite(bp);
> -			xfs_buf_relse(bp);
> -			if (error)
> -				goto error0;
> -		}
> -
> -		/*
> -		 * refcount btree root block
> -		 */
> -		if (xfs_sb_version_hasreflink(&mp->m_sb)) {
> -			bp = xfs_growfs_get_hdr_buf(mp,
> -				XFS_AGB_TO_DADDR(mp, agno, xfs_refc_block(mp)),
> -				BTOBB(mp->m_sb.sb_blocksize), 0,
> -				&xfs_refcountbt_buf_ops);
> -			if (!bp) {
> -				error = -ENOMEM;
> -				goto error0;
> -			}
> -
> -			xfs_btree_init_block(mp, bp, XFS_BTNUM_REFC,
> -					     0, 0, agno, 0);
> -
> -			error = xfs_bwrite(bp);
> -			xfs_buf_relse(bp);
> -			if (error)
> -				goto error0;
> -		}
>  	}
>  	xfs_trans_agblocks_delta(tp, nfree);
> +
>  	/*
>  	 * There are new blocks in the old last a.g.
>  	 */
> -- 
> 2.17.0
> 
> --
> To unsubscribe from this list: send the line "unsubscribe linux-xfs" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [PATCH 04/10] xfs: factor ag btree root block initialisation
  2018-05-12  0:48   ` Darrick J. Wong
@ 2018-05-12  2:01     ` Dave Chinner
  0 siblings, 0 replies; 17+ messages in thread
From: Dave Chinner @ 2018-05-12  2:01 UTC (permalink / raw)
  To: Darrick J. Wong; +Cc: linux-xfs

On Fri, May 11, 2018 at 05:48:35PM -0700, Darrick J. Wong wrote:
> On Sat, May 12, 2018 at 08:51:01AM +1000, Dave Chinner wrote:
> > From: Dave Chinner <dchinner@redhat.com>
> > 
> > Cookie cutter code, easily factored.
> > 
> > Signed-Off-By: Dave Chinner <dchinner@redhat.com>
> > ---
> >  fs/xfs/xfs_fsops.c | 485 ++++++++++++++++++++++++---------------------
> >  1 file changed, 263 insertions(+), 222 deletions(-)
> > 
> > diff --git a/fs/xfs/xfs_fsops.c b/fs/xfs/xfs_fsops.c
> > index 4b560caaf397..4dbb7b41aeca 100644
> > --- a/fs/xfs/xfs_fsops.c
> > +++ b/fs/xfs/xfs_fsops.c
> > @@ -71,46 +71,141 @@ xfs_growfs_get_hdr_buf(
> >  	return bp;
> >  }
> >  
> > +struct aghdr_init_data {
> > +	/* per ag data */
> > +	xfs_agblock_t		agno;
> 
> xfs_agnumber_t ?

Oops, yes.

Cheers,

Dave.
-- 
Dave Chinner
david@fromorbit.com

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [PATCH 04/10] xfs: factor ag btree root block initialisation
  2018-05-11 22:51 ` [PATCH 04/10] xfs: factor ag btree root block initialisation Dave Chinner
@ 2018-05-12  0:48   ` Darrick J. Wong
  2018-05-12  2:01     ` Dave Chinner
  0 siblings, 1 reply; 17+ messages in thread
From: Darrick J. Wong @ 2018-05-12  0:48 UTC (permalink / raw)
  To: Dave Chinner; +Cc: linux-xfs

On Sat, May 12, 2018 at 08:51:01AM +1000, Dave Chinner wrote:
> From: Dave Chinner <dchinner@redhat.com>
> 
> Cookie cutter code, easily factored.
> 
> Signed-Off-By: Dave Chinner <dchinner@redhat.com>
> ---
>  fs/xfs/xfs_fsops.c | 485 ++++++++++++++++++++++++---------------------
>  1 file changed, 263 insertions(+), 222 deletions(-)
> 
> diff --git a/fs/xfs/xfs_fsops.c b/fs/xfs/xfs_fsops.c
> index 4b560caaf397..4dbb7b41aeca 100644
> --- a/fs/xfs/xfs_fsops.c
> +++ b/fs/xfs/xfs_fsops.c
> @@ -71,46 +71,141 @@ xfs_growfs_get_hdr_buf(
>  	return bp;
>  }
>  
> +struct aghdr_init_data {
> +	/* per ag data */
> +	xfs_agblock_t		agno;

xfs_agnumber_t ?

Otherwise more or less looks ok to me.

--D

> +	xfs_extlen_t		agsize;
> +	struct list_head	buffer_list;
> +	xfs_rfsblock_t		nfree;
> +
> +	/* per header data */
> +	xfs_daddr_t		daddr;
> +	size_t			numblks;
> +	xfs_btnum_t		type;
> +	int			numrecs;
> +};
> +
>  /*
> - * Write new AG headers to disk. Non-transactional, but written
> - * synchronously so they are completed prior to the growfs transaction
> - * being logged.
> + * Generic btree root block init function
>   */
> -static int
> -xfs_grow_ag_headers(
> +static void
> +xfs_btroot_init(
>  	struct xfs_mount	*mp,
> -	xfs_agnumber_t		agno,
> -	xfs_extlen_t		agsize,
> -	xfs_rfsblock_t		*nfree,
> -	struct list_head	*buffer_list)
> +	struct xfs_buf		*bp,
> +	struct aghdr_init_data	*id)
>  {
> -	struct xfs_agf		*agf;
> -	struct xfs_agi		*agi;
> -	struct xfs_agfl		*agfl;
> -	__be32			*agfl_bno;
> -	xfs_alloc_rec_t		*arec;
> -	struct xfs_buf		*bp;
> -	int			bucket;
> -	xfs_extlen_t		tmpsize;
> -	int			error = 0;
> +	xfs_btree_init_block(mp, bp, id->type, 0, id->numrecs, id->agno, 0);
> +}
> +
> +/*
> + * Alloc btree root block init functions
> + */
> +static void
> +xfs_bnoroot_init(
> +	struct xfs_mount	*mp,
> +	struct xfs_buf		*bp,
> +	struct aghdr_init_data	*id)
> +{
> +	struct xfs_alloc_rec	*arec;
> +
> +	xfs_btree_init_block(mp, bp, XFS_BTNUM_BNO, 0, 1, id->agno, 0);
> +	arec = XFS_ALLOC_REC_ADDR(mp, XFS_BUF_TO_BLOCK(bp), 1);
> +	arec->ar_startblock = cpu_to_be32(mp->m_ag_prealloc_blocks);
> +	arec->ar_blockcount = cpu_to_be32(id->agsize -
> +					  be32_to_cpu(arec->ar_startblock));
> +}
> +
> +static void
> +xfs_cntroot_init(
> +	struct xfs_mount	*mp,
> +	struct xfs_buf		*bp,
> +	struct aghdr_init_data	*id)
> +{
> +	struct xfs_alloc_rec	*arec;
> +
> +	xfs_btree_init_block(mp, bp, XFS_BTNUM_CNT, 0, 1, id->agno, 0);
> +	arec = XFS_ALLOC_REC_ADDR(mp, XFS_BUF_TO_BLOCK(bp), 1);
> +	arec->ar_startblock = cpu_to_be32(mp->m_ag_prealloc_blocks);
> +	arec->ar_blockcount = cpu_to_be32(id->agsize -
> +					  be32_to_cpu(arec->ar_startblock));
> +}
> +
> +/*
> + * Reverse map root block init
> + */
> +static void
> +xfs_rmaproot_init(
> +	struct xfs_mount	*mp,
> +	struct xfs_buf		*bp,
> +	struct aghdr_init_data	*id)
> +{
> +	struct xfs_btree_block	*block = XFS_BUF_TO_BLOCK(bp);
> +	struct xfs_rmap_rec	*rrec;
> +
> +	xfs_btree_init_block(mp, bp, XFS_BTNUM_RMAP, 0, 4, id->agno, 0);
>  
>  	/*
> -	 * AG freespace header block
> +	 * mark the AG header regions as static metadata The BNO
> +	 * btree block is the first block after the headers, so
> +	 * it's location defines the size of region the static
> +	 * metadata consumes.
> +	 *
> +	 * Note: unlike mkfs, we never have to account for log
> +	 * space when growing the data regions
>  	 */
> -	bp = xfs_growfs_get_hdr_buf(mp,
> -			XFS_AG_DADDR(mp, agno, XFS_AGF_DADDR(mp)),
> -			XFS_FSS_TO_BB(mp, 1), 0,
> -			&xfs_agf_buf_ops);
> -	if (!bp) {
> -		error = -ENOMEM;
> -		goto out_error;
> +	rrec = XFS_RMAP_REC_ADDR(block, 1);
> +	rrec->rm_startblock = 0;
> +	rrec->rm_blockcount = cpu_to_be32(XFS_BNO_BLOCK(mp));
> +	rrec->rm_owner = cpu_to_be64(XFS_RMAP_OWN_FS);
> +	rrec->rm_offset = 0;
> +
> +	/* account freespace btree root blocks */
> +	rrec = XFS_RMAP_REC_ADDR(block, 2);
> +	rrec->rm_startblock = cpu_to_be32(XFS_BNO_BLOCK(mp));
> +	rrec->rm_blockcount = cpu_to_be32(2);
> +	rrec->rm_owner = cpu_to_be64(XFS_RMAP_OWN_AG);
> +	rrec->rm_offset = 0;
> +
> +	/* account inode btree root blocks */
> +	rrec = XFS_RMAP_REC_ADDR(block, 3);
> +	rrec->rm_startblock = cpu_to_be32(XFS_IBT_BLOCK(mp));
> +	rrec->rm_blockcount = cpu_to_be32(XFS_RMAP_BLOCK(mp) -
> +					  XFS_IBT_BLOCK(mp));
> +	rrec->rm_owner = cpu_to_be64(XFS_RMAP_OWN_INOBT);
> +	rrec->rm_offset = 0;
> +
> +	/* account for rmap btree root */
> +	rrec = XFS_RMAP_REC_ADDR(block, 4);
> +	rrec->rm_startblock = cpu_to_be32(XFS_RMAP_BLOCK(mp));
> +	rrec->rm_blockcount = cpu_to_be32(1);
> +	rrec->rm_owner = cpu_to_be64(XFS_RMAP_OWN_AG);
> +	rrec->rm_offset = 0;
> +
> +	/* account for refc btree root */
> +	if (xfs_sb_version_hasreflink(&mp->m_sb)) {
> +		rrec = XFS_RMAP_REC_ADDR(block, 5);
> +		rrec->rm_startblock = cpu_to_be32(xfs_refc_block(mp));
> +		rrec->rm_blockcount = cpu_to_be32(1);
> +		rrec->rm_owner = cpu_to_be64(XFS_RMAP_OWN_REFC);
> +		rrec->rm_offset = 0;
> +		be16_add_cpu(&block->bb_numrecs, 1);
>  	}
> +}
> +
> +
> +static void
> +xfs_agfblock_init(
> +	struct xfs_mount	*mp,
> +	struct xfs_buf		*bp,
> +	struct aghdr_init_data	*id)
> +{
> +	struct xfs_agf		*agf = XFS_BUF_TO_AGF(bp);
> +	xfs_extlen_t		tmpsize;
>  
> -	agf = XFS_BUF_TO_AGF(bp);
>  	agf->agf_magicnum = cpu_to_be32(XFS_AGF_MAGIC);
>  	agf->agf_versionnum = cpu_to_be32(XFS_AGF_VERSION);
> -	agf->agf_seqno = cpu_to_be32(agno);
> -	agf->agf_length = cpu_to_be32(agsize);
> +	agf->agf_seqno = cpu_to_be32(id->agno);
> +	agf->agf_length = cpu_to_be32(id->agsize);
>  	agf->agf_roots[XFS_BTNUM_BNOi] = cpu_to_be32(XFS_BNO_BLOCK(mp));
>  	agf->agf_roots[XFS_BTNUM_CNTi] = cpu_to_be32(XFS_CNT_BLOCK(mp));
>  	agf->agf_levels[XFS_BTNUM_BNOi] = cpu_to_be32(1);
> @@ -125,7 +220,7 @@ xfs_grow_ag_headers(
>  	agf->agf_flfirst = cpu_to_be32(1);
>  	agf->agf_fllast = 0;
>  	agf->agf_flcount = 0;
> -	tmpsize = agsize - mp->m_ag_prealloc_blocks;
> +	tmpsize = id->agsize - mp->m_ag_prealloc_blocks;
>  	agf->agf_freeblks = cpu_to_be32(tmpsize);
>  	agf->agf_longest = cpu_to_be32(tmpsize);
>  	if (xfs_sb_version_hascrc(&mp->m_sb))
> @@ -136,52 +231,42 @@ xfs_grow_ag_headers(
>  		agf->agf_refcount_level = cpu_to_be32(1);
>  		agf->agf_refcount_blocks = cpu_to_be32(1);
>  	}
> -	xfs_buf_delwri_queue(bp, buffer_list);
> -	xfs_buf_relse(bp);
> +}
>  
> -	/*
> -	 * AG freelist header block
> -	 */
> -	bp = xfs_growfs_get_hdr_buf(mp,
> -			XFS_AG_DADDR(mp, agno, XFS_AGFL_DADDR(mp)),
> -			XFS_FSS_TO_BB(mp, 1), 0,
> -			&xfs_agfl_buf_ops);
> -	if (!bp) {
> -		error = -ENOMEM;
> -		goto out_error;
> -	}
> +static void
> +xfs_agflblock_init(
> +	struct xfs_mount	*mp,
> +	struct xfs_buf		*bp,
> +	struct aghdr_init_data	*id)
> +{
> +	struct xfs_agfl		*agfl = XFS_BUF_TO_AGFL(bp);
> +	__be32			*agfl_bno;
> +	int			bucket;
>  
> -	agfl = XFS_BUF_TO_AGFL(bp);
>  	if (xfs_sb_version_hascrc(&mp->m_sb)) {
>  		agfl->agfl_magicnum = cpu_to_be32(XFS_AGFL_MAGIC);
> -		agfl->agfl_seqno = cpu_to_be32(agno);
> +		agfl->agfl_seqno = cpu_to_be32(id->agno);
>  		uuid_copy(&agfl->agfl_uuid, &mp->m_sb.sb_meta_uuid);
>  	}
>  
>  	agfl_bno = XFS_BUF_TO_AGFL_BNO(mp, bp);
>  	for (bucket = 0; bucket < xfs_agfl_size(mp); bucket++)
>  		agfl_bno[bucket] = cpu_to_be32(NULLAGBLOCK);
> +}
>  
> -	xfs_buf_delwri_queue(bp, buffer_list);
> -	xfs_buf_relse(bp);
> -
> -	/*
> -	 * AG inode header block
> -	 */
> -	bp = xfs_growfs_get_hdr_buf(mp,
> -			XFS_AG_DADDR(mp, agno, XFS_AGI_DADDR(mp)),
> -			XFS_FSS_TO_BB(mp, 1), 0,
> -			&xfs_agi_buf_ops);
> -	if (!bp) {
> -		error = -ENOMEM;
> -		goto out_error;
> -	}
> +static void
> +xfs_agiblock_init(
> +	struct xfs_mount	*mp,
> +	struct xfs_buf		*bp,
> +	struct aghdr_init_data	*id)
> +{
> +	struct xfs_agi		*agi = XFS_BUF_TO_AGI(bp);
> +	int			bucket;
>  
> -	agi = XFS_BUF_TO_AGI(bp);
>  	agi->agi_magicnum = cpu_to_be32(XFS_AGI_MAGIC);
>  	agi->agi_versionnum = cpu_to_be32(XFS_AGI_VERSION);
> -	agi->agi_seqno = cpu_to_be32(agno);
> -	agi->agi_length = cpu_to_be32(agsize);
> +	agi->agi_seqno = cpu_to_be32(id->agno);
> +	agi->agi_length = cpu_to_be32(id->agsize);
>  	agi->agi_count = 0;
>  	agi->agi_root = cpu_to_be32(XFS_IBT_BLOCK(mp));
>  	agi->agi_level = cpu_to_be32(1);
> @@ -196,180 +281,136 @@ xfs_grow_ag_headers(
>  	}
>  	for (bucket = 0; bucket < XFS_AGI_UNLINKED_BUCKETS; bucket++)
>  		agi->agi_unlinked[bucket] = cpu_to_be32(NULLAGINO);
> +}
>  
> -	xfs_buf_delwri_queue(bp, buffer_list);
> -	xfs_buf_relse(bp);
> -
> -	/*
> -	 * BNO btree root block
> -	 */
> -	bp = xfs_growfs_get_hdr_buf(mp,
> -			XFS_AGB_TO_DADDR(mp, agno, XFS_BNO_BLOCK(mp)),
> -			BTOBB(mp->m_sb.sb_blocksize), 0,
> -			&xfs_allocbt_buf_ops);
> +static int
> +xfs_growfs_init_aghdr(
> +	struct xfs_mount	*mp,
> +	struct aghdr_init_data	*id,
> +	void			(*work)(struct xfs_mount *, struct xfs_buf *,
> +					struct aghdr_init_data *),
> +	const struct xfs_buf_ops *ops)
>  
> -	if (!bp) {
> -		error = -ENOMEM;
> -		goto out_error;
> -	}
> +{
> +	struct xfs_buf		*bp;
>  
> -	xfs_btree_init_block(mp, bp, XFS_BTNUM_BNO, 0, 1, agno, 0);
> +	bp = xfs_growfs_get_hdr_buf(mp, id->daddr, id->numblks, 0, ops);
> +	if (!bp)
> +		return -ENOMEM;
>  
> -	arec = XFS_ALLOC_REC_ADDR(mp, XFS_BUF_TO_BLOCK(bp), 1);
> -	arec->ar_startblock = cpu_to_be32(mp->m_ag_prealloc_blocks);
> -	arec->ar_blockcount = cpu_to_be32(
> -		agsize - be32_to_cpu(arec->ar_startblock));
> +	(*work)(mp, bp, id);
>  
> -	xfs_buf_delwri_queue(bp, buffer_list);
> +	xfs_buf_delwri_queue(bp, &id->buffer_list);
>  	xfs_buf_relse(bp);
> +	return 0;
> +}
>  
> -	/*
> -	 * CNT btree root block
> -	 */
> -	bp = xfs_growfs_get_hdr_buf(mp,
> -			XFS_AGB_TO_DADDR(mp, agno, XFS_CNT_BLOCK(mp)),
> -			BTOBB(mp->m_sb.sb_blocksize), 0,
> -			&xfs_allocbt_buf_ops);
> -	if (!bp) {
> -		error = -ENOMEM;
> -		goto out_error;
> -	}
> -
> -	xfs_btree_init_block(mp, bp, XFS_BTNUM_CNT, 0, 1, agno, 0);
> +/*
> + * Write new AG headers to disk. Non-transactional, but written
> + * synchronously so they are completed prior to the growfs transaction
> + * being logged.
> + */
> +static int
> +xfs_grow_ag_headers(
> +	struct xfs_mount	*mp,
> +	struct aghdr_init_data	*id)
>  
> -	arec = XFS_ALLOC_REC_ADDR(mp, XFS_BUF_TO_BLOCK(bp), 1);
> -	arec->ar_startblock = cpu_to_be32(mp->m_ag_prealloc_blocks);
> -	arec->ar_blockcount = cpu_to_be32(
> -		agsize - be32_to_cpu(arec->ar_startblock));
> -	*nfree += be32_to_cpu(arec->ar_blockcount);
> +{
> +	int			error = 0;
>  
> -	xfs_buf_delwri_queue(bp, buffer_list);
> -	xfs_buf_relse(bp);
> +	/* Account for AG free space in new AG */
> +	id->nfree += id->agsize - mp->m_ag_prealloc_blocks;
>  
> -	/* RMAP btree root block */
> -	if (xfs_sb_version_hasrmapbt(&mp->m_sb)) {
> -		struct xfs_rmap_rec	*rrec;
> -		struct xfs_btree_block	*block;
> -
> -		bp = xfs_growfs_get_hdr_buf(mp,
> -			XFS_AGB_TO_DADDR(mp, agno, XFS_RMAP_BLOCK(mp)),
> -			BTOBB(mp->m_sb.sb_blocksize), 0,
> -			&xfs_rmapbt_buf_ops);
> -		if (!bp) {
> -			error = -ENOMEM;
> -			goto out_error;
> -		}
> +	/* AG freespace header block */
> +	id->daddr = XFS_AG_DADDR(mp, id->agno, XFS_AGF_DADDR(mp));
> +	id->numblks = XFS_FSS_TO_BB(mp, 1);
> +	error = xfs_growfs_init_aghdr(mp, id, xfs_agfblock_init,
> +					&xfs_agf_buf_ops);
> +	if (error)
> +		goto out_error;
>  
> -		xfs_btree_init_block(mp, bp, XFS_BTNUM_RMAP, 0, 0,
> -					agno, 0);
> -		block = XFS_BUF_TO_BLOCK(bp);
> +	/* AG freelist header block */
> +	id->daddr = XFS_AG_DADDR(mp, id->agno, XFS_AGFL_DADDR(mp));
> +	id->numblks = XFS_FSS_TO_BB(mp, 1);
> +	error = xfs_growfs_init_aghdr(mp, id, xfs_agflblock_init,
> +					&xfs_agfl_buf_ops);
> +	if (error)
> +		goto out_error;
>  
> +	/* AG inode header block */
> +	id->daddr = XFS_AG_DADDR(mp, id->agno, XFS_AGI_DADDR(mp));
> +	id->numblks = XFS_FSS_TO_BB(mp, 1);
> +	error = xfs_growfs_init_aghdr(mp, id, xfs_agiblock_init,
> +					&xfs_agi_buf_ops);
> +	if (error)
> +		goto out_error;
>  
> -		/*
> -		 * mark the AG header regions as static metadata The BNO
> -		 * btree block is the first block after the headers, so
> -		 * it's location defines the size of region the static
> -		 * metadata consumes.
> -		 *
> -		 * Note: unlike mkfs, we never have to account for log
> -		 * space when growing the data regions
> -		 */
> -		rrec = XFS_RMAP_REC_ADDR(block, 1);
> -		rrec->rm_startblock = 0;
> -		rrec->rm_blockcount = cpu_to_be32(XFS_BNO_BLOCK(mp));
> -		rrec->rm_owner = cpu_to_be64(XFS_RMAP_OWN_FS);
> -		rrec->rm_offset = 0;
> -		be16_add_cpu(&block->bb_numrecs, 1);
>  
> -		/* account freespace btree root blocks */
> -		rrec = XFS_RMAP_REC_ADDR(block, 2);
> -		rrec->rm_startblock = cpu_to_be32(XFS_BNO_BLOCK(mp));
> -		rrec->rm_blockcount = cpu_to_be32(2);
> -		rrec->rm_owner = cpu_to_be64(XFS_RMAP_OWN_AG);
> -		rrec->rm_offset = 0;
> -		be16_add_cpu(&block->bb_numrecs, 1);
> +	/* BNO btree root block */
> +	id->daddr = XFS_AGB_TO_DADDR(mp, id->agno, XFS_BNO_BLOCK(mp));
> +	id->numblks = BTOBB(mp->m_sb.sb_blocksize);
> +	error = xfs_growfs_init_aghdr(mp, id, xfs_bnoroot_init,
> +				   &xfs_allocbt_buf_ops);
> +	if (error)
> +		goto out_error;
>  
> -		/* account inode btree root blocks */
> -		rrec = XFS_RMAP_REC_ADDR(block, 3);
> -		rrec->rm_startblock = cpu_to_be32(XFS_IBT_BLOCK(mp));
> -		rrec->rm_blockcount = cpu_to_be32(XFS_RMAP_BLOCK(mp) -
> -						XFS_IBT_BLOCK(mp));
> -		rrec->rm_owner = cpu_to_be64(XFS_RMAP_OWN_INOBT);
> -		rrec->rm_offset = 0;
> -		be16_add_cpu(&block->bb_numrecs, 1);
>  
> -		/* account for rmap btree root */
> -		rrec = XFS_RMAP_REC_ADDR(block, 4);
> -		rrec->rm_startblock = cpu_to_be32(XFS_RMAP_BLOCK(mp));
> -		rrec->rm_blockcount = cpu_to_be32(1);
> -		rrec->rm_owner = cpu_to_be64(XFS_RMAP_OWN_AG);
> -		rrec->rm_offset = 0;
> -		be16_add_cpu(&block->bb_numrecs, 1);
> +	/* CNT btree root block */
> +	id->daddr = XFS_AGB_TO_DADDR(mp, id->agno, XFS_CNT_BLOCK(mp));
> +	id->numblks = BTOBB(mp->m_sb.sb_blocksize);
> +	error = xfs_growfs_init_aghdr(mp, id, xfs_cntroot_init,
> +				   &xfs_allocbt_buf_ops);
> +	if (error)
> +		goto out_error;
>  
> -		/* account for refc btree root */
> -		if (xfs_sb_version_hasreflink(&mp->m_sb)) {
> -			rrec = XFS_RMAP_REC_ADDR(block, 5);
> -			rrec->rm_startblock = cpu_to_be32(xfs_refc_block(mp));
> -			rrec->rm_blockcount = cpu_to_be32(1);
> -			rrec->rm_owner = cpu_to_be64(XFS_RMAP_OWN_REFC);
> -			rrec->rm_offset = 0;
> -			be16_add_cpu(&block->bb_numrecs, 1);
> -		}
> +	/* RMAP btree root block */
> +	if (xfs_sb_version_hasrmapbt(&mp->m_sb)) {
> +		id->daddr = XFS_AGB_TO_DADDR(mp, id->agno, XFS_RMAP_BLOCK(mp));
> +		id->numblks = BTOBB(mp->m_sb.sb_blocksize);
> +		error = xfs_growfs_init_aghdr(mp, id, xfs_rmaproot_init,
> +					   &xfs_rmapbt_buf_ops);
> +		if (error)
> +			goto out_error;
>  
> -		xfs_buf_delwri_queue(bp, buffer_list);
> -		xfs_buf_relse(bp);
>  	}
>  
> -	/*
> -	 * INO btree root block
> -	 */
> -	bp = xfs_growfs_get_hdr_buf(mp,
> -			XFS_AGB_TO_DADDR(mp, agno, XFS_IBT_BLOCK(mp)),
> -			BTOBB(mp->m_sb.sb_blocksize), 0,
> -			&xfs_inobt_buf_ops);
> -	if (!bp) {
> -		error = -ENOMEM;
> +	/* INO btree root block */
> +	id->daddr = XFS_AGB_TO_DADDR(mp, id->agno, XFS_IBT_BLOCK(mp));
> +	id->numblks = BTOBB(mp->m_sb.sb_blocksize);
> +	id->type = XFS_BTNUM_INO;
> +	id->numrecs = 0;
> +	error = xfs_growfs_init_aghdr(mp, id, xfs_btroot_init,
> +				   &xfs_inobt_buf_ops);
> +	if (error)
>  		goto out_error;
> -	}
>  
> -	xfs_btree_init_block(mp, bp, XFS_BTNUM_INO , 0, 0, agno, 0);
> -	xfs_buf_delwri_queue(bp, buffer_list);
> -	xfs_buf_relse(bp);
>  
>  	/*
>  	 * FINO btree root block
>  	 */
>  	if (xfs_sb_version_hasfinobt(&mp->m_sb)) {
> -		bp = xfs_growfs_get_hdr_buf(mp,
> -			XFS_AGB_TO_DADDR(mp, agno, XFS_FIBT_BLOCK(mp)),
> -			BTOBB(mp->m_sb.sb_blocksize), 0,
> -			&xfs_inobt_buf_ops);
> -		if (!bp) {
> -			error = -ENOMEM;
> +		id->daddr = XFS_AGB_TO_DADDR(mp, id->agno, XFS_FIBT_BLOCK(mp));
> +		id->numblks = BTOBB(mp->m_sb.sb_blocksize);
> +		id->type = XFS_BTNUM_FINO;
> +		id->numrecs = 0;
> +		error = xfs_growfs_init_aghdr(mp, id, xfs_btroot_init,
> +					   &xfs_inobt_buf_ops);
> +		if (error)
>  			goto out_error;
> -		}
> -
> -		xfs_btree_init_block(mp, bp, XFS_BTNUM_FINO, 0, 0, agno, 0);
> -		xfs_buf_delwri_queue(bp, buffer_list);
> -		xfs_buf_relse(bp);
>  	}
>  
>  	/*
>  	 * refcount btree root block
>  	 */
>  	if (xfs_sb_version_hasreflink(&mp->m_sb)) {
> -		bp = xfs_growfs_get_hdr_buf(mp,
> -			XFS_AGB_TO_DADDR(mp, agno, xfs_refc_block(mp)),
> -			BTOBB(mp->m_sb.sb_blocksize), 0,
> -			&xfs_refcountbt_buf_ops);
> -		if (!bp) {
> -			error = -ENOMEM;
> +		id->daddr = XFS_AGB_TO_DADDR(mp, id->agno, xfs_refc_block(mp));
> +		id->numblks = BTOBB(mp->m_sb.sb_blocksize);
> +		id->type = XFS_BTNUM_REFC;
> +		id->numrecs = 0;
> +		error = xfs_growfs_init_aghdr(mp, id, xfs_btroot_init,
> +					   &xfs_refcountbt_buf_ops);
> +		if (error)
>  			goto out_error;
> -		}
> -
> -		xfs_btree_init_block(mp, bp, XFS_BTNUM_REFC, 0, 0, agno, 0);
> -		xfs_buf_delwri_queue(bp, buffer_list);
> -		xfs_buf_relse(bp);
>  	}
>  
>  out_error:
> @@ -384,7 +425,6 @@ xfs_growfs_data_private(
>  	xfs_agf_t		*agf;
>  	xfs_agi_t		*agi;
>  	xfs_agnumber_t		agno;
> -	xfs_extlen_t		agsize;
>  	xfs_buf_t		*bp;
>  	int			dpct;
>  	int			error, saved_error = 0;
> @@ -392,11 +432,11 @@ xfs_growfs_data_private(
>  	xfs_agnumber_t		nagimax = 0;
>  	xfs_rfsblock_t		nb, nb_mod;
>  	xfs_rfsblock_t		new;
> -	xfs_rfsblock_t		nfree;
>  	xfs_agnumber_t		oagcount;
>  	int			pct;
>  	xfs_trans_t		*tp;
>  	LIST_HEAD		(buffer_list);
> +	struct aghdr_init_data	id = {};
>  
>  	nb = in->newblocks;
>  	pct = in->imaxpct;
> @@ -448,27 +488,28 @@ xfs_growfs_data_private(
>  	 * list to write, we can cancel the entire list without having written
>  	 * anything.
>  	 */
> -	nfree = 0;
> -	for (agno = nagcount - 1; agno >= oagcount; agno--, new -= agsize) {
> -
> -		if (agno == nagcount - 1)
> -			agsize = nb -
> -				(agno * (xfs_rfsblock_t)mp->m_sb.sb_agblocks);
> +	INIT_LIST_HEAD(&id.buffer_list);
> +	for (id.agno = nagcount - 1;
> +	     id.agno >= oagcount;
> +	     id.agno--, new -= id.agsize) {
> +
> +		if (id.agno == nagcount - 1)
> +			id.agsize = nb -
> +				(id.agno * (xfs_rfsblock_t)mp->m_sb.sb_agblocks);
>  		else
> -			agsize = mp->m_sb.sb_agblocks;
> +			id.agsize = mp->m_sb.sb_agblocks;
>  
> -		error = xfs_grow_ag_headers(mp, agno, agsize, &nfree,
> -					    &buffer_list);
> +		error = xfs_grow_ag_headers(mp, &id);
>  		if (error) {
> -			xfs_buf_delwri_cancel(&buffer_list);
> +			xfs_buf_delwri_cancel(&id.buffer_list);
>  			goto error0;
>  		}
>  	}
> -	error = xfs_buf_delwri_submit(&buffer_list);
> +	error = xfs_buf_delwri_submit(&id.buffer_list);
>  	if (error)
>  		goto error0;
>  
> -	xfs_trans_agblocks_delta(tp, nfree);
> +	xfs_trans_agblocks_delta(tp, id.nfree);
>  
>  	/*
>  	 * There are new blocks in the old last a.g.
> @@ -479,7 +520,7 @@ xfs_growfs_data_private(
>  		/*
>  		 * Change the agi length.
>  		 */
> -		error = xfs_ialloc_read_agi(mp, tp, agno, &bp);
> +		error = xfs_ialloc_read_agi(mp, tp, id.agno, &bp);
>  		if (error) {
>  			goto error0;
>  		}
> @@ -492,7 +533,7 @@ xfs_growfs_data_private(
>  		/*
>  		 * Change agf length.
>  		 */
> -		error = xfs_alloc_read_agf(mp, tp, agno, 0, &bp);
> +		error = xfs_alloc_read_agf(mp, tp, id.agno, 0, &bp);
>  		if (error) {
>  			goto error0;
>  		}
> @@ -511,13 +552,13 @@ xfs_growfs_data_private(
>  		 * this doesn't actually exist in the rmap btree.
>  		 */
>  		xfs_rmap_ag_owner(&oinfo, XFS_RMAP_OWN_NULL);
> -		error = xfs_rmap_free(tp, bp, agno,
> +		error = xfs_rmap_free(tp, bp, id.agno,
>  				be32_to_cpu(agf->agf_length) - new,
>  				new, &oinfo);
>  		if (error)
>  			goto error0;
>  		error = xfs_free_extent(tp,
> -				XFS_AGB_TO_FSB(mp, agno,
> +				XFS_AGB_TO_FSB(mp, id.agno,
>  					be32_to_cpu(agf->agf_length) - new),
>  				new, &oinfo, XFS_AG_RESV_NONE);
>  		if (error)
> @@ -534,8 +575,8 @@ xfs_growfs_data_private(
>  	if (nb > mp->m_sb.sb_dblocks)
>  		xfs_trans_mod_sb(tp, XFS_TRANS_SB_DBLOCKS,
>  				 nb - mp->m_sb.sb_dblocks);
> -	if (nfree)
> -		xfs_trans_mod_sb(tp, XFS_TRANS_SB_FDBLOCKS, nfree);
> +	if (id.nfree)
> +		xfs_trans_mod_sb(tp, XFS_TRANS_SB_FDBLOCKS, id.nfree);
>  	if (dpct)
>  		xfs_trans_mod_sb(tp, XFS_TRANS_SB_IMAXPCT, dpct);
>  	xfs_trans_set_sync(tp);
> @@ -562,7 +603,7 @@ xfs_growfs_data_private(
>  	if (new) {
>  		struct xfs_perag	*pag;
>  
> -		pag = xfs_perag_get(mp, agno);
> +		pag = xfs_perag_get(mp, id.agno);
>  		error = xfs_ag_resv_free(pag);
>  		xfs_perag_put(pag);
>  		if (error)
> -- 
> 2.17.0
> 
> --
> To unsubscribe from this list: send the line "unsubscribe linux-xfs" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 17+ messages in thread

* [PATCH 04/10] xfs: factor ag btree root block initialisation
  2018-05-11 22:50 [PATCH v2] xfs: refactor and tablise growfs Dave Chinner
@ 2018-05-11 22:51 ` Dave Chinner
  2018-05-12  0:48   ` Darrick J. Wong
  0 siblings, 1 reply; 17+ messages in thread
From: Dave Chinner @ 2018-05-11 22:51 UTC (permalink / raw)
  To: linux-xfs

From: Dave Chinner <dchinner@redhat.com>

Cookie cutter code, easily factored.

Signed-Off-By: Dave Chinner <dchinner@redhat.com>
---
 fs/xfs/xfs_fsops.c | 485 ++++++++++++++++++++++++---------------------
 1 file changed, 263 insertions(+), 222 deletions(-)

diff --git a/fs/xfs/xfs_fsops.c b/fs/xfs/xfs_fsops.c
index 4b560caaf397..4dbb7b41aeca 100644
--- a/fs/xfs/xfs_fsops.c
+++ b/fs/xfs/xfs_fsops.c
@@ -71,46 +71,141 @@ xfs_growfs_get_hdr_buf(
 	return bp;
 }
 
+struct aghdr_init_data {
+	/* per ag data */
+	xfs_agblock_t		agno;
+	xfs_extlen_t		agsize;
+	struct list_head	buffer_list;
+	xfs_rfsblock_t		nfree;
+
+	/* per header data */
+	xfs_daddr_t		daddr;
+	size_t			numblks;
+	xfs_btnum_t		type;
+	int			numrecs;
+};
+
 /*
- * Write new AG headers to disk. Non-transactional, but written
- * synchronously so they are completed prior to the growfs transaction
- * being logged.
+ * Generic btree root block init function
  */
-static int
-xfs_grow_ag_headers(
+static void
+xfs_btroot_init(
 	struct xfs_mount	*mp,
-	xfs_agnumber_t		agno,
-	xfs_extlen_t		agsize,
-	xfs_rfsblock_t		*nfree,
-	struct list_head	*buffer_list)
+	struct xfs_buf		*bp,
+	struct aghdr_init_data	*id)
 {
-	struct xfs_agf		*agf;
-	struct xfs_agi		*agi;
-	struct xfs_agfl		*agfl;
-	__be32			*agfl_bno;
-	xfs_alloc_rec_t		*arec;
-	struct xfs_buf		*bp;
-	int			bucket;
-	xfs_extlen_t		tmpsize;
-	int			error = 0;
+	xfs_btree_init_block(mp, bp, id->type, 0, id->numrecs, id->agno, 0);
+}
+
+/*
+ * Alloc btree root block init functions
+ */
+static void
+xfs_bnoroot_init(
+	struct xfs_mount	*mp,
+	struct xfs_buf		*bp,
+	struct aghdr_init_data	*id)
+{
+	struct xfs_alloc_rec	*arec;
+
+	xfs_btree_init_block(mp, bp, XFS_BTNUM_BNO, 0, 1, id->agno, 0);
+	arec = XFS_ALLOC_REC_ADDR(mp, XFS_BUF_TO_BLOCK(bp), 1);
+	arec->ar_startblock = cpu_to_be32(mp->m_ag_prealloc_blocks);
+	arec->ar_blockcount = cpu_to_be32(id->agsize -
+					  be32_to_cpu(arec->ar_startblock));
+}
+
+static void
+xfs_cntroot_init(
+	struct xfs_mount	*mp,
+	struct xfs_buf		*bp,
+	struct aghdr_init_data	*id)
+{
+	struct xfs_alloc_rec	*arec;
+
+	xfs_btree_init_block(mp, bp, XFS_BTNUM_CNT, 0, 1, id->agno, 0);
+	arec = XFS_ALLOC_REC_ADDR(mp, XFS_BUF_TO_BLOCK(bp), 1);
+	arec->ar_startblock = cpu_to_be32(mp->m_ag_prealloc_blocks);
+	arec->ar_blockcount = cpu_to_be32(id->agsize -
+					  be32_to_cpu(arec->ar_startblock));
+}
+
+/*
+ * Reverse map root block init
+ */
+static void
+xfs_rmaproot_init(
+	struct xfs_mount	*mp,
+	struct xfs_buf		*bp,
+	struct aghdr_init_data	*id)
+{
+	struct xfs_btree_block	*block = XFS_BUF_TO_BLOCK(bp);
+	struct xfs_rmap_rec	*rrec;
+
+	xfs_btree_init_block(mp, bp, XFS_BTNUM_RMAP, 0, 4, id->agno, 0);
 
 	/*
-	 * AG freespace header block
+	 * mark the AG header regions as static metadata The BNO
+	 * btree block is the first block after the headers, so
+	 * it's location defines the size of region the static
+	 * metadata consumes.
+	 *
+	 * Note: unlike mkfs, we never have to account for log
+	 * space when growing the data regions
 	 */
-	bp = xfs_growfs_get_hdr_buf(mp,
-			XFS_AG_DADDR(mp, agno, XFS_AGF_DADDR(mp)),
-			XFS_FSS_TO_BB(mp, 1), 0,
-			&xfs_agf_buf_ops);
-	if (!bp) {
-		error = -ENOMEM;
-		goto out_error;
+	rrec = XFS_RMAP_REC_ADDR(block, 1);
+	rrec->rm_startblock = 0;
+	rrec->rm_blockcount = cpu_to_be32(XFS_BNO_BLOCK(mp));
+	rrec->rm_owner = cpu_to_be64(XFS_RMAP_OWN_FS);
+	rrec->rm_offset = 0;
+
+	/* account freespace btree root blocks */
+	rrec = XFS_RMAP_REC_ADDR(block, 2);
+	rrec->rm_startblock = cpu_to_be32(XFS_BNO_BLOCK(mp));
+	rrec->rm_blockcount = cpu_to_be32(2);
+	rrec->rm_owner = cpu_to_be64(XFS_RMAP_OWN_AG);
+	rrec->rm_offset = 0;
+
+	/* account inode btree root blocks */
+	rrec = XFS_RMAP_REC_ADDR(block, 3);
+	rrec->rm_startblock = cpu_to_be32(XFS_IBT_BLOCK(mp));
+	rrec->rm_blockcount = cpu_to_be32(XFS_RMAP_BLOCK(mp) -
+					  XFS_IBT_BLOCK(mp));
+	rrec->rm_owner = cpu_to_be64(XFS_RMAP_OWN_INOBT);
+	rrec->rm_offset = 0;
+
+	/* account for rmap btree root */
+	rrec = XFS_RMAP_REC_ADDR(block, 4);
+	rrec->rm_startblock = cpu_to_be32(XFS_RMAP_BLOCK(mp));
+	rrec->rm_blockcount = cpu_to_be32(1);
+	rrec->rm_owner = cpu_to_be64(XFS_RMAP_OWN_AG);
+	rrec->rm_offset = 0;
+
+	/* account for refc btree root */
+	if (xfs_sb_version_hasreflink(&mp->m_sb)) {
+		rrec = XFS_RMAP_REC_ADDR(block, 5);
+		rrec->rm_startblock = cpu_to_be32(xfs_refc_block(mp));
+		rrec->rm_blockcount = cpu_to_be32(1);
+		rrec->rm_owner = cpu_to_be64(XFS_RMAP_OWN_REFC);
+		rrec->rm_offset = 0;
+		be16_add_cpu(&block->bb_numrecs, 1);
 	}
+}
+
+
+static void
+xfs_agfblock_init(
+	struct xfs_mount	*mp,
+	struct xfs_buf		*bp,
+	struct aghdr_init_data	*id)
+{
+	struct xfs_agf		*agf = XFS_BUF_TO_AGF(bp);
+	xfs_extlen_t		tmpsize;
 
-	agf = XFS_BUF_TO_AGF(bp);
 	agf->agf_magicnum = cpu_to_be32(XFS_AGF_MAGIC);
 	agf->agf_versionnum = cpu_to_be32(XFS_AGF_VERSION);
-	agf->agf_seqno = cpu_to_be32(agno);
-	agf->agf_length = cpu_to_be32(agsize);
+	agf->agf_seqno = cpu_to_be32(id->agno);
+	agf->agf_length = cpu_to_be32(id->agsize);
 	agf->agf_roots[XFS_BTNUM_BNOi] = cpu_to_be32(XFS_BNO_BLOCK(mp));
 	agf->agf_roots[XFS_BTNUM_CNTi] = cpu_to_be32(XFS_CNT_BLOCK(mp));
 	agf->agf_levels[XFS_BTNUM_BNOi] = cpu_to_be32(1);
@@ -125,7 +220,7 @@ xfs_grow_ag_headers(
 	agf->agf_flfirst = cpu_to_be32(1);
 	agf->agf_fllast = 0;
 	agf->agf_flcount = 0;
-	tmpsize = agsize - mp->m_ag_prealloc_blocks;
+	tmpsize = id->agsize - mp->m_ag_prealloc_blocks;
 	agf->agf_freeblks = cpu_to_be32(tmpsize);
 	agf->agf_longest = cpu_to_be32(tmpsize);
 	if (xfs_sb_version_hascrc(&mp->m_sb))
@@ -136,52 +231,42 @@ xfs_grow_ag_headers(
 		agf->agf_refcount_level = cpu_to_be32(1);
 		agf->agf_refcount_blocks = cpu_to_be32(1);
 	}
-	xfs_buf_delwri_queue(bp, buffer_list);
-	xfs_buf_relse(bp);
+}
 
-	/*
-	 * AG freelist header block
-	 */
-	bp = xfs_growfs_get_hdr_buf(mp,
-			XFS_AG_DADDR(mp, agno, XFS_AGFL_DADDR(mp)),
-			XFS_FSS_TO_BB(mp, 1), 0,
-			&xfs_agfl_buf_ops);
-	if (!bp) {
-		error = -ENOMEM;
-		goto out_error;
-	}
+static void
+xfs_agflblock_init(
+	struct xfs_mount	*mp,
+	struct xfs_buf		*bp,
+	struct aghdr_init_data	*id)
+{
+	struct xfs_agfl		*agfl = XFS_BUF_TO_AGFL(bp);
+	__be32			*agfl_bno;
+	int			bucket;
 
-	agfl = XFS_BUF_TO_AGFL(bp);
 	if (xfs_sb_version_hascrc(&mp->m_sb)) {
 		agfl->agfl_magicnum = cpu_to_be32(XFS_AGFL_MAGIC);
-		agfl->agfl_seqno = cpu_to_be32(agno);
+		agfl->agfl_seqno = cpu_to_be32(id->agno);
 		uuid_copy(&agfl->agfl_uuid, &mp->m_sb.sb_meta_uuid);
 	}
 
 	agfl_bno = XFS_BUF_TO_AGFL_BNO(mp, bp);
 	for (bucket = 0; bucket < xfs_agfl_size(mp); bucket++)
 		agfl_bno[bucket] = cpu_to_be32(NULLAGBLOCK);
+}
 
-	xfs_buf_delwri_queue(bp, buffer_list);
-	xfs_buf_relse(bp);
-
-	/*
-	 * AG inode header block
-	 */
-	bp = xfs_growfs_get_hdr_buf(mp,
-			XFS_AG_DADDR(mp, agno, XFS_AGI_DADDR(mp)),
-			XFS_FSS_TO_BB(mp, 1), 0,
-			&xfs_agi_buf_ops);
-	if (!bp) {
-		error = -ENOMEM;
-		goto out_error;
-	}
+static void
+xfs_agiblock_init(
+	struct xfs_mount	*mp,
+	struct xfs_buf		*bp,
+	struct aghdr_init_data	*id)
+{
+	struct xfs_agi		*agi = XFS_BUF_TO_AGI(bp);
+	int			bucket;
 
-	agi = XFS_BUF_TO_AGI(bp);
 	agi->agi_magicnum = cpu_to_be32(XFS_AGI_MAGIC);
 	agi->agi_versionnum = cpu_to_be32(XFS_AGI_VERSION);
-	agi->agi_seqno = cpu_to_be32(agno);
-	agi->agi_length = cpu_to_be32(agsize);
+	agi->agi_seqno = cpu_to_be32(id->agno);
+	agi->agi_length = cpu_to_be32(id->agsize);
 	agi->agi_count = 0;
 	agi->agi_root = cpu_to_be32(XFS_IBT_BLOCK(mp));
 	agi->agi_level = cpu_to_be32(1);
@@ -196,180 +281,136 @@ xfs_grow_ag_headers(
 	}
 	for (bucket = 0; bucket < XFS_AGI_UNLINKED_BUCKETS; bucket++)
 		agi->agi_unlinked[bucket] = cpu_to_be32(NULLAGINO);
+}
 
-	xfs_buf_delwri_queue(bp, buffer_list);
-	xfs_buf_relse(bp);
-
-	/*
-	 * BNO btree root block
-	 */
-	bp = xfs_growfs_get_hdr_buf(mp,
-			XFS_AGB_TO_DADDR(mp, agno, XFS_BNO_BLOCK(mp)),
-			BTOBB(mp->m_sb.sb_blocksize), 0,
-			&xfs_allocbt_buf_ops);
+static int
+xfs_growfs_init_aghdr(
+	struct xfs_mount	*mp,
+	struct aghdr_init_data	*id,
+	void			(*work)(struct xfs_mount *, struct xfs_buf *,
+					struct aghdr_init_data *),
+	const struct xfs_buf_ops *ops)
 
-	if (!bp) {
-		error = -ENOMEM;
-		goto out_error;
-	}
+{
+	struct xfs_buf		*bp;
 
-	xfs_btree_init_block(mp, bp, XFS_BTNUM_BNO, 0, 1, agno, 0);
+	bp = xfs_growfs_get_hdr_buf(mp, id->daddr, id->numblks, 0, ops);
+	if (!bp)
+		return -ENOMEM;
 
-	arec = XFS_ALLOC_REC_ADDR(mp, XFS_BUF_TO_BLOCK(bp), 1);
-	arec->ar_startblock = cpu_to_be32(mp->m_ag_prealloc_blocks);
-	arec->ar_blockcount = cpu_to_be32(
-		agsize - be32_to_cpu(arec->ar_startblock));
+	(*work)(mp, bp, id);
 
-	xfs_buf_delwri_queue(bp, buffer_list);
+	xfs_buf_delwri_queue(bp, &id->buffer_list);
 	xfs_buf_relse(bp);
+	return 0;
+}
 
-	/*
-	 * CNT btree root block
-	 */
-	bp = xfs_growfs_get_hdr_buf(mp,
-			XFS_AGB_TO_DADDR(mp, agno, XFS_CNT_BLOCK(mp)),
-			BTOBB(mp->m_sb.sb_blocksize), 0,
-			&xfs_allocbt_buf_ops);
-	if (!bp) {
-		error = -ENOMEM;
-		goto out_error;
-	}
-
-	xfs_btree_init_block(mp, bp, XFS_BTNUM_CNT, 0, 1, agno, 0);
+/*
+ * Write new AG headers to disk. Non-transactional, but written
+ * synchronously so they are completed prior to the growfs transaction
+ * being logged.
+ */
+static int
+xfs_grow_ag_headers(
+	struct xfs_mount	*mp,
+	struct aghdr_init_data	*id)
 
-	arec = XFS_ALLOC_REC_ADDR(mp, XFS_BUF_TO_BLOCK(bp), 1);
-	arec->ar_startblock = cpu_to_be32(mp->m_ag_prealloc_blocks);
-	arec->ar_blockcount = cpu_to_be32(
-		agsize - be32_to_cpu(arec->ar_startblock));
-	*nfree += be32_to_cpu(arec->ar_blockcount);
+{
+	int			error = 0;
 
-	xfs_buf_delwri_queue(bp, buffer_list);
-	xfs_buf_relse(bp);
+	/* Account for AG free space in new AG */
+	id->nfree += id->agsize - mp->m_ag_prealloc_blocks;
 
-	/* RMAP btree root block */
-	if (xfs_sb_version_hasrmapbt(&mp->m_sb)) {
-		struct xfs_rmap_rec	*rrec;
-		struct xfs_btree_block	*block;
-
-		bp = xfs_growfs_get_hdr_buf(mp,
-			XFS_AGB_TO_DADDR(mp, agno, XFS_RMAP_BLOCK(mp)),
-			BTOBB(mp->m_sb.sb_blocksize), 0,
-			&xfs_rmapbt_buf_ops);
-		if (!bp) {
-			error = -ENOMEM;
-			goto out_error;
-		}
+	/* AG freespace header block */
+	id->daddr = XFS_AG_DADDR(mp, id->agno, XFS_AGF_DADDR(mp));
+	id->numblks = XFS_FSS_TO_BB(mp, 1);
+	error = xfs_growfs_init_aghdr(mp, id, xfs_agfblock_init,
+					&xfs_agf_buf_ops);
+	if (error)
+		goto out_error;
 
-		xfs_btree_init_block(mp, bp, XFS_BTNUM_RMAP, 0, 0,
-					agno, 0);
-		block = XFS_BUF_TO_BLOCK(bp);
+	/* AG freelist header block */
+	id->daddr = XFS_AG_DADDR(mp, id->agno, XFS_AGFL_DADDR(mp));
+	id->numblks = XFS_FSS_TO_BB(mp, 1);
+	error = xfs_growfs_init_aghdr(mp, id, xfs_agflblock_init,
+					&xfs_agfl_buf_ops);
+	if (error)
+		goto out_error;
 
+	/* AG inode header block */
+	id->daddr = XFS_AG_DADDR(mp, id->agno, XFS_AGI_DADDR(mp));
+	id->numblks = XFS_FSS_TO_BB(mp, 1);
+	error = xfs_growfs_init_aghdr(mp, id, xfs_agiblock_init,
+					&xfs_agi_buf_ops);
+	if (error)
+		goto out_error;
 
-		/*
-		 * mark the AG header regions as static metadata The BNO
-		 * btree block is the first block after the headers, so
-		 * it's location defines the size of region the static
-		 * metadata consumes.
-		 *
-		 * Note: unlike mkfs, we never have to account for log
-		 * space when growing the data regions
-		 */
-		rrec = XFS_RMAP_REC_ADDR(block, 1);
-		rrec->rm_startblock = 0;
-		rrec->rm_blockcount = cpu_to_be32(XFS_BNO_BLOCK(mp));
-		rrec->rm_owner = cpu_to_be64(XFS_RMAP_OWN_FS);
-		rrec->rm_offset = 0;
-		be16_add_cpu(&block->bb_numrecs, 1);
 
-		/* account freespace btree root blocks */
-		rrec = XFS_RMAP_REC_ADDR(block, 2);
-		rrec->rm_startblock = cpu_to_be32(XFS_BNO_BLOCK(mp));
-		rrec->rm_blockcount = cpu_to_be32(2);
-		rrec->rm_owner = cpu_to_be64(XFS_RMAP_OWN_AG);
-		rrec->rm_offset = 0;
-		be16_add_cpu(&block->bb_numrecs, 1);
+	/* BNO btree root block */
+	id->daddr = XFS_AGB_TO_DADDR(mp, id->agno, XFS_BNO_BLOCK(mp));
+	id->numblks = BTOBB(mp->m_sb.sb_blocksize);
+	error = xfs_growfs_init_aghdr(mp, id, xfs_bnoroot_init,
+				   &xfs_allocbt_buf_ops);
+	if (error)
+		goto out_error;
 
-		/* account inode btree root blocks */
-		rrec = XFS_RMAP_REC_ADDR(block, 3);
-		rrec->rm_startblock = cpu_to_be32(XFS_IBT_BLOCK(mp));
-		rrec->rm_blockcount = cpu_to_be32(XFS_RMAP_BLOCK(mp) -
-						XFS_IBT_BLOCK(mp));
-		rrec->rm_owner = cpu_to_be64(XFS_RMAP_OWN_INOBT);
-		rrec->rm_offset = 0;
-		be16_add_cpu(&block->bb_numrecs, 1);
 
-		/* account for rmap btree root */
-		rrec = XFS_RMAP_REC_ADDR(block, 4);
-		rrec->rm_startblock = cpu_to_be32(XFS_RMAP_BLOCK(mp));
-		rrec->rm_blockcount = cpu_to_be32(1);
-		rrec->rm_owner = cpu_to_be64(XFS_RMAP_OWN_AG);
-		rrec->rm_offset = 0;
-		be16_add_cpu(&block->bb_numrecs, 1);
+	/* CNT btree root block */
+	id->daddr = XFS_AGB_TO_DADDR(mp, id->agno, XFS_CNT_BLOCK(mp));
+	id->numblks = BTOBB(mp->m_sb.sb_blocksize);
+	error = xfs_growfs_init_aghdr(mp, id, xfs_cntroot_init,
+				   &xfs_allocbt_buf_ops);
+	if (error)
+		goto out_error;
 
-		/* account for refc btree root */
-		if (xfs_sb_version_hasreflink(&mp->m_sb)) {
-			rrec = XFS_RMAP_REC_ADDR(block, 5);
-			rrec->rm_startblock = cpu_to_be32(xfs_refc_block(mp));
-			rrec->rm_blockcount = cpu_to_be32(1);
-			rrec->rm_owner = cpu_to_be64(XFS_RMAP_OWN_REFC);
-			rrec->rm_offset = 0;
-			be16_add_cpu(&block->bb_numrecs, 1);
-		}
+	/* RMAP btree root block */
+	if (xfs_sb_version_hasrmapbt(&mp->m_sb)) {
+		id->daddr = XFS_AGB_TO_DADDR(mp, id->agno, XFS_RMAP_BLOCK(mp));
+		id->numblks = BTOBB(mp->m_sb.sb_blocksize);
+		error = xfs_growfs_init_aghdr(mp, id, xfs_rmaproot_init,
+					   &xfs_rmapbt_buf_ops);
+		if (error)
+			goto out_error;
 
-		xfs_buf_delwri_queue(bp, buffer_list);
-		xfs_buf_relse(bp);
 	}
 
-	/*
-	 * INO btree root block
-	 */
-	bp = xfs_growfs_get_hdr_buf(mp,
-			XFS_AGB_TO_DADDR(mp, agno, XFS_IBT_BLOCK(mp)),
-			BTOBB(mp->m_sb.sb_blocksize), 0,
-			&xfs_inobt_buf_ops);
-	if (!bp) {
-		error = -ENOMEM;
+	/* INO btree root block */
+	id->daddr = XFS_AGB_TO_DADDR(mp, id->agno, XFS_IBT_BLOCK(mp));
+	id->numblks = BTOBB(mp->m_sb.sb_blocksize);
+	id->type = XFS_BTNUM_INO;
+	id->numrecs = 0;
+	error = xfs_growfs_init_aghdr(mp, id, xfs_btroot_init,
+				   &xfs_inobt_buf_ops);
+	if (error)
 		goto out_error;
-	}
 
-	xfs_btree_init_block(mp, bp, XFS_BTNUM_INO , 0, 0, agno, 0);
-	xfs_buf_delwri_queue(bp, buffer_list);
-	xfs_buf_relse(bp);
 
 	/*
 	 * FINO btree root block
 	 */
 	if (xfs_sb_version_hasfinobt(&mp->m_sb)) {
-		bp = xfs_growfs_get_hdr_buf(mp,
-			XFS_AGB_TO_DADDR(mp, agno, XFS_FIBT_BLOCK(mp)),
-			BTOBB(mp->m_sb.sb_blocksize), 0,
-			&xfs_inobt_buf_ops);
-		if (!bp) {
-			error = -ENOMEM;
+		id->daddr = XFS_AGB_TO_DADDR(mp, id->agno, XFS_FIBT_BLOCK(mp));
+		id->numblks = BTOBB(mp->m_sb.sb_blocksize);
+		id->type = XFS_BTNUM_FINO;
+		id->numrecs = 0;
+		error = xfs_growfs_init_aghdr(mp, id, xfs_btroot_init,
+					   &xfs_inobt_buf_ops);
+		if (error)
 			goto out_error;
-		}
-
-		xfs_btree_init_block(mp, bp, XFS_BTNUM_FINO, 0, 0, agno, 0);
-		xfs_buf_delwri_queue(bp, buffer_list);
-		xfs_buf_relse(bp);
 	}
 
 	/*
 	 * refcount btree root block
 	 */
 	if (xfs_sb_version_hasreflink(&mp->m_sb)) {
-		bp = xfs_growfs_get_hdr_buf(mp,
-			XFS_AGB_TO_DADDR(mp, agno, xfs_refc_block(mp)),
-			BTOBB(mp->m_sb.sb_blocksize), 0,
-			&xfs_refcountbt_buf_ops);
-		if (!bp) {
-			error = -ENOMEM;
+		id->daddr = XFS_AGB_TO_DADDR(mp, id->agno, xfs_refc_block(mp));
+		id->numblks = BTOBB(mp->m_sb.sb_blocksize);
+		id->type = XFS_BTNUM_REFC;
+		id->numrecs = 0;
+		error = xfs_growfs_init_aghdr(mp, id, xfs_btroot_init,
+					   &xfs_refcountbt_buf_ops);
+		if (error)
 			goto out_error;
-		}
-
-		xfs_btree_init_block(mp, bp, XFS_BTNUM_REFC, 0, 0, agno, 0);
-		xfs_buf_delwri_queue(bp, buffer_list);
-		xfs_buf_relse(bp);
 	}
 
 out_error:
@@ -384,7 +425,6 @@ xfs_growfs_data_private(
 	xfs_agf_t		*agf;
 	xfs_agi_t		*agi;
 	xfs_agnumber_t		agno;
-	xfs_extlen_t		agsize;
 	xfs_buf_t		*bp;
 	int			dpct;
 	int			error, saved_error = 0;
@@ -392,11 +432,11 @@ xfs_growfs_data_private(
 	xfs_agnumber_t		nagimax = 0;
 	xfs_rfsblock_t		nb, nb_mod;
 	xfs_rfsblock_t		new;
-	xfs_rfsblock_t		nfree;
 	xfs_agnumber_t		oagcount;
 	int			pct;
 	xfs_trans_t		*tp;
 	LIST_HEAD		(buffer_list);
+	struct aghdr_init_data	id = {};
 
 	nb = in->newblocks;
 	pct = in->imaxpct;
@@ -448,27 +488,28 @@ xfs_growfs_data_private(
 	 * list to write, we can cancel the entire list without having written
 	 * anything.
 	 */
-	nfree = 0;
-	for (agno = nagcount - 1; agno >= oagcount; agno--, new -= agsize) {
-
-		if (agno == nagcount - 1)
-			agsize = nb -
-				(agno * (xfs_rfsblock_t)mp->m_sb.sb_agblocks);
+	INIT_LIST_HEAD(&id.buffer_list);
+	for (id.agno = nagcount - 1;
+	     id.agno >= oagcount;
+	     id.agno--, new -= id.agsize) {
+
+		if (id.agno == nagcount - 1)
+			id.agsize = nb -
+				(id.agno * (xfs_rfsblock_t)mp->m_sb.sb_agblocks);
 		else
-			agsize = mp->m_sb.sb_agblocks;
+			id.agsize = mp->m_sb.sb_agblocks;
 
-		error = xfs_grow_ag_headers(mp, agno, agsize, &nfree,
-					    &buffer_list);
+		error = xfs_grow_ag_headers(mp, &id);
 		if (error) {
-			xfs_buf_delwri_cancel(&buffer_list);
+			xfs_buf_delwri_cancel(&id.buffer_list);
 			goto error0;
 		}
 	}
-	error = xfs_buf_delwri_submit(&buffer_list);
+	error = xfs_buf_delwri_submit(&id.buffer_list);
 	if (error)
 		goto error0;
 
-	xfs_trans_agblocks_delta(tp, nfree);
+	xfs_trans_agblocks_delta(tp, id.nfree);
 
 	/*
 	 * There are new blocks in the old last a.g.
@@ -479,7 +520,7 @@ xfs_growfs_data_private(
 		/*
 		 * Change the agi length.
 		 */
-		error = xfs_ialloc_read_agi(mp, tp, agno, &bp);
+		error = xfs_ialloc_read_agi(mp, tp, id.agno, &bp);
 		if (error) {
 			goto error0;
 		}
@@ -492,7 +533,7 @@ xfs_growfs_data_private(
 		/*
 		 * Change agf length.
 		 */
-		error = xfs_alloc_read_agf(mp, tp, agno, 0, &bp);
+		error = xfs_alloc_read_agf(mp, tp, id.agno, 0, &bp);
 		if (error) {
 			goto error0;
 		}
@@ -511,13 +552,13 @@ xfs_growfs_data_private(
 		 * this doesn't actually exist in the rmap btree.
 		 */
 		xfs_rmap_ag_owner(&oinfo, XFS_RMAP_OWN_NULL);
-		error = xfs_rmap_free(tp, bp, agno,
+		error = xfs_rmap_free(tp, bp, id.agno,
 				be32_to_cpu(agf->agf_length) - new,
 				new, &oinfo);
 		if (error)
 			goto error0;
 		error = xfs_free_extent(tp,
-				XFS_AGB_TO_FSB(mp, agno,
+				XFS_AGB_TO_FSB(mp, id.agno,
 					be32_to_cpu(agf->agf_length) - new),
 				new, &oinfo, XFS_AG_RESV_NONE);
 		if (error)
@@ -534,8 +575,8 @@ xfs_growfs_data_private(
 	if (nb > mp->m_sb.sb_dblocks)
 		xfs_trans_mod_sb(tp, XFS_TRANS_SB_DBLOCKS,
 				 nb - mp->m_sb.sb_dblocks);
-	if (nfree)
-		xfs_trans_mod_sb(tp, XFS_TRANS_SB_FDBLOCKS, nfree);
+	if (id.nfree)
+		xfs_trans_mod_sb(tp, XFS_TRANS_SB_FDBLOCKS, id.nfree);
 	if (dpct)
 		xfs_trans_mod_sb(tp, XFS_TRANS_SB_IMAXPCT, dpct);
 	xfs_trans_set_sync(tp);
@@ -562,7 +603,7 @@ xfs_growfs_data_private(
 	if (new) {
 		struct xfs_perag	*pag;
 
-		pag = xfs_perag_get(mp, agno);
+		pag = xfs_perag_get(mp, id.agno);
 		error = xfs_ag_resv_free(pag);
 		xfs_perag_put(pag);
 		if (error)
-- 
2.17.0


^ permalink raw reply related	[flat|nested] 17+ messages in thread

end of thread, other threads:[~2018-05-14 19:28 UTC | newest]

Thread overview: 17+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2018-05-14  4:18 [PATCH 0/10 v3] xfs: refactor and tablise growfs Dave Chinner
2018-05-14  4:18 ` [PATCH 01/10] xfs: one-shot cached buffers Dave Chinner
2018-05-14  4:18 ` [PATCH 02/10] xfs: factor out AG header initialisation from growfs core Dave Chinner
2018-05-14 19:28   ` Darrick J. Wong
2018-05-14  4:18 ` [PATCH 03/10] xfs: convert growfs AG header init to use buffer lists Dave Chinner
2018-05-14  4:18 ` [PATCH 04/10] xfs: factor ag btree root block initialisation Dave Chinner
2018-05-14 19:25   ` Darrick J. Wong
2018-05-14  4:18 ` [PATCH 05/10] xfs: turn ag header initialisation into a table driven operation Dave Chinner
2018-05-14 19:27   ` Darrick J. Wong
2018-05-14  4:19 ` [PATCH 06/10] xfs: make imaxpct changes in growfs separate Dave Chinner
2018-05-14  4:19 ` [PATCH 07/10] xfs: separate secondary sb update in growfs Dave Chinner
2018-05-14  4:19 ` [PATCH 08/10] xfs: rework secondary superblock updates " Dave Chinner
2018-05-14  4:19 ` [PATCH 09/10] xfs: move growfs core to libxfs Dave Chinner
2018-05-14  4:19 ` [PATCH 10/10] xfs: factor the ag length extension code into libxfs Dave Chinner
  -- strict thread matches above, loose matches on Subject: below --
2018-05-11 22:50 [PATCH v2] xfs: refactor and tablise growfs Dave Chinner
2018-05-11 22:51 ` [PATCH 04/10] xfs: factor ag btree root block initialisation Dave Chinner
2018-05-12  0:48   ` Darrick J. Wong
2018-05-12  2:01     ` Dave Chinner

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.