From 0934f5832fa575e6ac3a2ade98c43339b6222f7d Mon Sep 17 00:00:00 2001 From: Matthew Wilcox Date: Wed, 21 Jan 2026 06:38:29 +0000 Subject: [PATCH] mm/khugepaged: free empty xa_nodes when rollbacks in collapse_file ANBZ: #31319 cherry-picked from https://lore.kernel.org/all/aXB0Zcu4bIEvSSuX@casper.infradead.org/ collapse_file() calls xas_create_range() to pre-create all slots needed. If collapse_file() finally fails, these pre-created slots are empty nodes and aren't destroyed. To fix it, xa_destroy() can free all internal data instead of just lock i_pages during the process of clear_inode(). Fixes: 77da9389b9d5 ("mm: Convert collapse_shmem to XArray") Signed-off-by: Matthew Wilcox Signed-off-by: Jinjiang Tu Signed-off-by: Kaihao Bai --- fs/inode.c | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/fs/inode.c b/fs/inode.c index bd71059cae4d..b3c3f1cad756 100644 --- a/fs/inode.c +++ b/fs/inode.c @@ -645,22 +645,18 @@ void dump_mapping(const struct address_space *mapping) void clear_inode(struct inode *inode) { - /* - * We have to cycle the i_pages lock here because reclaim can be in the - * process of removing the last page (in __filemap_remove_folio()) - * and we must not free the mapping under it. - */ - xa_lock_irq(&inode->i_data.i_pages); - BUG_ON(inode->i_data.nrpages); /* * Almost always, mapping_empty(&inode->i_data) here; but there are * two known and long-standing ways in which nodes may get left behind * (when deep radix-tree node allocation failed partway; or when THP - * collapse_file() failed). Until those two known cases are cleaned up, - * or a cleanup function is called here, do not BUG_ON(!mapping_empty), - * nor even WARN_ON(!mapping_empty). + * collapse_file() failed). + * + * xa_destroy() also cycles the lock for us, which is needed because + * reclaim can be in the process of removing the last folio (in + * __filemap_remove_folio()) and we must not free the mapping under it. */ - xa_unlock_irq(&inode->i_data.i_pages); + xa_destroy(&inode->i_data.i_pages); + BUG_ON(inode->i_data.nrpages); BUG_ON(!list_empty(&inode->i_data.private_list)); BUG_ON(!(inode->i_state & I_FREEING)); BUG_ON(inode->i_state & I_CLEAR); -- Gitee