]> git.ipfire.org Git - thirdparty/kernel/stable-queue.git/commitdiff
6.6-stable patches
authorGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Fri, 29 Mar 2024 09:55:04 +0000 (10:55 +0100)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Fri, 29 Mar 2024 09:55:04 +0000 (10:55 +0100)
added patches:
xfs-add-lock-protection-when-remove-perag-from-radix-tree.patch
xfs-add-missing-nrext64-inode-flag-check-to-scrub.patch
xfs-consider-minlen-sized-extents-in-xfs_rtallocate_extent_block.patch
xfs-convert-rt-bitmap-extent-lengths-to-xfs_rtbxlen_t.patch
xfs-don-t-allow-overly-small-or-large-realtime-volumes.patch
xfs-don-t-leak-recovered-attri-intent-items.patch
xfs-ensure-logflagsp-is-initialized-in-xfs_bmap_del_extent_real.patch
xfs-fix-32-bit-truncation-in-xfs_compute_rextslog.patch
xfs-fix-an-off-by-one-error-in-xreap_agextent_binval.patch
xfs-fix-perag-leak-when-growfs-fails.patch
xfs-force-all-buffers-to-be-written-during-btree-bulk-load.patch
xfs-initialise-di_crc-in-xfs_log_dinode.patch
xfs-make-rextslog-computation-consistent-with-mkfs.patch
xfs-make-xchk_iget-safer-in-the-presence-of-corrupt-inode-btrees.patch
xfs-move-the-xfs_rtbitmap.c-declarations-to-xfs_rtbitmap.h.patch
xfs-pass-the-xfs_defer_pending-object-to-iop_recover.patch
xfs-recompute-growfsrtfree-transaction-reservation-while-growing-rt-volume.patch
xfs-remove-conditional-building-of-rt-geometry-validator-functions.patch
xfs-remove-unused-fields-from-struct-xbtree_ifakeroot.patch
xfs-reset-xfs_attr_incomplete-filter-on-node-removal.patch
xfs-short-circuit-xfs_growfs_data_private-if-delta-is-zero.patch
xfs-transfer-recovered-intent-item-ownership-in-iop_recover.patch
xfs-update-dir3-leaf-block-metadata-after-swap.patch
xfs-use-xfs_defer_pending-objects-to-recover-intent-items.patch

25 files changed:
queue-6.6/series
queue-6.6/xfs-add-lock-protection-when-remove-perag-from-radix-tree.patch [new file with mode: 0644]
queue-6.6/xfs-add-missing-nrext64-inode-flag-check-to-scrub.patch [new file with mode: 0644]
queue-6.6/xfs-consider-minlen-sized-extents-in-xfs_rtallocate_extent_block.patch [new file with mode: 0644]
queue-6.6/xfs-convert-rt-bitmap-extent-lengths-to-xfs_rtbxlen_t.patch [new file with mode: 0644]
queue-6.6/xfs-don-t-allow-overly-small-or-large-realtime-volumes.patch [new file with mode: 0644]
queue-6.6/xfs-don-t-leak-recovered-attri-intent-items.patch [new file with mode: 0644]
queue-6.6/xfs-ensure-logflagsp-is-initialized-in-xfs_bmap_del_extent_real.patch [new file with mode: 0644]
queue-6.6/xfs-fix-32-bit-truncation-in-xfs_compute_rextslog.patch [new file with mode: 0644]
queue-6.6/xfs-fix-an-off-by-one-error-in-xreap_agextent_binval.patch [new file with mode: 0644]
queue-6.6/xfs-fix-perag-leak-when-growfs-fails.patch [new file with mode: 0644]
queue-6.6/xfs-force-all-buffers-to-be-written-during-btree-bulk-load.patch [new file with mode: 0644]
queue-6.6/xfs-initialise-di_crc-in-xfs_log_dinode.patch [new file with mode: 0644]
queue-6.6/xfs-make-rextslog-computation-consistent-with-mkfs.patch [new file with mode: 0644]
queue-6.6/xfs-make-xchk_iget-safer-in-the-presence-of-corrupt-inode-btrees.patch [new file with mode: 0644]
queue-6.6/xfs-move-the-xfs_rtbitmap.c-declarations-to-xfs_rtbitmap.h.patch [new file with mode: 0644]
queue-6.6/xfs-pass-the-xfs_defer_pending-object-to-iop_recover.patch [new file with mode: 0644]
queue-6.6/xfs-recompute-growfsrtfree-transaction-reservation-while-growing-rt-volume.patch [new file with mode: 0644]
queue-6.6/xfs-remove-conditional-building-of-rt-geometry-validator-functions.patch [new file with mode: 0644]
queue-6.6/xfs-remove-unused-fields-from-struct-xbtree_ifakeroot.patch [new file with mode: 0644]
queue-6.6/xfs-reset-xfs_attr_incomplete-filter-on-node-removal.patch [new file with mode: 0644]
queue-6.6/xfs-short-circuit-xfs_growfs_data_private-if-delta-is-zero.patch [new file with mode: 0644]
queue-6.6/xfs-transfer-recovered-intent-item-ownership-in-iop_recover.patch [new file with mode: 0644]
queue-6.6/xfs-update-dir3-leaf-block-metadata-after-swap.patch [new file with mode: 0644]
queue-6.6/xfs-use-xfs_defer_pending-objects-to-recover-intent-items.patch [new file with mode: 0644]

index 40b8e88f496101bb5504426a285510ef071e4905..3f29ab9d9cbb18279bbedcb2f5315bd878a6c7c2 100644 (file)
@@ -250,3 +250,27 @@ x86-efistub-call-mixed-mode-boot-services-on-the-firmware-s-stack.patch
 asoc-amd-yc-revert-fix-non-functional-mic-on-lenovo-21j2.patch
 wifi-iwlwifi-pcie-fix-rb-status-reading.patch
 wifi-rtw88-8821cu-fix-connection-failure.patch
