6.18-stable review patch. If anyone has any objections, please let me know. ------------------ From: Joseph Qi [ Upstream commit 8ad5fa0c11246d260d7b12c9672e604b1f431b2e ] Patch series "ocfs2: fix metadata reservation for xattr value trees". ocfs2_calc_xattr_set_need() reserves too little metadata for a new xattr value's extent tree on three paths. The reservation cannot be topped up once the transaction has started, so ocfs2_add_clusters_in_btree() gives up with RESTART_META when the value root fills and nothing is left. ocfs2_xattr_extend_allocation() turns that into -ENOSPC, after ocfs2_xa_cleanup_value_truncate() has dropped the entry and leaked the clusters already handed out; before commit 0cdc7dde00ec ("ocfs2: fix missing metadata reservation for large xattrs") it was a BUG_ON() instead. It takes a fragmented cluster allocator to get there, since the first extent always fits in the root. Patch 1 fixes the create side of meta_guess: commit 3ed2be719eb9 ("ocfs2: allow for more than one data extent when creating xattr") replaced the block reserved for the new xattr block with the two the value tree needs instead of adding them together. Patch 2 fixes the arm that moves an xattr back into the inode, which charges credits for a value tree but no metadata for one. Patch 3 drops a second copy of the same reservation on the path that falls through to meta_guess. That one is cleanup rather than a fix and is not for stable, and it must not be backported without patch 1: the duplicate it removes is what currently covers the shortfall patch 1 fixes. This patch (of 3): The create side of meta_guess in ocfs2_calc_xattr_set_need() reserves metadata for the new value's extent tree and nothing for the xattr block that will hold it: } else { credits += OCFS2_XATTR_BLOCK_CREATE_CREDITS; if (xi->xi_value_len > OCFS2_XATTR_INLINE_SIZE) { struct ocfs2_extent_list *el = &def_xv.xv.xr_list; meta_add += ocfs2_extend_meta_needed(el); ... Commit 3ed2be719eb9 ("ocfs2: allow for more than one data extent when creating xattr") meant to add that tree on top of the meta_add += 1 the branch already had, but it moved the 1 into the else arm instead, so the external-value case lost it. The 1 is not optional: it is what ocfs2_create_xattr_block() takes from meta_ac through ocfs2_claim_metadata(), and it is spent before the value tree exists. ocfs2_extend_meta_needed() on the empty def_xv list returns 2, so the branch reserves 2 where 3 are needed. The xattr block consumes one, and when the cluster allocator is fragmented enough that the value needs more than one extent, ocfs2_add_clusters_in_btree() comes back around with a full value root and ocfs2_alloc_context_bits_left() of 1 against a threshold of 2. It answers RESTART_META, which ocfs2_xattr_extend_allocation() turns into a BUG_ON(). Commit 0cdc7dde00ec ("ocfs2: fix missing metadata reservation for large xattrs") downgraded that to -ENOSPC, at the cost of leaking the clusters already handed out: ocfs2_xa_cleanup_value_truncate() takes its !orig_clusters arm, drops the entry and leaves the clusters for fsck.ocfs2 to reclaim. Reserve for the block and the tree separately. Link: https://lore.kernel.org/20261010113454.1420939-1-joseph.qi@linux.alibaba.com Link: https://lore.kernel.org/20261010113454.1420939-2-joseph.qi@linux.alibaba.com Fixes: 3ed2be719eb9 ("ocfs2: allow for more than one data extent when creating xattr") Signed-off-by: Joseph Qi Signed-off-by: Andrew Morton Cc: Heming Zhao Cc: Mark Fasheh Cc: Joel Becker Cc: Signed-off-by: Sasha Levin --- fs/ocfs2/xattr.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/fs/ocfs2/xattr.c b/fs/ocfs2/xattr.c index b5029c4e3784e..a47a038b3311b 100644 --- a/fs/ocfs2/xattr.c +++ b/fs/ocfs2/xattr.c @@ -3243,7 +3243,13 @@ static int ocfs2_calc_xattr_set_need(struct inode *inode, credits += OCFS2_XATTR_BLOCK_CREATE_CREDITS; if (xi->xi_value_len > OCFS2_XATTR_INLINE_SIZE) { struct ocfs2_extent_list *el = &def_xv.xv.xr_list; - meta_add += ocfs2_extend_meta_needed(el); + /* + * One block for the xattr block we may have to + * allocate, the rest for growing the value tree it + * would hold. If ocfs2_xattr_ibody_set() succeeds + * instead, that first block goes unused. + */ + meta_add += 1 + ocfs2_extend_meta_needed(el); credits += ocfs2_calc_extend_credits(inode->i_sb, el); } else { -- 2.53.0