From mboxrd@z Thu Jan 1 00:00:00 1970 Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.remote-ip=100.103.45.18 ARC-Seal: i=1; d=subspace.kernel.org; s=arc20261015; a=rsa-sha256; cv=none; t=1793270684; b=AMFnvX/C9LKle7ac0+yDHZDw6iAAqvi8hHmIlTJuFl/BVrb6t/KRxenkt/a+4v0zGA2O 6Mye74Sg+5KAoMaJ24BuK++KgFWN3Ocwzjr/CRltP5Zft8ErTlSI5DpDoJl6I0PBZHLCz pZEUvhm4+jDnmA7BxUYjVAtFNnmmbGW+IqoB7lEeuPMmqdaz0AFRtrwCV0U8URG3g5KVj EWxyGpHj0eOhWghZJEZ5vBZo9PcndhNO0ATmm5559uMKhOO7aBb5MQXzAidS6S+Ncyz2d FyHZ1v9C+iQl0X9s0TXhmWYYXHGb5PuQIccCxAh4f0IDunhrm2KdPPQwoOUnelzbz1A== ARC-Message-Signature: i=1; d=subspace.kernel.org; s=arc20261015; a=rsa-sha256; c=relaxed/relaxed; t=1793270684; h=Received:DKIM-Signature:From:Date:Subject:MIME-Version:Content-Type: Content-Transfer-Encoding:Message-Id:References:In-Reply-To:To:Cc: X-Mailer:X-Developer-Signature:X-Developer-Key; bh=ogc5g2DbOVRsIRSN2sEJBqCeAyv1h3vNItmdoJK+73w=; b=f/ixkTjxuTeaNdk1MH43sVuSHZ/jhilkm8KUfT5RtANWejgWZWWZX6FUPoLlyGSdOMLd zbZH4k2tzrTPisjIVPgnuS/Pp997HjVIq4zm6ZdmpY5JfX40+3LBkIWo9joiE8Ol+0B8L 4exKn+Xbtaxcro34HMvYqR/HRfpwr/sxUjmTyWk+ytLoISUv5JNzWmtR+CYScN6kuq0Ci U1b8rx4jW8PcTJXr1yauXCQRYY2m+cFxcdyY5yP2PAPYIEM2hbIu6NBMdoa+FbjsP5UKE JiX8ARBd8qMX41dPCF6NFIxMyqlELs9Do3KCsCxkGaCze8Iwejn2HK/yiC8Cla5lt3A== ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass header.d=kernel.org header.i=@kernel.org header.a=rsa-sha256 header.s=k20260515 header.b=jRBTHA0Y; arc=none smtp.remote-ip=100.103.45.18 Received: from smtp.kernel.org (quasi.space.kernel.org [100.103.45.18]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature ECDSA (prime256v1)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 3FAF2606A31D; Thu, 29 Oct 2026 10:44:44 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key, unprotected) header.d=kernel.org header.i=@kernel.org header.a=rsa-sha256 header.s=k20260515 header.b=jRBTHA0Y Received: by smtp.kernel.org (Postfix) with ESMTPSA id CAD371F01828; Thu, 29 Oct 2026 10:44:34 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=kernel.org; s=k20260515; t=1793270684; bh=ogc5g2DbOVRsIRSN2sEJBqCeAyv1h3vNItmdoJK+73w=; h=From:Date:Subject:References:In-Reply-To:To:Cc; b=jRBTHA0Ydgyd5HdUnj9mVTbUSXB8/j72t0rcA89afuwVEbsgCfE/IdoPE3pE5erzB VnEUXsXeiZOoZ0iuRM6QSO6LkX857TE0pCpeDO86NOq4eLe0Hs0jTgYPU50c1iZDKe 1FUMtMQNM8t4JJwaM0jU1Lxx15mzmE7g70MWfWb3hwHGSncxAM9vrvou9FssSbIucr 6ZDtpoSiAgwD29LL74aKYc1dBM4p644tbEGm93Vu98QA2RuOCh5lv7vpUqvSl73FD/ pkXiTFJWmnQCdGGweTpN6d6VlBg55HNa/bjBaKRbp2KGSUxNH3Jprf00RTbUFa9NlU Kdy6T5KY3KWkA== From: "David Hildenbrand (Arm)" Date: Thu, 29 Oct 2026 11:43:27 +0100 Subject: [PATCH v4 06/15] mm/shmem: hold off eviction in shmem_writeout() X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Post: List-Help: List-Subscribe: List-Unsubscribe: List-Owner: Precedence: list MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: 7bit Message-Id: <20261029-fixes_shmem-v4-6-392fc715d8f6@kernel.org> References: <20261029-fixes_shmem-v4-0-392fc715d8f6@kernel.org> In-Reply-To: <20261029-fixes_shmem-v4-0-392fc715d8f6@kernel.org> To: Andrew Morton , Gregory Price , Zi Yan , Joshua Hahn , Rakie Kim , Ying Huang , Johannes Weiner , Vladimir Davydov , Michal Hocko , Hugh Dickins , Baolin Wang , Lukas Czerner , Carlos Maiolino , Christian Brauner , Jan Kara , Daniel Verkamp , Jeff Xu , Muchun Song , Oscar Salvador , David Stevens , "Matthew Wilcox (Oracle)" , William Kucharski , Chris Li , Kairui Song , Nhat Pham , Miaohe Lin , Naoya Horiguchi Cc: linux-mm@kvack.org, linux-kernel@vger.kernel.org, "David Hildenbrand (Arm)" , Josef Bacik , stable@vger.kernel.org, Pedro Falcato , "Kiryl Shutsemau (Meta)" X-Mailer: b4 0.16.0 X-Developer-Signature: v=1; a=openpgp-sha256; l=5771; i=david@kernel.org; h=from:subject:message-id; bh=rlVfOwdBZ9cOTWL2PYhkeURitXGy+X/4r5BveDLAWvc=; b=owGbwMvMwCF2/OVpCdfQ3ZWMp9WSGLIeK4c1Fdi7yEyfybBXImfdmZBZMv222tcrNyb8PJy97 LeKdJZCRykLgxgHg6yYIktQ8H2Dly/X1Va78THDzGFlAhnCwMUpABOZHs7IMMFRxWKLzbN170/W aO/bU2Uo+1e1JUZr851f294d6XmetpHhv0d/8j6Zt9M59ebMNwhg5KhWZZhoPU0jdLd1YpupvM9 lLgA= X-Developer-Key: i=david@kernel.org; a=openpgp; fpr=1BD9CAAD735C4C3A460DFCCA4DDE10F700FF835A From: Josef Bacik Once shmem_writeout() has moved the folio from the page cache to the swap cache, the folio lock no longer keeps the inode alive: truncation and eviction find a swap entry, free it and do not wait for anybody. That did not matter as long as swap_writeout() was the last thing the function did. But since it puts the folio back when swap_writeout() returns AOP_WRITEPAGE_ACTIVATE, it uses the mapping and the inode afterwards: reclaim last close of an unlinked file shmem_writeout() shmem_delete_from_page_cache() swap_writeout() zswap_store() fails shmem_evict_inode() writeback is disabled shmem_truncate_range() return AOP_WRITEPAGE_ACTIVATE shmem_free_swap() destroy_inode() ... RCU grace period, freed shmem_add_to_page_cache(mapping) xas_lock_irq() <- use after free swap_writeout() behaves that way if zswap is enabled, cannot take the folio and the memcg or one of its ancestors has memory.zswap.writeback set to 0. shmem_unuse() already added infrastructure to deal with that. Reuse info->stop_eviction to protect the inode during writeout. Increment it before removing the folio from the page cache, and decrement it after swap_writeout() and any attempted reinsertion and inode accounting. In shmem_evict_inode(), wait for the counter to reach zero even if swapoff has already removed the inode from the swap list. Add some helpers to make the code easier to digest. There is a known remaining issue with two swapoffs racing, and the empty list check not being able to detect that race. That will be addressed separately later. Fix generated by an LLM and sent by Josef Bacik in private. Fixes: 6344a6d9ce13 ("mm/shmem: writeout free swap if swap_writeout() reactivates") Cc: stable@vger.kernel.org Signed-off-by: Josef Bacik Acked-by: Pedro Falcato Acked-by: Kiryl Shutsemau (Meta) Reviewed-by: Nhat Pham Reviewed-by: Baolin Wang Tested-by: Baolin Wang [ david: code cleanups; improve description ] Tested-by: Gregory Price (Meta) Signed-off-by: David Hildenbrand (Arm) --- mm/shmem.c | 36 ++++++++++++++++++++++++++++++------ 1 file changed, 30 insertions(+), 6 deletions(-) diff --git a/mm/shmem.c b/mm/shmem.c index 6df0237832c0..c44d2dd71c89 100644 --- a/mm/shmem.c +++ b/mm/shmem.c @@ -1616,6 +1616,17 @@ static int shmem_setattr(const struct mnt_idmap *idmap, return error; } +static void shmem_disallow_eviction(struct shmem_inode_info *info) +{ + atomic_inc(&info->stop_eviction); +} + +static void shmem_allow_eviction(struct shmem_inode_info *info) +{ + if (atomic_dec_and_test(&info->stop_eviction)) + wake_up_var(&info->stop_eviction); +} + static void shmem_evict_inode(struct inode *inode) { struct shmem_inode_info *info = SHMEM_I(inode); @@ -1628,16 +1639,21 @@ static void shmem_evict_inode(struct inode *inode) mapping_set_exiting(inode->i_mapping); shmem_truncate_range(inode, 0, (loff_t)-1); shmem_unused_huge_del(inode); - while (!list_empty(&info->swaplist)) { - /* Wait while shmem_unuse() is scanning this inode... */ + /* + * Writeout may still use the inode after swapoff has removed it + * from the swaplist, so always wait at least once. + */ + do { wait_var_event(&info->stop_eviction, !atomic_read(&info->stop_eviction)); + if (list_empty(&info->swaplist)) + break; spin_lock(&shmem_swaplist_lock); /* ...but beware of the race if we peeked too early */ if (!atomic_read(&info->stop_eviction)) list_del_init(&info->swaplist); spin_unlock(&shmem_swaplist_lock); - } + } while (!list_empty(&info->swaplist)); } simple_xattrs_free(&sbinfo->xa_cache, &info->xattrs, sbinfo->max_inodes ? &freed : NULL); @@ -1776,15 +1792,14 @@ int shmem_unuse(unsigned int type) * remove placeholder inode from swaplist, nor let it be freed * (igrab() would protect from unlink, but not from unmount). */ - atomic_inc(&info->stop_eviction); + shmem_disallow_eviction(info); spin_unlock(&shmem_swaplist_lock); error = shmem_unuse_inode(&info->vfs_inode, type); cond_resched(); spin_lock(&shmem_swaplist_lock); - if (atomic_dec_and_test(&info->stop_eviction)) - wake_up_var(&info->stop_eviction); + shmem_allow_eviction(info); if (error) break; if (list_empty(&info->swaplist)) @@ -1915,6 +1930,13 @@ int shmem_writeout(struct swap_io_ctx *ctx, struct folio *folio, spin_unlock(&shmem_swaplist_lock); } + /* + * Once the folio has left the page cache, its lock no longer + * keeps the inode from being evicted and freed: hold eviction + * off, in case the folio has to be put back below. + */ + shmem_disallow_eviction(info); + folio_dup_swap(folio, NULL); shmem_delete_from_page_cache(folio, swp_to_radix_entry(folio->swap)); @@ -1922,6 +1944,7 @@ int shmem_writeout(struct swap_io_ctx *ctx, struct folio *folio, error = swap_writeout(ctx, folio); if (error != AOP_WRITEPAGE_ACTIVATE) { /* folio has been unlocked */ + shmem_allow_eviction(info); return error; } @@ -1938,6 +1961,7 @@ int shmem_writeout(struct swap_io_ctx *ctx, struct folio *folio, shmem_recalc_inode(inode, 0, -nr_pages); folio_put_swap(folio, NULL); } + shmem_allow_eviction(info); /* * The swap_cache_del_folio() below could be left for -- 2.43.0