* [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 [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 [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
all inboxes | Powered by JetHome®