Linux-mm Archive on lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v2 0/2] mm/ksm: clean up scan_get_next_rmap_item() initialization
@ 2026-11-15  7:58 Xu Xin (vivo)
  2026-11-15  8:00 ` [PATCH v2 1/2] mm/ksm: extract helper functions for scan cycle cleanup Xu Xin (vivo)
  2026-11-15  8:00 ` [PATCH v2 2/2] mm/ksm: add initialization helpers for scan_get_next_rmap_item() Xu Xin (vivo)
  0 siblings, 2 replies; 4+ messages in thread
From: Xu Xin (vivo) @ 2026-11-15  7:58 UTC (permalink / raw)
  To: David Hildenbrand; +Cc: chengming.zhou, ljs, linux-kernel, linux-mm, xu.xin

Currently, scan_get_next_rmap_item() has grown quite large and complex.
When introducing new features, such as the only-merge-zero-page mode we
are currently working on, we don't want to add more complexity to it
and don't want to touch rmap_item structures to avoid unnecessary overhead,
so we need to extract some common logic.

With these changes, the entry of scan_get_next_rmap_item() becomes much
cleaner and easier to follow, instead of being cluttered with unrelated
boilerplate such as stale stable_node pruning and cycle initialization.

Let's start by cleaning up these two parts at the beginning (where I think
abstraction is necessary), and we can continue cleaning up further if
needed later.

Changes in v2:
- Rename scan_begin_new_cycle() to scan_should_start_new_cycle() and keep
  it simple (David).
- Keep lru_add_drain_all() in scan_get_next_rmap_item() (David).
- Use scoped_guard(spinlock, &ksm_mmlist_lock) in scan_get_first_mm()
  (Lorenzo).
- Collect Reviewed-by and Acked-by tags from v1.

Xu Xin (vivo) (2):
  mm/ksm: extract helper functions for scan cycle cleanup
  mm/ksm: add initialization helpers for scan_get_next_rmap_item()

 mm/ksm.c | 98 +++++++++++++++++++++++++++++++++++---------------------
 1 file changed, 62 insertions(+), 36 deletions(-)

-- 
2.34.1



^ permalink raw reply	[flat|nested] 4+ messages in thread

* [PATCH v2 1/2] mm/ksm: extract helper functions for scan cycle cleanup
  2026-11-15  7:58 [PATCH v2 0/2] mm/ksm: clean up scan_get_next_rmap_item() initialization Xu Xin (vivo)
@ 2026-11-15  8:00 ` Xu Xin (vivo)
  2026-11-15  8:00 ` [PATCH v2 2/2] mm/ksm: add initialization helpers for scan_get_next_rmap_item() Xu Xin (vivo)
  1 sibling, 0 replies; 4+ messages in thread
From: Xu Xin (vivo) @ 2026-11-15  8:00 UTC (permalink / raw)
  To: xu.xin; +Cc: chengming.zhou, david, linux-kernel, linux-mm, ljs

Since scan_get_next_rmap_item() is getting too large, extract two new
helper functions, ksm_prune_migrate_nodes() and ksm_reset_unstable_trees(),
to reduce indentation levels, isolate local variables, and improve the
readability of the main scan loop.

No functional change intended.

Signed-off-by: Xu Xin (vivo) <xu.xin@linux.dev>
Reviewed-by: Lorenzo Stoakes (ARM) <ljs@kernel.org>
Acked-by: David Hildenbrand (Arm) <david@kernel.org>
---
 mm/ksm.c | 54 +++++++++++++++++++++++++++++++-----------------------
 1 file changed, 31 insertions(+), 23 deletions(-)

diff --git a/mm/ksm.c b/mm/ksm.c
index 5e24b5ca8dc8..8db6743ece6d 100644
--- a/mm/ksm.c
+++ b/mm/ksm.c
@@ -2609,6 +2609,35 @@ static struct mm_walk_ops ksm_next_page_ops = {
 	.walk_lock = PGWALK_RDLOCK,
 };
 
