From: Qiliang Yuan <odys.yuan@gmail.com>
To: Andrew Morton <akpm@linux-foundation.org>,
David Hildenbrand <david@kernel.org>, Xu Xin <xu.xin@linux.dev>,
Chengming Zhou <chengming.zhou@linux.dev>,
Stefan Roesch <shr@devkernel.io>,
Lorenzo Stoakes <ljs@kernel.org>,
"Liam R. Howlett" <liam@infradead.org>,
Vlastimil Babka <vbabka@kernel.org>,
Mike Rapoport <rppt@kernel.org>,
Suren Baghdasaryan <surenb@google.com>,
Michal Hocko <mhocko@suse.com>, Shuah Khan <shuah@kernel.org>,
Jonathan Corbet <corbet@lwn.net>,
Shuah Khan <skhan@linuxfoundation.org>,
Randy Dunlap <rdunlap@infradead.org>
Cc: linux-mm@kvack.org, linux-kernel@vger.kernel.org,
linux-kselftest@vger.kernel.org, linux-doc@vger.kernel.org,
Qiliang Yuan <odys.yuan@gmail.com>
Subject: [PATCH v3 4/4] selftests/mm: cover merging KSM pages aged by smart scan
Date: Sat, 07 Nov 2026 11:57:03 -0500 [thread overview]
Message-ID: <20261107-bug-mm-ksm-smart-scan-align-skips-v3-4-16d5ce8b5fd6@gmail.com> (raw)
In-Reply-To: <20261107-bug-mm-ksm-smart-scan-align-skips-v3-0-16d5ce8b5fd6@gmail.com>
ksm_functional_tests checks merging and unmerging with ksmd's default
settings, but nothing there exercises smart scan, which skips pages KSM
has failed to merge for a while.
Smart scan used to time those skips per page, so two identical pages
that became mergeable some scans apart could end up never being looked
at in the same scan, and were never merged. "mm/ksm: align smart scan
skips with the scan number" fixed that.
Add ksm_get_smart_scan() and ksm_set_smart_scan() to vm_util, and a
case that enables smart scan, lets sixteen unique pages age for a dozen
scans, then makes a duplicate of each mergeable one scan apart, and
expects every pair to be merged 32 scans later:
kernel failures, two boots
before the fix 20/20, 20/20
with the fix 0/20, 0/20
Signed-off-by: Qiliang Yuan <odys.yuan@gmail.com>
---
tools/testing/selftests/mm/ksm_functional_tests.c | 89 ++++++++++++++++++++++-
tools/testing/selftests/mm/vm_util.c | 32 ++++++++
tools/testing/selftests/mm/vm_util.h | 2 +
3 files changed, 122 insertions(+), 1 deletion(-)
diff --git a/tools/testing/selftests/mm/ksm_functional_tests.c b/tools/testing/selftests/mm/ksm_functional_tests.c
index 31c06c72203fd..135191f99962d 100644
--- a/tools/testing/selftests/mm/ksm_functional_tests.c
+++ b/tools/testing/selftests/mm/ksm_functional_tests.c
@@ -695,6 +695,92 @@ static void test_fork_ksm_merging_page_count(void)
munmap(map, size);
}
+static void test_merge_aged_pairs(void)
+{
+ const unsigned int pairs = 16;
+ const unsigned long size = 2 * pairs * pagesize;
+ int old_smart_scan;
+ unsigned int i;
+ char *map;
+
+ ksft_print_msg("[RUN] %s\n", __func__);
+
+ old_smart_scan = ksm_get_smart_scan();
+ if (old_smart_scan < 0) {
+ ksft_test_result_skip("smart_scan not supported\n");
+ return;
+ }
+ if (ksm_set_smart_scan(true) < 0) {
+ ksft_test_result_fail("Enabling smart_scan failed\n");
+ return;
+ }
+
+ if (ksm_stop() < 0) {
+ ksft_test_result_fail("Disabling (unmerging) KSM failed\n");
+ goto restore;
+ }
+
+ map = mmap(NULL, size, PROT_READ | PROT_WRITE,
+ MAP_PRIVATE | MAP_ANON, -1, 0);
+ if (map == MAP_FAILED) {
+ ksft_test_result_fail("mmap() failed\n");
+ goto restore;
+ }
+ if (madvise(map, size, MADV_NOHUGEPAGE) && errno != EINVAL) {
+ ksft_test_result_fail("MADV_NOHUGEPAGE failed\n");
+ goto unmap;
+ }
+
+ /*
+ * Page i of the first half and page i of the second half form a
+ * pair with content no other page shares.
+ */
+ for (i = 0; i < pairs; i++) {
+ memset(map + i * pagesize, i + 1, pagesize);
+ memset(map + (pairs + i) * pagesize, i + 1, pagesize);
+ }
+
+ /* Let the first page of each pair age without a duplicate. */
+ if (madvise(map, size / 2, MADV_MERGEABLE)) {
+ ksft_test_result_fail("MADV_MERGEABLE failed\n");
+ goto unmap;
+ }
+ if (ksm_start() < 0 || ksm_wait_full_scans(12) < 0) {
+ ksft_test_result_fail("Running KSM failed\n");
+ goto unmap;
+ }
+
+ /*
+ * Make the duplicates mergeable one scan apart so that each pair
+ * starts out with a different distance in scans.
+ */
+ for (i = 0; i < pairs; i++) {
+ if (madvise(map + (pairs + i) * pagesize, pagesize,
+ MADV_MERGEABLE)) {
+ ksft_test_result_fail("MADV_MERGEABLE failed\n");
+ goto unmap;
+ }
+ if (ksm_wait_full_scans(1) < 0) {
+ ksft_test_result_fail("Running KSM failed\n");
+ goto unmap;
+ }
+ }
+
+ if (ksm_wait_full_scans(32) < 0) {
+ ksft_test_result_fail("Running KSM failed\n");
+ goto unmap;
+ }
+
+ ksft_test_result(ksm_get_self_merging_pages() == 2 * pairs,
+ "Pages aged by smart scan got merged\n");
+unmap:
+ ksm_stop();
+ munmap(map, size);
+restore:
+ if (ksm_set_smart_scan(old_smart_scan) < 0)
+ ksft_print_msg("Restoring smart_scan failed\n");
+}
+
static void init_global_file_handles(void)
{
mem_fd = open("/proc/self/mem", O_RDWR);
@@ -720,7 +806,7 @@ static void init_global_file_handles(void)
int main(int argc, char **argv)
{
- unsigned int tests = 9;
+ unsigned int tests = 10;
int err;
if (argc > 1 && !strcmp(argv[1], FORK_EXEC_CHILD_PRG_NAME)) {
@@ -753,6 +839,7 @@ int main(int argc, char **argv)
test_prctl_fork_exec();
test_prctl_unmerge();
test_fork_ksm_merging_page_count();
+ test_merge_aged_pairs();
err = ksft_get_fail_cnt();
if (err)
diff --git a/tools/testing/selftests/mm/vm_util.c b/tools/testing/selftests/mm/vm_util.c
index 1c2bac54696dd..490e3c7161e8d 100644
--- a/tools/testing/selftests/mm/vm_util.c
+++ b/tools/testing/selftests/mm/vm_util.c
@@ -984,6 +984,38 @@ int ksm_use_zero_pages(void)
return ret == 1 ? 0 : -errno;
}
+int ksm_get_smart_scan(void)
+{
+ int smart_scan_fd;
+ char buf;
+ ssize_t ret;
+
+ smart_scan_fd = open("/sys/kernel/mm/ksm/smart_scan", O_RDONLY);
+ if (smart_scan_fd < 0)
+ return -errno;
+
+ ret = pread(smart_scan_fd, &buf, 1, 0);
+ close(smart_scan_fd);
+ if (ret != 1)
+ return -errno;
+
+ return buf == '1';
+}
+
+int ksm_set_smart_scan(bool enable)
+{
+ int smart_scan_fd;
+ ssize_t ret;
+
+ smart_scan_fd = open("/sys/kernel/mm/ksm/smart_scan", O_WRONLY);
+ if (smart_scan_fd < 0)
+ return -errno;
+
+ ret = write(smart_scan_fd, enable ? "1" : "0", 1);
+ close(smart_scan_fd);
+ return ret == 1 ? 0 : -errno;
+}
+
int ksm_wait_full_scans(long nr)
{
long start_scans, end_scans;
diff --git a/tools/testing/selftests/mm/vm_util.h b/tools/testing/selftests/mm/vm_util.h
index 7e00030077d2f..ab1325dc65800 100644
--- a/tools/testing/selftests/mm/vm_util.h
+++ b/tools/testing/selftests/mm/vm_util.h
@@ -163,6 +163,8 @@ long ksm_get_self_zero_pages(void);
long ksm_get_self_merging_pages(void);
long ksm_get_full_scans(void);
int ksm_wait_full_scans(long nr);
+int ksm_get_smart_scan(void);
+int ksm_set_smart_scan(bool enable);
int ksm_use_zero_pages(void);
int ksm_start(void);
int ksm_stop(void);
--
2.43.0
next prev parent reply other threads:[~2026-11-07 16:57 UTC|newest]
Thread overview: 11+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-11-07 16:56 [PATCH v3 0/4] mm/ksm: keep smart scan from losing duplicate pages Qiliang Yuan
2026-11-07 16:57 ` [PATCH v3 1/4] mm/ksm: align smart scan skips with the scan number Qiliang Yuan
2026-11-07 19:49 ` David Hildenbrand (Arm)
2026-11-07 19:51 ` David Hildenbrand (Arm)
2026-11-07 16:57 ` [PATCH v3 2/4] docs/mm: ksm: describe how smart scan picks the scans to skip Qiliang Yuan
2026-11-07 19:48 ` David Hildenbrand (Arm)
2026-11-07 16:57 ` [PATCH v3 3/4] selftests/mm: move waiting for KSM full scans into a helper Qiliang Yuan
2026-11-07 19:48 ` David Hildenbrand (Arm)
2026-11-07 16:57 ` Qiliang Yuan [this message]
2026-11-07 19:53 ` [PATCH v3 4/4] selftests/mm: cover merging KSM pages aged by smart scan David Hildenbrand (Arm)
2026-11-07 19:55 ` [PATCH v3 0/4] mm/ksm: keep smart scan from losing duplicate pages David Hildenbrand (Arm)
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20261107-bug-mm-ksm-smart-scan-align-skips-v3-4-16d5ce8b5fd6@gmail.com \
--to=odys.yuan@gmail.com \
--cc=akpm@linux-foundation.org \
--cc=chengming.zhou@linux.dev \
--cc=corbet@lwn.net \
--cc=david@kernel.org \
--cc=liam@infradead.org \
--cc=linux-doc@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-kselftest@vger.kernel.org \
--cc=linux-mm@kvack.org \
--cc=ljs@kernel.org \
--cc=mhocko@suse.com \
--cc=rdunlap@infradead.org \
--cc=rppt@kernel.org \
--cc=shr@devkernel.io \
--cc=shuah@kernel.org \
--cc=skhan@linuxfoundation.org \
--cc=surenb@google.com \
--cc=vbabka@kernel.org \
--cc=xu.xin@linux.dev \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
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®