* FAILED: patch "[PATCH] mm/shmem: hold off eviction in shmem_writeout()" failed to apply to 7.3-stable tree
@ 2026-11-10 1:01 Sasha Levin
2026-11-12 2:19 ` [PATCH 7.3.y 1/2] mm: shmem: move unused huge shrinklist queuing past the truncation check Sasha Levin
0 siblings, 1 reply; 3+ messages in thread
From: Sasha Levin @ 2026-11-10 1:01 UTC (permalink / raw)
To: josef, pfalcato, kas, nphamcs, baolin.wang, gourry, david; +Cc: stable
The patch below does not apply to the 7.3-stable tree.
If someone wants it applied there, or to any other stable or longterm
tree, then please email the backport, including the original git commit
id to <stable@vger.kernel.org>.
To reproduce the conflict and resubmit, you may use the following commands:
git fetch https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/ linux-7.3.y
git checkout FETCH_HEAD
git cherry-pick -x 8f59173b5ca358bf4d1017b44da3cbe6faa80daf
# <resolve conflicts, build, test, etc.>
git commit -s
git send-email --to '<stable@vger.kernel.org>' --in-reply-to '20261109200119-failed-7.3-8f59173b5ca3@kernel.org' --subject-prefix 'PATCH 7.3.y' 'HEAD^..'
Thanks,
Sasha
------------------ original commit in Linus's tree ------------------
From 8f59173b5ca358bf4d1017b44da3cbe6faa80daf Mon Sep 17 00:00:00 2001
From: Josef Bacik <josef@toxicpanda.com>
Date: Thu, 29 Oct 2026 11:43:27 +0100
Subject: [PATCH] mm/shmem: hold off eviction in shmem_writeout()
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 <josef@toxicpanda.com>
Acked-by: Pedro Falcato <pfalcato@suse.de>
Acked-by: Kiryl Shutsemau (Meta) <kas@kernel.org>
Reviewed-by: Nhat Pham <nphamcs@gmail.com>
Reviewed-by: Baolin Wang <baolin.wang@linux.alibaba.com>
Tested-by: Baolin Wang <baolin.wang@linux.alibaba.com>
[ david: code cleanups; improve description ]
Tested-by: Gregory Price (Meta) <gourry@gourry.net>
Link: https://patch.msgid.link/20261029-fixes_shmem-v4-6-392fc715d8f6@kernel.org
Signed-off-by: David Hildenbrand (Arm) <david@kernel.org>
---
mm/shmem.c | 36 ++++++++++++++++++++++++++++++------
1 file changed, 30 insertions(+), 6 deletions(-)
diff --git a/mm/shmem.c b/mm/shmem.c
index 6df0237832c00..c44d2dd71c893 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.53.0
^ permalink raw reply related [flat|nested] 3+ messages in thread* [PATCH 7.3.y 1/2] mm: shmem: move unused huge shrinklist queuing past the truncation check
2026-11-10 1:01 FAILED: patch "[PATCH] mm/shmem: hold off eviction in shmem_writeout()" failed to apply to 7.3-stable tree Sasha Levin
@ 2026-11-12 2:19 ` Sasha Levin
2026-11-12 2:19 ` [PATCH 7.3.y 2/2] mm/shmem: hold off eviction in shmem_writeout() Sasha Levin
0 siblings, 1 reply; 3+ messages in thread
From: Sasha Levin @ 2026-11-12 2:19 UTC (permalink / raw)
To: stable
Cc: Qi Zheng, Andrew Morton, Baolin Wang, Christian Brauner,
David Hildenbrand, Hugh Dickins, Johannes Weiner, Michal Hocko,
Muchun Song, Roman Gushchin, Shakeel Butt, Sasha Levin
From: Qi Zheng <zhengqi.arch@bytedance.com>
[ Upstream commit 948715f5866cc5b91c096a0308dd5a93b1a7d79b ]
The shmem_get_folio_gfp() adds the inode to the unused huge shrinker list
at the alloced label, but a subsequent truncation check may still fail and
remove the folio, leaving the inode on the list with a stale folio.
The original code works because the shrinker re-looks-up the folio and
drops stale entries, but it is cleaner to queue the inode only after all
checks that might remove the folio have passed.
So just make the pure structural move with no functional change, and it
serves as preparation for the memcg-aware shrinker conversion.
Link: https://lore.kernel.org/17fcf64faec0dfbbe6cf8a97924e3f39cd3c51a4.1786955972.git.zhengqi.arch@bytedance.com
Signed-off-by: Qi Zheng <zhengqi.arch@bytedance.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Reviewed-by: Baolin Wang <baolin.wang@linux.alibaba.com>
Cc: Christian Brauner <brauner@kernel.org>
Cc: David Hildenbrand <david@kernel.org>
Cc: Hugh Dickins <hughd@google.com>
Cc: Johannes Weiner <hannes@cmpxchg.org>
Cc: Michal Hocko <mhocko@kernel.org>
Cc: Muchun Song <muchun.song@linux.dev>
Cc: Roman Gushchin <roman.gushchin@linux.dev>
Cc: Shakeel Butt <shakeel.butt@linux.dev>
Stable-dep-of: 8f59173b5ca3 ("mm/shmem: hold off eviction in shmem_writeout()")
Signed-off-by: Sasha Levin <sashal@kernel.org>
---
mm/shmem.c | 48 +++++++++++++++++++++++++++---------------------
1 file changed, 27 insertions(+), 21 deletions(-)
diff --git a/mm/shmem.c b/mm/shmem.c
index efc99d5f86588..bb5d24e7202dd 100644
--- a/mm/shmem.c
+++ b/mm/shmem.c
@@ -2560,27 +2560,6 @@ static int shmem_get_folio_gfp(struct inode *inode, pgoff_t index,
alloced:
alloced = true;
- if (folio_test_large(folio) &&
- DIV_ROUND_UP(i_size_read(inode), PAGE_SIZE) <
- folio_next_index(folio)) {
- struct shmem_sb_info *sbinfo = SHMEM_SB(inode->i_sb);
- struct shmem_inode_info *info = SHMEM_I(inode);
- /*
- * Part of the large folio is beyond i_size: subject
- * to shrink under memory pressure.
- */
- spin_lock(&sbinfo->shrinklist_lock);
- /*
- * _careful to defend against unlocked access to
- * ->shrink_list in shmem_unused_huge_shrink()
- */
- if (list_empty_careful(&info->shrinklist)) {
- list_add_tail(&info->shrinklist,
- &sbinfo->shrinklist);
- sbinfo->shrinklist_len++;
- }
- spin_unlock(&sbinfo->shrinklist_lock);
- }
if (sgp == SGP_WRITE)
folio_set_referenced(folio);
@@ -2610,6 +2589,33 @@ static int shmem_get_folio_gfp(struct inode *inode, pgoff_t index,
error = -EINVAL;
goto unlock;
}
+
+ /*
+ * Queue the inode on the shrink list only after all checks that might
+ * remove the folio have passed. Otherwise the inode could be left on
+ * the shrinker list with a stale folio.
+ */
+ if (alloced && folio_test_large(folio) &&
+ DIV_ROUND_UP(i_size_read(inode), PAGE_SIZE) < folio_next_index(folio)) {
+ struct shmem_sb_info *sbinfo = SHMEM_SB(inode->i_sb);
+ struct shmem_inode_info *info = SHMEM_I(inode);
+ /*
+ * Part of the large folio is beyond i_size: subject
+ * to shrink under memory pressure.
+ */
+ spin_lock(&sbinfo->shrinklist_lock);
+ /*
+ * _careful to defend against unlocked access to
+ * ->shrink_list in shmem_unused_huge_shrink()
+ */
+ if (list_empty_careful(&info->shrinklist)) {
+ list_add_tail(&info->shrinklist,
+ &sbinfo->shrinklist);
+ sbinfo->shrinklist_len++;
+ }
+ spin_unlock(&sbinfo->shrinklist_lock);
+ }
+
out:
*foliop = folio;
return 0;
--
2.53.0
^ permalink raw reply related [flat|nested] 3+ messages in thread* [PATCH 7.3.y 2/2] mm/shmem: hold off eviction in shmem_writeout()
2026-11-12 2:19 ` [PATCH 7.3.y 1/2] mm: shmem: move unused huge shrinklist queuing past the truncation check Sasha Levin
@ 2026-11-12 2:19 ` Sasha Levin
0 siblings, 0 replies; 3+ messages in thread
From: Sasha Levin @ 2026-11-12 2:19 UTC (permalink / raw)
To: stable
Cc: Josef Bacik, Pedro Falcato, Kiryl Shutsemau (Meta), Nhat Pham,
Baolin Wang, Gregory Price (Meta), David Hildenbrand (Arm),
Sasha Levin
From: Josef Bacik <josef@toxicpanda.com>
[ Upstream commit 8f59173b5ca358bf4d1017b44da3cbe6faa80daf ]
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 <josef@toxicpanda.com>
Acked-by: Pedro Falcato <pfalcato@suse.de>
Acked-by: Kiryl Shutsemau (Meta) <kas@kernel.org>
Reviewed-by: Nhat Pham <nphamcs@gmail.com>
Reviewed-by: Baolin Wang <baolin.wang@linux.alibaba.com>
Tested-by: Baolin Wang <baolin.wang@linux.alibaba.com>
[ david: code cleanups; improve description ]
Tested-by: Gregory Price (Meta) <gourry@gourry.net>
Link: https://patch.msgid.link/20261029-fixes_shmem-v4-6-392fc715d8f6@kernel.org
Signed-off-by: David Hildenbrand (Arm) <david@kernel.org>
[ sashal: expanded upstream eviction helpers inline at their call sites to avoid adding new functions ]
Signed-off-by: Sasha Levin <sashal@kernel.org>
---
mm/shmem.c | 22 +++++++++++++++++++---
1 file changed, 19 insertions(+), 3 deletions(-)
diff --git a/mm/shmem.c b/mm/shmem.c
index bb5d24e7202dd..b20587118f227 100644
--- a/mm/shmem.c
+++ b/mm/shmem.c
@@ -1442,16 +1442,21 @@ static void shmem_evict_inode(struct inode *inode)
}
spin_unlock(&sbinfo->shrinklist_lock);
}
- 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);
@@ -1732,6 +1737,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.
+ */
+ atomic_inc(&info->stop_eviction);
+
folio_dup_swap(folio, NULL);
shmem_delete_from_page_cache(folio, swp_to_radix_entry(folio->swap));
@@ -1739,6 +1751,8 @@ 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 */
+ if (atomic_dec_and_test(&info->stop_eviction))
+ wake_up_var(&info->stop_eviction);
return error;
}
@@ -1755,6 +1769,8 @@ int shmem_writeout(struct swap_io_ctx *ctx, struct folio *folio,
shmem_recalc_inode(inode, 0, -nr_pages);
folio_put_swap(folio, NULL);
}
+ if (atomic_dec_and_test(&info->stop_eviction))
+ wake_up_var(&info->stop_eviction);
/*
* The swap_cache_del_folio() below could be left for
--
2.53.0
^ permalink raw reply related [flat|nested] 3+ messages in thread
end of thread, other threads:[~2026-11-12 2:19 UTC | newest]
Thread overview: 3+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-11-10 1:01 FAILED: patch "[PATCH] mm/shmem: hold off eviction in shmem_writeout()" failed to apply to 7.3-stable tree Sasha Levin
2026-11-12 2:19 ` [PATCH 7.3.y 1/2] mm: shmem: move unused huge shrinklist queuing past the truncation check Sasha Levin
2026-11-12 2:19 ` [PATCH 7.3.y 2/2] mm/shmem: hold off eviction in shmem_writeout() Sasha Levin
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox