[PATCH OLK-6.6 0/9] add ksm_only_merge_zero_pages mode and some fixes
Andrew Morton (1): mm/hugetlb.c: undo errant change Chengming Zhou (2): mm/ksm: refactor out try_to_merge_with_zero_page() mm/ksm: don't waste time searching stable tree for fast changing page Jinjiang Tu (4): mm/ksm: fix ksm exec support for prctl mm/memcontrol: add ksm state for memcg mm/memcontrol: enable KSM for tasks moving to new memcg mm/ksm: add ksm_only_merge_zero_pages mode to merge zero pages only Johannes Weiner (1): mm: page_alloc: avoid kswapd thrashing due to NUMA restrictions Stefan Roesch (1): mm/ksm: support fork/exec for prctl fs/exec.c | 11 ++ include/linux/ksm.h | 14 +++ include/linux/memcontrol.h | 4 + include/linux/sched/coredump.h | 8 +- mm/ksm.c | 190 +++++++++++++++++++++++++-------- mm/memcontrol.c | 67 +++++++++++- mm/page_alloc.c | 24 +++++ 7 files changed, 270 insertions(+), 48 deletions(-) -- 2.43.0
From: Stefan Roesch <shr@devkernel.io> mainline inclusion from mainline-v6.7-rc1 commit 3c6f33b7273a7e2f2b2497b62c8400bd957b2fbe category: feature bugzilla: https://atomgit.com/openeuler/kernel/issues/10169 Reference: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?i... -------------------------------- Patch series "mm/ksm: add fork-exec support for prctl", v4. A process can enable KSM with the prctl system call. When the process is forked the KSM flag is inherited by the child process. However if the process is executing an exec system call directly after the fork, the KSM setting is cleared. This patch series addresses this problem. 1) Change the mask in coredump.h for execing a new process 2) Add a new test case in ksm_functional_tests This patch (of 2): Today we have two ways to enable KSM: 1) madvise system call This allows to enable KSM for a memory region for a long time. 2) prctl system call This is a recent addition to enable KSM for the complete process. In addition when a process is forked, the KSM setting is inherited. This change only affects the second case. One of the use cases for (2) was to support the ability to enable KSM for cgroups. This allows systemd to enable KSM for the seed process. By enabling it in the seed process all child processes inherit the setting. This works correctly when the process is forked. However it doesn't support fork/exec workflow. From the previous cover letter: .... Use case 3: With the madvise call sharing opportunities are only enabled for the current process: it is a workload-local decision. A considerable number of sharing opportunities may exist across multiple workloads or jobs (if they are part of the same security domain). Only a higler level entity like a job scheduler or container can know for certain if its running one or more instances of a job. That job scheduler however doesn't have the necessary internal workload knowledge to make targeted madvise calls. .... In addition it can also be a bit surprising that fork keeps the KSM setting and fork/exec does not. Link: https://lkml.kernel.org/r/20230922211141.320789-1-shr@devkernel.io Link: https://lkml.kernel.org/r/20230922211141.320789-2-shr@devkernel.io Signed-off-by: Stefan Roesch <shr@devkernel.io> Fixes: d7597f59d1d3 ("mm: add new api to enable ksm per process") Reviewed-by: David Hildenbrand <david@redhat.com> Reported-by: Carl Klemm <carl@uvos.xyz> Tested-by: Carl Klemm <carl@uvos.xyz> Cc: Johannes Weiner <hannes@cmpxchg.org> Cc: Rik van Riel <riel@surriel.com> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Conflicts: include/linux/sched/coredump.h [Context conflicts.] Signed-off-by: Jinjiang Tu <tujinjiang@huawei.com> --- include/linux/sched/coredump.h | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/include/linux/sched/coredump.h b/include/linux/sched/coredump.h index 28d71cc496bc..097f323fee54 100644 --- a/include/linux/sched/coredump.h +++ b/include/linux/sched/coredump.h @@ -71,6 +71,7 @@ static inline int get_dumpable(struct mm_struct *mm) #define MMF_UNSTABLE 22 /* mm is unstable for copy_from_user */ #define MMF_HUGE_ZERO_PAGE 23 /* mm has ever used the global huge zero page */ #define MMF_DISABLE_THP 24 /* disable THP for all VMAs */ +#define MMF_DISABLE_THP_MASK (1 << MMF_DISABLE_THP) #define MMF_OOM_REAP_QUEUED 25 /* mm was queued for oom_reaper */ #define MMF_MULTIPROCESS 26 /* mm is shared between processes */ /* @@ -85,12 +86,13 @@ static inline int get_dumpable(struct mm_struct *mm) #define MMF_HAS_MDWE 28 #define MMF_HAS_MDWE_MASK (1 << MMF_HAS_MDWE) -#define MMF_DISABLE_THP_MASK (1 << MMF_DISABLE_THP) +#define MMF_VM_MERGE_ANY 29 +#define MMF_VM_MERGE_ANY_MASK (1 << MMF_VM_MERGE_ANY) #define MMF_INIT_MASK (MMF_DUMPABLE_MASK | MMF_DUMP_FILTER_MASK |\ - MMF_DISABLE_THP_MASK | MMF_HAS_MDWE_MASK) + MMF_DISABLE_THP_MASK | MMF_HAS_MDWE_MASK |\ + MMF_VM_MERGE_ANY_MASK) -#define MMF_VM_MERGE_ANY 29 #define MMF_HAS_MDWE_NO_INHERIT 30 #ifdef CONFIG_ACPI_APEI_RAS_CRITICAL -- 2.43.0
mainline inclusion from mainline-v6.10-rc1 commit 3a9e567ca45fb5280065283d10d9a11f0db61d2b category: bugfix bugzilla: https://atomgit.com/openeuler/kernel/issues/10169 Reference: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?i... -------------------------------- Patch series "mm/ksm: fix ksm exec support for prctl", v4. commit 3c6f33b7273a ("mm/ksm: support fork/exec for prctl") inherits MMF_VM_MERGE_ANY flag when a task calls execve(). However, it doesn't create the mm_slot, so ksmd will not try to scan this task. The first patch fixes the issue. The second patch refactors to prepare for the third patch. The third patch extends the selftests of ksm to verfity the deduplication really happens after fork/exec inherits ths KSM setting. This patch (of 3): commit 3c6f33b7273a ("mm/ksm: support fork/exec for prctl") inherits MMF_VM_MERGE_ANY flag when a task calls execve(). Howerver, it doesn't create the mm_slot, so ksmd will not try to scan this task. To fix it, allocate and add the mm_slot to ksm_mm_head in __bprm_mm_init() when the mm has MMF_VM_MERGE_ANY flag. Link: https://lkml.kernel.org/r/20240328111010.1502191-1-tujinjiang@huawei.com Link: https://lkml.kernel.org/r/20240328111010.1502191-2-tujinjiang@huawei.com Fixes: 3c6f33b7273a ("mm/ksm: support fork/exec for prctl") Signed-off-by: Jinjiang Tu <tujinjiang@huawei.com> Reviewed-by: David Hildenbrand <david@redhat.com> Cc: Johannes Weiner <hannes@cmpxchg.org> Cc: Kefeng Wang <wangkefeng.wang@huawei.com> Cc: Nanyong Sun <sunnanyong@huawei.com> Cc: Rik van Riel <riel@surriel.com> Cc: Stefan Roesch <shr@devkernel.io> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Jinjiang Tu <tujinjiang@huawei.com> --- fs/exec.c | 11 +++++++++++ include/linux/ksm.h | 14 ++++++++++++++ 2 files changed, 25 insertions(+) diff --git a/fs/exec.c b/fs/exec.c index 162d0f3ccd64..ebd26709a9fd 100644 --- a/fs/exec.c +++ b/fs/exec.c @@ -66,6 +66,7 @@ #include <linux/coredump.h> #include <linux/time_namespace.h> #include <linux/user_events.h> +#include <linux/ksm.h> #include <linux/uaccess.h> #include <asm/mmu_context.h> @@ -267,6 +268,14 @@ static int __bprm_mm_init(struct linux_binprm *bprm) goto err_free; } + /* + * Need to be called with mmap write lock + * held, to avoid race with ksmd. + */ + err = ksm_execve(mm); + if (err) + goto err_ksm; + /* * Place the stack at the largest stack address the architecture * supports. Later, we'll move this to an appropriate place. We don't @@ -288,6 +297,8 @@ static int __bprm_mm_init(struct linux_binprm *bprm) bprm->p = vma->vm_end - sizeof(void *); return 0; err: + ksm_exit(mm); +err_ksm: mmap_write_unlock(mm); err_free: bprm->vma = NULL; diff --git a/include/linux/ksm.h b/include/linux/ksm.h index 5c96c3c524c1..a807950f8d67 100644 --- a/include/linux/ksm.h +++ b/include/linux/ksm.h @@ -67,6 +67,14 @@ static inline void ksm_fork(struct mm_struct *mm, struct mm_struct *oldmm) } } +static inline int ksm_execve(struct mm_struct *mm) +{ + if (test_bit(MMF_VM_MERGE_ANY, &mm->flags)) + return __ksm_enter(mm); + + return 0; +} + static inline void ksm_exit(struct mm_struct *mm) { if (test_bit(MMF_VM_MERGEABLE, &mm->flags)) @@ -112,6 +120,12 @@ static inline void ksm_fork(struct mm_struct *mm, struct mm_struct *oldmm) { } +static inline int ksm_execve(struct mm_struct *mm) +{ + return 0; +} + + static inline void ksm_exit(struct mm_struct *mm) { } -- 2.43.0
From: Chengming Zhou <chengming.zhou@linux.dev> mainline inclusion from mainline-v6.11-rc1 commit ac90c56bbd734addc9bfb4567f64f1c180c64f5d category: bugfix bugzilla: https://atomgit.com/openeuler/kernel/issues/10169 Reference: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?i... -------------------------------- Patch series "mm/ksm: cmp_and_merge_page() optimizations and cleanup", v2. This series mainly optimizes cmp_and_merge_page() to have more efficient separate code flow for ksm page and non-ksm anon page. - ksm page: don't need to calculate the checksum obviously. - anon page: don't need to search stable tree if changing fast and try to merge with zero page before searching ksm page on stable tree. Please see the patch-2 for details. Patch-3 is cleanup also a little optimization for the chain()/chain_prune interfaces, which made the stable_tree_search()/stable_tree_insert() over complex. I have done simple testing using "hackbench -g 1 -l 300000" (maybe I need to use a better workload) on my machine, have seen a little CPU usage decrease of ksmd and some improvements of cmp_and_merge_page() latency: We can see the latency of cmp_and_merge_page() when handling non-ksm anon pages has been improved. This patch (of 3): In preparation for later changes, refactor out a new function called try_to_merge_with_zero_page(), which tries to merge with zero page. Link: https://lkml.kernel.org/r/20240621-b4-ksm-scan-optimize-v2-0-1c328aa9e30b@li... Link: https://lkml.kernel.org/r/20240621-b4-ksm-scan-optimize-v2-1-1c328aa9e30b@li... Signed-off-by: Chengming Zhou <chengming.zhou@linux.dev> Cc: Andrea Arcangeli <aarcange@redhat.com> Cc: David Hildenbrand <david@redhat.com> Cc: Hugh Dickins <hughd@google.com> Cc: Stefan Roesch <shr@devkernel.io> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Jinjiang Tu <tujinjiang@huawei.com> --- mm/hugetlb.c | 1 - mm/ksm.c | 70 ++++++++++++++++++++++++++++++---------------------- 2 files changed, 40 insertions(+), 31 deletions(-) diff --git a/mm/hugetlb.c b/mm/hugetlb.c index f9ecf50a8f74..88164254cebc 100644 --- a/mm/hugetlb.c +++ b/mm/hugetlb.c @@ -2703,7 +2703,6 @@ static int gather_surplus_pages(struct hstate *h, long delta) retry: spin_unlock_irq(&hugetlb_lock); for (i = 0; i < needed; i++) { - folio = NULL; for_each_node_mask(node, cpuset_current_mems_allowed) { if (!mbind_nodemask || node_isset(node, *mbind_nodemask)) { folio = alloc_surplus_hugetlb_folio(h, htlb_alloc_mask(h), diff --git a/mm/ksm.c b/mm/ksm.c index 0de3de6df144..7e1738b0fb69 100644 --- a/mm/ksm.c +++ b/mm/ksm.c @@ -1347,6 +1347,44 @@ static int try_to_merge_one_page(struct vm_area_struct *vma, return err; } +/* + * This function returns 0 if the pages were merged or if they are + * no longer merging candidates (e.g., VMA stale), -EFAULT otherwise. + */ +static int try_to_merge_with_zero_page(struct ksm_rmap_item *rmap_item, + struct page *page) +{ + struct mm_struct *mm = rmap_item->mm; + int err = -EFAULT; + + /* + * Same checksum as an empty page. We attempt to merge it with the + * appropriate zero page if the user enabled this via sysfs. + */ + if (ksm_use_zero_pages && (rmap_item->oldchecksum == zero_checksum)) { + struct vm_area_struct *vma; + + mmap_read_lock(mm); + vma = find_mergeable_vma(mm, rmap_item->address); + if (vma) { + err = try_to_merge_one_page(vma, page, + ZERO_PAGE(rmap_item->address)); + trace_ksm_merge_one_page( + page_to_pfn(ZERO_PAGE(rmap_item->address)), + rmap_item, mm, err); + } else { + /* + * If the vma is out of date, we do not need to + * continue. + */ + err = 0; + } + mmap_read_unlock(mm); + } + + return err; +} + /* * try_to_merge_with_ksm_page - like try_to_merge_two_pages, * but no new kernel page is allocated: kpage must already be a ksm page. @@ -2117,7 +2155,6 @@ static void stable_tree_append(struct ksm_rmap_item *rmap_item, */ static void cmp_and_merge_page(struct page *page, struct ksm_rmap_item *rmap_item) { - struct mm_struct *mm = rmap_item->mm; struct ksm_rmap_item *tree_rmap_item; struct page *tree_page = NULL; struct ksm_stable_node *stable_node; @@ -2186,36 +2223,9 @@ static void cmp_and_merge_page(struct page *page, struct ksm_rmap_item *rmap_ite return; } - /* - * Same checksum as an empty page. We attempt to merge it with the - * appropriate zero page if the user enabled this via sysfs. - */ - if (ksm_use_zero_pages && (checksum == zero_checksum)) { - struct vm_area_struct *vma; + if (!try_to_merge_with_zero_page(rmap_item, page)) + return; - mmap_read_lock(mm); - vma = find_mergeable_vma(mm, rmap_item->address); - if (vma) { - err = try_to_merge_one_page(vma, page, - ZERO_PAGE(rmap_item->address)); - trace_ksm_merge_one_page( - page_to_pfn(ZERO_PAGE(rmap_item->address)), - rmap_item, mm, err); - } else { - /* - * If the vma is out of date, we do not need to - * continue. - */ - err = 0; - } - mmap_read_unlock(mm); - /* - * In case of failure, the page was not really empty, so we - * need to continue. Otherwise we're done. - */ - if (!err) - return; - } tree_rmap_item = unstable_tree_search_insert(rmap_item, page, &tree_page); if (tree_rmap_item) { -- 2.43.0
From: Andrew Morton <akpm@linux-foundation.org> mainline inclusion from mainline-v6.11-rc1 commit 998d4e2c33be49297c780e0e59a333918b7c97e4 category: bugfix bugzilla: https://atomgit.com/openeuler/kernel/issues/10169 Reference: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?i... -------------------------------- During conflict resolution a line was unintentionally removed by a ksm.c patch. Link: https://lkml.kernel.org/r/85b0d694-d1ac-8e7a-2e50-1edc03eee21a@google.com Fixes: ac90c56bbd73 ("mm/ksm: refactor out try_to_merge_with_zero_page()") Reported-by: Hugh Dickins <hughd@google.com> Cc: Aristeu Rozanski <aris@redhat.com> Cc: Chengming Zhou <chengming.zhou@linux.dev> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Jinjiang Tu <tujinjiang@huawei.com> --- mm/hugetlb.c | 1 + 1 file changed, 1 insertion(+) diff --git a/mm/hugetlb.c b/mm/hugetlb.c index 88164254cebc..f9ecf50a8f74 100644 --- a/mm/hugetlb.c +++ b/mm/hugetlb.c @@ -2703,6 +2703,7 @@ static int gather_surplus_pages(struct hstate *h, long delta) retry: spin_unlock_irq(&hugetlb_lock); for (i = 0; i < needed; i++) { + folio = NULL; for_each_node_mask(node, cpuset_current_mems_allowed) { if (!mbind_nodemask || node_isset(node, *mbind_nodemask)) { folio = alloc_surplus_hugetlb_folio(h, htlb_alloc_mask(h), -- 2.43.0
From: Chengming Zhou <chengming.zhou@linux.dev> mainline inclusion from mainline-v6.11-rc1 commit d58a361b0350128bf5a5cf47773edaedbb6ea838 category: bugfix bugzilla: https://atomgit.com/openeuler/kernel/issues/10169 Reference: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?i... -------------------------------- The code flow in cmp_and_merge_page() is suboptimal for handling the ksm page and non-ksm page at the same time. For example: - ksm page 1. Mostly just return if this ksm page is not migrated and this rmap_item has been on the rmap hlist. Or we have to fix this rmap_item mapping. 2. But we absolutely don't need to checksum for this ksm page, since it can't change. - non-ksm page 1. First don't need to waste time searching stable tree if fast changing. 2. Should try to merge with zero page before search the stable tree. 3. Then search stable tree to find mergeable ksm page. This patch optimizes the code flow so the handling differences between ksm page and non-ksm page become clearer and more efficient too. Link: https://lkml.kernel.org/r/20240621-b4-ksm-scan-optimize-v2-2-1c328aa9e30b@li... Signed-off-by: Chengming Zhou <chengming.zhou@linux.dev> Cc: Andrea Arcangeli <aarcange@redhat.com> Cc: David Hildenbrand <david@redhat.com> Cc: Hugh Dickins <hughd@google.com> Cc: Stefan Roesch <shr@devkernel.io> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Jinjiang Tu <tujinjiang@huawei.com> --- mm/ksm.c | 32 +++++++++++++++++--------------- 1 file changed, 17 insertions(+), 15 deletions(-) diff --git a/mm/ksm.c b/mm/ksm.c index 7e1738b0fb69..e73cdde7b537 100644 --- a/mm/ksm.c +++ b/mm/ksm.c @@ -2181,6 +2181,23 @@ static void cmp_and_merge_page(struct page *page, struct ksm_rmap_item *rmap_ite */ if (!is_page_sharing_candidate(stable_node)) max_page_sharing_bypass = true; + } else { + remove_rmap_item_from_tree(rmap_item); + + /* + * If the hash value of the page has changed from the last time + * we calculated it, this page is changing frequently: therefore we + * don't want to insert it in the unstable tree, and we don't want + * to waste our time searching for something identical to it there. + */ + checksum = calc_checksum(page); + if (rmap_item->oldchecksum != checksum) { + rmap_item->oldchecksum = checksum; + return; + } + + if (!try_to_merge_with_zero_page(rmap_item, page)) + return; } /* We first start with searching the page inside the stable tree */ @@ -2211,21 +2228,6 @@ static void cmp_and_merge_page(struct page *page, struct ksm_rmap_item *rmap_ite return; } - /* - * If the hash value of the page has changed from the last time - * we calculated it, this page is changing frequently: therefore we - * don't want to insert it in the unstable tree, and we don't want - * to waste our time searching for something identical to it there. - */ - checksum = calc_checksum(page); - if (rmap_item->oldchecksum != checksum) { - rmap_item->oldchecksum = checksum; - return; - } - - if (!try_to_merge_with_zero_page(rmap_item, page)) - return; - tree_rmap_item = unstable_tree_search_insert(rmap_item, page, &tree_page); if (tree_rmap_item) { -- 2.43.0
hulk inclusion category: feature bugzilla: https://atomgit.com/openeuler/kernel/issues/10169 ---------------------------------------- Add KSM state for memcg, the valid values include 0 and 1. When changing auto_ksm_enabled from 0 to 1, enable KSM for tasks in the memcg. When changing auto_ksm_enabled from 1 to 0, disable KSM for tasks in the memcg. If enable/disable fails, return the error code and don't change auto_ksm_enabled. If the auto_ksm_state of the child memcgs differ, also enable/disable KSM for the tasks in the memcgs. If enable/disable for a child memcg fails, stop traversing child memcgs and return the error code. When writing the value same to auto_ksm_enabled of the memcg, i.e. from 0 to 0 and 1 to 1, do nothing. Signed-off-by: Jinjiang Tu <tujinjiang@huawei.com> --- include/linux/memcontrol.h | 4 ++++ mm/memcontrol.c | 30 +++++++++++++++++++++++++++++- 2 files changed, 33 insertions(+), 1 deletion(-) diff --git a/include/linux/memcontrol.h b/include/linux/memcontrol.h index 89ef30f20275..0f16b1702258 100644 --- a/include/linux/memcontrol.h +++ b/include/linux/memcontrol.h @@ -412,7 +412,11 @@ struct mem_cgroup { struct dynamic_pool *dpool; #endif +#ifdef CONFIG_KSM + KABI_USE(1, bool auto_ksm_enabled) +#else KABI_RESERVE(1) +#endif KABI_RESERVE(2) KABI_RESERVE(3) KABI_RESERVE(4) diff --git a/mm/memcontrol.c b/mm/memcontrol.c index dfd82674eaa3..7ca63824ab74 100644 --- a/mm/memcontrol.c +++ b/mm/memcontrol.c @@ -5950,7 +5950,7 @@ static int mem_cgroup_slab_show(struct seq_file *m, void *p) #endif #ifdef CONFIG_KSM -static int memcg_set_ksm_for_tasks(struct mem_cgroup *memcg, bool enable) +static int __memcg_set_ksm_for_tasks(struct mem_cgroup *memcg, bool enable) { struct task_struct *task; struct mm_struct *mm; @@ -5984,6 +5984,27 @@ static int memcg_set_ksm_for_tasks(struct mem_cgroup *memcg, bool enable) return ret; } +static int memcg_set_ksm_for_tasks(struct mem_cgroup *memcg, bool enable) +{ + struct mem_cgroup *iter; + int ret = 0; + + for_each_mem_cgroup_tree(iter, memcg) { + if (READ_ONCE(iter->auto_ksm_enabled) == enable) + continue; + + ret = __memcg_set_ksm_for_tasks(iter, enable); + if (ret) { + mem_cgroup_iter_break(memcg, iter); + break; + } + + WRITE_ONCE(iter->auto_ksm_enabled, enable); + } + + return ret; +} + static int memory_ksm_show(struct seq_file *m, void *v) { unsigned long ksm_merging_pages = 0; @@ -6011,6 +6032,7 @@ static int memory_ksm_show(struct seq_file *m, void *v) } css_task_iter_end(&it); + seq_printf(m, "auto ksm enabled: %d\n", READ_ONCE(memcg->auto_ksm_enabled)); seq_printf(m, "merge any tasks: %u\n", tasks); seq_printf(m, "ksm_rmap_items %lu\n", ksm_rmap_items); seq_printf(m, "ksm_merging_pages %lu\n", ksm_merging_pages); @@ -6033,6 +6055,9 @@ static ssize_t memory_ksm_write(struct kernfs_open_file *of, char *buf, if (err) return err; + if (READ_ONCE(memcg->auto_ksm_enabled) == enable) + return nbytes; + err = memcg_set_ksm_for_tasks(memcg, enable); if (err) return err; @@ -6763,6 +6788,9 @@ mem_cgroup_css_alloc(struct cgroup_subsys_state *parent_css) page_counter_init(&memcg->kmem, &parent->kmem); page_counter_init(&memcg->tcpmem, &parent->tcpmem); memcg_swap_device_init(memcg, parent); +#ifdef CONFIG_KSM + memcg->auto_ksm_enabled = READ_ONCE(parent->auto_ksm_enabled); +#endif } else { init_memcg_events(); page_counter_init(&memcg->memory, NULL); -- 2.43.0
hulk inclusion category: feature bugzilla: https://atomgit.com/openeuler/kernel/issues/10169 ---------------------------------------- When a task moves to a new memcg, enable KSM for the task if the auto_ksm_enabled of the memcg is 1. Signed-off-by: Jinjiang Tu <tujinjiang@huawei.com> --- mm/memcontrol.c | 37 ++++++++++++++++++++++++++++++++++++- 1 file changed, 36 insertions(+), 1 deletion(-) diff --git a/mm/memcontrol.c b/mm/memcontrol.c index 7ca63824ab74..675243166542 100644 --- a/mm/memcontrol.c +++ b/mm/memcontrol.c @@ -6064,6 +6064,39 @@ static ssize_t memory_ksm_write(struct kernfs_open_file *of, char *buf, return nbytes; } + +static void memcg_attach_ksm(struct cgroup_taskset *tset) +{ + struct cgroup_subsys_state *css; + struct mem_cgroup *memcg; + struct task_struct *task; + + cgroup_taskset_first(tset, &css); + memcg = mem_cgroup_from_css(css); + if (!READ_ONCE(memcg->auto_ksm_enabled)) + return; + + cgroup_taskset_for_each(task, css, tset) { + struct mm_struct *mm = get_task_mm(task); + + if (!mm) + continue; + + if (mmap_write_lock_killable(mm)) { + mmput(mm); + continue; + } + + ksm_enable_merge_any(mm); + + mmap_write_unlock(mm); + mmput(mm); + } +} +#else +static inline void memcg_attach_ksm(struct cgroup_taskset *tset) +{ +} #endif /* CONFIG_KSM */ #ifdef CONFIG_DYNAMIC_POOL @@ -7910,6 +7943,8 @@ static void mem_cgroup_attach(struct cgroup_taskset *tset) { mem_cgroup_lru_gen_attach(tset); mem_cgroup_kmem_attach(tset); + if (!cgroup_subsys_on_dfl(memory_cgrp_subsys)) + memcg_attach_ksm(tset); } #endif @@ -8401,7 +8436,7 @@ struct cgroup_subsys memory_cgrp_subsys = { .css_reset = mem_cgroup_css_reset, .css_rstat_flush = mem_cgroup_css_rstat_flush, .can_attach = mem_cgroup_can_attach, -#if defined(CONFIG_LRU_GEN) || defined(CONFIG_MEMCG_KMEM) +#if defined(CONFIG_LRU_GEN) || defined(CONFIG_MEMCG_KMEM) || defined(CONFIG_KSM) .attach = mem_cgroup_attach, #endif .cancel_attach = mem_cgroup_cancel_attach, -- 2.43.0
hulk inclusion category: feature bugzilla: https://atomgit.com/openeuler/kernel/issues/10169 ---------------------------------------- For each KSM candidate page, kmsd needs a struct rmap_item to store the state of the candidate page. struct rmap_item is both used when the page is trying to be merged and when the page has been merged. If the system has too many KSM candidate pages that fail to be merged, the memory consumption of metadata is even larger than the memory saving brought by merging. This patch adds a new mode called ksm_only_merge_zero_pages. When this mode is enabled, ksmd only tries to merge empty pages. This ensures ksmd could reduce memory usage without worrying that the benefits of mering will be offset by metadata overhead. Besides, this mode could increase the scanning speed. When ksm_use_zero_pages mode is enabled, the empty pages are merged with the system zero pages. System zero pages don't need reserve mapping, so the memory consumption of rmap_item metdata are not needed too. If ksm_use_zero_pages mode is enabled too when ksm_only_merge_zero_pages mode is enabled, the profit will be improved more. During ksmd walks the page table to find candidate pages, We add a trivial check to filter out nozero pages. walk_page_range_vma() needs to walk servel levels of page table to find the target pte. Too many calls of walk_page_range_vma() are too slow. So we add a trivial check to filter out nozero pages in one walk_page_range_vma() call, to speed up scanning. Signed-off-by: Jinjiang Tu <tujinjiang@huawei.com> --- mm/ksm.c | 92 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 92 insertions(+) diff --git a/mm/ksm.c b/mm/ksm.c index e73cdde7b537..c7be71802847 100644 --- a/mm/ksm.c +++ b/mm/ksm.c @@ -281,6 +281,8 @@ static unsigned int zero_checksum __read_mostly; /* Whether to merge empty (zeroed) pages with actual zero pages */ static bool ksm_use_zero_pages __read_mostly; +static bool ksm_only_merge_zero_pages __read_mostly; + /* The number of zero pages which is placed by KSM */ atomic_long_t ksm_zero_pages = ATOMIC_LONG_INIT(0); @@ -2324,8 +2326,22 @@ struct ksm_next_page_arg { struct folio *folio; struct page *page; unsigned long addr; + unsigned int nr_skipped; }; +static inline bool nozero_page_inprecise_check(struct page *page) +{ + unsigned long *p; + bool ret = true; + unsigned int last_pos = PAGE_SIZE / sizeof(*p) - 1; + + p = kmap_atomic(page); + if (p[0] || p[last_pos]) + ret = false; + kunmap_atomic(p); + return ret; +} + static int ksm_next_page_pmd_entry(pmd_t *pmdp, unsigned long addr, unsigned long end, struct mm_walk *walk) { @@ -2385,6 +2401,12 @@ static int ksm_next_page_pmd_entry(pmd_t *pmdp, unsigned long addr, unsigned lon if (folio_is_zone_device(folio) || !folio_test_anon(folio)) continue; + + if (ksm_only_merge_zero_pages && private->nr_skipped--) { + if (!nozero_page_inprecise_check(page)) + continue; + } + goto found_unlock; } @@ -2409,6 +2431,40 @@ static struct mm_walk_ops ksm_next_page_ops = { .walk_lock = PGWALK_RDLOCK, }; +static bool should_skip_page(struct ksm_rmap_item **rmap_list, + unsigned long addr, struct page *page) +{ + struct ksm_rmap_item *rmap_item; + unsigned long *p; + int i; + + if (!ksm_only_merge_zero_pages) + return false; + + if (PageKsm(page)) + return false; + + p = kmap_atomic(page); + for (i = 0; i < PAGE_SIZE / sizeof(unsigned long); ++i) { + if (p[i] != 0) { + kunmap_atomic(p); + while (*rmap_list) { + rmap_item = *rmap_list; + if (rmap_item->address > addr) + break; + *rmap_list = rmap_item->rmap_list; + remove_rmap_item_from_tree(rmap_item); + free_rmap_item(rmap_item); + } + return true; + } + } + kunmap_atomic(p); + + return false; +} + + static struct ksm_rmap_item *scan_get_next_rmap_item(struct page **page) { struct mm_struct *mm; @@ -2504,6 +2560,7 @@ static struct ksm_rmap_item *scan_get_next_rmap_item(struct page **page) int found; + ksm_next_page_arg.nr_skipped = 1024; found = walk_page_range_vma(vma, ksm_scan.address, vma->vm_end, &ksm_next_page_ops, @@ -2520,6 +2577,14 @@ static struct ksm_rmap_item *scan_get_next_rmap_item(struct page **page) if (tmp_page) { flush_anon_page(vma, tmp_page, ksm_scan.address); flush_dcache_page(tmp_page); + + if (should_skip_page(ksm_scan.rmap_list, ksm_scan.address, tmp_page)) { + folio_put(folio); + ksm_scan.address += PAGE_SIZE; + mmap_read_unlock(mm); + return ERR_PTR(-EAGAIN); + } + rmap_item = get_next_rmap_item(mm_slot, ksm_scan.rmap_list, ksm_scan.address); if (rmap_item) { @@ -2613,6 +2678,10 @@ static void ksm_do_scan(unsigned int scan_npages) rmap_item = scan_get_next_rmap_item(&page); if (!rmap_item) return; + if (rmap_item == ERR_PTR(-EAGAIN)) { + ksm_pages_scanned++; + continue; + } cmp_and_merge_page(page, rmap_item); put_page(page); ksm_pages_scanned++; @@ -3428,6 +3497,28 @@ static ssize_t use_zero_pages_store(struct kobject *kobj, } KSM_ATTR(use_zero_pages); +static ssize_t only_merge_zero_pages_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + return sprintf(buf, "%u\n", ksm_only_merge_zero_pages); +} +static ssize_t only_merge_zero_pages_store(struct kobject *kobj, + struct kobj_attribute *attr, + const char *buf, size_t count) +{ + int err; + bool value; + + err = kstrtobool(buf, &value); + if (err) + return -EINVAL; + + ksm_only_merge_zero_pages = value; + + return count; +} +KSM_ATTR(only_merge_zero_pages); + static ssize_t max_page_sharing_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf) { @@ -3599,6 +3690,7 @@ static struct attribute *ksm_attrs[] = { &stable_node_dups_attr.attr, &stable_node_chains_prune_millisecs_attr.attr, &use_zero_pages_attr.attr, + &only_merge_zero_pages_attr.attr, &general_profit_attr.attr, NULL, }; -- 2.43.0
From: Johannes Weiner <hannes@cmpxchg.org> mainline inclusion from mainline-v6.18-rc1 commit 19c5fb83f2a4dde7b53b3aeb1fa87bfa3559286b category: bugfix bugzilla: https://atomgit.com/openeuler/kernel/issues/10169 Reference: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?i... -------------------------------- On NUMA systems without bindings, allocations check all nodes for free space, then wake up the kswapds on all nodes and retry. This ensures all available space is evenly used before reclaim begins. However, when one process or certain allocations have node restrictions, they can cause kswapds on only a subset of nodes to be woken up. Since kswapd hysteresis targets watermarks that are *higher* than needed for allocation, even *unrestricted* allocations can now get suckered onto such nodes that are already pressured. This ends up concentrating all allocations on them, even when there are idle nodes available for the unrestricted requests. This was observed with two numa nodes, where node0 is normal and node1 is ZONE_MOVABLE to facilitate hotplugging: a kernel allocation wakes kswapd on node0 only (since node1 is not eligible); once kswapd0 is active, the watermarks hover between low and high, and then even the movable allocations end up on node0, only to be kicked out again; meanwhile node1 is empty and idle. Similar behavior is possible when a process with NUMA bindings is causing selective kswapd wakeups. To fix this, on NUMA systems augment the (misleading) watermark test with a check for whether kswapd is already active during the first iteration through the zonelist. If this fails to place the request, kswapd must be running everywhere already, and the watermark test is good enough to decide placement. With this patch, unrestricted requests successfully make use of node1, even while kswapd is reclaiming node0 for restricted allocations. [gourry@gourry.net: don't retry if no kswapds were active] Link: https://lkml.kernel.org/r/20250919162134.1098208-1-hannes@cmpxchg.org Signed-off-by: Gregory Price <gourry@gourry.net> Tested-by: Joshua Hahn <joshua.hahnjy@gmail.com> Signed-off-by: Johannes Weiner <hannes@cmpxchg.org> Acked-by: Zi Yan <ziy@nvidia.com> Cc: Brendan Jackman <jackmanb@google.com> Cc: Joshua Hahn <joshua.hahnjy@gmail.com> Cc: Michal Hocko <mhocko@suse.com> Cc: Suren Baghdasaryan <surenb@google.com> Cc: Vlastimil Babka <vbabka@suse.cz> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Conflicts: mm/page_alloc.c [Context conflicts.] Signed-off-by: Jinjiang Tu <tujinjiang@huawei.com> --- mm/page_alloc.c | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/mm/page_alloc.c b/mm/page_alloc.c index 596f0f1e4017..b1ffe4ea5e66 100644 --- a/mm/page_alloc.c +++ b/mm/page_alloc.c @@ -3414,6 +3414,8 @@ get_page_from_freelist(gfp_t gfp_mask, unsigned int order, int alloc_flags, struct pglist_data *last_pgdat = NULL; bool last_pgdat_dirty_ok = false; bool no_fallback; + bool skip_kswapd_nodes = nr_online_nodes > 1; + bool skipped_kswapd_nodes = false; retry: /* @@ -3480,6 +3482,19 @@ get_page_from_freelist(gfp_t gfp_mask, unsigned int order, int alloc_flags, } } + /* + * If kswapd is already active on a node, keep looking + * for other nodes that might be idle. This can happen + * if another process has NUMA bindings and is causing + * kswapd wakeups on only some nodes. Avoid accidental + * "node_reclaim_mode"-like behavior in this case. + */ + if (skip_kswapd_nodes && + !waitqueue_active(&zone->zone_pgdat->kswapd_wait)) { + skipped_kswapd_nodes = true; + continue; + } + cond_accept_memory(zone, order); /* @@ -3582,6 +3597,15 @@ get_page_from_freelist(gfp_t gfp_mask, unsigned int order, int alloc_flags, } } + /* + * If we skipped over nodes with active kswapds and found no + * idle nodes, retry and place anywhere the watermarks permit. + */ + if (skip_kswapd_nodes && skipped_kswapd_nodes) { + skip_kswapd_nodes = false; + goto retry; + } + /* * It's possible on a UMA machine to get through all zones that are * fragmented. If avoiding fragmentation, reset and try again. -- 2.43.0
participants (1)
-
Jinjiang Tu