--- /dev/null
+From f6b384631e1e3482c24e35b53adbd3da50e47e8f Mon Sep 17 00:00:00 2001
+From: "Darrick J. Wong" <djwong@kernel.org>
+Date: Tue, 11 Apr 2023 18:59:54 -0700
+Subject: xfs: give xfs_extfree_intent its own perag reference
+
+From: Darrick J. Wong <djwong@kernel.org>
+
+commit f6b384631e1e3482c24e35b53adbd3da50e47e8f upstream.
+
+Give the xfs_extfree_intent an passive reference to the perag structure
+data. This reference will be used to enable scrub intent draining
+functionality in subsequent patches. The space being freed must already
+be allocated, so we need to able to run even if the AG is being offlined
+or shrunk.
+
+Signed-off-by: Darrick J. Wong <djwong@kernel.org>
+Reviewed-by: Dave Chinner <dchinner@redhat.com>
+Signed-off-by: Leah Rumancik <leah.rumancik@gmail.com>
+Acked-by: "Darrick J. Wong" <djwong@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+fs/xfs/libxfs/xfs_alloc.c | 7 +++--
+ fs/xfs/libxfs/xfs_alloc.c | 7 +++--
+ fs/xfs/libxfs/xfs_alloc.h | 4 +++
+ fs/xfs/xfs_extfree_item.c | 58 ++++++++++++++++++++++++++++++----------------
+ 3 files changed, 47 insertions(+), 22 deletions(-)
+
+--- a/fs/xfs/libxfs/xfs_alloc.c
++++ b/fs/xfs/libxfs/xfs_alloc.c
+@@ -2511,6 +2511,7 @@ xfs_defer_agfl_block(
+
+ trace_xfs_agfl_free_defer(mp, agno, 0, agbno, 1);
+
++ xfs_extent_free_get_group(mp, xefi);
+ xfs_defer_add(tp, XFS_DEFER_OPS_TYPE_AGFL_FREE, &xefi->xefi_list);
+ return 0;
+ }
+@@ -2529,8 +2530,8 @@ __xfs_free_extent_later(
+ bool skip_discard)
+ {
+ struct xfs_extent_free_item *xefi;
+-#ifdef DEBUG
+ struct xfs_mount *mp = tp->t_mountp;
++#ifdef DEBUG
+ xfs_agnumber_t agno;
+ xfs_agblock_t agbno;
+
+@@ -2569,9 +2570,11 @@ __xfs_free_extent_later(
+ } else {
+ xefi->xefi_owner = XFS_RMAP_OWN_NULL;
+ }
+- trace_xfs_bmap_free_defer(tp->t_mountp,
++ trace_xfs_bmap_free_defer(mp,
+ XFS_FSB_TO_AGNO(tp->t_mountp, bno), 0,
+ XFS_FSB_TO_AGBNO(tp->t_mountp, bno), len);
++
++ xfs_extent_free_get_group(mp, xefi);
+ xfs_defer_add(tp, XFS_DEFER_OPS_TYPE_FREE, &xefi->xefi_list);
+ return 0;
+ }
+--- a/fs/xfs/libxfs/xfs_alloc.h
++++ b/fs/xfs/libxfs/xfs_alloc.h
+@@ -226,10 +226,14 @@ struct xfs_extent_free_item {
+ uint64_t xefi_owner;
+ xfs_fsblock_t xefi_startblock;/* starting fs block number */
+ xfs_extlen_t xefi_blockcount;/* number of blocks in extent */
++ struct xfs_perag *xefi_pag;
+ unsigned int xefi_flags;
+ enum xfs_ag_resv_type xefi_agresv;
+ };
+
++void xfs_extent_free_get_group(struct xfs_mount *mp,
++ struct xfs_extent_free_item *xefi);
++
+ #define XFS_EFI_SKIP_DISCARD (1U << 0) /* don't issue discard */
+ #define XFS_EFI_ATTR_FORK (1U << 1) /* freeing attr fork block */
+ #define XFS_EFI_BMBT_BLOCK (1U << 2) /* freeing bmap btree block */
+--- a/fs/xfs/xfs_extfree_item.c
++++ b/fs/xfs/xfs_extfree_item.c
+@@ -350,10 +350,7 @@ xfs_trans_free_extent(
+ struct xfs_owner_info oinfo = { };
+ struct xfs_mount *mp = tp->t_mountp;
+ struct xfs_extent *extp;
+- struct xfs_perag *pag;
+ uint next_extent;
+- xfs_agnumber_t agno = XFS_FSB_TO_AGNO(mp,
+- xefi->xefi_startblock);
+ xfs_agblock_t agbno = XFS_FSB_TO_AGBNO(mp,
+ xefi->xefi_startblock);
+ int error;
+@@ -364,14 +361,12 @@ xfs_trans_free_extent(
+ if (xefi->xefi_flags & XFS_EFI_BMBT_BLOCK)
+ oinfo.oi_flags |= XFS_OWNER_INFO_BMBT_BLOCK;
+
+- trace_xfs_bmap_free_deferred(tp->t_mountp, agno, 0, agbno,
+- xefi->xefi_blockcount);
++ trace_xfs_bmap_free_deferred(tp->t_mountp, xefi->xefi_pag->pag_agno, 0,
++ agbno, xefi->xefi_blockcount);
+
+- pag = xfs_perag_get(mp, agno);
+- error = __xfs_free_extent(tp, pag, agbno, xefi->xefi_blockcount,
+- &oinfo, xefi->xefi_agresv,
++ error = __xfs_free_extent(tp, xefi->xefi_pag, agbno,
++ xefi->xefi_blockcount, &oinfo, xefi->xefi_agresv,
+ xefi->xefi_flags & XFS_EFI_SKIP_DISCARD);
+- xfs_perag_put(pag);
+
+ /*
+ * Mark the transaction dirty, even on error. This ensures the
+@@ -400,14 +395,13 @@ xfs_extent_free_diff_items(
+ const struct list_head *a,
+ const struct list_head *b)
+ {
+- struct xfs_mount *mp = priv;
+ struct xfs_extent_free_item *ra;
+ struct xfs_extent_free_item *rb;
+
+ ra = container_of(a, struct xfs_extent_free_item, xefi_list);
+ rb = container_of(b, struct xfs_extent_free_item, xefi_list);
+- return XFS_FSB_TO_AGNO(mp, ra->xefi_startblock) -
+- XFS_FSB_TO_AGNO(mp, rb->xefi_startblock);
++
++ return ra->xefi_pag->pag_agno - rb->xefi_pag->pag_agno;
+ }
+
+ /* Log a free extent to the intent item. */
+@@ -466,6 +460,26 @@ xfs_extent_free_create_done(
+ return &xfs_trans_get_efd(tp, EFI_ITEM(intent), count)->efd_item;
+ }
+
++/* Take a passive ref to the AG containing the space we're freeing. */
++void
++xfs_extent_free_get_group(
++ struct xfs_mount *mp,
++ struct xfs_extent_free_item *xefi)
++{
++ xfs_agnumber_t agno;
++
++ agno = XFS_FSB_TO_AGNO(mp, xefi->xefi_startblock);
++ xefi->xefi_pag = xfs_perag_get(mp, agno);
++}
++
++/* Release a passive AG ref after some freeing work. */
++static inline void
++xfs_extent_free_put_group(
++ struct xfs_extent_free_item *xefi)
++{
++ xfs_perag_put(xefi->xefi_pag);
++}
++
+ /* Process a free extent. */
+ STATIC int
+ xfs_extent_free_finish_item(
+@@ -480,6 +494,8 @@ xfs_extent_free_finish_item(
+ xefi = container_of(item, struct xfs_extent_free_item, xefi_list);
+
+ error = xfs_trans_free_extent(tp, EFD_ITEM(done), xefi);
++
++ xfs_extent_free_put_group(xefi);
+ kmem_cache_free(xfs_extfree_item_cache, xefi);
+ return error;
+ }
+@@ -500,6 +516,8 @@ xfs_extent_free_cancel_item(
+ struct xfs_extent_free_item *xefi;
+
+ xefi = container_of(item, struct xfs_extent_free_item, xefi_list);
++
++ xfs_extent_free_put_group(xefi);
+ kmem_cache_free(xfs_extfree_item_cache, xefi);
+ }
+
+@@ -530,24 +548,21 @@ xfs_agfl_free_finish_item(
+ struct xfs_extent *extp;
+ struct xfs_buf *agbp;
+ int error;
+- xfs_agnumber_t agno;
+ xfs_agblock_t agbno;
+ uint next_extent;
+- struct xfs_perag *pag;
+
+ xefi = container_of(item, struct xfs_extent_free_item, xefi_list);
+ ASSERT(xefi->xefi_blockcount == 1);
+- agno = XFS_FSB_TO_AGNO(mp, xefi->xefi_startblock);
+ agbno = XFS_FSB_TO_AGBNO(mp, xefi->xefi_startblock);
+ oinfo.oi_owner = xefi->xefi_owner;
+
+- trace_xfs_agfl_free_deferred(mp, agno, 0, agbno, xefi->xefi_blockcount);
++ trace_xfs_agfl_free_deferred(mp, xefi->xefi_pag->pag_agno, 0, agbno,
++ xefi->xefi_blockcount);
+
+- pag = xfs_perag_get(mp, agno);
+- error = xfs_alloc_read_agf(pag, tp, 0, &agbp);
++ error = xfs_alloc_read_agf(xefi->xefi_pag, tp, 0, &agbp);
+ if (!error)
+- error = xfs_free_agfl_block(tp, agno, agbno, agbp, &oinfo);
+- xfs_perag_put(pag);
++ error = xfs_free_agfl_block(tp, xefi->xefi_pag->pag_agno,
++ agbno, agbp, &oinfo);
+
+ /*
+ * Mark the transaction dirty, even on error. This ensures the
+@@ -566,6 +581,7 @@ xfs_agfl_free_finish_item(
+ extp->ext_len = xefi->xefi_blockcount;
+ efdp->efd_next_extent++;
+
++ xfs_extent_free_put_group(xefi);
+ kmem_cache_free(xfs_extfree_item_cache, xefi);
+ return error;
+ }
+@@ -639,7 +655,9 @@ xfs_efi_item_recover(
+ fake.xefi_startblock = extp->ext_start;
+ fake.xefi_blockcount = extp->ext_len;
+
++ xfs_extent_free_get_group(mp, &fake);
+ error = xfs_trans_free_extent(tp, efdp, &fake);
++ xfs_extent_free_put_group(&fake);
+ if (error == -EFSCORRUPTED)
+ XFS_CORRUPTION_ERROR(__func__, XFS_ERRLEVEL_LOW, mp,
+ extp, sizeof(*extp));