Currently, the KSM-related counters in `mm_struct` such as `ksm_merging_pages`, `ksm_rmap_items`, and `ksm_zero_pages` are inherited by the child process during fork. This results in incorrect accounting, since the child has not performed any KSM page merging. To fix this, reset these counters to 0 in the newly created `mm_struct` during fork. This ensures that KSM statistics remain accurate and only reflect the activity of each process. Signed-off-by: Donet Tom --- include/linux/ksm.h | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/include/linux/ksm.h b/include/linux/ksm.h index 22e67ca7cba3..61b8892c632b 100644 --- a/include/linux/ksm.h +++ b/include/linux/ksm.h @@ -56,8 +56,12 @@ static inline long mm_ksm_zero_pages(struct mm_struct *mm) static inline void ksm_fork(struct mm_struct *mm, struct mm_struct *oldmm) { /* Adding mm to ksm is best effort on fork. */ - if (mm_flags_test(MMF_VM_MERGEABLE, oldmm)) + if (mm_flags_test(MMF_VM_MERGEABLE, oldmm)) { + mm->ksm_merging_pages = 0; + mm->ksm_rmap_items = 0; + atomic_long_set(&mm->ksm_zero_pages, 0); __ksm_enter(mm); + } } static inline int ksm_execve(struct mm_struct *mm) -- 2.51.0 Add a new selftest to verify whether the `ksm_merging_pages` counter in `mm_struct` is inherited by a child process after fork. This helps ensure correctness of KSM accounting across process creation. Signed-off-by: Donet Tom --- .../selftests/mm/ksm_functional_tests.c | 42 ++++++++++++++++++- 1 file changed, 41 insertions(+), 1 deletion(-) diff --git a/tools/testing/selftests/mm/ksm_functional_tests.c b/tools/testing/selftests/mm/ksm_functional_tests.c index 712f43c87736..d971394c9567 100644 --- a/tools/testing/selftests/mm/ksm_functional_tests.c +++ b/tools/testing/selftests/mm/ksm_functional_tests.c @@ -602,6 +602,45 @@ static void test_prot_none(void) munmap(map, size); } +static void test_fork_ksm_merging_page(void) +{ + const unsigned int size = 2 * MiB; + char *map; + pid_t child_pid; + int status; + + ksft_print_msg("[RUN] %s\n", __func__); + + map = mmap_and_merge_range(0xcf, size, PROT_READ | PROT_WRITE, KSM_MERGE_MADVISE); + if (map == MAP_FAILED) + return; + + child_pid = fork(); + if (!child_pid) { + int mpages; + + init_global_file_handles(); + mpages = ksm_get_self_merging_pages(); + if (mpages > 0) + ksft_test_result_fail("ksm_merging_page in child: %d\n", mpages); + + exit(0); + } else if (child_pid < 0) { + ksft_test_result_fail("fork() failed\n"); + return; + } + + if (waitpid(child_pid, &status, 0) < 0) { + ksft_test_result_fail("waitpid() failed\n"); + return; + } + + ksft_test_result_pass("ksm_merging_pages is not inherited after fork\n"); + + ksm_stop(); + munmap(map, size); +} + static void init_global_file_handles(void) { mem_fd = open("/proc/self/mem", O_RDWR); @@ -620,7 +659,7 @@ static void init_global_file_handles(void) int main(int argc, char **argv) { - unsigned int tests = 8; + unsigned int tests = 9; int err; if (argc > 1 && !strcmp(argv[1], FORK_EXEC_CHILD_PRG_NAME)) { @@ -652,6 +691,7 @@ int main(int argc, char **argv) test_prctl_fork(); test_prctl_fork_exec(); test_prctl_unmerge(); + test_fork_ksm_merging_page(); err = ksft_get_fail_cnt(); if (err) -- 2.51.0