+xfs-move-the-xfs_rtbitmap.c-declarations-to-xfs_rtbitmap.h.patch
+xfs-convert-rt-bitmap-extent-lengths-to-xfs_rtbxlen_t.patch
+xfs-consider-minlen-sized-extents-in-xfs_rtallocate_extent_block.patch
+xfs-don-t-leak-recovered-attri-intent-items.patch
+xfs-use-xfs_defer_pending-objects-to-recover-intent-items.patch
+xfs-pass-the-xfs_defer_pending-object-to-iop_recover.patch
+xfs-transfer-recovered-intent-item-ownership-in-iop_recover.patch
+xfs-make-rextslog-computation-consistent-with-mkfs.patch
+xfs-fix-32-bit-truncation-in-xfs_compute_rextslog.patch
+xfs-don-t-allow-overly-small-or-large-realtime-volumes.patch
+xfs-make-xchk_iget-safer-in-the-presence-of-corrupt-inode-btrees.patch
+xfs-remove-unused-fields-from-struct-xbtree_ifakeroot.patch
+xfs-recompute-growfsrtfree-transaction-reservation-while-growing-rt-volume.patch
+xfs-fix-an-off-by-one-error-in-xreap_agextent_binval.patch
+xfs-force-all-buffers-to-be-written-during-btree-bulk-load.patch
+xfs-add-missing-nrext64-inode-flag-check-to-scrub.patch
+xfs-initialise-di_crc-in-xfs_log_dinode.patch
+xfs-short-circuit-xfs_growfs_data_private-if-delta-is-zero.patch
+xfs-add-lock-protection-when-remove-perag-from-radix-tree.patch
+xfs-fix-perag-leak-when-growfs-fails.patch
+xfs-ensure-logflagsp-is-initialized-in-xfs_bmap_del_extent_real.patch
+xfs-update-dir3-leaf-block-metadata-after-swap.patch
+xfs-reset-xfs_attr_incomplete-filter-on-node-removal.patch
+xfs-remove-conditional-building-of-rt-geometry-validator-functions.patch
diff --git a/queue-6.6/xfs-add-lock-protection-when-remove-perag-from-radix-tree.patch b/queue-6.6/xfs-add-lock-protection-when-remove-perag-from-radix-tree.patch
new file mode 100644 (file)
index 0000000..83696f1
--- /dev/null
@@ -0,0 +1,64 @@
+From stable+bounces-32428-greg=kroah.com@vger.kernel.org Wed Mar 27 01:13:50 2024
+From: Catherine Hoang <catherine.hoang@oracle.com>
+Date: Tue, 26 Mar 2024 17:12:28 -0700
+Subject: xfs: add lock protection when remove perag from radix tree
+To: stable@vger.kernel.org
+Cc: linux-xfs@vger.kernel.org
+Message-ID: <20240327001233.51675-20-catherine.hoang@oracle.com>
+
+From: Long Li <leo.lilong@huawei.com>
+
+commit 07afd3173d0c6d24a47441839a835955ec6cf0d4 upstream.
+
+Take mp->m_perag_lock for deletions from the perag radix tree in
+xfs_initialize_perag to prevent racing with tagging operations.
+Lookups are fine - they are RCU protected so already deal with the
+tree changing shape underneath the lookup - but tagging operations
+require the tree to be stable while the tags are propagated back up
+to the root.
+
+Right now there's nothing stopping radix tree tagging from operating
+while a growfs operation is progress and adding/removing new entries
+into the radix tree.
+
+Hence we can have traversals that require a stable tree occurring at
+the same time we are removing unused entries from the radix tree which
+causes the shape of the tree to change.
+
+Likely this hasn't caused a problem in the past because we are only
+doing append addition and removal so the active AG part of the tree
+is not changing shape, but that doesn't mean it is safe. Just making
+the radix tree modifications serialise against each other is obviously
+correct.
+
+Signed-off-by: Long Li <leo.lilong@huawei.com>
+Reviewed-by: Christoph Hellwig <hch@lst.de>
+Reviewed-by: "Darrick J. Wong" <djwong@kernel.org>
+Signed-off-by: Chandan Babu R <chandanbabu@kernel.org>
+Signed-off-by: Catherine Hoang <catherine.hoang@oracle.com>
+Acked-by: Darrick J. Wong <djwong@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/xfs/libxfs/xfs_ag.c |    4 ++++
+ 1 file changed, 4 insertions(+)
+
+--- a/fs/xfs/libxfs/xfs_ag.c
++++ b/fs/xfs/libxfs/xfs_ag.c
+@@ -424,13 +424,17 @@ xfs_initialize_perag(
+ out_remove_pag:
+       xfs_defer_drain_free(&pag->pag_intents_drain);
++      spin_lock(&mp->m_perag_lock);
+       radix_tree_delete(&mp->m_perag_tree, index);
++      spin_unlock(&mp->m_perag_lock);
+ out_free_pag:
+       kmem_free(pag);
+ out_unwind_new_pags:
+       /* unwind any prior newly initialized pags */
+       for (index = first_initialised; index < agcount; index++) {
++              spin_lock(&mp->m_perag_lock);
+               pag = radix_tree_delete(&mp->m_perag_tree, index);
++              spin_unlock(&mp->m_perag_lock);
+               if (!pag)
+                       break;
+               xfs_buf_hash_destroy(pag);
diff --git a/queue-6.6/xfs-add-missing-nrext64-inode-flag-check-to-scrub.patch b/queue-6.6/xfs-add-missing-nrext64-inode-flag-check-to-scrub.patch
new file mode 100644 (file)
index 0000000..f544718
--- /dev/null
@@ -0,0 +1,38 @@
+From stable+bounces-32424-greg=kroah.com@vger.kernel.org Wed Mar 27 01:13:40 2024
+From: Catherine Hoang <catherine.hoang@oracle.com>
+Date: Tue, 26 Mar 2024 17:12:25 -0700
+Subject: xfs: add missing nrext64 inode flag check to scrub
+To: stable@vger.kernel.org
+Cc: linux-xfs@vger.kernel.org
+Message-ID: <20240327001233.51675-17-catherine.hoang@oracle.com>
+
+From: "Darrick J. Wong" <djwong@kernel.org>
+
+commit 576d30ecb620ae3bc156dfb2a4e91143e7f3256d upstream.
+
+Add this missing check that the superblock nrext64 flag is set if the
+inode flag is set.
+
+Fixes: 9b7d16e34bbeb ("xfs: Introduce XFS_DIFLAG2_NREXT64 and associated helpers")
+Signed-off-by: Darrick J. Wong <djwong@kernel.org>
+Reviewed-by: Christoph Hellwig <hch@lst.de>
+Signed-off-by: Catherine Hoang <catherine.hoang@oracle.com>
+Acked-by: Darrick J. Wong <djwong@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/xfs/scrub/inode.c |    4 ++++
+ 1 file changed, 4 insertions(+)
+
+--- a/fs/xfs/scrub/inode.c
++++ b/fs/xfs/scrub/inode.c
+@@ -337,6 +337,10 @@ xchk_inode_flags2(
+       if (xfs_dinode_has_bigtime(dip) && !xfs_has_bigtime(mp))
+               goto bad;
++      /* no large extent counts without the filesystem feature */
++      if ((flags2 & XFS_DIFLAG2_NREXT64) && !xfs_has_large_extent_counts(mp))
++              goto bad;
++
+       return;
+ bad:
+       xchk_ino_set_corrupt(sc, ino);
diff --git a/queue-6.6/xfs-consider-minlen-sized-extents-in-xfs_rtallocate_extent_block.patch b/queue-6.6/xfs-consider-minlen-sized-extents-in-xfs_rtallocate_extent_block.patch
new file mode 100644 (file)
index 0000000..f683108
--- /dev/null
@@ -0,0 +1,41 @@
+From stable+bounces-32412-greg=kroah.com@vger.kernel.org Wed Mar 27 01:13:03 2024
+From: Catherine Hoang <catherine.hoang@oracle.com>
+Date: Tue, 26 Mar 2024 17:12:12 -0700
+Subject: xfs: consider minlen sized extents in xfs_rtallocate_extent_block
+To: stable@vger.kernel.org
+Cc: linux-xfs@vger.kernel.org
+Message-ID: <20240327001233.51675-4-catherine.hoang@oracle.com>
+
+From: Christoph Hellwig <hch@lst.de>
+
+commit 944df75958807d56f2db9fdc769eb15dd9f0366a upstream.
+
+[backport: resolve merge conflict due to missing xfs_rtxlen_t type]
+
+minlen is the lower bound on the extent length that the caller can
+accept, and maxlen is at this point the maximal available length.
+This means a minlen extent is perfectly fine to use, so do it.  This
+matches the equivalent logic in xfs_rtallocate_extent_exact that also
+accepts a minlen sized extent.
+
+Signed-off-by: Christoph Hellwig <hch@lst.de>
+Reviewed-by: "Darrick J. Wong" <djwong@kernel.org>
+Signed-off-by: Chandan Babu R <chandanbabu@kernel.org>
+Signed-off-by: Catherine Hoang <catherine.hoang@oracle.com>
+Acked-by: Darrick J. Wong <djwong@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/xfs/xfs_rtalloc.c |    2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/fs/xfs/xfs_rtalloc.c
++++ b/fs/xfs/xfs_rtalloc.c
+@@ -318,7 +318,7 @@ xfs_rtallocate_extent_block(
+       /*
+        * Searched the whole thing & didn't find a maxlen free extent.
+        */
+-      if (minlen < maxlen && besti != -1) {
++      if (minlen <= maxlen && besti != -1) {
+               xfs_extlen_t    p;      /* amount to trim length by */
+               /*
diff --git a/queue-6.6/xfs-convert-rt-bitmap-extent-lengths-to-xfs_rtbxlen_t.patch b/queue-6.6/xfs-convert-rt-bitmap-extent-lengths-to-xfs_rtbxlen_t.patch
new file mode 100644 (file)
index 0000000..762b32d
--- /dev/null
@@ -0,0 +1,79 @@
+From stable+bounces-32411-greg=kroah.com@vger.kernel.org Wed Mar 27 01:13:01 2024
+From: Catherine Hoang <catherine.hoang@oracle.com>
+Date: Tue, 26 Mar 2024 17:12:11 -0700
+Subject: xfs: convert rt bitmap extent lengths to xfs_rtbxlen_t
+To: stable@vger.kernel.org
+Cc: linux-xfs@vger.kernel.org
+Message-ID: <20240327001233.51675-3-catherine.hoang@oracle.com>
+
+From: "Darrick J. Wong" <djwong@kernel.org>
+
+commit f29c3e745dc253bf9d9d06ddc36af1a534ba1dd0 upstream.
+
+XFS uses xfs_rtblock_t for many different uses, which makes it much more
+difficult to perform a unit analysis on the codebase.  One of these
+(ab)uses is when we need to store the length of a free space extent as
+stored in the realtime bitmap.  Because there can be up to 2^64 realtime
+extents in a filesystem, we need a new type that is larger than
+xfs_rtxlen_t for callers that are querying the bitmap directly.  This
+means scrub and growfs.
+
+Create this type as "xfs_rtbxlen_t" and use it to store 64-bit rtx
+lengths.  'b' stands for 'bitmap' or 'big'; reader's choice.
+
+Signed-off-by: Darrick J. Wong <djwong@kernel.org>
+Reviewed-by: Christoph Hellwig <hch@lst.de>
+Signed-off-by: Catherine Hoang <catherine.hoang@oracle.com>
+Acked-by: Darrick J. Wong <djwong@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/xfs/libxfs/xfs_format.h   |    2 +-
+ fs/xfs/libxfs/xfs_rtbitmap.h |    2 +-
+ fs/xfs/libxfs/xfs_types.h    |    1 +
+ fs/xfs/scrub/trace.h         |    3 ++-
+ 4 files changed, 5 insertions(+), 3 deletions(-)
+
+--- a/fs/xfs/libxfs/xfs_format.h
++++ b/fs/xfs/libxfs/xfs_format.h
+@@ -98,7 +98,7 @@ typedef struct xfs_sb {
+       uint32_t        sb_blocksize;   /* logical block size, bytes */
+       xfs_rfsblock_t  sb_dblocks;     /* number of data blocks */
+       xfs_rfsblock_t  sb_rblocks;     /* number of realtime blocks */
+-      xfs_rtblock_t   sb_rextents;    /* number of realtime extents */
++      xfs_rtbxlen_t   sb_rextents;    /* number of realtime extents */
+       uuid_t          sb_uuid;        /* user-visible file system unique id */
+       xfs_fsblock_t   sb_logstart;    /* starting block of log if internal */
+       xfs_ino_t       sb_rootino;     /* root inode number */
+--- a/fs/xfs/libxfs/xfs_rtbitmap.h
++++ b/fs/xfs/libxfs/xfs_rtbitmap.h
+@@ -13,7 +13,7 @@
+  */
+ struct xfs_rtalloc_rec {
+       xfs_rtblock_t           ar_startext;
+-      xfs_rtblock_t           ar_extcount;
++      xfs_rtbxlen_t           ar_extcount;
+ };
+ typedef int (*xfs_rtalloc_query_range_fn)(
+--- a/fs/xfs/libxfs/xfs_types.h
++++ b/fs/xfs/libxfs/xfs_types.h
+@@ -31,6 +31,7 @@ typedef uint64_t     xfs_rfsblock_t; /* bloc
+ typedef uint64_t      xfs_rtblock_t;  /* extent (block) in realtime area */
+ typedef uint64_t      xfs_fileoff_t;  /* block number in a file */
+ typedef uint64_t      xfs_filblks_t;  /* number of blocks in a file */
++typedef uint64_t      xfs_rtbxlen_t;  /* rtbitmap extent length in rtextents */
+ typedef int64_t               xfs_srtblock_t; /* signed version of xfs_rtblock_t */
+--- a/fs/xfs/scrub/trace.h
++++ b/fs/xfs/scrub/trace.h
+@@ -1037,7 +1037,8 @@ TRACE_EVENT(xfarray_sort_stats,
+ #ifdef CONFIG_XFS_RT
+ TRACE_EVENT(xchk_rtsum_record_free,
+       TP_PROTO(struct xfs_mount *mp, xfs_rtblock_t start,
+-               uint64_t len, unsigned int log, loff_t pos, xfs_suminfo_t v),
++               xfs_rtbxlen_t len, unsigned int log, loff_t pos,
++               xfs_suminfo_t v),
+       TP_ARGS(mp, start, len, log, pos, v),
+       TP_STRUCT__entry(
+               __field(dev_t, dev)
diff --git a/queue-6.6/xfs-don-t-allow-overly-small-or-large-realtime-volumes.patch b/queue-6.6/xfs-don-t-allow-overly-small-or-large-realtime-volumes.patch
new file mode 100644 (file)
index 0000000..96652db
--- /dev/null
@@ -0,0 +1,82 @@
+From stable+bounces-32419-greg=kroah.com@vger.kernel.org Wed Mar 27 01:13:24 2024
+From: Catherine Hoang <catherine.hoang@oracle.com>
+Date: Tue, 26 Mar 2024 17:12:19 -0700
+Subject: xfs: don't allow overly small or large realtime volumes
+To: stable@vger.kernel.org
+Cc: linux-xfs@vger.kernel.org
+Message-ID: <20240327001233.51675-11-catherine.hoang@oracle.com>
+
+From: "Darrick J. Wong" <djwong@kernel.org>
+
+commit e14293803f4e84eb23a417b462b56251033b5a66 upstream.
+
+[backport: resolve merge conflicts due to refactoring rtbitmap/summary
+macros and accessors]
+
+Don't allow realtime volumes that are less than one rt extent long.
+This has been broken across 4 LTS kernels with nobody noticing, so let's
+just disable it.
+
+Signed-off-by: Darrick J. Wong <djwong@kernel.org>
+Reviewed-by: Christoph Hellwig <hch@lst.de>
+Signed-off-by: Catherine Hoang <catherine.hoang@oracle.com>
+Acked-by: Darrick J. Wong <djwong@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/xfs/libxfs/xfs_rtbitmap.h |   13 +++++++++++++
+ fs/xfs/libxfs/xfs_sb.c       |    3 ++-
+ fs/xfs/xfs_rtalloc.c         |    2 ++
+ 3 files changed, 17 insertions(+), 1 deletion(-)
+
+--- a/fs/xfs/libxfs/xfs_rtbitmap.h
++++ b/fs/xfs/libxfs/xfs_rtbitmap.h
+@@ -73,6 +73,18 @@ int xfs_rtfree_blocks(struct xfs_trans *
+ uint8_t xfs_compute_rextslog(xfs_rtbxlen_t rtextents);
++/* Do we support an rt volume having this number of rtextents? */
++static inline bool
++xfs_validate_rtextents(
++      xfs_rtbxlen_t           rtextents)
++{
++      /* No runt rt volumes */
++      if (rtextents == 0)
++              return false;
++
++      return true;
++}
++
+ #else /* CONFIG_XFS_RT */
+ # define xfs_rtfree_extent(t,b,l)                     (-ENOSYS)
+ # define xfs_rtfree_blocks(t,rb,rl)                   (-ENOSYS)
+@@ -81,6 +93,7 @@ uint8_t xfs_compute_rextslog(xfs_rtbxlen
+ # define xfs_rtbuf_get(m,t,b,i,p)                     (-ENOSYS)
+ # define xfs_rtalloc_extent_is_free(m,t,s,l,i)                (-ENOSYS)
+ # define xfs_compute_rextslog(rtx)                    (0)
++# define xfs_validate_rtextents(rtx)                  (false)
+ #endif /* CONFIG_XFS_RT */
+ #endif /* __XFS_RTBITMAP_H__ */
+--- a/fs/xfs/libxfs/xfs_sb.c
++++ b/fs/xfs/libxfs/xfs_sb.c
+@@ -509,7 +509,8 @@ xfs_validate_sb_common(
+               rbmblocks = howmany_64(sbp->sb_rextents,
+                                      NBBY * sbp->sb_blocksize);
+-              if (sbp->sb_rextents != rexts ||
++              if (!xfs_validate_rtextents(rexts) ||
++                  sbp->sb_rextents != rexts ||
+                   sbp->sb_rextslog != xfs_compute_rextslog(rexts) ||
+                   sbp->sb_rbmblocks != rbmblocks) {
+                       xfs_notice(mp,
+--- a/fs/xfs/xfs_rtalloc.c
++++ b/fs/xfs/xfs_rtalloc.c
+@@ -998,6 +998,8 @@ xfs_growfs_rt(
+        */
+       nrextents = nrblocks;
+       do_div(nrextents, in->extsize);
++      if (!xfs_validate_rtextents(nrextents))
++              return -EINVAL;
+       nrbmblocks = howmany_64(nrextents, NBBY * sbp->sb_blocksize);
+       nrextslog = xfs_compute_rextslog(nrextents);
+       nrsumlevels = nrextslog + 1;
diff --git a/queue-6.6/xfs-don-t-leak-recovered-attri-intent-items.patch b/queue-6.6/xfs-don-t-leak-recovered-attri-intent-items.patch
new file mode 100644 (file)
index 0000000..87fd201
--- /dev/null
@@ -0,0 +1,62 @@
+From stable+bounces-32413-greg=kroah.com@vger.kernel.org Wed Mar 27 01:13:10 2024
+From: Catherine Hoang <catherine.hoang@oracle.com>
+Date: Tue, 26 Mar 2024 17:12:13 -0700
+Subject: xfs: don't leak recovered attri intent items
+To: stable@vger.kernel.org
+Cc: linux-xfs@vger.kernel.org
+Message-ID: <20240327001233.51675-5-catherine.hoang@oracle.com>
+
+From: "Darrick J. Wong" <djwong@kernel.org>
+
+commit 07bcbdf020c9fd3c14bec51c50225a2a02707b94 upstream.
+
+If recovery finds an xattr log intent item calling for the removal of an
+attribute and the file doesn't even have an attr fork, we know that the
+removal is trivially complete.  However, we can't just exit the recovery
+function without doing something about the recovered log intent item --
+it's still on the AIL, and not logging an attrd item means it stays
+there forever.
+
+This has likely not been seen in practice because few people use LARP
+and the runtime code won't log the attri for a no-attrfork removexattr
+operation.  But let's fix this anyway.
+
+Also we shouldn't really be testing the attr fork presence until we've
+taken the ILOCK, though this doesn't matter much in recovery, which is
+single threaded.
+
+Fixes: fdaf1bb3cafc ("xfs: ATTR_REPLACE algorithm with LARP enabled needs rework")
+Signed-off-by: Darrick J. Wong <djwong@kernel.org>
+Reviewed-by: Christoph Hellwig <hch@lst.de>
+Signed-off-by: Catherine Hoang <catherine.hoang@oracle.com>
+Acked-by: Darrick J. Wong <djwong@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/xfs/xfs_attr_item.c |    9 +++++++--
+ 1 file changed, 7 insertions(+), 2 deletions(-)
+
+--- a/fs/xfs/xfs_attr_item.c
++++ b/fs/xfs/xfs_attr_item.c
+@@ -329,6 +329,13 @@ xfs_xattri_finish_update(
+               goto out;
+       }
++      /* If an attr removal is trivially complete, we're done. */
++      if (attr->xattri_op_flags == XFS_ATTRI_OP_FLAGS_REMOVE &&
++          !xfs_inode_hasattr(args->dp)) {
++              error = 0;
++              goto out;
++      }
++
+       error = xfs_attr_set_iter(attr);
+       if (!error && attr->xattri_dela_state != XFS_DAS_DONE)
+               error = -EAGAIN;
+@@ -608,8 +615,6 @@ xfs_attri_item_recover(
+                       attr->xattri_dela_state = xfs_attr_init_add_state(args);
+               break;
+       case XFS_ATTRI_OP_FLAGS_REMOVE:
+-              if (!xfs_inode_hasattr(args->dp))
+-                      goto out;
+               attr->xattri_dela_state = xfs_attr_init_remove_state(args);
+               break;
+       default:
diff --git a/queue-6.6/xfs-ensure-logflagsp-is-initialized-in-xfs_bmap_del_extent_real.patch b/queue-6.6/xfs-ensure-logflagsp-is-initialized-in-xfs_bmap_del_extent_real.patch
new file mode 100644 (file)
index 0000000..ad5e239
--- /dev/null
@@ -0,0 +1,229 @@
+From stable+bounces-32430-greg=kroah.com@vger.kernel.org Wed Mar 27 01:13:56 2024
+From: Catherine Hoang <catherine.hoang@oracle.com>
+Date: Tue, 26 Mar 2024 17:12:30 -0700
+Subject: xfs: ensure logflagsp is initialized in xfs_bmap_del_extent_real
+To: stable@vger.kernel.org
+Cc: linux-xfs@vger.kernel.org
+Message-ID: <20240327001233.51675-22-catherine.hoang@oracle.com>
+
+From: Jiachen Zhang <zhangjiachen.jaycee@bytedance.com>
+
+commit e6af9c98cbf0164a619d95572136bfb54d482dd6 upstream.
+
+In the case of returning -ENOSPC, ensure logflagsp is initialized by 0.
+Otherwise the caller __xfs_bunmapi will set uninitialized illegal
+tmp_logflags value into xfs log, which might cause unpredictable error
+in the log recovery procedure.
+
+Also, remove the flags variable and set the *logflagsp directly, so that
+the code should be more robust in the long run.
+
+Fixes: 1b24b633aafe ("xfs: move some more code into xfs_bmap_del_extent_real")
+Signed-off-by: Jiachen Zhang <zhangjiachen.jaycee@bytedance.com>
+Reviewed-by: Christoph Hellwig <hch@lst.de>
+Reviewed-by: "Darrick J. Wong" <djwong@kernel.org>
+Signed-off-by: Chandan Babu R <chandanbabu@kernel.org>
+Signed-off-by: Catherine Hoang <catherine.hoang@oracle.com>
+Acked-by: Darrick J. Wong <djwong@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/xfs/libxfs/xfs_bmap.c |   73 +++++++++++++++++++----------------------------
+ 1 file changed, 31 insertions(+), 42 deletions(-)
+
+--- a/fs/xfs/libxfs/xfs_bmap.c
++++ b/fs/xfs/libxfs/xfs_bmap.c
+@@ -5014,7 +5014,6 @@ xfs_bmap_del_extent_real(
+       xfs_fileoff_t           del_endoff;     /* first offset past del */
+       int                     do_fx;  /* free extent at end of routine */
+       int                     error;  /* error return value */
+-      int                     flags = 0;/* inode logging flags */
+       struct xfs_bmbt_irec    got;    /* current extent entry */
+       xfs_fileoff_t           got_endoff;     /* first offset past got */
+       int                     i;      /* temp state */
+@@ -5027,6 +5026,8 @@ xfs_bmap_del_extent_real(
+       uint32_t                state = xfs_bmap_fork_to_state(whichfork);
+       struct xfs_bmbt_irec    old;
++      *logflagsp = 0;
++
+       mp = ip->i_mount;
+       XFS_STATS_INC(mp, xs_del_exlist);
+@@ -5039,7 +5040,6 @@ xfs_bmap_del_extent_real(
+       ASSERT(got_endoff >= del_endoff);
+       ASSERT(!isnullstartblock(got.br_startblock));
+       qfield = 0;
+-      error = 0;
+       /*
+        * If it's the case where the directory code is running with no block
+@@ -5055,13 +5055,13 @@ xfs_bmap_del_extent_real(
+           del->br_startoff > got.br_startoff && del_endoff < got_endoff)
+               return -ENOSPC;
+-      flags = XFS_ILOG_CORE;
++      *logflagsp = XFS_ILOG_CORE;
+       if (whichfork == XFS_DATA_FORK && XFS_IS_REALTIME_INODE(ip)) {
+               if (!(bflags & XFS_BMAPI_REMAP)) {
+                       error = xfs_rtfree_blocks(tp, del->br_startblock,
+                                       del->br_blockcount);
+                       if (error)
+-                              goto done;
++                              return error;
+               }
+               do_fx = 0;
+@@ -5076,11 +5076,9 @@ xfs_bmap_del_extent_real(
+       if (cur) {
+               error = xfs_bmbt_lookup_eq(cur, &got, &i);
+               if (error)
+-                      goto done;
+-              if (XFS_IS_CORRUPT(mp, i != 1)) {
+-                      error = -EFSCORRUPTED;
+-                      goto done;
+-              }
++                      return error;
++              if (XFS_IS_CORRUPT(mp, i != 1))
++                      return -EFSCORRUPTED;
+       }
+       if (got.br_startoff == del->br_startoff)
+@@ -5097,17 +5095,15 @@ xfs_bmap_del_extent_real(
+               xfs_iext_prev(ifp, icur);
+               ifp->if_nextents--;
+-              flags |= XFS_ILOG_CORE;
++              *logflagsp |= XFS_ILOG_CORE;
+               if (!cur) {
+-                      flags |= xfs_ilog_fext(whichfork);
++                      *logflagsp |= xfs_ilog_fext(whichfork);
+                       break;
+               }
+               if ((error = xfs_btree_delete(cur, &i)))
+-                      goto done;
+-              if (XFS_IS_CORRUPT(mp, i != 1)) {
+-                      error = -EFSCORRUPTED;
+-                      goto done;
+-              }
++                      return error;
++              if (XFS_IS_CORRUPT(mp, i != 1))
++                      return -EFSCORRUPTED;
+               break;
+       case BMAP_LEFT_FILLING:
+               /*
+@@ -5118,12 +5114,12 @@ xfs_bmap_del_extent_real(
+               got.br_blockcount -= del->br_blockcount;
+               xfs_iext_update_extent(ip, state, icur, &got);
+               if (!cur) {
+-                      flags |= xfs_ilog_fext(whichfork);
++                      *logflagsp |= xfs_ilog_fext(whichfork);
+                       break;
+               }
+               error = xfs_bmbt_update(cur, &got);
+               if (error)
+-                      goto done;
++                      return error;
+               break;
+       case BMAP_RIGHT_FILLING:
+               /*
+@@ -5132,12 +5128,12 @@ xfs_bmap_del_extent_real(
+               got.br_blockcount -= del->br_blockcount;
+               xfs_iext_update_extent(ip, state, icur, &got);
+               if (!cur) {
+-                      flags |= xfs_ilog_fext(whichfork);
++                      *logflagsp |= xfs_ilog_fext(whichfork);
+                       break;
+               }
+               error = xfs_bmbt_update(cur, &got);
+               if (error)
+-                      goto done;
++                      return error;
+               break;
+       case 0:
+               /*
+@@ -5154,18 +5150,18 @@ xfs_bmap_del_extent_real(
+               new.br_state = got.br_state;
+               new.br_startblock = del_endblock;
+-              flags |= XFS_ILOG_CORE;
++              *logflagsp |= XFS_ILOG_CORE;
+               if (cur) {
+                       error = xfs_bmbt_update(cur, &got);
+                       if (error)
+-                              goto done;
++                              return error;
+                       error = xfs_btree_increment(cur, 0, &i);
+                       if (error)
+-                              goto done;
++                              return error;
+                       cur->bc_rec.b = new;
+                       error = xfs_btree_insert(cur, &i);
+                       if (error && error != -ENOSPC)
+-                              goto done;
++                              return error;
+                       /*
+                        * If get no-space back from btree insert, it tried a
+                        * split, and we have a zero block reservation.  Fix up
+@@ -5178,33 +5174,28 @@ xfs_bmap_del_extent_real(
+                                */
+                               error = xfs_bmbt_lookup_eq(cur, &got, &i);
+                               if (error)
+-                                      goto done;
+-                              if (XFS_IS_CORRUPT(mp, i != 1)) {
+-                                      error = -EFSCORRUPTED;
+-                                      goto done;
+-                              }
++                                      return error;
++                              if (XFS_IS_CORRUPT(mp, i != 1))
++                                      return -EFSCORRUPTED;
+                               /*
+                                * Update the btree record back
+                                * to the original value.
+                                */
+                               error = xfs_bmbt_update(cur, &old);
+                               if (error)
+-                                      goto done;
++                                      return error;
+                               /*
+                                * Reset the extent record back
+                                * to the original value.
+                                */
+                               xfs_iext_update_extent(ip, state, icur, &old);
+-                              flags = 0;
+-                              error = -ENOSPC;
+-                              goto done;
+-                      }
+-                      if (XFS_IS_CORRUPT(mp, i != 1)) {
+-                              error = -EFSCORRUPTED;
+-                              goto done;
++                              *logflagsp = 0;
++                              return -ENOSPC;
+                       }
++                      if (XFS_IS_CORRUPT(mp, i != 1))
++                              return -EFSCORRUPTED;
+               } else
+-                      flags |= xfs_ilog_fext(whichfork);
++                      *logflagsp |= xfs_ilog_fext(whichfork);
+               ifp->if_nextents++;
+               xfs_iext_next(ifp, icur);
+@@ -5228,7 +5219,7 @@ xfs_bmap_del_extent_real(
+                                       ((bflags & XFS_BMAPI_NODISCARD) ||
+                                       del->br_state == XFS_EXT_UNWRITTEN));
+                       if (error)
+-                              goto done;
++                              return error;
+               }
+       }
+@@ -5243,9 +5234,7 @@ xfs_bmap_del_extent_real(
+       if (qfield && !(bflags & XFS_BMAPI_REMAP))
+               xfs_trans_mod_dquot_byino(tp, ip, qfield, (long)-nblks);
+-done:
+-      *logflagsp = flags;
+-      return error;
++      return 0;
+ }
+ /*
diff --git a/queue-6.6/xfs-fix-32-bit-truncation-in-xfs_compute_rextslog.patch b/queue-6.6/xfs-fix-32-bit-truncation-in-xfs_compute_rextslog.patch
new file mode 100644 (file)
index 0000000..bc80eda
--- /dev/null
@@ -0,0 +1,49 @@
+From stable+bounces-32418-greg=kroah.com@vger.kernel.org Wed Mar 27 01:13:25 2024
+From: Catherine Hoang <catherine.hoang@oracle.com>
+Date: Tue, 26 Mar 2024 17:12:18 -0700
+Subject: xfs: fix 32-bit truncation in xfs_compute_rextslog
+To: stable@vger.kernel.org
+Cc: linux-xfs@vger.kernel.org
+Message-ID: <20240327001233.51675-10-catherine.hoang@oracle.com>
+
+From: "Darrick J. Wong" <djwong@kernel.org>
+
+commit cf8f0e6c1429be7652869059ea44696b72d5b726 upstream.
+
+It's quite reasonable that some customer somewhere will want to
+configure a realtime volume with more than 2^32 extents.  If they try to
+do this, the highbit32() call will truncate the upper bits of the
+xfs_rtbxlen_t and produce the wrong value for rextslog.  This in turn
+causes the rsumlevels to be wrong, which results in a realtime summary
+file that is the wrong length.  Fix that.
+
+Signed-off-by: Darrick J. Wong <djwong@kernel.org>
+Reviewed-by: Christoph Hellwig <hch@lst.de>
+Signed-off-by: Catherine Hoang <catherine.hoang@oracle.com>
+Acked-by: Darrick J. Wong <djwong@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/xfs/libxfs/xfs_rtbitmap.c |    8 +++++---
+ 1 file changed, 5 insertions(+), 3 deletions(-)
+
+--- a/fs/xfs/libxfs/xfs_rtbitmap.c
++++ b/fs/xfs/libxfs/xfs_rtbitmap.c
+@@ -1133,13 +1133,15 @@ xfs_rtalloc_extent_is_free(
+ /*
+  * Compute the maximum level number of the realtime summary file, as defined by
+- * mkfs.  The use of highbit32 on a 64-bit quantity is a historic artifact that
+- * prohibits correct use of rt volumes with more than 2^32 extents.
++ * mkfs.  The historic use of highbit32 on a 64-bit quantity prohibited correct
++ * use of rt volumes with more than 2^32 extents.
+  */
+ uint8_t
+ xfs_compute_rextslog(
+       xfs_rtbxlen_t           rtextents)
+ {
+-      return rtextents ? xfs_highbit32(rtextents) : 0;
++      if (!rtextents)
++              return 0;
++      return xfs_highbit64(rtextents);
+ }
diff --git a/queue-6.6/xfs-fix-an-off-by-one-error-in-xreap_agextent_binval.patch b/queue-6.6/xfs-fix-an-off-by-one-error-in-xreap_agextent_binval.patch
new file mode 100644 (file)
index 0000000..1229eb4
--- /dev/null
@@ -0,0 +1,76 @@
+From stable+bounces-32423-greg=kroah.com@vger.kernel.org Wed Mar 27 01:13:36 2024
+From: Catherine Hoang <catherine.hoang@oracle.com>
+Date: Tue, 26 Mar 2024 17:12:23 -0700
+Subject: xfs: fix an off-by-one error in xreap_agextent_binval
+To: stable@vger.kernel.org
+Cc: linux-xfs@vger.kernel.org
+Message-ID: <20240327001233.51675-15-catherine.hoang@oracle.com>
+
+From: "Darrick J. Wong" <djwong@kernel.org>
+
+commit c0e37f07d2bd3c1ee3fb5a650da7d8673557ed16 upstream.
+
+Overall, this function tries to find and invalidate all buffers for a
+given extent of space on the data device.  The inner for loop in this
+function tries to find all xfs_bufs for a given daddr.  The lengths of
+all possible cached buffers range from 1 fsblock to the largest needed
+to contain a 64k xattr value (~17fsb).  The scan is capped to avoid
+looking at anything buffer going past the given extent.
+
+Unfortunately, the loop continuation test is wrong -- max_fsbs is the
+largest size we want to scan, not one past that.  Put another way, this
+loop is actually 1-indexed, not 0-indexed.  Therefore, the continuation
+test should use <=, not <.
+
+As a result, online repairs of btree blocks fails to stale any buffers
+for btrees that are being torn down, which causes later assertions in
+the buffer cache when another thread creates a different-sized buffer.
+This happens in xfs/709 when allocating an inode cluster buffer:
+
+ ------------[ cut here ]------------
+ WARNING: CPU: 0 PID: 3346128 at fs/xfs/xfs_message.c:104 assfail+0x3a/0x40 [xfs]
+ CPU: 0 PID: 3346128 Comm: fsstress Not tainted 6.7.0-rc4-djwx #rc4
+ RIP: 0010:assfail+0x3a/0x40 [xfs]
+ Call Trace:
+  <TASK>
+  _xfs_buf_obj_cmp+0x4a/0x50
+  xfs_buf_get_map+0x191/0xba0
+  xfs_trans_get_buf_map+0x136/0x280
+  xfs_ialloc_inode_init+0x186/0x340
+  xfs_ialloc_ag_alloc+0x254/0x720
+  xfs_dialloc+0x21f/0x870
+  xfs_create_tmpfile+0x1a9/0x2f0
+  xfs_rename+0x369/0xfd0
+  xfs_vn_rename+0xfa/0x170
+  vfs_rename+0x5fb/0xc30
+  do_renameat2+0x52d/0x6e0
+  __x64_sys_renameat2+0x4b/0x60
+  do_syscall_64+0x3b/0xe0
+  entry_SYSCALL_64_after_hwframe+0x46/0x4e
+
+A later refactoring patch in the online repair series fixed this by
+accident, which is why I didn't notice this until I started testing only
+the patches that are likely to end up in 6.8.
+
+Fixes: 1c7ce115e521 ("xfs: reap large AG metadata extents when possible")
+Signed-off-by: "Darrick J. Wong" <djwong@kernel.org>
+Reviewed-by: Dave Chinner <dchinner@redhat.com>
+Signed-off-by: Chandan Babu R <chandanbabu@kernel.org>
+Signed-off-by: Catherine Hoang <catherine.hoang@oracle.com>
+Acked-by: Darrick J. Wong <djwong@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/xfs/scrub/reap.c |    2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/fs/xfs/scrub/reap.c
++++ b/fs/xfs/scrub/reap.c
+@@ -247,7 +247,7 @@ xreap_agextent_binval(
+               max_fsbs = min_t(xfs_agblock_t, agbno_next - bno,
+                               xfs_attr3_rmt_blocks(mp, XFS_XATTR_SIZE_MAX));
+-              for (fsbcount = 1; fsbcount < max_fsbs; fsbcount++) {
++              for (fsbcount = 1; fsbcount <= max_fsbs; fsbcount++) {
+                       struct xfs_buf  *bp = NULL;
+                       xfs_daddr_t     daddr;
+                       int             error;
diff --git a/queue-6.6/xfs-fix-perag-leak-when-growfs-fails.patch b/queue-6.6/xfs-fix-perag-leak-when-growfs-fails.patch
new file mode 100644 (file)
index 0000000..4fbf95e
--- /dev/null
@@ -0,0 +1,150 @@
+From stable+bounces-32429-greg=kroah.com@vger.kernel.org Wed Mar 27 01:13:55 2024
+From: Catherine Hoang <catherine.hoang@oracle.com>
+Date: Tue, 26 Mar 2024 17:12:29 -0700
+Subject: xfs: fix perag leak when growfs fails
+To: stable@vger.kernel.org
+Cc: linux-xfs@vger.kernel.org
+Message-ID: <20240327001233.51675-21-catherine.hoang@oracle.com>
+
+From: Long Li <leo.lilong@huawei.com>
+
+commit 7823921887750b39d02e6b44faafdd1cc617c651 upstream.
+
+During growfs, if new ag in memory has been initialized, however
+sb_agcount has not been updated, if an error occurs at this time it
+will cause perag leaks as follows, these new AGs will not been freed
+during umount , because of these new AGs are not visible(that is
+included in mp->m_sb.sb_agcount).
+
+unreferenced object 0xffff88810be40200 (size 512):
+  comm "xfs_growfs", pid 857, jiffies 4294909093
+  hex dump (first 32 bytes):
+    00 c0 c1 05 81 88 ff ff 04 00 00 00 00 00 00 00  ................
+    01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
+  backtrace (crc 381741e2):
+    [<ffffffff8191aef6>] __kmalloc+0x386/0x4f0
+    [<ffffffff82553e65>] kmem_alloc+0xb5/0x2f0
+    [<ffffffff8238dac5>] xfs_initialize_perag+0xc5/0x810
+    [<ffffffff824f679c>] xfs_growfs_data+0x9bc/0xbc0
+    [<ffffffff8250b90e>] xfs_file_ioctl+0x5fe/0x14d0
+    [<ffffffff81aa5194>] __x64_sys_ioctl+0x144/0x1c0
+    [<ffffffff83c3d81f>] do_syscall_64+0x3f/0xe0
+    [<ffffffff83e00087>] entry_SYSCALL_64_after_hwframe+0x62/0x6a
+unreferenced object 0xffff88810be40800 (size 512):
+  comm "xfs_growfs", pid 857, jiffies 4294909093
+  hex dump (first 32 bytes):
+    20 00 00 00 00 00 00 00 57 ef be dc 00 00 00 00   .......W.......
+    10 08 e4 0b 81 88 ff ff 10 08 e4 0b 81 88 ff ff  ................
+  backtrace (crc bde50e2d):
+    [<ffffffff8191b43a>] __kmalloc_node+0x3da/0x540
+    [<ffffffff81814489>] kvmalloc_node+0x99/0x160
+    [<ffffffff8286acff>] bucket_table_alloc.isra.0+0x5f/0x400
+    [<ffffffff8286bdc5>] rhashtable_init+0x405/0x760
+    [<ffffffff8238dda3>] xfs_initialize_perag+0x3a3/0x810
+    [<ffffffff824f679c>] xfs_growfs_data+0x9bc/0xbc0
+    [<ffffffff8250b90e>] xfs_file_ioctl+0x5fe/0x14d0
+    [<ffffffff81aa5194>] __x64_sys_ioctl+0x144/0x1c0
+    [<ffffffff83c3d81f>] do_syscall_64+0x3f/0xe0
+    [<ffffffff83e00087>] entry_SYSCALL_64_after_hwframe+0x62/0x6a
+
+Factor out xfs_free_unused_perag_range() from xfs_initialize_perag(),
+used for freeing unused perag within a specified range in error handling,
+included in the error path of the growfs failure.
+
+Fixes: 1c1c6ebcf528 ("xfs: Replace per-ag array with a radix tree")
+Signed-off-by: Long Li <leo.lilong@huawei.com>
+Reviewed-by: "Darrick J. Wong" <djwong@kernel.org>
+Signed-off-by: Chandan Babu R <chandanbabu@kernel.org>
+Signed-off-by: Catherine Hoang <catherine.hoang@oracle.com>
+Acked-by: Darrick J. Wong <djwong@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/xfs/libxfs/xfs_ag.c |   36 ++++++++++++++++++++++++++----------
+ fs/xfs/libxfs/xfs_ag.h |    2 ++
+ fs/xfs/xfs_fsops.c     |    5 ++++-
+ 3 files changed, 32 insertions(+), 11 deletions(-)
+
+--- a/fs/xfs/libxfs/xfs_ag.c
++++ b/fs/xfs/libxfs/xfs_ag.c
+@@ -332,6 +332,31 @@ xfs_agino_range(
+       return __xfs_agino_range(mp, xfs_ag_block_count(mp, agno), first, last);
+ }
++/*
++ * Free perag within the specified AG range, it is only used to free unused
++ * perags under the error handling path.
++ */
++void
++xfs_free_unused_perag_range(
++      struct xfs_mount        *mp,
++      xfs_agnumber_t          agstart,
++      xfs_agnumber_t          agend)
++{
++      struct xfs_perag        *pag;
++      xfs_agnumber_t          index;
++
++      for (index = agstart; index < agend; index++) {
++              spin_lock(&mp->m_perag_lock);
++              pag = radix_tree_delete(&mp->m_perag_tree, index);
++              spin_unlock(&mp->m_perag_lock);
++              if (!pag)
++                      break;
++              xfs_buf_hash_destroy(pag);
++              xfs_defer_drain_free(&pag->pag_intents_drain);
++              kmem_free(pag);
++      }
++}
++
+ int
+ xfs_initialize_perag(
+       struct xfs_mount        *mp,
+@@ -431,16 +456,7 @@ out_free_pag:
+       kmem_free(pag);
+ out_unwind_new_pags:
+       /* unwind any prior newly initialized pags */
+-      for (index = first_initialised; index < agcount; index++) {
+-              spin_lock(&mp->m_perag_lock);
+-              pag = radix_tree_delete(&mp->m_perag_tree, index);
+-              spin_unlock(&mp->m_perag_lock);
+-              if (!pag)
+-                      break;
+-              xfs_buf_hash_destroy(pag);
+-              xfs_defer_drain_free(&pag->pag_intents_drain);
+-              kmem_free(pag);
+-      }
++      xfs_free_unused_perag_range(mp, first_initialised, agcount);
+       return error;
+ }
+--- a/fs/xfs/libxfs/xfs_ag.h
++++ b/fs/xfs/libxfs/xfs_ag.h
+@@ -133,6 +133,8 @@ __XFS_AG_OPSTATE(prefers_metadata, PREFE
+ __XFS_AG_OPSTATE(allows_inodes, ALLOWS_INODES)
+ __XFS_AG_OPSTATE(agfl_needs_reset, AGFL_NEEDS_RESET)
++void xfs_free_unused_perag_range(struct xfs_mount *mp, xfs_agnumber_t agstart,
++                      xfs_agnumber_t agend);
+ int xfs_initialize_perag(struct xfs_mount *mp, xfs_agnumber_t agcount,
+                       xfs_rfsblock_t dcount, xfs_agnumber_t *maxagi);
+ int xfs_initialize_perag_data(struct xfs_mount *mp, xfs_agnumber_t agno);
+--- a/fs/xfs/xfs_fsops.c
++++ b/fs/xfs/xfs_fsops.c
+@@ -157,7 +157,7 @@ xfs_growfs_data_private(
+               error = xfs_trans_alloc(mp, &M_RES(mp)->tr_growdata, -delta, 0,
+                               0, &tp);
+       if (error)
+-              return error;
++              goto out_free_unused_perag;
+       last_pag = xfs_perag_get(mp, oagcount - 1);
+       if (delta > 0) {
+@@ -231,6 +231,9 @@ xfs_growfs_data_private(
+ out_trans_cancel:
+       xfs_trans_cancel(tp);
++out_free_unused_perag:
++      if (nagcount > oagcount)
++              xfs_free_unused_perag_range(mp, oagcount, nagcount);
+       return error;
+ }
diff --git a/queue-6.6/xfs-force-all-buffers-to-be-written-during-btree-bulk-load.patch b/queue-6.6/xfs-force-all-buffers-to-be-written-during-btree-bulk-load.patch
new file mode 100644 (file)
index 0000000..9a6032a
--- /dev/null
@@ -0,0 +1,220 @@
+From stable+bounces-32425-greg=kroah.com@vger.kernel.org Wed Mar 27 01:13:43 2024
+From: Catherine Hoang <catherine.hoang@oracle.com>
+Date: Tue, 26 Mar 2024 17:12:24 -0700
+Subject: xfs: force all buffers to be written during btree bulk load
+To: stable@vger.kernel.org
+Cc: linux-xfs@vger.kernel.org
+Message-ID: <20240327001233.51675-16-catherine.hoang@oracle.com>
+
+From: "Darrick J. Wong" <djwong@kernel.org>
+
+commit 13ae04d8d45227c2ba51e188daf9fc13d08a1b12 upstream.
+
+While stress-testing online repair of btrees, I noticed periodic
+assertion failures from the buffer cache about buffers with incorrect
+DELWRI_Q state.  Looking further, I observed this race between the AIL
+trying to write out a btree block and repair zapping a btree block after
+the fact:
+
+AIL:    Repair0:
+
+pin buffer X
+delwri_queue:
+set DELWRI_Q
+add to delwri list
+
+        stale buf X:
+        clear DELWRI_Q
+        does not clear b_list
+        free space X
+        commit
+
+delwri_submit   # oops
+
+Worse yet, I discovered that running the same repair over and over in a
+tight loop can result in a second race that cause data integrity
+problems with the repair:
+
+AIL:    Repair0:        Repair1:
+
+pin buffer X
+delwri_queue:
+set DELWRI_Q
+add to delwri list
+
+        stale buf X:
+        clear DELWRI_Q
+        does not clear b_list
+        free space X
+        commit
+
+                        find free space X
+                        get buffer
+                        rewrite buffer
+                        delwri_queue:
+                        set DELWRI_Q
+                        already on a list, do not add
+                        commit
+
+                        BAD: committed tree root before all blocks written
+
+delwri_submit   # too late now
+
+I traced this to my own misunderstanding of how the delwri lists work,
+particularly with regards to the AIL's buffer list.  If a buffer is
+logged and committed, the buffer can end up on that AIL buffer list.  If
+btree repairs are run twice in rapid succession, it's possible that the
+first repair will invalidate the buffer and free it before the next time
+the AIL wakes up.  Marking the buffer stale clears DELWRI_Q from the
+buffer state without removing the buffer from its delwri list.  The
+buffer doesn't know which list it's on, so it cannot know which lock to
+take to protect the list for a removal.
+
+If the second repair allocates the same block, it will then recycle the
+buffer to start writing the new btree block.  Meanwhile, if the AIL
+wakes up and walks the buffer list, it will ignore the buffer because it
+can't lock it, and go back to sleep.
+
+When the second repair calls delwri_queue to put the buffer on the
+list of buffers to write before committing the new btree, it will set
+DELWRI_Q again, but since the buffer hasn't been removed from the AIL's
+buffer list, it won't add it to the bulkload buffer's list.
+
+This is incorrect, because the bulkload caller relies on delwri_submit
+to ensure that all the buffers have been sent to disk /before/
+committing the new btree root pointer.  This ordering requirement is
+required for data consistency.
+
+Worse, the AIL won't clear DELWRI_Q from the buffer when it does finally
+drop it, so the next thread to walk through the btree will trip over a
+debug assertion on that flag.
+
+To fix this, create a new function that waits for the buffer to be
+removed from any other delwri lists before adding the buffer to the
+caller's delwri list.  By waiting for the buffer to clear both the
+delwri list and any potential delwri wait list, we can be sure that
+repair will initiate writes of all buffers and report all write errors
+back to userspace instead of committing the new structure.
+
+Signed-off-by: Darrick J. Wong <djwong@kernel.org>
+Reviewed-by: Christoph Hellwig <hch@lst.de>
+Signed-off-by: Catherine Hoang <catherine.hoang@oracle.com>
+Acked-by: Darrick J. Wong <djwong@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/xfs/libxfs/xfs_btree_staging.c |    4 ---
+ fs/xfs/xfs_buf.c                  |   44 ++++++++++++++++++++++++++++++++++----
+ fs/xfs/xfs_buf.h                  |    1 
+ 3 files changed, 42 insertions(+), 7 deletions(-)
+
+--- a/fs/xfs/libxfs/xfs_btree_staging.c
++++ b/fs/xfs/libxfs/xfs_btree_staging.c
+@@ -342,9 +342,7 @@ xfs_btree_bload_drop_buf(
+       if (*bpp == NULL)
+               return;
+-      if (!xfs_buf_delwri_queue(*bpp, buffers_list))
+-              ASSERT(0);
+-
++      xfs_buf_delwri_queue_here(*bpp, buffers_list);
+       xfs_buf_relse(*bpp);
+       *bpp = NULL;
+ }
+--- a/fs/xfs/xfs_buf.c
++++ b/fs/xfs/xfs_buf.c
+@@ -2049,6 +2049,14 @@ error_free:
+       return NULL;
+ }
++static inline void
++xfs_buf_list_del(
++      struct xfs_buf          *bp)
++{
++      list_del_init(&bp->b_list);
++      wake_up_var(&bp->b_list);
++}
++
+ /*
+  * Cancel a delayed write list.
+  *
+@@ -2066,7 +2074,7 @@ xfs_buf_delwri_cancel(
+               xfs_buf_lock(bp);
+               bp->b_flags &= ~_XBF_DELWRI_Q;
+-              list_del_init(&bp->b_list);
++              xfs_buf_list_del(bp);
+               xfs_buf_relse(bp);
+       }
+ }
+@@ -2120,6 +2128,34 @@ xfs_buf_delwri_queue(
+ }
+ /*
++ * Queue a buffer to this delwri list as part of a data integrity operation.
++ * If the buffer is on any other delwri list, we'll wait for that to clear
++ * so that the caller can submit the buffer for IO and wait for the result.
++ * Callers must ensure the buffer is not already on the list.
++ */
++void
++xfs_buf_delwri_queue_here(
++      struct xfs_buf          *bp,
++      struct list_head        *buffer_list)
++{
++      /*
++       * We need this buffer to end up on the /caller's/ delwri list, not any
++       * old list.  This can happen if the buffer is marked stale (which
++       * clears DELWRI_Q) after the AIL queues the buffer to its list but
++       * before the AIL has a chance to submit the list.
++       */
++      while (!list_empty(&bp->b_list)) {
++              xfs_buf_unlock(bp);
++              wait_var_event(&bp->b_list, list_empty(&bp->b_list));
++              xfs_buf_lock(bp);
++      }
++
++      ASSERT(!(bp->b_flags & _XBF_DELWRI_Q));
++
++      xfs_buf_delwri_queue(bp, buffer_list);
++}
++
++/*
+  * Compare function is more complex than it needs to be because
+  * the return value is only 32 bits and we are doing comparisons
+  * on 64 bit values
+@@ -2181,7 +2217,7 @@ xfs_buf_delwri_submit_buffers(
+                * reference and remove it from the list here.
+                */
+               if (!(bp->b_flags & _XBF_DELWRI_Q)) {
+-                      list_del_init(&bp->b_list);
++                      xfs_buf_list_del(bp);
+                       xfs_buf_relse(bp);
+                       continue;
+               }
+@@ -2201,7 +2237,7 @@ xfs_buf_delwri_submit_buffers(
+                       list_move_tail(&bp->b_list, wait_list);
+               } else {
+                       bp->b_flags |= XBF_ASYNC;
+-                      list_del_init(&bp->b_list);
++                      xfs_buf_list_del(bp);
+               }
+               __xfs_buf_submit(bp, false);
+       }
+@@ -2255,7 +2291,7 @@ xfs_buf_delwri_submit(
+       while (!list_empty(&wait_list)) {
+               bp = list_first_entry(&wait_list, struct xfs_buf, b_list);
+-              list_del_init(&bp->b_list);
++              xfs_buf_list_del(bp);
+               /*
+                * Wait on the locked buffer, check for errors and unlock and
+--- a/fs/xfs/xfs_buf.h
++++ b/fs/xfs/xfs_buf.h
+@@ -318,6 +318,7 @@ extern void xfs_buf_stale(struct xfs_buf
+ /* Delayed Write Buffer Routines */
+ extern void xfs_buf_delwri_cancel(struct list_head *);
+ extern bool xfs_buf_delwri_queue(struct xfs_buf *, struct list_head *);
++void xfs_buf_delwri_queue_here(struct xfs_buf *bp, struct list_head *bl);
+ extern int xfs_buf_delwri_submit(struct list_head *);
+ extern int xfs_buf_delwri_submit_nowait(struct list_head *);
+ extern int xfs_buf_delwri_pushbuf(struct xfs_buf *, struct list_head *);
diff --git a/queue-6.6/xfs-initialise-di_crc-in-xfs_log_dinode.patch b/queue-6.6/xfs-initialise-di_crc-in-xfs_log_dinode.patch
new file mode 100644 (file)
index 0000000..519f487
--- /dev/null
@@ -0,0 +1,111 @@
+From stable+bounces-32427-greg=kroah.com@vger.kernel.org Wed Mar 27 01:13:48 2024
+From: Catherine Hoang <catherine.hoang@oracle.com>
+Date: Tue, 26 Mar 2024 17:12:26 -0700
+Subject: xfs: initialise di_crc in xfs_log_dinode
+To: stable@vger.kernel.org
+Cc: linux-xfs@vger.kernel.org
+Message-ID: <20240327001233.51675-18-catherine.hoang@oracle.com>
+
+From: Dave Chinner <dchinner@redhat.com>
+
+commit 0573676fdde7ce3829ee6a42a8e5a56355234712 upstream.
+
+Alexander Potapenko report that KMSAN was issuing these warnings:
+
+kmalloc-ed xlog buffer of size 512 : ffff88802fc26200
+kmalloc-ed xlog buffer of size 368 : ffff88802fc24a00
+kmalloc-ed xlog buffer of size 648 : ffff88802b631000
+kmalloc-ed xlog buffer of size 648 : ffff88802b632800
+kmalloc-ed xlog buffer of size 648 : ffff88802b631c00
+xlog_write_iovec: copying 12 bytes from ffff888017ddbbd8 to ffff88802c300400
+xlog_write_iovec: copying 28 bytes from ffff888017ddbbe4 to ffff88802c30040c
+xlog_write_iovec: copying 68 bytes from ffff88802fc26274 to ffff88802c300428
+xlog_write_iovec: copying 188 bytes from ffff88802fc262bc to ffff88802c30046c
+=====================================================
+BUG: KMSAN: uninit-value in xlog_write_iovec fs/xfs/xfs_log.c:2227
+BUG: KMSAN: uninit-value in xlog_write_full fs/xfs/xfs_log.c:2263
+BUG: KMSAN: uninit-value in xlog_write+0x1fac/0x2600 fs/xfs/xfs_log.c:2532
+ xlog_write_iovec fs/xfs/xfs_log.c:2227
+ xlog_write_full fs/xfs/xfs_log.c:2263
+ xlog_write+0x1fac/0x2600 fs/xfs/xfs_log.c:2532
+ xlog_cil_write_chain fs/xfs/xfs_log_cil.c:918
+ xlog_cil_push_work+0x30f2/0x44e0 fs/xfs/xfs_log_cil.c:1263
+ process_one_work kernel/workqueue.c:2630
+ process_scheduled_works+0x1188/0x1e30 kernel/workqueue.c:2703
+ worker_thread+0xee5/0x14f0 kernel/workqueue.c:2784
+ kthread+0x391/0x500 kernel/kthread.c:388
+ ret_from_fork+0x66/0x80 arch/x86/kernel/process.c:147
+ ret_from_fork_asm+0x11/0x20 arch/x86/entry/entry_64.S:242
+
+Uninit was created at:
+ slab_post_alloc_hook+0x101/0xac0 mm/slab.h:768
+ slab_alloc_node mm/slub.c:3482
+ __kmem_cache_alloc_node+0x612/0xae0 mm/slub.c:3521
+ __do_kmalloc_node mm/slab_common.c:1006
+ __kmalloc+0x11a/0x410 mm/slab_common.c:1020
+ kmalloc ./include/linux/slab.h:604
+ xlog_kvmalloc fs/xfs/xfs_log_priv.h:704
+ xlog_cil_alloc_shadow_bufs fs/xfs/xfs_log_cil.c:343
+ xlog_cil_commit+0x487/0x4dc0 fs/xfs/xfs_log_cil.c:1574
+ __xfs_trans_commit+0x8df/0x1930 fs/xfs/xfs_trans.c:1017
+ xfs_trans_commit+0x30/0x40 fs/xfs/xfs_trans.c:1061
+ xfs_create+0x15af/0x2150 fs/xfs/xfs_inode.c:1076
+ xfs_generic_create+0x4cd/0x1550 fs/xfs/xfs_iops.c:199
+ xfs_vn_create+0x4a/0x60 fs/xfs/xfs_iops.c:275
+ lookup_open fs/namei.c:3477
+ open_last_lookups fs/namei.c:3546
+ path_openat+0x29ac/0x6180 fs/namei.c:3776
+ do_filp_open+0x24d/0x680 fs/namei.c:3809
+ do_sys_openat2+0x1bc/0x330 fs/open.c:1440
+ do_sys_open fs/open.c:1455
+ __do_sys_openat fs/open.c:1471
+ __se_sys_openat fs/open.c:1466
+ __x64_sys_openat+0x253/0x330 fs/open.c:1466
+ do_syscall_x64 arch/x86/entry/common.c:51
+ do_syscall_64+0x4f/0x140 arch/x86/entry/common.c:82
+ entry_SYSCALL_64_after_hwframe+0x63/0x6b arch/x86/entry/entry_64.S:120
+
+Bytes 112-115 of 188 are uninitialized
+Memory access of size 188 starts at ffff88802fc262bc
+
+This is caused by the struct xfs_log_dinode not having the di_crc
+field initialised. Log recovery never uses this field (it is only
+present these days for on-disk format compatibility reasons) and so
+it's value is never checked so nothing in XFS has caught this.
+
+Further, none of the uninitialised memory access warning tools have
+caught this (despite catching other uninit memory accesses in the
+struct xfs_log_dinode back in 2017!) until recently. Alexander
+annotated the XFS code to get the dump of the actual bytes that were
+detected as uninitialised, and from that report it took me about 30s
+to realise what the issue was.
+
+The issue was introduced back in 2016 and every inode that is logged
+fails to initialise this field. This is no actual bad behaviour
+caused by this issue - I find it hard to even classify it as a
+bug...
+
+Reported-and-tested-by: Alexander Potapenko <glider@google.com>
+Fixes: f8d55aa0523a ("xfs: introduce inode log format object")
+Signed-off-by: Dave Chinner <dchinner@redhat.com>
+Reviewed-by: "Darrick J. Wong" <djwong@kernel.org>
+Signed-off-by: Chandan Babu R <chandanbabu@kernel.org>
+Signed-off-by: Catherine Hoang <catherine.hoang@oracle.com>
+Acked-by: Darrick J. Wong <djwong@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/xfs/xfs_inode_item.c |    3 +++
+ 1 file changed, 3 insertions(+)
+
+--- a/fs/xfs/xfs_inode_item.c
++++ b/fs/xfs/xfs_inode_item.c
+@@ -556,6 +556,9 @@ xfs_inode_to_log_dinode(
+               memset(to->di_pad2, 0, sizeof(to->di_pad2));
+               uuid_copy(&to->di_uuid, &ip->i_mount->m_sb.sb_meta_uuid);
+               to->di_v3_pad = 0;
++
++              /* dummy value for initialisation */
++              to->di_crc = 0;
+       } else {
+               to->di_version = 2;
+               to->di_flushiter = ip->i_flushiter;
diff --git a/queue-6.6/xfs-make-rextslog-computation-consistent-with-mkfs.patch b/queue-6.6/xfs-make-rextslog-computation-consistent-with-mkfs.patch
new file mode 100644 (file)
index 0000000..d83b098
--- /dev/null
@@ -0,0 +1,154 @@
+From stable+bounces-32417-greg=kroah.com@vger.kernel.org Wed Mar 27 01:13:18 2024
+From: Catherine Hoang <catherine.hoang@oracle.com>
+Date: Tue, 26 Mar 2024 17:12:17 -0700
+Subject: xfs: make rextslog computation consistent with mkfs
+To: stable@vger.kernel.org
+Cc: linux-xfs@vger.kernel.org
+Message-ID: <20240327001233.51675-9-catherine.hoang@oracle.com>
+
+From: "Darrick J. Wong" <djwong@kernel.org>
+
+commit a6a38f309afc4a7ede01242b603f36c433997780 upstream.
+
+[backport: resolve merge conflicts due to refactoring rtbitmap/summary
+macros and accessors]
+
+There's a weird discrepancy in xfsprogs dating back to the creation of
+the Linux port -- if there are zero rt extents, mkfs will set
+sb_rextents and sb_rextslog both to zero:
+
+       sbp->sb_rextslog =
+               (uint8_t)(rtextents ?
+                       libxfs_highbit32((unsigned int)rtextents) : 0);
+
+However, that's not the check that xfs_repair uses for nonzero rtblocks:
+
+       if (sb->sb_rextslog !=
+                       libxfs_highbit32((unsigned int)sb->sb_rextents))
+
+The difference here is that xfs_highbit32 returns -1 if its argument is
+zero.  Unfortunately, this means that in the weird corner case of a
+realtime volume shorter than 1 rt extent, xfs_repair will immediately
+flag a freshly formatted filesystem as corrupt.  Because mkfs has been
+writing ondisk artifacts like this for decades, we have to accept that
+as "correct".  TBH, zero rextslog for zero rtextents makes more sense to
+me anyway.
+
+Regrettably, the superblock verifier checks created in commit copied
+xfs_repair even though mkfs has been writing out such filesystems for
+ages.  Fix the superblock verifier to accept what mkfs spits out; the
+userspace version of this patch will have to fix xfs_repair as well.
+
+Note that the new helper leaves the zeroday bug where the upper 32 bits
+of sb_rextents is ripped off and fed to highbit32.  This leads to a
+seriously undersized rt summary file, which immediately breaks mkfs:
+
+$ hugedisk.sh foo /dev/sdc $(( 0x100000080 * 4096))B
+$ /sbin/mkfs.xfs -f /dev/sda -m rmapbt=0,reflink=0 -r rtdev=/dev/mapper/foo
+meta-data=/dev/sda               isize=512    agcount=4, agsize=1298176 blks
+         =                       sectsz=512   attr=2, projid32bit=1
+         =                       crc=1        finobt=1, sparse=1, rmapbt=0
+         =                       reflink=0    bigtime=1 inobtcount=1 nrext64=1
+data     =                       bsize=4096   blocks=5192704, imaxpct=25
+         =                       sunit=0      swidth=0 blks
+naming   =version 2              bsize=4096   ascii-ci=0, ftype=1
+log      =internal log           bsize=4096   blocks=16384, version=2
+         =                       sectsz=512   sunit=0 blks, lazy-count=1
+realtime =/dev/mapper/foo        extsz=4096   blocks=4294967424, rtextents=4294967424
+Discarding blocks...Done.
+mkfs.xfs: Error initializing the realtime space [117 - Structure needs cleaning]
+
+The next patch will drop support for rt volumes with fewer than 1 or
+more than 2^32-1 rt extents, since they've clearly been broken forever.
+
+Fixes: f8e566c0f5e1f ("xfs: validate the realtime geometry in xfs_validate_sb_common")
+Signed-off-by: Darrick J. Wong <djwong@kernel.org>
+Reviewed-by: Christoph Hellwig <hch@lst.de>
+Signed-off-by: Catherine Hoang <catherine.hoang@oracle.com>
+Acked-by: Darrick J. Wong <djwong@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/xfs/libxfs/xfs_rtbitmap.c |   13 +++++++++++++
+ fs/xfs/libxfs/xfs_rtbitmap.h |    4 ++++
+ fs/xfs/libxfs/xfs_sb.c       |    3 ++-
+ fs/xfs/xfs_rtalloc.c         |    4 ++--
+ 4 files changed, 21 insertions(+), 3 deletions(-)
+
+--- a/fs/xfs/libxfs/xfs_rtbitmap.c
++++ b/fs/xfs/libxfs/xfs_rtbitmap.c
+@@ -1130,3 +1130,16 @@ xfs_rtalloc_extent_is_free(
+       *is_free = matches;
+       return 0;
+ }
++
++/*
++ * Compute the maximum level number of the realtime summary file, as defined by
++ * mkfs.  The use of highbit32 on a 64-bit quantity is a historic artifact that
++ * prohibits correct use of rt volumes with more than 2^32 extents.
++ */
++uint8_t
++xfs_compute_rextslog(
++      xfs_rtbxlen_t           rtextents)
++{
++      return rtextents ? xfs_highbit32(rtextents) : 0;
++}
++
+--- a/fs/xfs/libxfs/xfs_rtbitmap.h
++++ b/fs/xfs/libxfs/xfs_rtbitmap.h
+@@ -70,6 +70,9 @@ xfs_rtfree_extent(
+ /* Same as above, but in units of rt blocks. */
+ int xfs_rtfree_blocks(struct xfs_trans *tp, xfs_fsblock_t rtbno,
+               xfs_filblks_t rtlen);
++
++uint8_t xfs_compute_rextslog(xfs_rtbxlen_t rtextents);
++
+ #else /* CONFIG_XFS_RT */
+ # define xfs_rtfree_extent(t,b,l)                     (-ENOSYS)
+ # define xfs_rtfree_blocks(t,rb,rl)                   (-ENOSYS)
+@@ -77,6 +80,7 @@ int xfs_rtfree_blocks(struct xfs_trans *
+ # define xfs_rtalloc_query_all(m,t,f,p)                       (-ENOSYS)
+ # define xfs_rtbuf_get(m,t,b,i,p)                     (-ENOSYS)
+ # define xfs_rtalloc_extent_is_free(m,t,s,l,i)                (-ENOSYS)
++# define xfs_compute_rextslog(rtx)                    (0)
+ #endif /* CONFIG_XFS_RT */
+ #endif /* __XFS_RTBITMAP_H__ */
+--- a/fs/xfs/libxfs/xfs_sb.c
++++ b/fs/xfs/libxfs/xfs_sb.c
+@@ -25,6 +25,7 @@
+ #include "xfs_da_format.h"
+ #include "xfs_health.h"
+ #include "xfs_ag.h"
++#include "xfs_rtbitmap.h"
+ /*
+  * Physical superblock buffer manipulations. Shared with libxfs in userspace.
+@@ -509,7 +510,7 @@ xfs_validate_sb_common(
+                                      NBBY * sbp->sb_blocksize);
+               if (sbp->sb_rextents != rexts ||
+-                  sbp->sb_rextslog != xfs_highbit32(sbp->sb_rextents) ||
++                  sbp->sb_rextslog != xfs_compute_rextslog(rexts) ||
+                   sbp->sb_rbmblocks != rbmblocks) {
+                       xfs_notice(mp,
+                               "realtime geometry sanity check failed");
+--- a/fs/xfs/xfs_rtalloc.c
++++ b/fs/xfs/xfs_rtalloc.c
+@@ -999,7 +999,7 @@ xfs_growfs_rt(
+       nrextents = nrblocks;
+       do_div(nrextents, in->extsize);
+       nrbmblocks = howmany_64(nrextents, NBBY * sbp->sb_blocksize);
+-      nrextslog = xfs_highbit32(nrextents);
++      nrextslog = xfs_compute_rextslog(nrextents);
+       nrsumlevels = nrextslog + 1;
+       nrsumsize = (uint)sizeof(xfs_suminfo_t) * nrsumlevels * nrbmblocks;
+       nrsumblocks = XFS_B_TO_FSB(mp, nrsumsize);
+@@ -1061,7 +1061,7 @@ xfs_growfs_rt(
+               nsbp->sb_rextents = nsbp->sb_rblocks;
+               do_div(nsbp->sb_rextents, nsbp->sb_rextsize);
+               ASSERT(nsbp->sb_rextents != 0);
+-              nsbp->sb_rextslog = xfs_highbit32(nsbp->sb_rextents);
++              nsbp->sb_rextslog = xfs_compute_rextslog(nsbp->sb_rextents);
+               nrsumlevels = nmp->m_rsumlevels = nsbp->sb_rextslog + 1;
+               nrsumsize =
+                       (uint)sizeof(xfs_suminfo_t) * nrsumlevels *
diff --git a/queue-6.6/xfs-make-xchk_iget-safer-in-the-presence-of-corrupt-inode-btrees.patch b/queue-6.6/xfs-make-xchk_iget-safer-in-the-presence-of-corrupt-inode-btrees.patch
new file mode 100644 (file)
index 0000000..3802ccc
--- /dev/null
@@ -0,0 +1,104 @@
+From stable+bounces-32420-greg=kroah.com@vger.kernel.org Wed Mar 27 01:13:28 2024
+From: Catherine Hoang <catherine.hoang@oracle.com>
+Date: Tue, 26 Mar 2024 17:12:20 -0700
+Subject: xfs: make xchk_iget safer in the presence of corrupt inode btrees
+To: stable@vger.kernel.org
+Cc: linux-xfs@vger.kernel.org
+Message-ID: <20240327001233.51675-12-catherine.hoang@oracle.com>
+
+From: "Darrick J. Wong" <djwong@kernel.org>
+
+commit 3f113c2739b1b068854c7ffed635c2bd790d1492 upstream.
+
+When scrub is trying to iget an inode, ensure that it won't end up
+deadlocked on a cycle in the inode btree by using an empty transaction
+to store all the buffers.
+
+Signed-off-by: Darrick J. Wong <djwong@kernel.org>
+Reviewed-by: Dave Chinner <dchinner@redhat.com>
+Reviewed-by: Christoph Hellwig <hch@lst.de>
+Signed-off-by: Catherine Hoang <catherine.hoang@oracle.com>
+Acked-by: Darrick J. Wong <djwong@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/xfs/scrub/common.c |    6 ++++--
+ fs/xfs/scrub/common.h |   25 +++++++++++++++++++++++++
+ fs/xfs/scrub/inode.c  |    4 ++--
+ 3 files changed, 31 insertions(+), 4 deletions(-)
+
+--- a/fs/xfs/scrub/common.c
++++ b/fs/xfs/scrub/common.c
+@@ -733,6 +733,8 @@ xchk_iget(
+       xfs_ino_t               inum,
+       struct xfs_inode        **ipp)
+ {
++      ASSERT(sc->tp != NULL);
++
+       return xfs_iget(sc->mp, sc->tp, inum, XFS_IGET_UNTRUSTED, 0, ipp);
+ }
+@@ -882,8 +884,8 @@ xchk_iget_for_scrubbing(
+       if (!xfs_verify_ino(sc->mp, sc->sm->sm_ino))
+               return -ENOENT;
+-      /* Try a regular untrusted iget. */
+-      error = xchk_iget(sc, sc->sm->sm_ino, &ip);
++      /* Try a safe untrusted iget. */
++      error = xchk_iget_safe(sc, sc->sm->sm_ino, &ip);
+       if (!error)
+               return xchk_install_handle_inode(sc, ip);
+       if (error == -ENOENT)
+--- a/fs/xfs/scrub/common.h
++++ b/fs/xfs/scrub/common.h
+@@ -151,6 +151,11 @@ void xchk_iunlock(struct xfs_scrub *sc,
+ void xchk_buffer_recheck(struct xfs_scrub *sc, struct xfs_buf *bp);
++/*
++ * Grab the inode at @inum.  The caller must have created a scrub transaction
++ * so that we can confirm the inumber by walking the inobt and not deadlock on
++ * a loop in the inobt.
++ */
+ int xchk_iget(struct xfs_scrub *sc, xfs_ino_t inum, struct xfs_inode **ipp);
+ int xchk_iget_agi(struct xfs_scrub *sc, xfs_ino_t inum,
+               struct xfs_buf **agi_bpp, struct xfs_inode **ipp);
+@@ -158,6 +163,26 @@ void xchk_irele(struct xfs_scrub *sc, st
+ int xchk_install_handle_inode(struct xfs_scrub *sc, struct xfs_inode *ip);
+ /*
++ * Safe version of (untrusted) xchk_iget that uses an empty transaction to
++ * avoid deadlocking on loops in the inobt.  This should only be used in a
++ * scrub or repair setup routine, and only prior to grabbing a transaction.
++ */
++static inline int
++xchk_iget_safe(struct xfs_scrub *sc, xfs_ino_t inum, struct xfs_inode **ipp)
++{
++      int     error;
++
++      ASSERT(sc->tp == NULL);
++
++      error = xchk_trans_alloc(sc, 0);
++      if (error)
++              return error;
++      error = xchk_iget(sc, inum, ipp);
++      xchk_trans_cancel(sc);
++      return error;
++}
++
++/*
+  * Don't bother cross-referencing if we already found corruption or cross
+  * referencing discrepancies.
+  */
+--- a/fs/xfs/scrub/inode.c
++++ b/fs/xfs/scrub/inode.c
+@@ -94,8 +94,8 @@ xchk_setup_inode(
+       if (!xfs_verify_ino(sc->mp, sc->sm->sm_ino))
+               return -ENOENT;
+-      /* Try a regular untrusted iget. */
+-      error = xchk_iget(sc, sc->sm->sm_ino, &ip);
++      /* Try a safe untrusted iget. */
++      error = xchk_iget_safe(sc, sc->sm->sm_ino, &ip);
+       if (!error)
+               return xchk_install_handle_iscrub(sc, ip);
+       if (error == -ENOENT)
diff --git a/queue-6.6/xfs-move-the-xfs_rtbitmap.c-declarations-to-xfs_rtbitmap.h.patch b/queue-6.6/xfs-move-the-xfs_rtbitmap.c-declarations-to-xfs_rtbitmap.h.patch
new file mode 100644 (file)
index 0000000..a252520
--- /dev/null
@@ -0,0 +1,294 @@
+From stable+bounces-32410-greg=kroah.com@vger.kernel.org Wed Mar 27 01:13:00 2024
+From: Catherine Hoang <catherine.hoang@oracle.com>
+Date: Tue, 26 Mar 2024 17:12:10 -0700
+Subject: xfs: move the xfs_rtbitmap.c declarations to xfs_rtbitmap.h
+To: stable@vger.kernel.org
+Cc: linux-xfs@vger.kernel.org
+Message-ID: <20240327001233.51675-2-catherine.hoang@oracle.com>
+
+From: "Darrick J. Wong" <djwong@kernel.org>
+
+commit 13928113fc5b5e79c91796290a99ed991ac0efe2 upstream.
+
+Move all the declarations for functionality in xfs_rtbitmap.c into a
+separate xfs_rtbitmap.h header file.
+
+Signed-off-by: Darrick J. Wong <djwong@kernel.org>
+Reviewed-by: Christoph Hellwig <hch@lst.de>
+Signed-off-by: Catherine Hoang <catherine.hoang@oracle.com>
+Acked-by: Darrick J. Wong <djwong@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/xfs/libxfs/xfs_bmap.c     |    2 -
+ fs/xfs/libxfs/xfs_rtbitmap.c |    1 
+ fs/xfs/libxfs/xfs_rtbitmap.h |   82 +++++++++++++++++++++++++++++++++++++++++++
+ fs/xfs/scrub/fscounters.c    |    2 -
+ fs/xfs/scrub/rtbitmap.c      |    2 -
+ fs/xfs/scrub/rtsummary.c     |    2 -
+ fs/xfs/xfs_fsmap.c           |    2 -
+ fs/xfs/xfs_rtalloc.c         |    1 
+ fs/xfs/xfs_rtalloc.h         |   73 --------------------------------------
+ 9 files changed, 89 insertions(+), 78 deletions(-)
+ create mode 100644 fs/xfs/libxfs/xfs_rtbitmap.h
+
+--- a/fs/xfs/libxfs/xfs_bmap.c
++++ b/fs/xfs/libxfs/xfs_bmap.c
+@@ -21,7 +21,7 @@
+ #include "xfs_bmap.h"
+ #include "xfs_bmap_util.h"
+ #include "xfs_bmap_btree.h"
+-#include "xfs_rtalloc.h"
++#include "xfs_rtbitmap.h"
+ #include "xfs_errortag.h"
+ #include "xfs_error.h"
+ #include "xfs_quota.h"
+--- a/fs/xfs/libxfs/xfs_rtbitmap.c
++++ b/fs/xfs/libxfs/xfs_rtbitmap.c
+@@ -16,6 +16,7 @@
+ #include "xfs_trans.h"
+ #include "xfs_rtalloc.h"
+ #include "xfs_error.h"
++#include "xfs_rtbitmap.h"
+ /*
+  * Realtime allocator bitmap functions shared with userspace.
+--- /dev/null
++++ b/fs/xfs/libxfs/xfs_rtbitmap.h
+@@ -0,0 +1,82 @@
++// SPDX-License-Identifier: GPL-2.0
++/*
++ * Copyright (c) 2000-2003,2005 Silicon Graphics, Inc.
++ * All Rights Reserved.
++ */
++#ifndef __XFS_RTBITMAP_H__
++#define       __XFS_RTBITMAP_H__
++
++/*
++ * XXX: Most of the realtime allocation functions deal in units of realtime
++ * extents, not realtime blocks.  This looks funny when paired with the type
++ * name and screams for a larger cleanup.
++ */
++struct xfs_rtalloc_rec {
++      xfs_rtblock_t           ar_startext;
++      xfs_rtblock_t           ar_extcount;
++};
++
++typedef int (*xfs_rtalloc_query_range_fn)(
++      struct xfs_mount                *mp,
++      struct xfs_trans                *tp,
++      const struct xfs_rtalloc_rec    *rec,
++      void                            *priv);
++
++#ifdef CONFIG_XFS_RT
++int xfs_rtbuf_get(struct xfs_mount *mp, struct xfs_trans *tp,
++                xfs_rtblock_t block, int issum, struct xfs_buf **bpp);
++int xfs_rtcheck_range(struct xfs_mount *mp, struct xfs_trans *tp,
++                    xfs_rtblock_t start, xfs_extlen_t len, int val,
++                    xfs_rtblock_t *new, int *stat);
++int xfs_rtfind_back(struct xfs_mount *mp, struct xfs_trans *tp,
++                  xfs_rtblock_t start, xfs_rtblock_t limit,
++                  xfs_rtblock_t *rtblock);
++int xfs_rtfind_forw(struct xfs_mount *mp, struct xfs_trans *tp,
++                  xfs_rtblock_t start, xfs_rtblock_t limit,
++                  xfs_rtblock_t *rtblock);
++int xfs_rtmodify_range(struct xfs_mount *mp, struct xfs_trans *tp,
++                     xfs_rtblock_t start, xfs_extlen_t len, int val);
++int xfs_rtmodify_summary_int(struct xfs_mount *mp, struct xfs_trans *tp,
++                           int log, xfs_rtblock_t bbno, int delta,
++                           struct xfs_buf **rbpp, xfs_fsblock_t *rsb,
++                           xfs_suminfo_t *sum);
++int xfs_rtmodify_summary(struct xfs_mount *mp, struct xfs_trans *tp, int log,
++                       xfs_rtblock_t bbno, int delta, struct xfs_buf **rbpp,
++                       xfs_fsblock_t *rsb);
++int xfs_rtfree_range(struct xfs_mount *mp, struct xfs_trans *tp,
++                   xfs_rtblock_t start, xfs_extlen_t len,
++                   struct xfs_buf **rbpp, xfs_fsblock_t *rsb);
++int xfs_rtalloc_query_range(struct xfs_mount *mp, struct xfs_trans *tp,
++              const struct xfs_rtalloc_rec *low_rec,
++              const struct xfs_rtalloc_rec *high_rec,
++              xfs_rtalloc_query_range_fn fn, void *priv);
++int xfs_rtalloc_query_all(struct xfs_mount *mp, struct xfs_trans *tp,
++                        xfs_rtalloc_query_range_fn fn,
++                        void *priv);
++bool xfs_verify_rtbno(struct xfs_mount *mp, xfs_rtblock_t rtbno);
++int xfs_rtalloc_extent_is_free(struct xfs_mount *mp, struct xfs_trans *tp,
++                             xfs_rtblock_t start, xfs_extlen_t len,
++                             bool *is_free);
++/*
++ * Free an extent in the realtime subvolume.  Length is expressed in
++ * realtime extents, as is the block number.
++ */
++int                                   /* error */
++xfs_rtfree_extent(
++      struct xfs_trans        *tp,    /* transaction pointer */
++      xfs_rtblock_t           bno,    /* starting block number to free */
++      xfs_extlen_t            len);   /* length of extent freed */
++
++/* Same as above, but in units of rt blocks. */
++int xfs_rtfree_blocks(struct xfs_trans *tp, xfs_fsblock_t rtbno,
++              xfs_filblks_t rtlen);
++#else /* CONFIG_XFS_RT */
++# define xfs_rtfree_extent(t,b,l)                     (-ENOSYS)
++# define xfs_rtfree_blocks(t,rb,rl)                   (-ENOSYS)
++# define xfs_rtalloc_query_range(m,t,l,h,f,p)         (-ENOSYS)
++# define xfs_rtalloc_query_all(m,t,f,p)                       (-ENOSYS)
++# define xfs_rtbuf_get(m,t,b,i,p)                     (-ENOSYS)
++# define xfs_rtalloc_extent_is_free(m,t,s,l,i)                (-ENOSYS)
++#endif /* CONFIG_XFS_RT */
++
++#endif /* __XFS_RTBITMAP_H__ */
+--- a/fs/xfs/scrub/fscounters.c
++++ b/fs/xfs/scrub/fscounters.c
+@@ -16,7 +16,7 @@
+ #include "xfs_health.h"
+ #include "xfs_btree.h"
+ #include "xfs_ag.h"
+-#include "xfs_rtalloc.h"
++#include "xfs_rtbitmap.h"
+ #include "xfs_inode.h"
+ #include "xfs_icache.h"
+ #include "scrub/scrub.h"
+--- a/fs/xfs/scrub/rtbitmap.c
++++ b/fs/xfs/scrub/rtbitmap.c
+@@ -11,7 +11,7 @@
+ #include "xfs_mount.h"
+ #include "xfs_log_format.h"
+ #include "xfs_trans.h"
+-#include "xfs_rtalloc.h"
++#include "xfs_rtbitmap.h"
+ #include "xfs_inode.h"
+ #include "xfs_bmap.h"
+ #include "scrub/scrub.h"
+--- a/fs/xfs/scrub/rtsummary.c
++++ b/fs/xfs/scrub/rtsummary.c
+@@ -13,7 +13,7 @@
+ #include "xfs_inode.h"
+ #include "xfs_log_format.h"
+ #include "xfs_trans.h"
+-#include "xfs_rtalloc.h"
++#include "xfs_rtbitmap.h"
+ #include "xfs_bit.h"
+ #include "xfs_bmap.h"
+ #include "scrub/scrub.h"
+--- a/fs/xfs/xfs_fsmap.c
++++ b/fs/xfs/xfs_fsmap.c
+@@ -23,7 +23,7 @@
+ #include "xfs_refcount.h"
+ #include "xfs_refcount_btree.h"
+ #include "xfs_alloc_btree.h"
+-#include "xfs_rtalloc.h"
++#include "xfs_rtbitmap.h"
+ #include "xfs_ag.h"
+ /* Convert an xfs_fsmap to an fsmap. */
+--- a/fs/xfs/xfs_rtalloc.c
++++ b/fs/xfs/xfs_rtalloc.c
+@@ -19,6 +19,7 @@
+ #include "xfs_icache.h"
+ #include "xfs_rtalloc.h"
+ #include "xfs_sb.h"
++#include "xfs_rtbitmap.h"
+ /*
+  * Read and return the summary information for a given extent size,
+--- a/fs/xfs/xfs_rtalloc.h
++++ b/fs/xfs/xfs_rtalloc.h
+@@ -11,22 +11,6 @@
+ struct xfs_mount;
+ struct xfs_trans;
+-/*
+- * XXX: Most of the realtime allocation functions deal in units of realtime
+- * extents, not realtime blocks.  This looks funny when paired with the type
+- * name and screams for a larger cleanup.
+- */
+-struct xfs_rtalloc_rec {
+-      xfs_rtblock_t           ar_startext;
+-      xfs_rtblock_t           ar_extcount;
+-};
+-
+-typedef int (*xfs_rtalloc_query_range_fn)(
+-      struct xfs_mount                *mp,
+-      struct xfs_trans                *tp,
+-      const struct xfs_rtalloc_rec    *rec,
+-      void                            *priv);
+-
+ #ifdef CONFIG_XFS_RT
+ /*
+  * Function prototypes for exported functions.
+@@ -48,19 +32,6 @@ xfs_rtallocate_extent(
+       xfs_extlen_t            prod,   /* extent product factor */
+       xfs_rtblock_t           *rtblock); /* out: start block allocated */
+-/*
+- * Free an extent in the realtime subvolume.  Length is expressed in
+- * realtime extents, as is the block number.
+- */
+-int                                   /* error */
+-xfs_rtfree_extent(
+-      struct xfs_trans        *tp,    /* transaction pointer */
+-      xfs_rtblock_t           bno,    /* starting block number to free */
+-      xfs_extlen_t            len);   /* length of extent freed */
+-
+-/* Same as above, but in units of rt blocks. */
+-int xfs_rtfree_blocks(struct xfs_trans *tp, xfs_fsblock_t rtbno,
+-              xfs_filblks_t rtlen);
+ /*
+  * Initialize realtime fields in the mount structure.
+@@ -102,55 +73,11 @@ xfs_growfs_rt(
+       struct xfs_mount        *mp,    /* file system mount structure */
+       xfs_growfs_rt_t         *in);   /* user supplied growfs struct */
+-/*
+- * From xfs_rtbitmap.c
+- */
+-int xfs_rtbuf_get(struct xfs_mount *mp, struct xfs_trans *tp,
+-                xfs_rtblock_t block, int issum, struct xfs_buf **bpp);
+-int xfs_rtcheck_range(struct xfs_mount *mp, struct xfs_trans *tp,
+-                    xfs_rtblock_t start, xfs_extlen_t len, int val,
+-                    xfs_rtblock_t *new, int *stat);
+-int xfs_rtfind_back(struct xfs_mount *mp, struct xfs_trans *tp,
+-                  xfs_rtblock_t start, xfs_rtblock_t limit,
+-                  xfs_rtblock_t *rtblock);
+-int xfs_rtfind_forw(struct xfs_mount *mp, struct xfs_trans *tp,
+-                  xfs_rtblock_t start, xfs_rtblock_t limit,
+-                  xfs_rtblock_t *rtblock);
+-int xfs_rtmodify_range(struct xfs_mount *mp, struct xfs_trans *tp,
+-                     xfs_rtblock_t start, xfs_extlen_t len, int val);
+-int xfs_rtmodify_summary_int(struct xfs_mount *mp, struct xfs_trans *tp,
+-                           int log, xfs_rtblock_t bbno, int delta,
+-                           struct xfs_buf **rbpp, xfs_fsblock_t *rsb,
+-                           xfs_suminfo_t *sum);
+-int xfs_rtmodify_summary(struct xfs_mount *mp, struct xfs_trans *tp, int log,
+-                       xfs_rtblock_t bbno, int delta, struct xfs_buf **rbpp,
+-                       xfs_fsblock_t *rsb);
+-int xfs_rtfree_range(struct xfs_mount *mp, struct xfs_trans *tp,
+-                   xfs_rtblock_t start, xfs_extlen_t len,
+-                   struct xfs_buf **rbpp, xfs_fsblock_t *rsb);
+-int xfs_rtalloc_query_range(struct xfs_mount *mp, struct xfs_trans *tp,
+-              const struct xfs_rtalloc_rec *low_rec,
+-              const struct xfs_rtalloc_rec *high_rec,
+-              xfs_rtalloc_query_range_fn fn, void *priv);
+-int xfs_rtalloc_query_all(struct xfs_mount *mp, struct xfs_trans *tp,
+-                        xfs_rtalloc_query_range_fn fn,
+-                        void *priv);
+-bool xfs_verify_rtbno(struct xfs_mount *mp, xfs_rtblock_t rtbno);
+-int xfs_rtalloc_extent_is_free(struct xfs_mount *mp, struct xfs_trans *tp,
+-                             xfs_rtblock_t start, xfs_extlen_t len,
+-                             bool *is_free);
+ int xfs_rtalloc_reinit_frextents(struct xfs_mount *mp);
+ #else
+ # define xfs_rtallocate_extent(t,b,min,max,l,f,p,rb)  (-ENOSYS)
+-# define xfs_rtfree_extent(t,b,l)                     (-ENOSYS)
+-# define xfs_rtfree_blocks(t,rb,rl)                   (-ENOSYS)
+ # define xfs_rtpick_extent(m,t,l,rb)                  (-ENOSYS)
+ # define xfs_growfs_rt(mp,in)                         (-ENOSYS)
+-# define xfs_rtalloc_query_range(m,t,l,h,f,p)         (-ENOSYS)
+-# define xfs_rtalloc_query_all(m,t,f,p)                       (-ENOSYS)
+-# define xfs_rtbuf_get(m,t,b,i,p)                     (-ENOSYS)
+-# define xfs_verify_rtbno(m, r)                               (false)
+-# define xfs_rtalloc_extent_is_free(m,t,s,l,i)                (-ENOSYS)
+ # define xfs_rtalloc_reinit_frextents(m)              (0)
+ static inline int             /* error */
+ xfs_rtmount_init(
diff --git a/queue-6.6/xfs-pass-the-xfs_defer_pending-object-to-iop_recover.patch b/queue-6.6/xfs-pass-the-xfs_defer_pending-object-to-iop_recover.patch
new file mode 100644 (file)
index 0000000..79e0dfd
--- /dev/null
@@ -0,0 +1,137 @@
+From stable+bounces-32415-greg=kroah.com@vger.kernel.org Wed Mar 27 01:13:13 2024
+From: Catherine Hoang <catherine.hoang@oracle.com>
+Date: Tue, 26 Mar 2024 17:12:15 -0700
+Subject: xfs: pass the xfs_defer_pending object to iop_recover
+To: stable@vger.kernel.org
+Cc: linux-xfs@vger.kernel.org
+Message-ID: <20240327001233.51675-7-catherine.hoang@oracle.com>
+
+From: "Darrick J. Wong" <djwong@kernel.org>
+
+commit a050acdfa8003a44eae4558fddafc7afb1aef458 upstream.
+
+Now that log intent item recovery recreates the xfs_defer_pending state,
+we should pass that into the ->iop_recover routines so that the intent
+item can finish the recreation work.
+
+Signed-off-by: Darrick J. Wong <djwong@kernel.org>
+Reviewed-by: Christoph Hellwig <hch@lst.de>
+Signed-off-by: Catherine Hoang <catherine.hoang@oracle.com>
+Acked-by: Darrick J. Wong <djwong@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/xfs/xfs_attr_item.c     |    3 ++-
+ fs/xfs/xfs_bmap_item.c     |    3 ++-
+ fs/xfs/xfs_extfree_item.c  |    3 ++-
+ fs/xfs/xfs_log_recover.c   |    2 +-
+ fs/xfs/xfs_refcount_item.c |    3 ++-
+ fs/xfs/xfs_rmap_item.c     |    3 ++-
+ fs/xfs/xfs_trans.h         |    4 +++-
+ 7 files changed, 14 insertions(+), 7 deletions(-)
+
+--- a/fs/xfs/xfs_attr_item.c
++++ b/fs/xfs/xfs_attr_item.c
+@@ -545,9 +545,10 @@ xfs_attri_validate(
+  */
+ STATIC int
+ xfs_attri_item_recover(
+-      struct xfs_log_item             *lip,
++      struct xfs_defer_pending        *dfp,
+       struct list_head                *capture_list)
+ {
++      struct xfs_log_item             *lip = dfp->dfp_intent;
+       struct xfs_attri_log_item       *attrip = ATTRI_ITEM(lip);
+       struct xfs_attr_intent          *attr;
+       struct xfs_mount                *mp = lip->li_log->l_mp;
+--- a/fs/xfs/xfs_bmap_item.c
++++ b/fs/xfs/xfs_bmap_item.c
+@@ -486,11 +486,12 @@ xfs_bui_validate(
+  */
+ STATIC int
+ xfs_bui_item_recover(
+-      struct xfs_log_item             *lip,
++      struct xfs_defer_pending        *dfp,
+       struct list_head                *capture_list)
+ {
+       struct xfs_bmap_intent          fake = { };
+       struct xfs_trans_res            resv;
++      struct xfs_log_item             *lip = dfp->dfp_intent;
+       struct xfs_bui_log_item         *buip = BUI_ITEM(lip);
+       struct xfs_trans                *tp;
+       struct xfs_inode                *ip = NULL;
+--- a/fs/xfs/xfs_extfree_item.c
++++ b/fs/xfs/xfs_extfree_item.c
+@@ -657,10 +657,11 @@ xfs_efi_validate_ext(
+  */
+ STATIC int
+ xfs_efi_item_recover(
+-      struct xfs_log_item             *lip,
++      struct xfs_defer_pending        *dfp,
+       struct list_head                *capture_list)
+ {
+       struct xfs_trans_res            resv;
++      struct xfs_log_item             *lip = dfp->dfp_intent;
+       struct xfs_efi_log_item         *efip = EFI_ITEM(lip);
+       struct xfs_mount                *mp = lip->li_log->l_mp;
+       struct xfs_efd_log_item         *efdp;
+--- a/fs/xfs/xfs_log_recover.c
++++ b/fs/xfs/xfs_log_recover.c
+@@ -2583,7 +2583,7 @@ xlog_recover_process_intents(
+                * The recovery function can free the log item, so we must not
+                * access lip after it returns.
+                */
+-              error = ops->iop_recover(lip, &capture_list);
++              error = ops->iop_recover(dfp, &capture_list);
+               if (error) {
+                       trace_xlog_intent_recovery_failed(log->l_mp, error,
+                                       ops->iop_recover);
+--- a/fs/xfs/xfs_refcount_item.c
++++ b/fs/xfs/xfs_refcount_item.c
+@@ -474,10 +474,11 @@ xfs_cui_validate_phys(
+  */
+ STATIC int
+ xfs_cui_item_recover(
+-      struct xfs_log_item             *lip,
++      struct xfs_defer_pending        *dfp,
+       struct list_head                *capture_list)
+ {
+       struct xfs_trans_res            resv;
++      struct xfs_log_item             *lip = dfp->dfp_intent;
+       struct xfs_cui_log_item         *cuip = CUI_ITEM(lip);
+       struct xfs_cud_log_item         *cudp;
+       struct xfs_trans                *tp;
+--- a/fs/xfs/xfs_rmap_item.c
++++ b/fs/xfs/xfs_rmap_item.c
+@@ -504,10 +504,11 @@ xfs_rui_validate_map(
+  */
+ STATIC int
+ xfs_rui_item_recover(
+-      struct xfs_log_item             *lip,
++      struct xfs_defer_pending        *dfp,
+       struct list_head                *capture_list)
+ {
+       struct xfs_trans_res            resv;
++      struct xfs_log_item             *lip = dfp->dfp_intent;
+       struct xfs_rui_log_item         *ruip = RUI_ITEM(lip);
+       struct xfs_rud_log_item         *rudp;
+       struct xfs_trans                *tp;
+--- a/fs/xfs/xfs_trans.h
++++ b/fs/xfs/xfs_trans.h
+@@ -66,6 +66,8 @@ struct xfs_log_item {
+       { (1u << XFS_LI_DIRTY),         "DIRTY" }, \
+       { (1u << XFS_LI_WHITEOUT),      "WHITEOUT" }
++struct xfs_defer_pending;
++
+ struct xfs_item_ops {
+       unsigned flags;
+       void (*iop_size)(struct xfs_log_item *, int *, int *);
+@@ -78,7 +80,7 @@ struct xfs_item_ops {
+       xfs_lsn_t (*iop_committed)(struct xfs_log_item *, xfs_lsn_t);
+       uint (*iop_push)(struct xfs_log_item *, struct list_head *);
+       void (*iop_release)(struct xfs_log_item *);
+-      int (*iop_recover)(struct xfs_log_item *lip,
++      int (*iop_recover)(struct xfs_defer_pending *dfp,
+                          struct list_head *capture_list);
+       bool (*iop_match)(struct xfs_log_item *item, uint64_t id);
+       struct xfs_log_item *(*iop_relog)(struct xfs_log_item *intent,
diff --git a/queue-6.6/xfs-recompute-growfsrtfree-transaction-reservation-while-growing-rt-volume.patch b/queue-6.6/xfs-recompute-growfsrtfree-transaction-reservation-while-growing-rt-volume.patch
new file mode 100644 (file)
index 0000000..48bf164
--- /dev/null
@@ -0,0 +1,57 @@
+From stable+bounces-32422-greg=kroah.com@vger.kernel.org Wed Mar 27 01:13:32 2024
+From: Catherine Hoang <catherine.hoang@oracle.com>
+Date: Tue, 26 Mar 2024 17:12:22 -0700
+Subject: xfs: recompute growfsrtfree transaction reservation while growing rt volume
+To: stable@vger.kernel.org
+Cc: linux-xfs@vger.kernel.org
+Message-ID: <20240327001233.51675-14-catherine.hoang@oracle.com>
+
+From: "Darrick J. Wong" <djwong@kernel.org>
+
+commit 578bd4ce7100ae34f98c6b0147fe75cfa0dadbac upstream.
+
+While playing with growfs to create a 20TB realtime section on a
+filesystem that didn't previously have an rt section, I noticed that
+growfs would occasionally shut down the log due to a transaction
+reservation overflow.
+
+xfs_calc_growrtfree_reservation uses the current size of the realtime
+summary file (m_rsumsize) to compute the transaction reservation for a
+growrtfree transaction.  The reservations are computed at mount time,
+which means that m_rsumsize is zero when growfs starts "freeing" the new
+realtime extents into the rt volume.  As a result, the transaction is
+undersized and fails.
+
+Fix this by recomputing the transaction reservations every time we
+change m_rsumsize.
+
+Signed-off-by: Darrick J. Wong <djwong@kernel.org>
+Reviewed-by: Christoph Hellwig <hch@lst.de>
+Signed-off-by: Catherine Hoang <catherine.hoang@oracle.com>
+Acked-by: Darrick J. Wong <djwong@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/xfs/xfs_rtalloc.c |    5 +++++
+ 1 file changed, 5 insertions(+)
+
+--- a/fs/xfs/xfs_rtalloc.c
++++ b/fs/xfs/xfs_rtalloc.c
+@@ -1070,6 +1070,9 @@ xfs_growfs_rt(
+                       nsbp->sb_rbmblocks;
+               nrsumblocks = XFS_B_TO_FSB(mp, nrsumsize);
+               nmp->m_rsumsize = nrsumsize = XFS_FSB_TO_B(mp, nrsumblocks);
++              /* recompute growfsrt reservation from new rsumsize */
++              xfs_trans_resv_calc(nmp, &nmp->m_resv);
++
+               /*
+                * Start a transaction, get the log reservation.
+                */
+@@ -1153,6 +1156,8 @@ error_cancel:
+                */
+               mp->m_rsumlevels = nrsumlevels;
+               mp->m_rsumsize = nrsumsize;
++              /* recompute growfsrt reservation from new rsumsize */
++              xfs_trans_resv_calc(mp, &mp->m_resv);
+               error = xfs_trans_commit(tp);
+               if (error)
diff --git a/queue-6.6/xfs-remove-conditional-building-of-rt-geometry-validator-functions.patch b/queue-6.6/xfs-remove-conditional-building-of-rt-geometry-validator-functions.patch
new file mode 100644 (file)
index 0000000..ad58c30
--- /dev/null
@@ -0,0 +1,176 @@
+From stable+bounces-32433-greg=kroah.com@vger.kernel.org Wed Mar 27 01:14:06 2024
+From: Catherine Hoang <catherine.hoang@oracle.com>
+Date: Tue, 26 Mar 2024 17:12:33 -0700
+Subject: xfs: remove conditional building of rt geometry validator functions
+To: stable@vger.kernel.org
+Cc: linux-xfs@vger.kernel.org
+Message-ID: <20240327001233.51675-25-catherine.hoang@oracle.com>
+
+From: "Darrick J. Wong" <djwong@kernel.org>
+
+commit 881f78f472556ed05588172d5b5676b48dc48240 upstream.
+
+[backport: resolve merge conflicts due to refactoring rtbitmap/summary
+macros and accessors]
+
+I mistakenly turned off CONFIG_XFS_RT in the Kconfig file for arm64
+variant of the djwong-wtf git branch.  Unfortunately, it took me a good
+hour to figure out that RT wasn't built because this is what got printed
+to dmesg:
+
+XFS (sda2): realtime geometry sanity check failed
+XFS (sda2): Metadata corruption detected at xfs_sb_read_verify+0x170/0x190 [xfs], xfs_sb block 0x0
+
+Whereas I would have expected:
+
+XFS (sda2): Not built with CONFIG_XFS_RT
+XFS (sda2): RT mount failed
+
+The root cause of these problems is the conditional compilation of the
+new functions xfs_validate_rtextents and xfs_compute_rextslog that I
+introduced in the two commits listed below.  The !RT versions of these
+functions return false and 0, respectively, which causes primary
+superblock validation to fail, which explains the first message.
+
+Move the two functions to other parts of libxfs that are not
+conditionally defined by CONFIG_XFS_RT and remove the broken stubs so
+that validation works again.
+
+Fixes: e14293803f4e ("xfs: don't allow overly small or large realtime volumes")
+Fixes: a6a38f309afc ("xfs: make rextslog computation consistent with mkfs")
+Signed-off-by: "Darrick J. Wong" <djwong@kernel.org>
+Reviewed-by: Christoph Hellwig <hch@lst.de>
+Signed-off-by: Chandan Babu R <chandanbabu@kernel.org>
+Signed-off-by: Catherine Hoang <catherine.hoang@oracle.com>
+Acked-by: Darrick J. Wong <djwong@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/xfs/libxfs/xfs_rtbitmap.c |   14 --------------
+ fs/xfs/libxfs/xfs_rtbitmap.h |   16 ----------------
+ fs/xfs/libxfs/xfs_sb.c       |   14 ++++++++++++++
+ fs/xfs/libxfs/xfs_sb.h       |    2 ++
+ fs/xfs/libxfs/xfs_types.h    |   12 ++++++++++++
+ fs/xfs/scrub/rtbitmap.c      |    1 +
+ fs/xfs/scrub/rtsummary.c     |    1 +
+ 7 files changed, 30 insertions(+), 30 deletions(-)
+
+--- a/fs/xfs/libxfs/xfs_rtbitmap.c
++++ b/fs/xfs/libxfs/xfs_rtbitmap.c
+@@ -1131,17 +1131,3 @@ xfs_rtalloc_extent_is_free(
+       return 0;
+ }
+-/*
+- * Compute the maximum level number of the realtime summary file, as defined by
+- * mkfs.  The historic use of highbit32 on a 64-bit quantity prohibited correct
+- * use of rt volumes with more than 2^32 extents.
+- */
+-uint8_t
+-xfs_compute_rextslog(
+-      xfs_rtbxlen_t           rtextents)
+-{
+-      if (!rtextents)
+-              return 0;
+-      return xfs_highbit64(rtextents);
+-}
+-
+--- a/fs/xfs/libxfs/xfs_rtbitmap.h
++++ b/fs/xfs/libxfs/xfs_rtbitmap.h
+@@ -71,20 +71,6 @@ xfs_rtfree_extent(
+ int xfs_rtfree_blocks(struct xfs_trans *tp, xfs_fsblock_t rtbno,
+               xfs_filblks_t rtlen);
+-uint8_t xfs_compute_rextslog(xfs_rtbxlen_t rtextents);
+-
+-/* Do we support an rt volume having this number of rtextents? */
+-static inline bool
+-xfs_validate_rtextents(
+-      xfs_rtbxlen_t           rtextents)
+-{
+-      /* No runt rt volumes */
+-      if (rtextents == 0)
+-              return false;
+-
+-      return true;
+-}
+-
+ #else /* CONFIG_XFS_RT */
+ # define xfs_rtfree_extent(t,b,l)                     (-ENOSYS)
+ # define xfs_rtfree_blocks(t,rb,rl)                   (-ENOSYS)
+@@ -92,8 +78,6 @@ xfs_validate_rtextents(
+ # define xfs_rtalloc_query_all(m,t,f,p)                       (-ENOSYS)
+ # define xfs_rtbuf_get(m,t,b,i,p)                     (-ENOSYS)
+ # define xfs_rtalloc_extent_is_free(m,t,s,l,i)                (-ENOSYS)
+-# define xfs_compute_rextslog(rtx)                    (0)
+-# define xfs_validate_rtextents(rtx)                  (false)
+ #endif /* CONFIG_XFS_RT */
+ #endif /* __XFS_RTBITMAP_H__ */
+--- a/fs/xfs/libxfs/xfs_sb.c
++++ b/fs/xfs/libxfs/xfs_sb.c
+@@ -1375,3 +1375,17 @@ xfs_validate_stripe_geometry(
+       }
+       return true;
+ }
++
++/*
++ * Compute the maximum level number of the realtime summary file, as defined by
++ * mkfs.  The historic use of highbit32 on a 64-bit quantity prohibited correct
++ * use of rt volumes with more than 2^32 extents.
++ */
++uint8_t
++xfs_compute_rextslog(
++      xfs_rtbxlen_t           rtextents)
++{
++      if (!rtextents)
++              return 0;
++      return xfs_highbit64(rtextents);
++}
+--- a/fs/xfs/libxfs/xfs_sb.h
++++ b/fs/xfs/libxfs/xfs_sb.h
+@@ -38,4 +38,6 @@ extern int   xfs_sb_get_secondary(struct x
+ extern bool   xfs_validate_stripe_geometry(struct xfs_mount *mp,
+               __s64 sunit, __s64 swidth, int sectorsize, bool silent);
++uint8_t xfs_compute_rextslog(xfs_rtbxlen_t rtextents);
++
+ #endif        /* __XFS_SB_H__ */
+--- a/fs/xfs/libxfs/xfs_types.h
++++ b/fs/xfs/libxfs/xfs_types.h
+@@ -240,4 +240,16 @@ bool xfs_verify_fileoff(struct xfs_mount
+ bool xfs_verify_fileext(struct xfs_mount *mp, xfs_fileoff_t off,
+               xfs_fileoff_t len);
++/* Do we support an rt volume having this number of rtextents? */
++static inline bool
++xfs_validate_rtextents(
++      xfs_rtbxlen_t           rtextents)
++{
++      /* No runt rt volumes */
++      if (rtextents == 0)
++              return false;
++
++      return true;
++}
++
+ #endif        /* __XFS_TYPES_H__ */
+--- a/fs/xfs/scrub/rtbitmap.c
++++ b/fs/xfs/scrub/rtbitmap.c
+@@ -14,6 +14,7 @@
+ #include "xfs_rtbitmap.h"
+ #include "xfs_inode.h"
+ #include "xfs_bmap.h"
++#include "xfs_sb.h"
+ #include "scrub/scrub.h"
+ #include "scrub/common.h"
+--- a/fs/xfs/scrub/rtsummary.c
++++ b/fs/xfs/scrub/rtsummary.c
+@@ -16,6 +16,7 @@
+ #include "xfs_rtbitmap.h"
+ #include "xfs_bit.h"
+ #include "xfs_bmap.h"
++#include "xfs_sb.h"
+ #include "scrub/scrub.h"
+ #include "scrub/common.h"
+ #include "scrub/trace.h"
diff --git a/queue-6.6/xfs-remove-unused-fields-from-struct-xbtree_ifakeroot.patch b/queue-6.6/xfs-remove-unused-fields-from-struct-xbtree_ifakeroot.patch
new file mode 100644 (file)
index 0000000..f1fb152
--- /dev/null
@@ -0,0 +1,42 @@
+From stable+bounces-32421-greg=kroah.com@vger.kernel.org Wed Mar 27 01:13:30 2024
+From: Catherine Hoang <catherine.hoang@oracle.com>
+Date: Tue, 26 Mar 2024 17:12:21 -0700
+Subject: xfs: remove unused fields from struct xbtree_ifakeroot
+To: stable@vger.kernel.org
+Cc: linux-xfs@vger.kernel.org
+Message-ID: <20240327001233.51675-13-catherine.hoang@oracle.com>
+
+From: "Darrick J. Wong" <djwong@kernel.org>
+
+commit 4c8ecd1cfdd01fb727121035014d9f654a30bdf2 upstream.
+
+Remove these unused fields since nobody uses them.  They should have
+been removed years ago in a different cleanup series from Christoph
+Hellwig.
+
+Fixes: daf83964a3681 ("xfs: move the per-fork nextents fields into struct xfs_ifork")
+Fixes: f7e67b20ecbbc ("xfs: move the fork format fields into struct xfs_ifork")
+Signed-off-by: Darrick J. Wong <djwong@kernel.org>
+Reviewed-by: Dave Chinner <dchinner@redhat.com>
+Signed-off-by: Catherine Hoang <catherine.hoang@oracle.com>
+Acked-by: Darrick J. Wong <djwong@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/xfs/libxfs/xfs_btree_staging.h |    6 ------
+ 1 file changed, 6 deletions(-)
+
+--- a/fs/xfs/libxfs/xfs_btree_staging.h
++++ b/fs/xfs/libxfs/xfs_btree_staging.h
+@@ -37,12 +37,6 @@ struct xbtree_ifakeroot {
+       /* Number of bytes available for this fork in the inode. */
+       unsigned int            if_fork_size;
+-
+-      /* Fork format. */
+-      unsigned int            if_format;
+-
+-      /* Number of records. */
+-      unsigned int            if_extents;
+ };
+ /* Cursor interactions with fake roots for inode-rooted btrees. */
diff --git a/queue-6.6/xfs-reset-xfs_attr_incomplete-filter-on-node-removal.patch b/queue-6.6/xfs-reset-xfs_attr_incomplete-filter-on-node-removal.patch
new file mode 100644 (file)
index 0000000..afc48bf
--- /dev/null
@@ -0,0 +1,51 @@
+From stable+bounces-32432-greg=kroah.com@vger.kernel.org Wed Mar 27 01:14:02 2024
+From: Catherine Hoang <catherine.hoang@oracle.com>
+Date: Tue, 26 Mar 2024 17:12:32 -0700
+Subject: xfs: reset XFS_ATTR_INCOMPLETE filter on node removal
+To: stable@vger.kernel.org
+Cc: linux-xfs@vger.kernel.org
+Message-ID: <20240327001233.51675-24-catherine.hoang@oracle.com>
+
+From: Andrey Albershteyn <aalbersh@redhat.com>
+
+commit 82ef1a5356572219f41f9123ca047259a77bd67b upstream.
+
+In XFS_DAS_NODE_REMOVE_ATTR case, xfs_attr_mode_remove_attr() sets
+filter to XFS_ATTR_INCOMPLETE. The filter is then reset in
+xfs_attr_complete_op() if XFS_DA_OP_REPLACE operation is performed.
+
+The filter is not reset though if XFS just removes the attribute
+(args->value == NULL) with xfs_attr_defer_remove(). attr code goes
+to XFS_DAS_DONE state.
+
+Fix this by always resetting XFS_ATTR_INCOMPLETE filter. The replace
+operation already resets this filter in anyway and others are
+completed at this step hence don't need it.
+
+Fixes: fdaf1bb3cafc ("xfs: ATTR_REPLACE algorithm with LARP enabled needs rework")
+Signed-off-by: Andrey Albershteyn <aalbersh@redhat.com>
+Reviewed-by: Christoph Hellwig <hch@lst.de>
+Signed-off-by: Chandan Babu R <chandanbabu@kernel.org>
+Signed-off-by: Catherine Hoang <catherine.hoang@oracle.com>
+Acked-by: Darrick J. Wong <djwong@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/xfs/libxfs/xfs_attr.c |    6 +++---
+ 1 file changed, 3 insertions(+), 3 deletions(-)
+
+--- a/fs/xfs/libxfs/xfs_attr.c
++++ b/fs/xfs/libxfs/xfs_attr.c
+@@ -421,10 +421,10 @@ xfs_attr_complete_op(
+       bool                    do_replace = args->op_flags & XFS_DA_OP_REPLACE;
+       args->op_flags &= ~XFS_DA_OP_REPLACE;
+-      if (do_replace) {
+-              args->attr_filter &= ~XFS_ATTR_INCOMPLETE;
++      args->attr_filter &= ~XFS_ATTR_INCOMPLETE;
++      if (do_replace)
+               return replace_state;
+-      }
++
+       return XFS_DAS_DONE;
+ }
diff --git a/queue-6.6/xfs-short-circuit-xfs_growfs_data_private-if-delta-is-zero.patch b/queue-6.6/xfs-short-circuit-xfs_growfs_data_private-if-delta-is-zero.patch
new file mode 100644 (file)
index 0000000..f34f0cf
--- /dev/null
@@ -0,0 +1,51 @@
+From stable+bounces-32426-greg=kroah.com@vger.kernel.org Wed Mar 27 01:13:46 2024
+From: Catherine Hoang <catherine.hoang@oracle.com>
+Date: Tue, 26 Mar 2024 17:12:27 -0700
+Subject: xfs: short circuit xfs_growfs_data_private() if delta is zero
+To: stable@vger.kernel.org
+Cc: linux-xfs@vger.kernel.org
+Message-ID: <20240327001233.51675-19-catherine.hoang@oracle.com>
+
+From: Eric Sandeen <sandeen@redhat.com>
+
+commit 84712492e6dab803bf595fb8494d11098b74a652 upstream.
+
+Although xfs_growfs_data() doesn't call xfs_growfs_data_private()
+if in->newblocks == mp->m_sb.sb_dblocks, xfs_growfs_data_private()
+further massages the new block count so that we don't i.e. try
+to create a too-small new AG.
+
+This may lead to a delta of "0" in xfs_growfs_data_private(), so
+we end up in the shrink case and emit the EXPERIMENTAL warning
+even if we're not changing anything at all.
+
+Fix this by returning straightaway if the block delta is zero.
+
+(nb: in older kernels, the result of entering the shrink case
+with delta == 0 may actually let an -ENOSPC escape to userspace,
+which is confusing for users.)
+
+Fixes: fb2fc1720185 ("xfs: support shrinking unused space in the last AG")
+Signed-off-by: Eric Sandeen <sandeen@redhat.com>
+Reviewed-by: "Darrick J. Wong" <djwong@kernel.org>
+Signed-off-by: Chandan Babu R <chandanbabu@kernel.org>
+Signed-off-by: Catherine Hoang <catherine.hoang@oracle.com>
+Acked-by: Darrick J. Wong <djwong@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/xfs/xfs_fsops.c |    4 ++++
+ 1 file changed, 4 insertions(+)
+
+--- a/fs/xfs/xfs_fsops.c
++++ b/fs/xfs/xfs_fsops.c
+@@ -134,6 +134,10 @@ xfs_growfs_data_private(
+       if (delta < 0 && nagcount < 2)
+               return -EINVAL;
++      /* No work to do */
++      if (delta == 0)
++              return 0;
++
+       oagcount = mp->m_sb.sb_agcount;
+       /* allocate the new per-ag structures */
+       if (nagcount > oagcount) {
diff --git a/queue-6.6/xfs-transfer-recovered-intent-item-ownership-in-iop_recover.patch b/queue-6.6/xfs-transfer-recovered-intent-item-ownership-in-iop_recover.patch
new file mode 100644 (file)
index 0000000..810a65c
--- /dev/null
@@ -0,0 +1,137 @@
+From stable+bounces-32416-greg=kroah.com@vger.kernel.org Wed Mar 27 01:13:15 2024
+From: Catherine Hoang <catherine.hoang@oracle.com>
+Date: Tue, 26 Mar 2024 17:12:16 -0700
+Subject: xfs: transfer recovered intent item ownership in ->iop_recover
+To: stable@vger.kernel.org
+Cc: linux-xfs@vger.kernel.org
+Message-ID: <20240327001233.51675-8-catherine.hoang@oracle.com>
+
+From: "Darrick J. Wong" <djwong@kernel.org>
+
+commit deb4cd8ba87f17b12c72b3827820d9c703e9fd95 upstream.
+
+Now that we pass the xfs_defer_pending object into the intent item
+recovery functions, we know exactly when ownership of the sole refcount
+passes from the recovery context to the intent done item.  At that
+point, we need to null out dfp_intent so that the recovery mechanism
+won't release it.  This should fix the UAF problem reported by Long Li.
+
+Note that we still want to recreate the full deferred work state.  That
+will be addressed in the next patches.
+
+Fixes: 2e76f188fd90 ("xfs: cancel intents immediately if process_intents fails")
+Signed-off-by: Darrick J. Wong <djwong@kernel.org>
+Reviewed-by: Christoph Hellwig <hch@lst.de>
+Signed-off-by: Catherine Hoang <catherine.hoang@oracle.com>
+Acked-by: Darrick J. Wong <djwong@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/xfs/libxfs/xfs_log_recover.h |    2 ++
+ fs/xfs/xfs_attr_item.c          |    1 +
+ fs/xfs/xfs_bmap_item.c          |    2 ++
+ fs/xfs/xfs_extfree_item.c       |    2 ++
+ fs/xfs/xfs_log_recover.c        |   19 ++++++++++++-------
+ fs/xfs/xfs_refcount_item.c      |    1 +
+ fs/xfs/xfs_rmap_item.c          |    2 ++
+ 7 files changed, 22 insertions(+), 7 deletions(-)
+
+--- a/fs/xfs/libxfs/xfs_log_recover.h
++++ b/fs/xfs/libxfs/xfs_log_recover.h
+@@ -155,5 +155,7 @@ xlog_recover_resv(const struct xfs_trans
+ void xlog_recover_intent_item(struct xlog *log, struct xfs_log_item *lip,
+               xfs_lsn_t lsn, unsigned int dfp_type);
++void xlog_recover_transfer_intent(struct xfs_trans *tp,
++              struct xfs_defer_pending *dfp);
+ #endif        /* __XFS_LOG_RECOVER_H__ */
+--- a/fs/xfs/xfs_attr_item.c
++++ b/fs/xfs/xfs_attr_item.c
+@@ -632,6 +632,7 @@ xfs_attri_item_recover(
+       args->trans = tp;
+       done_item = xfs_trans_get_attrd(tp, attrip);
++      xlog_recover_transfer_intent(tp, dfp);
+       xfs_ilock(ip, XFS_ILOCK_EXCL);
+       xfs_trans_ijoin(tp, ip, 0);
+--- a/fs/xfs/xfs_bmap_item.c
++++ b/fs/xfs/xfs_bmap_item.c
+@@ -524,6 +524,8 @@ xfs_bui_item_recover(
+               goto err_rele;
+       budp = xfs_trans_get_bud(tp, buip);
++      xlog_recover_transfer_intent(tp, dfp);
++
+       xfs_ilock(ip, XFS_ILOCK_EXCL);
+       xfs_trans_ijoin(tp, ip, 0);
+--- a/fs/xfs/xfs_extfree_item.c
++++ b/fs/xfs/xfs_extfree_item.c
+@@ -689,7 +689,9 @@ xfs_efi_item_recover(
+       error = xfs_trans_alloc(mp, &resv, 0, 0, 0, &tp);
+       if (error)
+               return error;
++
+       efdp = xfs_trans_get_efd(tp, efip, efip->efi_format.efi_nextents);
++      xlog_recover_transfer_intent(tp, dfp);
+       for (i = 0; i < efip->efi_format.efi_nextents; i++) {
+               struct xfs_extent_free_item     fake = {
+--- a/fs/xfs/xfs_log_recover.c
++++ b/fs/xfs/xfs_log_recover.c
+@@ -2590,13 +2590,6 @@ xlog_recover_process_intents(
+                       break;
+               }
+-              /*
+-               * XXX: @lip could have been freed, so detach the log item from
+-               * the pending item before freeing the pending item.  This does
+-               * not fix the existing UAF bug that occurs if ->iop_recover
+-               * fails after creating the intent done item.
+-               */
+-              dfp->dfp_intent = NULL;
+               xfs_defer_cancel_recovery(log->l_mp, dfp);
+       }
+       if (error)
+@@ -2631,6 +2624,18 @@ xlog_recover_cancel_intents(
+ }
+ /*
++ * Transfer ownership of the recovered log intent item to the recovery
++ * transaction.
++ */
++void
++xlog_recover_transfer_intent(
++      struct xfs_trans                *tp,
++      struct xfs_defer_pending        *dfp)
++{
++      dfp->dfp_intent = NULL;
++}
++
++/*
+  * This routine performs a transaction to null out a bad inode pointer
+  * in an agi unlinked inode hash bucket.
+  */
+--- a/fs/xfs/xfs_refcount_item.c
++++ b/fs/xfs/xfs_refcount_item.c
+@@ -523,6 +523,7 @@ xfs_cui_item_recover(
+               return error;
+       cudp = xfs_trans_get_cud(tp, cuip);
++      xlog_recover_transfer_intent(tp, dfp);
+       for (i = 0; i < cuip->cui_format.cui_nextents; i++) {
+               struct xfs_refcount_intent      fake = { };
+--- a/fs/xfs/xfs_rmap_item.c
++++ b/fs/xfs/xfs_rmap_item.c
+@@ -537,7 +537,9 @@ xfs_rui_item_recover(
+                       XFS_TRANS_RESERVE, &tp);
+       if (error)
+               return error;
++
+       rudp = xfs_trans_get_rud(tp, ruip);
++      xlog_recover_transfer_intent(tp, dfp);
+       for (i = 0; i < ruip->rui_format.rui_nextents; i++) {
+               struct xfs_rmap_intent  fake = { };
diff --git a/queue-6.6/xfs-update-dir3-leaf-block-metadata-after-swap.patch b/queue-6.6/xfs-update-dir3-leaf-block-metadata-after-swap.patch
new file mode 100644 (file)
index 0000000..a615a35
--- /dev/null
@@ -0,0 +1,71 @@
+From stable+bounces-32431-greg=kroah.com@vger.kernel.org Wed Mar 27 01:13:59 2024
+From: Catherine Hoang <catherine.hoang@oracle.com>
+Date: Tue, 26 Mar 2024 17:12:31 -0700
+Subject: xfs: update dir3 leaf block metadata after swap
+To: stable@vger.kernel.org
+Cc: linux-xfs@vger.kernel.org
+Message-ID: <20240327001233.51675-23-catherine.hoang@oracle.com>
+
+From: Zhang Tianci <zhangtianci.1997@bytedance.com>
+
+commit 5759aa4f956034b289b0ae2c99daddfc775442e1 upstream.
+
+xfs_da3_swap_lastblock() copy the last block content to the dead block,
+but do not update the metadata in it. We need update some metadata
+for some kinds of type block, such as dir3 leafn block records its
+blkno, we shall update it to the dead block blkno. Otherwise,
+before write the xfs_buf to disk, the verify_write() will fail in
+blk_hdr->blkno != xfs_buf->b_bn, then xfs will be shutdown.
+
+We will get this warning:
+
+  XFS (dm-0): Metadata corruption detected at xfs_dir3_leaf_verify+0xa8/0xe0 [xfs], xfs_dir3_leafn block 0x178
+  XFS (dm-0): Unmount and run xfs_repair
+  XFS (dm-0): First 128 bytes of corrupted metadata buffer:
+  00000000e80f1917: 00 80 00 0b 00 80 00 07 3d ff 00 00 00 00 00 00  ........=.......
+  000000009604c005: 00 00 00 00 00 00 01 a0 00 00 00 00 00 00 00 00  ................
+  000000006b6fb2bf: e4 44 e3 97 b5 64 44 41 8b 84 60 0e 50 43 d9 bf  .D...dDA..`.PC..
+  00000000678978a2: 00 00 00 00 00 00 00 83 01 73 00 93 00 00 00 00  .........s......
+  00000000b28b247c: 99 29 1d 38 00 00 00 00 99 29 1d 40 00 00 00 00  .).8.....).@....
+  000000002b2a662c: 99 29 1d 48 00 00 00 00 99 49 11 00 00 00 00 00  .).H.....I......
+  00000000ea2ffbb8: 99 49 11 08 00 00 45 25 99 49 11 10 00 00 48 fe  .I....E%.I....H.
+  0000000069e86440: 99 49 11 18 00 00 4c 6b 99 49 11 20 00 00 4d 97  .I....Lk.I. ..M.
+  XFS (dm-0): xfs_do_force_shutdown(0x8) called from line 1423 of file fs/xfs/xfs_buf.c.  Return address = 00000000c0ff63c1
+  XFS (dm-0): Corruption of in-memory data detected.  Shutting down filesystem
+  XFS (dm-0): Please umount the filesystem and rectify the problem(s)
+
+>>From the log above, we know xfs_buf->b_no is 0x178, but the block's hdr record
+its blkno is 0x1a0.
+
+Fixes: 24df33b45ecf ("xfs: add CRC checking to dir2 leaf blocks")
+Signed-off-by: Zhang Tianci <zhangtianci.1997@bytedance.com>
+Suggested-by: Dave Chinner <david@fromorbit.com>
+Reviewed-by: "Darrick J. Wong" <djwong@kernel.org>
+Signed-off-by: Chandan Babu R <chandanbabu@kernel.org>
+Signed-off-by: Catherine Hoang <catherine.hoang@oracle.com>
+Acked-by: Darrick J. Wong <djwong@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/xfs/libxfs/xfs_da_btree.c |    7 +++++++
+ 1 file changed, 7 insertions(+)
+
+--- a/fs/xfs/libxfs/xfs_da_btree.c
++++ b/fs/xfs/libxfs/xfs_da_btree.c
+@@ -2316,10 +2316,17 @@ xfs_da3_swap_lastblock(
+               return error;
+       /*
+        * Copy the last block into the dead buffer and log it.
++       * On CRC-enabled file systems, also update the stamped in blkno.
+        */
+       memcpy(dead_buf->b_addr, last_buf->b_addr, args->geo->blksize);
++      if (xfs_has_crc(mp)) {
++              struct xfs_da3_blkinfo *da3 = dead_buf->b_addr;
++
++              da3->blkno = cpu_to_be64(xfs_buf_daddr(dead_buf));
++      }
+       xfs_trans_log_buf(tp, dead_buf, 0, args->geo->blksize - 1);
+       dead_info = dead_buf->b_addr;
++
+       /*
+        * Get values from the moved block.
+        */
diff --git a/queue-6.6/xfs-use-xfs_defer_pending-objects-to-recover-intent-items.patch b/queue-6.6/xfs-use-xfs_defer_pending-objects-to-recover-intent-items.patch
new file mode 100644 (file)
index 0000000..0a25adc
--- /dev/null
@@ -0,0 +1,524 @@
+From stable+bounces-32414-greg=kroah.com@vger.kernel.org Wed Mar 27 01:13:09 2024
+From: Catherine Hoang <catherine.hoang@oracle.com>
+Date: Tue, 26 Mar 2024 17:12:14 -0700
+Subject: xfs: use xfs_defer_pending objects to recover intent items
+To: stable@vger.kernel.org
+Cc: linux-xfs@vger.kernel.org
+Message-ID: <20240327001233.51675-6-catherine.hoang@oracle.com>
+
+From: "Darrick J. Wong" <djwong@kernel.org>
+
+commit 03f7767c9f6120ac933378fdec3bfd78bf07bc11 upstream.
+
+One thing I never quite got around to doing is porting the log intent
+item recovery code to reconstruct the deferred pending work state.  As a
+result, each intent item open codes xfs_defer_finish_one in its recovery
+method, because that's what the EFI code did before xfs_defer.c even
+existed.
+
+This is a gross thing to have left unfixed -- if an EFI cannot proceed
+due to busy extents, we end up creating separate new EFIs for each
+unfinished work item, which is a change in behavior from what runtime
+would have done.
+
+Worse yet, Long Li pointed out that there's a UAF in the recovery code.
+The ->commit_pass2 function adds the intent item to the AIL and drops
+the refcount.  The one remaining refcount is now owned by the recovery
+mechanism (aka the log intent items in the AIL) with the intent of
+giving the refcount to the intent done item in the ->iop_recover
+function.
+
+However, if something fails later in recovery, xlog_recover_finish will
+walk the recovered intent items in the AIL and release them.  If the CIL
+hasn't been pushed before that point (which is possible since we don't
+force the log until later) then the intent done release will try to free
+its associated intent, which has already been freed.
+
+This patch starts to address this mess by having the ->commit_pass2
+functions recreate the xfs_defer_pending state.  The next few patches
+will fix the recovery functions.
+
+Signed-off-by: Darrick J. Wong <djwong@kernel.org>
+Reviewed-by: Christoph Hellwig <hch@lst.de>
+Signed-off-by: Catherine Hoang <catherine.hoang@oracle.com>
+Acked-by: Darrick J. Wong <djwong@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/xfs/libxfs/xfs_defer.c       |  105 ++++++++++++++++++++++++++----------
+ fs/xfs/libxfs/xfs_defer.h       |    5 +
+ fs/xfs/libxfs/xfs_log_recover.h |    3 +
+ fs/xfs/xfs_attr_item.c          |   10 ---
+ fs/xfs/xfs_bmap_item.c          |    9 +--
+ fs/xfs/xfs_extfree_item.c       |    9 +--
+ fs/xfs/xfs_log.c                |    1 
+ fs/xfs/xfs_log_priv.h           |    1 
+ fs/xfs/xfs_log_recover.c        |  115 ++++++++++++++++++++--------------------
+ fs/xfs/xfs_refcount_item.c      |    9 +--
+ fs/xfs/xfs_rmap_item.c          |    9 +--
+ 11 files changed, 159 insertions(+), 117 deletions(-)
+
+--- a/fs/xfs/libxfs/xfs_defer.c
++++ b/fs/xfs/libxfs/xfs_defer.c
+@@ -245,23 +245,53 @@ xfs_defer_create_intents(
+       return ret;
+ }
+-STATIC void
++static inline void
+ xfs_defer_pending_abort(
+       struct xfs_mount                *mp,
++      struct xfs_defer_pending        *dfp)
++{
++      const struct xfs_defer_op_type  *ops = defer_op_types[dfp->dfp_type];
++
++      trace_xfs_defer_pending_abort(mp, dfp);
++
++      if (dfp->dfp_intent && !dfp->dfp_done) {
++              ops->abort_intent(dfp->dfp_intent);
++              dfp->dfp_intent = NULL;
++      }
++}
++
++static inline void
++xfs_defer_pending_cancel_work(
++      struct xfs_mount                *mp,
++      struct xfs_defer_pending        *dfp)
++{
++      const struct xfs_defer_op_type  *ops = defer_op_types[dfp->dfp_type];
++      struct list_head                *pwi;
++      struct list_head                *n;
++
++      trace_xfs_defer_cancel_list(mp, dfp);
++
++      list_del(&dfp->dfp_list);
++      list_for_each_safe(pwi, n, &dfp->dfp_work) {
++              list_del(pwi);
++              dfp->dfp_count--;
++              trace_xfs_defer_cancel_item(mp, dfp, pwi);
++              ops->cancel_item(pwi);
++      }
++      ASSERT(dfp->dfp_count == 0);
++      kmem_cache_free(xfs_defer_pending_cache, dfp);
++}
++
++STATIC void
++xfs_defer_pending_abort_list(
++      struct xfs_mount                *mp,
+       struct list_head                *dop_list)
+ {
+       struct xfs_defer_pending        *dfp;
+-      const struct xfs_defer_op_type  *ops;
+       /* Abort intent items that don't have a done item. */
+-      list_for_each_entry(dfp, dop_list, dfp_list) {
+-              ops = defer_op_types[dfp->dfp_type];
+-              trace_xfs_defer_pending_abort(mp, dfp);
+-              if (dfp->dfp_intent && !dfp->dfp_done) {
+-                      ops->abort_intent(dfp->dfp_intent);
+-                      dfp->dfp_intent = NULL;
+-              }
+-      }
++      list_for_each_entry(dfp, dop_list, dfp_list)
++              xfs_defer_pending_abort(mp, dfp);
+ }
+ /* Abort all the intents that were committed. */
+@@ -271,7 +301,7 @@ xfs_defer_trans_abort(
+       struct list_head                *dop_pending)
+ {
+       trace_xfs_defer_trans_abort(tp, _RET_IP_);
+-      xfs_defer_pending_abort(tp->t_mountp, dop_pending);
++      xfs_defer_pending_abort_list(tp->t_mountp, dop_pending);
+ }
+ /*
+@@ -389,27 +419,13 @@ xfs_defer_cancel_list(
+ {
+       struct xfs_defer_pending        *dfp;
+       struct xfs_defer_pending        *pli;
+-      struct list_head                *pwi;
+-      struct list_head                *n;
+-      const struct xfs_defer_op_type  *ops;
+       /*
+        * Free the pending items.  Caller should already have arranged
+        * for the intent items to be released.
+        */
+-      list_for_each_entry_safe(dfp, pli, dop_list, dfp_list) {
+-              ops = defer_op_types[dfp->dfp_type];
+-              trace_xfs_defer_cancel_list(mp, dfp);
+-              list_del(&dfp->dfp_list);
+-              list_for_each_safe(pwi, n, &dfp->dfp_work) {
+-                      list_del(pwi);
+-                      dfp->dfp_count--;
+-                      trace_xfs_defer_cancel_item(mp, dfp, pwi);
+-                      ops->cancel_item(pwi);
+-              }
+-              ASSERT(dfp->dfp_count == 0);
+-              kmem_cache_free(xfs_defer_pending_cache, dfp);
+-      }
++      list_for_each_entry_safe(dfp, pli, dop_list, dfp_list)
++              xfs_defer_pending_cancel_work(mp, dfp);
+ }
+ /*
+@@ -666,6 +682,39 @@ xfs_defer_add(
+ }
+ /*
++ * Create a pending deferred work item to replay the recovered intent item
++ * and add it to the list.
++ */
++void
++xfs_defer_start_recovery(
++      struct xfs_log_item             *lip,
++      enum xfs_defer_ops_type         dfp_type,
++      struct list_head                *r_dfops)
++{
++      struct xfs_defer_pending        *dfp;
++
++      dfp = kmem_cache_zalloc(xfs_defer_pending_cache,
++                      GFP_NOFS | __GFP_NOFAIL);
++      dfp->dfp_type = dfp_type;
++      dfp->dfp_intent = lip;
++      INIT_LIST_HEAD(&dfp->dfp_work);
++      list_add_tail(&dfp->dfp_list, r_dfops);
++}
++
++/*
++ * Cancel a deferred work item created to recover a log intent item.  @dfp
++ * will be freed after this function returns.
++ */
++void
++xfs_defer_cancel_recovery(
++      struct xfs_mount                *mp,
++      struct xfs_defer_pending        *dfp)
++{
++      xfs_defer_pending_abort(mp, dfp);
++      xfs_defer_pending_cancel_work(mp, dfp);
++}
++
++/*
+  * Move deferred ops from one transaction to another and reset the source to
+  * initial state. This is primarily used to carry state forward across
+  * transaction rolls with pending dfops.
+@@ -769,7 +818,7 @@ xfs_defer_ops_capture_abort(
+ {
+       unsigned short                  i;
+-      xfs_defer_pending_abort(mp, &dfc->dfc_dfops);
++      xfs_defer_pending_abort_list(mp, &dfc->dfc_dfops);
+       xfs_defer_cancel_list(mp, &dfc->dfc_dfops);
+       for (i = 0; i < dfc->dfc_held.dr_bufs; i++)
+--- a/fs/xfs/libxfs/xfs_defer.h
++++ b/fs/xfs/libxfs/xfs_defer.h
+@@ -125,6 +125,11 @@ void xfs_defer_ops_capture_abort(struct
+               struct xfs_defer_capture *d);
+ void xfs_defer_resources_rele(struct xfs_defer_resources *dres);
++void xfs_defer_start_recovery(struct xfs_log_item *lip,
++              enum xfs_defer_ops_type dfp_type, struct list_head *r_dfops);
++void xfs_defer_cancel_recovery(struct xfs_mount *mp,
++              struct xfs_defer_pending *dfp);
++
+ int __init xfs_defer_init_item_caches(void);
+ void xfs_defer_destroy_item_caches(void);
+--- a/fs/xfs/libxfs/xfs_log_recover.h
++++ b/fs/xfs/libxfs/xfs_log_recover.h
+@@ -153,4 +153,7 @@ xlog_recover_resv(const struct xfs_trans
+       return ret;
+ }
++void xlog_recover_intent_item(struct xlog *log, struct xfs_log_item *lip,
++              xfs_lsn_t lsn, unsigned int dfp_type);
++
+ #endif        /* __XFS_LOG_RECOVER_H__ */
+--- a/fs/xfs/xfs_attr_item.c
++++ b/fs/xfs/xfs_attr_item.c
+@@ -772,14 +772,8 @@ xlog_recover_attri_commit_pass2(
+       attrip = xfs_attri_init(mp, nv);
+       memcpy(&attrip->attri_format, attri_formatp, len);
+-      /*
+-       * The ATTRI has two references. One for the ATTRD and one for ATTRI to
+-       * ensure it makes it into the AIL. Insert the ATTRI into the AIL
+-       * directly and drop the ATTRI reference. Note that
+-       * xfs_trans_ail_update() drops the AIL lock.
+-       */
+-      xfs_trans_ail_insert(log->l_ailp, &attrip->attri_item, lsn);
+-      xfs_attri_release(attrip);
++      xlog_recover_intent_item(log, &attrip->attri_item, lsn,
++                      XFS_DEFER_OPS_TYPE_ATTR);
+       xfs_attri_log_nameval_put(nv);
+       return 0;
+ }
+--- a/fs/xfs/xfs_bmap_item.c
++++ b/fs/xfs/xfs_bmap_item.c
+@@ -681,12 +681,9 @@ xlog_recover_bui_commit_pass2(
+       buip = xfs_bui_init(mp);
+       xfs_bui_copy_format(&buip->bui_format, bui_formatp);
+       atomic_set(&buip->bui_next_extent, bui_formatp->bui_nextents);
+-      /*
+-       * Insert the intent into the AIL directly and drop one reference so
+-       * that finishing or canceling the work will drop the other.
+-       */
+-      xfs_trans_ail_insert(log->l_ailp, &buip->bui_item, lsn);
+-      xfs_bui_release(buip);
++
++      xlog_recover_intent_item(log, &buip->bui_item, lsn,
++                      XFS_DEFER_OPS_TYPE_BMAP);
+       return 0;
+ }
+--- a/fs/xfs/xfs_extfree_item.c
++++ b/fs/xfs/xfs_extfree_item.c
+@@ -820,12 +820,9 @@ xlog_recover_efi_commit_pass2(
+               return error;
+       }
+       atomic_set(&efip->efi_next_extent, efi_formatp->efi_nextents);
+-      /*
+-       * Insert the intent into the AIL directly and drop one reference so
+-       * that finishing or canceling the work will drop the other.
+-       */
+-      xfs_trans_ail_insert(log->l_ailp, &efip->efi_item, lsn);
+-      xfs_efi_release(efip);
++
++      xlog_recover_intent_item(log, &efip->efi_item, lsn,
++                      XFS_DEFER_OPS_TYPE_FREE);
+       return 0;
+ }
+--- a/fs/xfs/xfs_log.c
++++ b/fs/xfs/xfs_log.c
+@@ -1542,6 +1542,7 @@ xlog_alloc_log(
+       log->l_covered_state = XLOG_STATE_COVER_IDLE;
+       set_bit(XLOG_ACTIVE_RECOVERY, &log->l_opstate);
+       INIT_DELAYED_WORK(&log->l_work, xfs_log_worker);
++      INIT_LIST_HEAD(&log->r_dfops);
+       log->l_prev_block  = -1;
+       /* log->l_tail_lsn = 0x100000000LL; cycle = 1; current block = 0 */
+--- a/fs/xfs/xfs_log_priv.h
++++ b/fs/xfs/xfs_log_priv.h
+@@ -407,6 +407,7 @@ struct xlog {
+       long                    l_opstate;      /* operational state */
+       uint                    l_quotaoffs_flag; /* XFS_DQ_*, for QUOTAOFFs */
+       struct list_head        *l_buf_cancel_table;
++      struct list_head        r_dfops;        /* recovered log intent items */
+       int                     l_iclog_hsize;  /* size of iclog header */
+       int                     l_iclog_heads;  /* # of iclog header sectors */
+       uint                    l_sectBBsize;   /* sector size in BBs (2^n) */
+--- a/fs/xfs/xfs_log_recover.c
++++ b/fs/xfs/xfs_log_recover.c
+@@ -1723,30 +1723,24 @@ xlog_clear_stale_blocks(
+  */
+ void
+ xlog_recover_release_intent(
+-      struct xlog             *log,
+-      unsigned short          intent_type,
+-      uint64_t                intent_id)
++      struct xlog                     *log,
++      unsigned short                  intent_type,
++      uint64_t                        intent_id)
+ {
+-      struct xfs_ail_cursor   cur;
+-      struct xfs_log_item     *lip;
+-      struct xfs_ail          *ailp = log->l_ailp;
+-
+-      spin_lock(&ailp->ail_lock);
+-      for (lip = xfs_trans_ail_cursor_first(ailp, &cur, 0); lip != NULL;
+-           lip = xfs_trans_ail_cursor_next(ailp, &cur)) {
++      struct xfs_defer_pending        *dfp, *n;
++
++      list_for_each_entry_safe(dfp, n, &log->r_dfops, dfp_list) {
++              struct xfs_log_item     *lip = dfp->dfp_intent;
++
+               if (lip->li_type != intent_type)
+                       continue;
+               if (!lip->li_ops->iop_match(lip, intent_id))
+                       continue;
+-              spin_unlock(&ailp->ail_lock);
+-              lip->li_ops->iop_release(lip);
+-              spin_lock(&ailp->ail_lock);
+-              break;
+-      }
++              ASSERT(xlog_item_is_intent(lip));
+-      xfs_trans_ail_cursor_done(&cur);
+-      spin_unlock(&ailp->ail_lock);
++              xfs_defer_cancel_recovery(log->l_mp, dfp);
++      }
+ }
+ int
+@@ -1939,6 +1933,29 @@ xlog_buf_readahead(
+               xfs_buf_readahead(log->l_mp->m_ddev_targp, blkno, len, ops);
+ }
++/*
++ * Create a deferred work structure for resuming and tracking the progress of a
++ * log intent item that was found during recovery.
++ */
++void
++xlog_recover_intent_item(
++      struct xlog                     *log,
++      struct xfs_log_item             *lip,
++      xfs_lsn_t                       lsn,
++      unsigned int                    dfp_type)
++{
++      ASSERT(xlog_item_is_intent(lip));
++
++      xfs_defer_start_recovery(lip, dfp_type, &log->r_dfops);
++
++      /*
++       * Insert the intent into the AIL directly and drop one reference so
++       * that finishing or canceling the work will drop the other.
++       */
++      xfs_trans_ail_insert(log->l_ailp, lip, lsn);
++      lip->li_ops->iop_unpin(lip, 0);
++}
++
+ STATIC int
+ xlog_recover_items_pass2(
+       struct xlog                     *log,
+@@ -2533,29 +2550,22 @@ xlog_abort_defer_ops(
+  */
+ STATIC int
+ xlog_recover_process_intents(
+-      struct xlog             *log)
++      struct xlog                     *log)
+ {
+       LIST_HEAD(capture_list);
+-      struct xfs_ail_cursor   cur;
+-      struct xfs_log_item     *lip;
+-      struct xfs_ail          *ailp;
+-      int                     error = 0;
++      struct xfs_defer_pending        *dfp, *n;
++      int                             error = 0;
+ #if defined(DEBUG) || defined(XFS_WARN)
+-      xfs_lsn_t               last_lsn;
+-#endif
++      xfs_lsn_t                       last_lsn;
+-      ailp = log->l_ailp;
+-      spin_lock(&ailp->ail_lock);
+-#if defined(DEBUG) || defined(XFS_WARN)
+       last_lsn = xlog_assign_lsn(log->l_curr_cycle, log->l_curr_block);
+ #endif
+-      for (lip = xfs_trans_ail_cursor_first(ailp, &cur, 0);
+-           lip != NULL;
+-           lip = xfs_trans_ail_cursor_next(ailp, &cur)) {
+-              const struct xfs_item_ops       *ops;
+-              if (!xlog_item_is_intent(lip))
+-                      break;
++      list_for_each_entry_safe(dfp, n, &log->r_dfops, dfp_list) {
++              struct xfs_log_item     *lip = dfp->dfp_intent;
++              const struct xfs_item_ops *ops = lip->li_ops;
++
++              ASSERT(xlog_item_is_intent(lip));
+               /*
+                * We should never see a redo item with a LSN higher than
+@@ -2573,19 +2583,22 @@ xlog_recover_process_intents(
+                * The recovery function can free the log item, so we must not
+                * access lip after it returns.
+                */
+-              spin_unlock(&ailp->ail_lock);
+-              ops = lip->li_ops;
+               error = ops->iop_recover(lip, &capture_list);
+-              spin_lock(&ailp->ail_lock);
+               if (error) {
+                       trace_xlog_intent_recovery_failed(log->l_mp, error,
+                                       ops->iop_recover);
+                       break;
+               }
+-      }
+-      xfs_trans_ail_cursor_done(&cur);
+-      spin_unlock(&ailp->ail_lock);
++              /*
++               * XXX: @lip could have been freed, so detach the log item from
++               * the pending item before freeing the pending item.  This does
++               * not fix the existing UAF bug that occurs if ->iop_recover
++               * fails after creating the intent done item.
++               */
++              dfp->dfp_intent = NULL;
++              xfs_defer_cancel_recovery(log->l_mp, dfp);
++      }
+       if (error)
+               goto err;
+@@ -2606,27 +2619,15 @@ err:
+  */
+ STATIC void
+ xlog_recover_cancel_intents(
+-      struct xlog             *log)
++      struct xlog                     *log)
+ {
+-      struct xfs_log_item     *lip;
+-      struct xfs_ail_cursor   cur;
+-      struct xfs_ail          *ailp;
+-
+-      ailp = log->l_ailp;
+-      spin_lock(&ailp->ail_lock);
+-      lip = xfs_trans_ail_cursor_first(ailp, &cur, 0);
+-      while (lip != NULL) {
+-              if (!xlog_item_is_intent(lip))
+-                      break;
++      struct xfs_defer_pending        *dfp, *n;
+-              spin_unlock(&ailp->ail_lock);
+-              lip->li_ops->iop_release(lip);
+-              spin_lock(&ailp->ail_lock);
+-              lip = xfs_trans_ail_cursor_next(ailp, &cur);
+-      }
++      list_for_each_entry_safe(dfp, n, &log->r_dfops, dfp_list) {
++              ASSERT(xlog_item_is_intent(dfp->dfp_intent));
+-      xfs_trans_ail_cursor_done(&cur);
+-      spin_unlock(&ailp->ail_lock);
++              xfs_defer_cancel_recovery(log->l_mp, dfp);
++      }
+ }
+ /*
+--- a/fs/xfs/xfs_refcount_item.c
++++ b/fs/xfs/xfs_refcount_item.c
+@@ -696,12 +696,9 @@ xlog_recover_cui_commit_pass2(
+       cuip = xfs_cui_init(mp, cui_formatp->cui_nextents);
+       xfs_cui_copy_format(&cuip->cui_format, cui_formatp);
+       atomic_set(&cuip->cui_next_extent, cui_formatp->cui_nextents);
+-      /*
+-       * Insert the intent into the AIL directly and drop one reference so
+-       * that finishing or canceling the work will drop the other.
+-       */
+-      xfs_trans_ail_insert(log->l_ailp, &cuip->cui_item, lsn);
+-      xfs_cui_release(cuip);
++
++      xlog_recover_intent_item(log, &cuip->cui_item, lsn,
++                      XFS_DEFER_OPS_TYPE_REFCOUNT);
+       return 0;
+ }
+--- a/fs/xfs/xfs_rmap_item.c
++++ b/fs/xfs/xfs_rmap_item.c
+@@ -702,12 +702,9 @@ xlog_recover_rui_commit_pass2(
+       ruip = xfs_rui_init(mp, rui_formatp->rui_nextents);
+       xfs_rui_copy_format(&ruip->rui_format, rui_formatp);
+       atomic_set(&ruip->rui_next_extent, rui_formatp->rui_nextents);
+-      /*
+-       * Insert the intent into the AIL directly and drop one reference so
+-       * that finishing or canceling the work will drop the other.
+-       */
+-      xfs_trans_ail_insert(log->l_ailp, &ruip->rui_item, lsn);
+-      xfs_rui_release(ruip);
++
++      xlog_recover_intent_item(log, &ruip->rui_item, lsn,
++                      XFS_DEFER_OPS_TYPE_RMAP);
+       return 0;
+ }