+static void ksm_prune_migrate_nodes(void)
+{
+	struct ksm_stable_node *stable_node, *next;
+	struct folio *folio;
+
+	if (ksm_merge_across_nodes)
+		return;
+
+	/*
+	 * Whereas stale stable_nodes on the stable_tree itself
+	 * get pruned in the regular course of stable_tree_search(),
+	 * those moved out to the migrate_nodes list can accumulate:
+	 * so prune them once before each full scan.
+	 */
+	list_for_each_entry_safe(stable_node, next, &migrate_nodes, list) {
+		folio = ksm_get_folio(stable_node, KSM_GET_FOLIO_NOLOCK);
+
+		if (folio)
+			folio_put(folio);
+		cond_resched();
+	}
+}
+
+static void ksm_reset_unstable_trees(void)
+{
+	for (int nid = 0; nid < ksm_nr_node_ids; nid++)
+		root_unstable_tree[nid] = RB_ROOT;
+}
+
 static struct ksm_rmap_item *scan_get_next_rmap_item(struct page **page)
 {
 	struct mm_struct *mm;
@@ -2617,7 +2646,6 @@ static struct ksm_rmap_item *scan_get_next_rmap_item(struct page **page)
 	struct vm_area_struct *vma;
 	struct ksm_rmap_item *rmap_item;
 	struct vma_iterator vmi;
-	int nid;
 
 	if (list_empty(&ksm_mm_head.slot.mm_node))
 		return NULL;
@@ -2639,28 +2667,8 @@ static struct ksm_rmap_item *scan_get_next_rmap_item(struct page **page)
 		 */
 		lru_add_drain_all();
 
-		/*
-		 * Whereas stale stable_nodes on the stable_tree itself
-		 * get pruned in the regular course of stable_tree_search(),
-		 * those moved out to the migrate_nodes list can accumulate:
-		 * so prune them once before each full scan.
-		 */
-		if (!ksm_merge_across_nodes) {
-			struct ksm_stable_node *stable_node, *next;
-			struct folio *folio;
-
-			list_for_each_entry_safe(stable_node, next,
-						 &migrate_nodes, list) {
-				folio = ksm_get_folio(stable_node,
-						      KSM_GET_FOLIO_NOLOCK);
-				if (folio)
-					folio_put(folio);
-				cond_resched();
-			}
-		}
-
-		for (nid = 0; nid < ksm_nr_node_ids; nid++)
-			root_unstable_tree[nid] = RB_ROOT;
+		ksm_prune_migrate_nodes();
+		ksm_reset_unstable_trees();
 
 		spin_lock(&ksm_mmlist_lock);
 		slot = list_entry(mm_slot->slot.mm_node.next,
-- 
2.34.1



^ permalink raw reply related	[flat|nested] 4+ messages in thread

* [PATCH v2 2/2] mm/ksm: add initialization helpers for scan_get_next_rmap_item()
  2026-11-15  7:58 [PATCH v2 0/2] mm/ksm: clean up scan_get_next_rmap_item() initialization Xu Xin (vivo)
  2026-11-15  8:00 ` [PATCH v2 1/2] mm/ksm: extract helper functions for scan cycle cleanup Xu Xin (vivo)
@ 2026-11-15  8:00 ` Xu Xin (vivo)
  2026-11-15 15:28   ` kernel test robot
  1 sibling, 1 reply; 4+ messages in thread
From: Xu Xin (vivo) @ 2026-11-15  8:00 UTC (permalink / raw)
  To: xu.xin; +Cc: chengming.zhou, david, linux-kernel, linux-mm, ljs

Extract the new scan cycle check into scan_should_start_new_cycle()
and add the helper scan_get_first_mm() that advances the scan
cursor to the first mm_slot.

This keeps the exact execution order intact while making
scan_get_next_rmap_item() cleaner and easier to follow.

No functional change intended.

Signed-off-by: Xu Xin (vivo) <xu.xin@linux.dev>
Reviewed-by: Lorenzo Stoakes (ARM) <ljs@kernel.org>
---
Changes in v2:
- Rename scan_begin_new_cycle() to scan_should_start_new_cycle() and keep
  it simple (David).
- Keep lru_add_drain_all() in scan_get_next_rmap_item() (David).
- Use scoped_guard(spinlock, &ksm_mmlist_lock) in scan_get_first_mm()
  (Lorenzo).
- Keep Lorenzo's Reviewed-by tag as the changes are minor and follow the
  suggested cleanups (please let me know if you prefer otherwise).

 mm/ksm.c | 44 +++++++++++++++++++++++++++++++-------------
 1 file changed, 31 insertions(+), 13 deletions(-)

diff --git a/mm/ksm.c b/mm/ksm.c
index 8db6743ece6d..337fcc9c6322 100644
--- a/mm/ksm.c
+++ b/mm/ksm.c
@@ -2638,6 +2638,34 @@ static void ksm_reset_unstable_trees(void)
 		root_unstable_tree[nid] = RB_ROOT;
 }
 
+static bool scan_should_start_new_cycle(void)
+{
+	/* Check if the cursor points to the head. */
+	return ksm_scan.mm_slot == &ksm_mm_head;
+}
+
+static struct ksm_mm_slot *scan_get_first_mm(void)
+{
+	struct ksm_mm_slot *mm_slot = &ksm_mm_head;
+	struct mm_slot *slot;
+
+	scoped_guard(spinlock, &ksm_mmlist_lock) {
+		slot = list_entry(mm_slot->slot.mm_node.next,
+				  struct mm_slot, mm_node);
+		mm_slot = mm_slot_entry(slot, struct ksm_mm_slot, slot);
+		ksm_scan.mm_slot = mm_slot;
+	}
+
+	/*
+	 * Although we tested list_empty() above, a racing __ksm_exit
+	 * of the last mm on the list may have removed it since then.
+	 */
+	if (mm_slot == &ksm_mm_head)
+		return NULL;
+
+	return mm_slot;
+}
+
 static struct ksm_rmap_item *scan_get_next_rmap_item(struct page **page)
 {
 	struct mm_struct *mm;
@@ -2650,8 +2678,7 @@ static struct ksm_rmap_item *scan_get_next_rmap_item(struct page **page)
 	if (list_empty(&ksm_mm_head.slot.mm_node))
 		return NULL;
 
-	mm_slot = ksm_scan.mm_slot;
-	if (mm_slot == &ksm_mm_head) {
+	if (scan_should_start_new_cycle()) {
 		advisor_start_scan();
 		trace_ksm_start_scan(ksm_scan.seqnr, ksm_rmap_items);
 
@@ -2670,17 +2697,8 @@ static struct ksm_rmap_item *scan_get_next_rmap_item(struct page **page)
 		ksm_prune_migrate_nodes();
 		ksm_reset_unstable_trees();
 
-		spin_lock(&ksm_mmlist_lock);
-		slot = list_entry(mm_slot->slot.mm_node.next,
-				  struct mm_slot, mm_node);
-		mm_slot = mm_slot_entry(slot, struct ksm_mm_slot, slot);
-		ksm_scan.mm_slot = mm_slot;
-		spin_unlock(&ksm_mmlist_lock);
-		/*
-		 * Although we tested list_empty() above, a racing __ksm_exit
-		 * of the last mm on the list may have removed it since then.
-		 */
-		if (mm_slot == &ksm_mm_head)
+		mm_slot = scan_get_first_mm();
+		if (!mm_slot)
 			return NULL;
 next_mm:
 		ksm_scan.address = 0;
-- 
2.34.1



^ permalink raw reply related	[flat|nested] 4+ messages in thread

* Re: [PATCH v2 2/2] mm/ksm: add initialization helpers for scan_get_next_rmap_item()
  2026-11-15  8:00 ` [PATCH v2 2/2] mm/ksm: add initialization helpers for scan_get_next_rmap_item() Xu Xin (vivo)
@ 2026-11-15 15:28   ` kernel test robot
  0 siblings, 0 replies; 4+ messages in thread
From: kernel test robot @ 2026-11-15 15:28 UTC (permalink / raw)
  To: Xu Xin (vivo)
  Cc: llvm, oe-kbuild-all, chengming.zhou, david, linux-kernel,
	linux-mm, ljs

Hi Xu,

kernel test robot noticed the following build warnings:

[auto build test WARNING on akpm-mm/mm-everything]

url:    https://github.com/intel-lab-lkp/linux/commits/Xu-Xin-vivo/mm-ksm-extract-helper-functions-for-scan-cycle-cleanup/20261115-160032
base:   https://git.kernel.org/pub/scm/linux/kernel/git/akpm/mm.git mm-everything
patch link:    https://lore.kernel.org/r/20261115080049.132515-1-xu.xin%40linux.dev
patch subject: [PATCH v2 2/2] mm/ksm: add initialization helpers for scan_get_next_rmap_item()
config: loongarch-defconfig (https://download.01.org/0day-ci/archive/20261115/202611152302.OUfZlgfW-lkp@intel.com/config)
compiler: clang version 24.0.0git (https://github.com/llvm/llvm-project ca14e3d05c4e63d6e8103945c86b958ed2f55d5f)
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20261115/202611152302.OUfZlgfW-lkp@intel.com/reproduce)

If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202611152302.OUfZlgfW-lkp@intel.com/

All warnings (new ones prefixed by >>):

>> mm/ksm.c:2693:6: warning: variable 'mm_slot' is used uninitialized whenever 'if' condition is false [-Wsometimes-uninitialized]
    2693 |         if (scan_should_start_new_cycle()) {
         |             ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   mm/ksm.c:2720:10: note: uninitialized use occurs here
    2720 |         slot = &mm_slot->slot;
         |                 ^~~~~~~
   mm/ksm.c:2693:2: note: remove the 'if' if its condition is always true
    2693 |         if (scan_should_start_new_cycle()) {
         |         ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   mm/ksm.c:2684:29: note: initialize the variable 'mm_slot' to silence this warning
    2684 |         struct ksm_mm_slot *mm_slot;
         |                                    ^
         |                                     = NULL
   1 warning generated.


vim +2693 mm/ksm.c

  2680	
  2681	static struct ksm_rmap_item *scan_get_next_rmap_item(struct page **page)
  2682	{
  2683		struct mm_struct *mm;
  2684		struct ksm_mm_slot *mm_slot;
  2685		struct mm_slot *slot;
  2686		struct vm_area_struct *vma;
  2687		struct ksm_rmap_item *rmap_item;
  2688		struct vma_iterator vmi;
  2689	
  2690		if (list_empty(&ksm_mm_head.slot.mm_node))
  2691			return NULL;
  2692	
> 2693		if (scan_should_start_new_cycle()) {
  2694			advisor_start_scan();
  2695			trace_ksm_start_scan(ksm_scan.seqnr, ksm_rmap_items);
  2696	
  2697			/*
  2698			 * A number of pages can hang around indefinitely in per-cpu
  2699			 * LRU cache, raised page count preventing write_protect_page
  2700			 * from merging them.  Though it doesn't really matter much,
  2701			 * it is puzzling to see some stuck in pages_volatile until
  2702			 * other activity jostles them out, and they also prevented
  2703			 * LTP's KSM test from succeeding deterministically; so drain
  2704			 * them here (here rather than on entry to ksm_do_scan(),
  2705			 * so we don't IPI too often when pages_to_scan is set low).
  2706			 */
  2707			lru_add_drain_all();
  2708	
  2709			ksm_prune_migrate_nodes();
  2710			ksm_reset_unstable_trees();
  2711	
  2712			mm_slot = scan_get_first_mm();
  2713			if (!mm_slot)
  2714				return NULL;
  2715	next_mm:
  2716			ksm_scan.address = 0;
  2717			ksm_scan.rmap_list = &mm_slot->rmap_list;
  2718		}
  2719	
  2720		slot = &mm_slot->slot;
  2721		mm = slot->mm;
  2722		vma_iter_init(&vmi, mm, ksm_scan.address);
  2723	
  2724		mmap_read_lock(mm);
  2725		if (ksm_test_exit(mm))
  2726			goto no_vmas;
  2727	
  2728		for_each_vma(vmi, vma) {
  2729			if (!(vma->vm_flags & VM_MERGEABLE))
  2730				continue;
  2731			if (ksm_scan.address < vma->vm_start)
  2732				ksm_scan.address = vma->vm_start;
  2733			if (!vma_has_anon_rmap(vma))
  2734				ksm_scan.address = vma->vm_end;
  2735	
  2736			while (ksm_scan.address < vma->vm_end) {
  2737				struct ksm_next_page_arg ksm_next_page_arg;
  2738				struct page *tmp_page = NULL;
  2739				struct folio *folio;
  2740	
  2741				if (ksm_test_exit(mm))
  2742					goto no_vmas;
  2743	
  2744				int found;
  2745	
  2746				found = walk_page_range_vma(vma, ksm_scan.address,
  2747							    vma->vm_end,
  2748							    &ksm_next_page_ops,
  2749							    &ksm_next_page_arg);
  2750	
  2751				if (found > 0) {
  2752					folio = ksm_next_page_arg.folio;
  2753					tmp_page = ksm_next_page_arg.page;
  2754					ksm_scan.address = ksm_next_page_arg.addr;
  2755				} else {
  2756					VM_WARN_ON_ONCE(found < 0);
  2757					ksm_scan.address = vma->vm_end - PAGE_SIZE;
  2758				}
  2759	
  2760				if (tmp_page) {
  2761					flush_anon_page(vma, tmp_page, ksm_scan.address);
  2762					flush_dcache_page(tmp_page);
  2763					rmap_item = get_next_rmap_item(mm_slot,
  2764						ksm_scan.rmap_list, ksm_scan.address);
  2765					if (rmap_item) {
  2766						ksm_scan.rmap_list =
  2767								&rmap_item->rmap_list;
  2768	
  2769						if (should_skip_rmap_item(folio, rmap_item)) {
  2770							folio_put(folio);
  2771							goto next_page;
  2772						}
  2773	
  2774						ksm_scan.address += PAGE_SIZE;
  2775						*page = tmp_page;
  2776					} else {
  2777						folio_put(folio);
  2778					}
  2779					mmap_read_unlock(mm);
  2780					return rmap_item;
  2781				}
  2782	next_page:
  2783				ksm_scan.address += PAGE_SIZE;
  2784				cond_resched();
  2785			}
  2786		}
  2787	
  2788		if (ksm_test_exit(mm)) {
  2789	no_vmas:
  2790			ksm_scan.address = 0;
  2791			ksm_scan.rmap_list = &mm_slot->rmap_list;
  2792		}
  2793		/*
  2794		 * Nuke all the rmap_items that are above this current rmap:
  2795		 * because there were no VM_MERGEABLE vmas with such addresses.
  2796		 */
  2797		remove_trailing_rmap_items(ksm_scan.rmap_list);
  2798	
  2799		spin_lock(&ksm_mmlist_lock);
  2800		slot = list_entry(mm_slot->slot.mm_node.next,
  2801				  struct mm_slot, mm_node);
  2802		ksm_scan.mm_slot = mm_slot_entry(slot, struct ksm_mm_slot, slot);
  2803		if (ksm_scan.address == 0) {
  2804			/*
  2805			 * We've completed a full scan of all vmas, holding mmap_lock
  2806			 * throughout, and found no VM_MERGEABLE: so do the same as
  2807			 * __ksm_exit does to remove this mm from all our lists now.
  2808			 * This applies either when cleaning up after __ksm_exit
  2809			 * (but beware: we can reach here even before __ksm_exit),
  2810			 * or when all VM_MERGEABLE areas have been unmapped (and
  2811			 * mmap_lock then protects against race with MADV_MERGEABLE).
  2812			 */
  2813			mm_slot_remove(&mm_slot->slot);
  2814			spin_unlock(&ksm_mmlist_lock);
  2815	
  2816			mm_slot_free(mm_slot_cache, mm_slot);
  2817			/*
  2818			 * Only clear MMF_VM_MERGEABLE. We must not clear
  2819			 * MMF_VM_MERGE_ANY, because for those MMF_VM_MERGE_ANY process,
  2820			 * perhaps their mm_struct has just been added to ksm_mm_slot
  2821			 * list, and its process has not yet officially started running
  2822			 * or has not yet performed mmap/brk to allocate anonymous VMAS.
  2823			 */
  2824			mm_flags_clear(MMF_VM_MERGEABLE, mm);
  2825			mmap_read_unlock(mm);
  2826			mmdrop(mm);
  2827		} else {
  2828			mmap_read_unlock(mm);
  2829			/*
  2830			 * mmap_read_unlock(mm) first because after
  2831			 * spin_unlock(&ksm_mmlist_lock) run, the "mm" may
  2832			 * already have been freed under us by __ksm_exit()
  2833			 * because the "mm_slot" is still hashed and
  2834			 * ksm_scan.mm_slot doesn't point to it anymore.
  2835			 */
  2836			spin_unlock(&ksm_mmlist_lock);
  2837		}
  2838	
  2839		/* Repeat until we've completed scanning the whole list */
  2840		mm_slot = ksm_scan.mm_slot;
  2841		if (mm_slot != &ksm_mm_head)
  2842			goto next_mm;
  2843	
  2844		advisor_stop_scan();
  2845	
  2846		trace_ksm_stop_scan(ksm_scan.seqnr, ksm_rmap_items);
  2847		ksm_scan.seqnr++;
  2848		return NULL;
  2849	}
  2850	

--
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki


^ permalink raw reply	[flat|nested] 4+ messages in thread

end of thread, other threads:[~2026-11-15 15:29 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-11-15  7:58 [PATCH v2 0/2] mm/ksm: clean up scan_get_next_rmap_item() initialization Xu Xin (vivo)
2026-11-15  8:00 ` [PATCH v2 1/2] mm/ksm: extract helper functions for scan cycle cleanup Xu Xin (vivo)
2026-11-15  8:00 ` [PATCH v2 2/2] mm/ksm: add initialization helpers for scan_get_next_rmap_item() Xu Xin (vivo)
2026-11-15 15:28   ` kernel test robot

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox