* [PATCH v3 0/4] mm, swap: some random fixes and cleanups
@ 2026-09-07 9:13 Kemeng Shi
2026-09-07 9:13 ` [PATCH v3 1/4] mm, swap: Fix potential NULL dereference when trying a sleep table allocation Kemeng Shi
` (3 more replies)
0 siblings, 4 replies; 6+ messages in thread
From: Kemeng Shi @ 2026-09-07 9:13 UTC (permalink / raw)
To: akpm, chrisl, nphamcs, baoquan.he, baohua, youngjun.park
Cc: linux-mm, linux-kernel, Kemeng Shi
Hello all,
This series contains some random fixes and cleanups. More details
can be found in respective patches.
Thanks!
v1->v2:
- Collect RVB from Youngjun nad Youngjun
- Some minor improvements
v2->v3:
- Address memleak problem in patch 1
- Add description of current memory order guarantee in patch 1
- Collect Ack-by from Kairui
Kemeng Shi (4):
mm, swap: Fix potential NULL dereference when trying a sleep table
allocation
mm, swap: Move setup_swap_clusters_info() after SWP_SOLIDSTATE
initialization
mm, swap: return early from swap_extend_table_try_free() on first
non-zero entry
mm, swap: Remove unneeded swap_extend_table_try_free() in
swap_dup_entries_cluster()
mm/swapfile.c | 53 ++++++++++++++++++++++++++++++---------------------
1 file changed, 31 insertions(+), 22 deletions(-)
--
2.36.1
^ permalink raw reply [flat|nested] 6+ messages in thread
* [PATCH v3 1/4] mm, swap: Fix potential NULL dereference when trying a sleep table allocation
2026-09-07 9:13 [PATCH v3 0/4] mm, swap: some random fixes and cleanups Kemeng Shi
@ 2026-09-07 9:13 ` Kemeng Shi
2026-09-11 0:17 ` Andrew Morton
2026-09-07 9:13 ` [PATCH v3 2/4] mm, swap: Move setup_swap_clusters_info() after SWP_SOLIDSTATE initialization Kemeng Shi
` (2 subsequent siblings)
3 siblings, 1 reply; 6+ messages in thread
From: Kemeng Shi @ 2026-09-07 9:13 UTC (permalink / raw)
To: akpm, chrisl, nphamcs, baoquan.he, baohua, youngjun.park
Cc: linux-mm, linux-kernel, Kemeng Shi
The root cause of this issue is because multi-tables are updated in non
atomic context. To be more specific, the issue could be triggerred as
following:
swap_alloc_fast swap_cluster_populate()
/* Try a sleep allocation */
spin_unlock(&ci->lock);
swap_cluster_alloc_table()
rcu_assign_pointer(ci->table, table);
ci = swap_cluster_lock(si, offset)
cluster_is_usable(ci, order)
if (!cluster_table_is_alloced(ci)) // ok
alloc_swap_scan_cluster()
cluster_scan_range()
__swap_table_get()
/* free table when more table allocation fails */
ci->memcg_table = kzalloc_obj(*ci->memcg_table,
gfp);
if (!ci->memcg_table)
swap_cluster_free_table()
rcu_assign_pointer(ci->table, NULL);
table = rcu_dereference_check(ci->table, lockdep_is_held(&ci->lock));
atomic_long_read(&table[off]); // NULL dereference
Since memory order guarantee between ci->table, as well as between
ci->table and ci->zero_bitmap, fix the issue by making tables
visible at the end of swap_cluster_populate().
Current memory order guarantee is as following:
On write side:
rcu_assign_pointer(ci->table, table) will offer release to ensure
zero_bitmap and memcg_table visible before ci->table.
On read side:
folio_alloc_swap
swap_alloc_fast/swap_alloc_slow
/* ci->table: protected by cluster lock */
swap_cluster_lock
cluster_is_usable
...
__swap_table_set
...
swap_cluster_unlock
mem_cgroup_try_charge_swap
...
/* memcg_table: protected by cluster lock */
swap_cluster_get_and_lock
__swap_cgroup_set
swap_cluster_unlock
swap_writeout
swap_zeromap_folio_set
/* zero_bitmap: protected by cluster lock */
swap_cluster_get_and_lock
__swap_table_set_zero
swap_cluster_unlock
Fixes: b197d41462c2 ("mm/memcg, swap: store cgroup id in cluster table directly")
Signed-off-by: Kemeng Shi <shikemeng@huaweicloud.com>
---
mm/swapfile.c | 30 ++++++++++++++++++++----------
1 file changed, 20 insertions(+), 10 deletions(-)
diff --git a/mm/swapfile.c b/mm/swapfile.c
index 53bf01d5f7f1..45b6154f85ad 100644
--- a/mm/swapfile.c
+++ b/mm/swapfile.c
@@ -418,6 +418,17 @@ static void swap_cluster_free_table_folio_rcu_cb(struct rcu_head *head)
folio_put(folio);
}
+static void swap_cluster_free_count_table(struct swap_table *table)
+{
+ if (!SWP_TABLE_USE_PAGE) {
+ kmem_cache_free(swap_table_cachep, table);
+ return;
+ }
+
+ call_rcu(&(folio_page(virt_to_folio(table), 0)->rcu_head),
+ swap_cluster_free_table_folio_rcu_cb);
+}
+
static void swap_cluster_free_table(struct swap_cluster_info *ci)
{
struct swap_table *table;
@@ -437,13 +448,7 @@ static void swap_cluster_free_table(struct swap_cluster_info *ci)
return;
rcu_assign_pointer(ci->table, NULL);
- if (!SWP_TABLE_USE_PAGE) {
- kmem_cache_free(swap_table_cachep, table);
- return;
- }
-
- call_rcu(&(folio_page(virt_to_folio(table), 0)->rcu_head),
- swap_cluster_free_table_folio_rcu_cb);
+ swap_cluster_free_count_table(table);
}
static int swap_cluster_alloc_table(struct swap_cluster_info *ci, gfp_t gfp)
@@ -466,14 +471,12 @@ static int swap_cluster_alloc_table(struct swap_cluster_info *ci, gfp_t gfp)
if (!table)
return -ENOMEM;
- rcu_assign_pointer(ci->table, table);
-
#ifdef CONFIG_MEMCG
if (!mem_cgroup_disabled()) {
VM_WARN_ON_ONCE(ci->memcg_table);
ci->memcg_table = kzalloc_obj(*ci->memcg_table, gfp);
if (!ci->memcg_table) {
- swap_cluster_free_table(ci);
+ swap_cluster_free_count_table(table);
return -ENOMEM;
}
}
@@ -484,9 +487,16 @@ static int swap_cluster_alloc_table(struct swap_cluster_info *ci, gfp_t gfp)
ci->zero_bitmap = bitmap_zalloc(SWAPFILE_CLUSTER, gfp);
if (!ci->zero_bitmap) {
swap_cluster_free_table(ci);
+ swap_cluster_free_count_table(table);
return -ENOMEM;
}
#endif
+
+ /*
+ * Make tables visible to cluster_is_usable() after everything is
+ * ready.
+ */
+ rcu_assign_pointer(ci->table, table);
return 0;
}
--
2.36.1
^ permalink raw reply related [flat|nested] 6+ messages in thread
* [PATCH v3 2/4] mm, swap: Move setup_swap_clusters_info() after SWP_SOLIDSTATE initialization
2026-09-07 9:13 [PATCH v3 0/4] mm, swap: some random fixes and cleanups Kemeng Shi
2026-09-07 9:13 ` [PATCH v3 1/4] mm, swap: Fix potential NULL dereference when trying a sleep table allocation Kemeng Shi
@ 2026-09-07 9:13 ` Kemeng Shi
2026-09-07 9:13 ` [PATCH v3 3/4] mm, swap: return early from swap_extend_table_try_free() on first non-zero entry Kemeng Shi
2026-09-07 9:13 ` [PATCH v3 4/4] mm, swap: Remove unneeded swap_extend_table_try_free() in swap_dup_entries_cluster() Kemeng Shi
3 siblings, 0 replies; 6+ messages in thread
From: Kemeng Shi @ 2026-09-07 9:13 UTC (permalink / raw)
To: akpm, chrisl, nphamcs, baoquan.he, baohua, youngjun.park
Cc: linux-mm, linux-kernel, Kemeng Shi, Luiz Capitulino, Kairui Song
In setup_swap_clusters_info(), SWP_SOLIDSTATE is used to decide
global_cluster allocation. Move setup_swap_clusters_info() after
SWP_SOLIDSTATE initialization to avoid unneeded global_cluster
allocation.
Fixes: 451c6326105b2 ("mm, swap: clean up swapon process and locking")
Signed-off-by: Kemeng Shi <shikemeng@huaweicloud.com>
Reviewed-by: Luiz Capitulino <luizcap@redhat.com>
Acked-by: Kairui Song <kasong@tencent.com>
---
mm/swapfile.c | 13 ++++++++-----
1 file changed, 8 insertions(+), 5 deletions(-)
diff --git a/mm/swapfile.c b/mm/swapfile.c
index 45b6154f85ad..767d83d89d0b 100644
--- a/mm/swapfile.c
+++ b/mm/swapfile.c
@@ -3731,11 +3731,6 @@ SYSCALL_DEFINE2(swapon, const char __user *, specialfile, int, swap_flags)
maxpages = si->max;
- /* Set up the swap cluster info */
- error = setup_swap_clusters_info(si, swap_header, maxpages);
- if (error)
- goto bad_swap_unlock_inode;
-
if (si->bdev && bdev_stable_writes(si->bdev))
si->flags |= SWP_STABLE_WRITES;
@@ -3749,6 +3744,14 @@ SYSCALL_DEFINE2(swapon, const char __user *, specialfile, int, swap_flags)
inced_nr_rotate_swap = true;
}
+ /*
+ * Set up the swap cluster info after SWP_ flags handling as
+ * setup_swap_clusters_info() checks SWP_SOLIDSTATE.
+ */
+ error = setup_swap_clusters_info(si, swap_header, maxpages);
+ if (error)
+ goto bad_swap_unlock_inode;
+
if ((swap_flags & SWAP_FLAG_DISCARD) &&
si->bdev && bdev_max_discard_sectors(si->bdev)) {
/*
--
2.36.1
^ permalink raw reply related [flat|nested] 6+ messages in thread
* [PATCH v3 3/4] mm, swap: return early from swap_extend_table_try_free() on first non-zero entry
2026-09-07 9:13 [PATCH v3 0/4] mm, swap: some random fixes and cleanups Kemeng Shi
2026-09-07 9:13 ` [PATCH v3 1/4] mm, swap: Fix potential NULL dereference when trying a sleep table allocation Kemeng Shi
2026-09-07 9:13 ` [PATCH v3 2/4] mm, swap: Move setup_swap_clusters_info() after SWP_SOLIDSTATE initialization Kemeng Shi
@ 2026-09-07 9:13 ` Kemeng Shi
2026-09-07 9:13 ` [PATCH v3 4/4] mm, swap: Remove unneeded swap_extend_table_try_free() in swap_dup_entries_cluster() Kemeng Shi
3 siblings, 0 replies; 6+ messages in thread
From: Kemeng Shi @ 2026-09-07 9:13 UTC (permalink / raw)
To: akpm, chrisl, nphamcs, baoquan.he, baohua, youngjun.park
Cc: linux-mm, linux-kernel, Kemeng Shi, Kairui Song
Return immediately when the first non-zero swap count is found as
any non-zero swap count prevents freeing extend_table and further
iteration is pointless.
Signed-off-by: Kemeng Shi <shikemeng@huaweicloud.com>
Reviewed-by: Youngjun Park <youngjun.park@lge.com>
Acked-by: Kairui Song <kasong@tencent.com>
---
mm/swapfile.c | 9 +++------
1 file changed, 3 insertions(+), 6 deletions(-)
diff --git a/mm/swapfile.c b/mm/swapfile.c
index 767d83d89d0b..b73f34ff44f8 100644
--- a/mm/swapfile.c
+++ b/mm/swapfile.c
@@ -1527,20 +1527,17 @@ int swap_retry_table_alloc(swp_entry_t entry, gfp_t gfp)
static void swap_extend_table_try_free(struct swap_cluster_info *ci)
{
unsigned long i;
- bool can_free = true;
if (!ci->extend_table)
return;
for (i = 0; i < SWAPFILE_CLUSTER; i++) {
if (ci->extend_table[i])
- can_free = false;
+ return;
}
- if (can_free) {
- kfree(ci->extend_table);
- ci->extend_table = NULL;
- }
+ kfree(ci->extend_table);
+ ci->extend_table = NULL;
}
/* Decrease the swap count of one slot, without freeing it */
--
2.36.1
^ permalink raw reply related [flat|nested] 6+ messages in thread
* [PATCH v3 4/4] mm, swap: Remove unneeded swap_extend_table_try_free() in swap_dup_entries_cluster()
2026-09-07 9:13 [PATCH v3 0/4] mm, swap: some random fixes and cleanups Kemeng Shi
` (2 preceding siblings ...)
2026-09-07 9:13 ` [PATCH v3 3/4] mm, swap: return early from swap_extend_table_try_free() on first non-zero entry Kemeng Shi
@ 2026-09-07 9:13 ` Kemeng Shi
3 siblings, 0 replies; 6+ messages in thread
From: Kemeng Shi @ 2026-09-07 9:13 UTC (permalink / raw)
To: akpm, chrisl, nphamcs, baoquan.he, baohua, youngjun.park
Cc: linux-mm, linux-kernel, Kemeng Shi
Since commit 0475fde0f68de ("mm, swap: avoid leaving unused extend table
after alloc race"), extend table is always allocated when any swap count
reach MAX - 1 and is always freed when swap count decrease to MAX - 1
or MAX - 2 with cluster lock held. So the extend table will always be
freed properly when decrease swap count in __swap_cluster_put_entry().
So swap_extend_table_try_free() outside of __swap_cluster_put_entry()
is unneeded and can be removed.
Signed-off-by: Kemeng Shi <shikemeng@huaweicloud.com>
Reviewed-by: Youngjun Park <youngjun.park@lge.com>
---
mm/swapfile.c | 1 -
1 file changed, 1 deletion(-)
diff --git a/mm/swapfile.c b/mm/swapfile.c
index b73f34ff44f8..449890074c28 100644
--- a/mm/swapfile.c
+++ b/mm/swapfile.c
@@ -1729,7 +1729,6 @@ static int swap_dup_entries_cluster(struct swap_info_struct *si,
failed:
while (ci_off-- > ci_start)
__swap_cluster_put_entry(ci, ci_off);
- swap_extend_table_try_free(ci);
swap_cluster_unlock(ci);
return err;
}
--
2.36.1
^ permalink raw reply related [flat|nested] 6+ messages in thread
* Re: [PATCH v3 1/4] mm, swap: Fix potential NULL dereference when trying a sleep table allocation
2026-09-07 9:13 ` [PATCH v3 1/4] mm, swap: Fix potential NULL dereference when trying a sleep table allocation Kemeng Shi
@ 2026-09-11 0:17 ` Andrew Morton
0 siblings, 0 replies; 6+ messages in thread
From: Andrew Morton @ 2026-09-11 0:17 UTC (permalink / raw)
To: Kemeng Shi
Cc: chrisl, nphamcs, baoquan.he, baohua, youngjun.park, linux-mm,
linux-kernel
On Mon, 7 Sep 2026 17:13:53 +0800 Kemeng Shi <shikemeng@huaweicloud.com> wrote:
> The root cause of this issue is because multi-tables are updated in non
> atomic context. To be more specific, the issue could be triggerred as
> following:
>
> ...
>
> Since memory order guarantee between ci->table, as well as between
> ci->table and ci->zero_bitmap, fix the issue by making tables
> visible at the end of swap_cluster_populate().
>
Sashiko suggests this might be incomplete:
https://sashiko.dev/#/patchset/20260907091356.53026-1-shikemeng@huaweicloud.com
^ permalink raw reply [flat|nested] 6+ messages in thread
end of thread, other threads:[~2026-09-11 0:17 UTC | newest]
Thread overview: 6+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-09-07 9:13 [PATCH v3 0/4] mm, swap: some random fixes and cleanups Kemeng Shi
2026-09-07 9:13 ` [PATCH v3 1/4] mm, swap: Fix potential NULL dereference when trying a sleep table allocation Kemeng Shi
2026-09-11 0:17 ` Andrew Morton
2026-09-07 9:13 ` [PATCH v3 2/4] mm, swap: Move setup_swap_clusters_info() after SWP_SOLIDSTATE initialization Kemeng Shi
2026-09-07 9:13 ` [PATCH v3 3/4] mm, swap: return early from swap_extend_table_try_free() on first non-zero entry Kemeng Shi
2026-09-07 9:13 ` [PATCH v3 4/4] mm, swap: Remove unneeded swap_extend_table_try_free() in swap_dup_entries_cluster() Kemeng Shi
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox