From: Greg Kroah-Hartman Date: Fri, 29 Mar 2024 09:55:04 +0000 (+0100) Subject: 6.6-stable patches X-Git-Tag: v6.7.12~179 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=6fe915f6878d85093fc91c4ca29a3903c1782c58;p=thirdparty%2Fkernel%2Fstable-queue.git 6.6-stable patches 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 --- diff --git a/queue-6.6/series b/queue-6.6/series index 40b8e88f496..3f29ab9d9cb 100644 --- a/queue-6.6/series +++ b/queue-6.6/series @@ -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 index 00000000000..83696f19d2e --- /dev/null +++ b/queue-6.6/xfs-add-lock-protection-when-remove-perag-from-radix-tree.patch @@ -0,0 +1,64 @@ +From stable+bounces-32428-greg=kroah.com@vger.kernel.org Wed Mar 27 01:13:50 2024 +From: Catherine Hoang +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 + +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 +Reviewed-by: Christoph Hellwig +Reviewed-by: "Darrick J. Wong" +Signed-off-by: Chandan Babu R +Signed-off-by: Catherine Hoang +Acked-by: Darrick J. Wong +Signed-off-by: Greg Kroah-Hartman +--- + 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 index 00000000000..f544718105c --- /dev/null +++ b/queue-6.6/xfs-add-missing-nrext64-inode-flag-check-to-scrub.patch @@ -0,0 +1,38 @@ +From stable+bounces-32424-greg=kroah.com@vger.kernel.org Wed Mar 27 01:13:40 2024 +From: Catherine Hoang +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" + +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 +Reviewed-by: Christoph Hellwig +Signed-off-by: Catherine Hoang +Acked-by: Darrick J. Wong +Signed-off-by: Greg Kroah-Hartman +--- + 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 index 00000000000..f6831086056 --- /dev/null +++ b/queue-6.6/xfs-consider-minlen-sized-extents-in-xfs_rtallocate_extent_block.patch @@ -0,0 +1,41 @@ +From stable+bounces-32412-greg=kroah.com@vger.kernel.org Wed Mar 27 01:13:03 2024 +From: Catherine Hoang +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 + +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 +Reviewed-by: "Darrick J. Wong" +Signed-off-by: Chandan Babu R +Signed-off-by: Catherine Hoang +Acked-by: Darrick J. Wong +Signed-off-by: Greg Kroah-Hartman +--- + 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 index 00000000000..762b32df55f --- /dev/null +++ b/queue-6.6/xfs-convert-rt-bitmap-extent-lengths-to-xfs_rtbxlen_t.patch @@ -0,0 +1,79 @@ +From stable+bounces-32411-greg=kroah.com@vger.kernel.org Wed Mar 27 01:13:01 2024 +From: Catherine Hoang +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" + +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 +Reviewed-by: Christoph Hellwig +Signed-off-by: Catherine Hoang +Acked-by: Darrick J. Wong +Signed-off-by: Greg Kroah-Hartman +--- + 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 index 00000000000..96652db07ee --- /dev/null +++ b/queue-6.6/xfs-don-t-allow-overly-small-or-large-realtime-volumes.patch @@ -0,0 +1,82 @@ +From stable+bounces-32419-greg=kroah.com@vger.kernel.org Wed Mar 27 01:13:24 2024 +From: Catherine Hoang +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" + +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 +Reviewed-by: Christoph Hellwig +Signed-off-by: Catherine Hoang +Acked-by: Darrick J. Wong +Signed-off-by: Greg Kroah-Hartman +--- + 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 index 00000000000..87fd201fd9c --- /dev/null +++ b/queue-6.6/xfs-don-t-leak-recovered-attri-intent-items.patch @@ -0,0 +1,62 @@ +From stable+bounces-32413-greg=kroah.com@vger.kernel.org Wed Mar 27 01:13:10 2024 +From: Catherine Hoang +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" + +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 +Reviewed-by: Christoph Hellwig +Signed-off-by: Catherine Hoang +Acked-by: Darrick J. Wong +Signed-off-by: Greg Kroah-Hartman +--- + 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 index 00000000000..ad5e239baae --- /dev/null +++ b/queue-6.6/xfs-ensure-logflagsp-is-initialized-in-xfs_bmap_del_extent_real.patch @@ -0,0 +1,229 @@ +From stable+bounces-32430-greg=kroah.com@vger.kernel.org Wed Mar 27 01:13:56 2024 +From: Catherine Hoang +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 + +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 +Reviewed-by: Christoph Hellwig +Reviewed-by: "Darrick J. Wong" +Signed-off-by: Chandan Babu R +Signed-off-by: Catherine Hoang +Acked-by: Darrick J. Wong +Signed-off-by: Greg Kroah-Hartman +--- + 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 index 00000000000..bc80edab1c9 --- /dev/null +++ b/queue-6.6/xfs-fix-32-bit-truncation-in-xfs_compute_rextslog.patch @@ -0,0 +1,49 @@ +From stable+bounces-32418-greg=kroah.com@vger.kernel.org Wed Mar 27 01:13:25 2024 +From: Catherine Hoang +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" + +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 +Reviewed-by: Christoph Hellwig +Signed-off-by: Catherine Hoang +Acked-by: Darrick J. Wong +Signed-off-by: Greg Kroah-Hartman +--- + 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 index 00000000000..1229eb41808 --- /dev/null +++ b/queue-6.6/xfs-fix-an-off-by-one-error-in-xreap_agextent_binval.patch @@ -0,0 +1,76 @@ +From stable+bounces-32423-greg=kroah.com@vger.kernel.org Wed Mar 27 01:13:36 2024 +From: Catherine Hoang +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" + +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: + + _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" +Reviewed-by: Dave Chinner +Signed-off-by: Chandan Babu R +Signed-off-by: Catherine Hoang +Acked-by: Darrick J. Wong +Signed-off-by: Greg Kroah-Hartman +--- + 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 index 00000000000..4fbf95e19f3 --- /dev/null +++ b/queue-6.6/xfs-fix-perag-leak-when-growfs-fails.patch @@ -0,0 +1,150 @@ +From stable+bounces-32429-greg=kroah.com@vger.kernel.org Wed Mar 27 01:13:55 2024 +From: Catherine Hoang +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 + +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): + [] __kmalloc+0x386/0x4f0 + [] kmem_alloc+0xb5/0x2f0 + [] xfs_initialize_perag+0xc5/0x810 + [] xfs_growfs_data+0x9bc/0xbc0 + [] xfs_file_ioctl+0x5fe/0x14d0 + [] __x64_sys_ioctl+0x144/0x1c0 + [] do_syscall_64+0x3f/0xe0 + [] 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): + [] __kmalloc_node+0x3da/0x540 + [] kvmalloc_node+0x99/0x160 + [] bucket_table_alloc.isra.0+0x5f/0x400 + [] rhashtable_init+0x405/0x760 + [] xfs_initialize_perag+0x3a3/0x810 + [] xfs_growfs_data+0x9bc/0xbc0 + [] xfs_file_ioctl+0x5fe/0x14d0 + [] __x64_sys_ioctl+0x144/0x1c0 + [] do_syscall_64+0x3f/0xe0 + [] 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 +Reviewed-by: "Darrick J. Wong" +Signed-off-by: Chandan Babu R +Signed-off-by: Catherine Hoang +Acked-by: Darrick J. Wong +Signed-off-by: Greg Kroah-Hartman +--- + 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 index 00000000000..9a6032a69d2 --- /dev/null +++ b/queue-6.6/xfs-force-all-buffers-to-be-written-during-btree-bulk-load.patch @@ -0,0 +1,220 @@ +From stable+bounces-32425-greg=kroah.com@vger.kernel.org Wed Mar 27 01:13:43 2024 +From: Catherine Hoang +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" + +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 +Reviewed-by: Christoph Hellwig +Signed-off-by: Catherine Hoang +Acked-by: Darrick J. Wong +Signed-off-by: Greg Kroah-Hartman +--- + 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 index 00000000000..519f4874861 --- /dev/null +++ b/queue-6.6/xfs-initialise-di_crc-in-xfs_log_dinode.patch @@ -0,0 +1,111 @@ +From stable+bounces-32427-greg=kroah.com@vger.kernel.org Wed Mar 27 01:13:48 2024 +From: Catherine Hoang +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 + +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 +Fixes: f8d55aa0523a ("xfs: introduce inode log format object") +Signed-off-by: Dave Chinner +Reviewed-by: "Darrick J. Wong" +Signed-off-by: Chandan Babu R +Signed-off-by: Catherine Hoang +Acked-by: Darrick J. Wong +Signed-off-by: Greg Kroah-Hartman +--- + 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 index 00000000000..d83b0986918 --- /dev/null +++ b/queue-6.6/xfs-make-rextslog-computation-consistent-with-mkfs.patch @@ -0,0 +1,154 @@ +From stable+bounces-32417-greg=kroah.com@vger.kernel.org Wed Mar 27 01:13:18 2024 +From: Catherine Hoang +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" + +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 +Reviewed-by: Christoph Hellwig +Signed-off-by: Catherine Hoang +Acked-by: Darrick J. Wong +Signed-off-by: Greg Kroah-Hartman +--- + 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 index 00000000000..3802ccc4882 --- /dev/null +++ b/queue-6.6/xfs-make-xchk_iget-safer-in-the-presence-of-corrupt-inode-btrees.patch @@ -0,0 +1,104 @@ +From stable+bounces-32420-greg=kroah.com@vger.kernel.org Wed Mar 27 01:13:28 2024 +From: Catherine Hoang +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" + +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 +Reviewed-by: Dave Chinner +Reviewed-by: Christoph Hellwig +Signed-off-by: Catherine Hoang +Acked-by: Darrick J. Wong +Signed-off-by: Greg Kroah-Hartman +--- + 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 index 00000000000..a252520cced --- /dev/null +++ b/queue-6.6/xfs-move-the-xfs_rtbitmap.c-declarations-to-xfs_rtbitmap.h.patch @@ -0,0 +1,294 @@ +From stable+bounces-32410-greg=kroah.com@vger.kernel.org Wed Mar 27 01:13:00 2024 +From: Catherine Hoang +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" + +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 +Reviewed-by: Christoph Hellwig +Signed-off-by: Catherine Hoang +Acked-by: Darrick J. Wong +Signed-off-by: Greg Kroah-Hartman +--- + 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 index 00000000000..79e0dfd68b8 --- /dev/null +++ b/queue-6.6/xfs-pass-the-xfs_defer_pending-object-to-iop_recover.patch @@ -0,0 +1,137 @@ +From stable+bounces-32415-greg=kroah.com@vger.kernel.org Wed Mar 27 01:13:13 2024 +From: Catherine Hoang +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" + +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 +Reviewed-by: Christoph Hellwig +Signed-off-by: Catherine Hoang +Acked-by: Darrick J. Wong +Signed-off-by: Greg Kroah-Hartman +--- + 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 index 00000000000..48bf164c09c --- /dev/null +++ b/queue-6.6/xfs-recompute-growfsrtfree-transaction-reservation-while-growing-rt-volume.patch @@ -0,0 +1,57 @@ +From stable+bounces-32422-greg=kroah.com@vger.kernel.org Wed Mar 27 01:13:32 2024 +From: Catherine Hoang +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" + +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 +Reviewed-by: Christoph Hellwig +Signed-off-by: Catherine Hoang +Acked-by: Darrick J. Wong +Signed-off-by: Greg Kroah-Hartman +--- + 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 index 00000000000..ad58c30ad9b --- /dev/null +++ b/queue-6.6/xfs-remove-conditional-building-of-rt-geometry-validator-functions.patch @@ -0,0 +1,176 @@ +From stable+bounces-32433-greg=kroah.com@vger.kernel.org Wed Mar 27 01:14:06 2024 +From: Catherine Hoang +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" + +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" +Reviewed-by: Christoph Hellwig +Signed-off-by: Chandan Babu R +Signed-off-by: Catherine Hoang +Acked-by: Darrick J. Wong +Signed-off-by: Greg Kroah-Hartman +--- + 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 index 00000000000..f1fb1527620 --- /dev/null +++ b/queue-6.6/xfs-remove-unused-fields-from-struct-xbtree_ifakeroot.patch @@ -0,0 +1,42 @@ +From stable+bounces-32421-greg=kroah.com@vger.kernel.org Wed Mar 27 01:13:30 2024 +From: Catherine Hoang +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" + +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 +Reviewed-by: Dave Chinner +Signed-off-by: Catherine Hoang +Acked-by: Darrick J. Wong +Signed-off-by: Greg Kroah-Hartman +--- + 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 index 00000000000..afc48bfe2a6 --- /dev/null +++ b/queue-6.6/xfs-reset-xfs_attr_incomplete-filter-on-node-removal.patch @@ -0,0 +1,51 @@ +From stable+bounces-32432-greg=kroah.com@vger.kernel.org Wed Mar 27 01:14:02 2024 +From: Catherine Hoang +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 + +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 +Reviewed-by: Christoph Hellwig +Signed-off-by: Chandan Babu R +Signed-off-by: Catherine Hoang +Acked-by: Darrick J. Wong +Signed-off-by: Greg Kroah-Hartman +--- + 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 index 00000000000..f34f0cfc6d0 --- /dev/null +++ b/queue-6.6/xfs-short-circuit-xfs_growfs_data_private-if-delta-is-zero.patch @@ -0,0 +1,51 @@ +From stable+bounces-32426-greg=kroah.com@vger.kernel.org Wed Mar 27 01:13:46 2024 +From: Catherine Hoang +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 + +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 +Reviewed-by: "Darrick J. Wong" +Signed-off-by: Chandan Babu R +Signed-off-by: Catherine Hoang +Acked-by: Darrick J. Wong +Signed-off-by: Greg Kroah-Hartman +--- + 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 index 00000000000..810a65c21a5 --- /dev/null +++ b/queue-6.6/xfs-transfer-recovered-intent-item-ownership-in-iop_recover.patch @@ -0,0 +1,137 @@ +From stable+bounces-32416-greg=kroah.com@vger.kernel.org Wed Mar 27 01:13:15 2024 +From: Catherine Hoang +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" + +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 +Reviewed-by: Christoph Hellwig +Signed-off-by: Catherine Hoang +Acked-by: Darrick J. Wong +Signed-off-by: Greg Kroah-Hartman +--- + 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 index 00000000000..a615a35410c --- /dev/null +++ b/queue-6.6/xfs-update-dir3-leaf-block-metadata-after-swap.patch @@ -0,0 +1,71 @@ +From stable+bounces-32431-greg=kroah.com@vger.kernel.org Wed Mar 27 01:13:59 2024 +From: Catherine Hoang +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 + +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 +Suggested-by: Dave Chinner +Reviewed-by: "Darrick J. Wong" +Signed-off-by: Chandan Babu R +Signed-off-by: Catherine Hoang +Acked-by: Darrick J. Wong +Signed-off-by: Greg Kroah-Hartman +--- + 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 index 00000000000..0a25adcd1e8 --- /dev/null +++ b/queue-6.6/xfs-use-xfs_defer_pending-objects-to-recover-intent-items.patch @@ -0,0 +1,524 @@ +From stable+bounces-32414-greg=kroah.com@vger.kernel.org Wed Mar 27 01:13:09 2024 +From: Catherine Hoang +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" + +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 +Reviewed-by: Christoph Hellwig +Signed-off-by: Catherine Hoang +Acked-by: Darrick J. Wong +Signed-off-by: Greg Kroah-Hartman +--- + 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; + } +