Linux-mm Archive on lore.kernel.org
 help / color / mirror / Atom feed
* [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