Skip to content

Commit 82e717a

Browse files
zhans00torvalds
authored andcommitted
selftests: vm: add KSM merging across nodes test
Add check_ksm_numa_merge() function to test that pages in different NUMA nodes are being handled properly. First, two duplicate pages are allocated in two separate NUMA nodes using the libnuma library. Since there is one unique page in each node, with merge_across_nodes = 0, there won't be any shared pages. If merge_across_nodes is set to 1, the pages will be treated as usual duplicate pages and will be merged. If NUMA config is not enabled or the number of NUMA nodes is less than two, then the test is skipped. The test is run as follows: ./ksm_tests -N Link: https://lkml.kernel.org/r/071c17b5b04ebb0dfeba137acc495e5dd9d2a719.1626252248.git.zhansayabagdaulet@gmail.com Signed-off-by: Zhansaya Bagdauletkyzy <[email protected]> Reviewed-by: Pavel Tatashin <[email protected]> Reviewed-by: Tyler Hicks <[email protected]> Cc: Hugh Dickins <[email protected]> Cc: Shuah Khan <[email protected]> Signed-off-by: Andrew Morton <[email protected]> Signed-off-by: Linus Torvalds <[email protected]>
1 parent 3961998 commit 82e717a

File tree

3 files changed

+119
-3
lines changed

3 files changed

+119
-3
lines changed

tools/testing/selftests/vm/Makefile

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,8 @@ $(OUTPUT)/hmm-tests: local_config.h
146146
# HMM_EXTRA_LIBS may get set in local_config.mk, or it may be left empty.
147147
$(OUTPUT)/hmm-tests: LDLIBS += $(HMM_EXTRA_LIBS)
148148

149+
$(OUTPUT)/ksm_tests: LDLIBS += -lnuma
150+
149151
local_config.mk local_config.h: check_config.sh
150152
/bin/sh ./check_config.sh $(CC)
151153

tools/testing/selftests/vm/ksm_tests.c

Lines changed: 85 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
#include <stdbool.h>
55
#include <time.h>
66
#include <string.h>
7+
#include <numa.h>
78

89
#include "../kselftest.h"
910

@@ -13,6 +14,7 @@
1314
#define KSM_PAGE_COUNT_DEFAULT 10l
1415
#define KSM_PROT_STR_DEFAULT "rw"
1516
#define KSM_USE_ZERO_PAGES_DEFAULT false
17+
#define KSM_MERGE_ACROSS_NODES_DEFAULT true
1618

1719
struct ksm_sysfs {
1820
unsigned long max_page_sharing;
@@ -27,7 +29,8 @@ struct ksm_sysfs {
2729
enum ksm_test_name {
2830
CHECK_KSM_MERGE,
2931
CHECK_KSM_UNMERGE,
30-
CHECK_KSM_ZERO_PAGE_MERGE
32+
CHECK_KSM_ZERO_PAGE_MERGE,
33+
CHECK_KSM_NUMA_MERGE
3134
};
3235

3336
static int ksm_write_sysfs(const char *file_path, unsigned long val)
@@ -83,11 +86,12 @@ static int str_to_prot(char *prot_str)
8386
static void print_help(void)
8487
{
8588
printf("usage: ksm_tests [-h] <test type> [-a prot] [-p page_count] [-l timeout]\n"
86-
"[-z use_zero_pages]\n");
89+
"[-z use_zero_pages] [-m merge_across_nodes]\n");
8790

8891
printf("Supported <test type>:\n"
8992
" -M (page merging)\n"
9093
" -Z (zero pages merging)\n"
94+
" -N (merging of pages in different NUMA nodes)\n"
9195
" -U (page unmerging)\n\n");
9296

9397
printf(" -a: specify the access protections of pages.\n"
@@ -99,6 +103,8 @@ static void print_help(void)
99103
" Default: %d seconds\n", KSM_SCAN_LIMIT_SEC_DEFAULT);
100104
printf(" -z: change use_zero_pages tunable\n"
101105
" Default: %d\n", KSM_USE_ZERO_PAGES_DEFAULT);
106+
printf(" -m: change merge_across_nodes tunable\n"
107+
" Default: %d\n", KSM_MERGE_ACROSS_NODES_DEFAULT);
102108

103109
exit(0);
104110
}
@@ -339,6 +345,68 @@ static int check_ksm_zero_page_merge(int mapping, int prot, long page_count, int
339345
return KSFT_FAIL;
340346
}
341347

348+
static int check_ksm_numa_merge(int mapping, int prot, int timeout, bool merge_across_nodes,
349+
size_t page_size)
350+
{
351+
void *numa1_map_ptr, *numa2_map_ptr;
352+
struct timespec start_time;
353+
int page_count = 2;
354+
355+
if (clock_gettime(CLOCK_MONOTONIC_RAW, &start_time)) {
356+
perror("clock_gettime");
357+
return KSFT_FAIL;
358+
}
359+
360+
if (numa_available() < 0) {
361+
perror("NUMA support not enabled");
362+
return KSFT_SKIP;
363+
}
364+
if (numa_max_node() < 1) {
365+
printf("At least 2 NUMA nodes must be available\n");
366+
return KSFT_SKIP;
367+
}
368+
if (ksm_write_sysfs(KSM_FP("merge_across_nodes"), merge_across_nodes))
369+
return KSFT_FAIL;
370+
371+
/* allocate 2 pages in 2 different NUMA nodes and fill them with the same data */
372+
numa1_map_ptr = numa_alloc_onnode(page_size, 0);
373+
numa2_map_ptr = numa_alloc_onnode(page_size, 1);
374+
if (!numa1_map_ptr || !numa2_map_ptr) {
375+
perror("numa_alloc_onnode");
376+
return KSFT_FAIL;
377+
}
378+
379+
memset(numa1_map_ptr, '*', page_size);
380+
memset(numa2_map_ptr, '*', page_size);
381+
382+
/* try to merge the pages */
383+
if (ksm_merge_pages(numa1_map_ptr, page_size, start_time, timeout) ||
384+
ksm_merge_pages(numa2_map_ptr, page_size, start_time, timeout))
385+
goto err_out;
386+
387+
/*
388+
* verify that the right number of pages are merged:
389+
* 1) if merge_across_nodes was enabled, 2 duplicate pages will be merged;
390+
* 2) if merge_across_nodes = 0, there must be 0 merged pages, since there is
391+
* only 1 unique page in each node and they can't be shared.
392+
*/
393+
if (merge_across_nodes && !assert_ksm_pages_count(page_count))
394+
goto err_out;
395+
else if (!merge_across_nodes && !assert_ksm_pages_count(0))
396+
goto err_out;
397+
398+
numa_free(numa1_map_ptr, page_size);
399+
numa_free(numa2_map_ptr, page_size);
400+
printf("OK\n");
401+
return KSFT_PASS;
402+
403+
err_out:
404+
numa_free(numa1_map_ptr, page_size);
405+
numa_free(numa2_map_ptr, page_size);
406+
printf("Not OK\n");
407+
return KSFT_FAIL;
408+
}
409+
342410
int main(int argc, char *argv[])
343411
{
344412
int ret, opt;
@@ -349,8 +417,9 @@ int main(int argc, char *argv[])
349417
struct ksm_sysfs ksm_sysfs_old;
350418
int test_name = CHECK_KSM_MERGE;
351419
bool use_zero_pages = KSM_USE_ZERO_PAGES_DEFAULT;
420+
bool merge_across_nodes = KSM_MERGE_ACROSS_NODES_DEFAULT;
352421

353-
while ((opt = getopt(argc, argv, "ha:p:l:z:MUZ")) != -1) {
422+
while ((opt = getopt(argc, argv, "ha:p:l:z:m:MUZN")) != -1) {
354423
switch (opt) {
355424
case 'a':
356425
prot = str_to_prot(optarg);
@@ -378,6 +447,12 @@ int main(int argc, char *argv[])
378447
else
379448
use_zero_pages = 1;
380449
break;
450+
case 'm':
451+
if (strcmp(optarg, "0") == 0)
452+
merge_across_nodes = 0;
453+
else
454+
merge_across_nodes = 1;
455+
break;
381456
case 'M':
382457
break;
383458
case 'U':
@@ -386,6 +461,9 @@ int main(int argc, char *argv[])
386461
case 'Z':
387462
test_name = CHECK_KSM_ZERO_PAGE_MERGE;
388463
break;
464+
case 'N':
465+
test_name = CHECK_KSM_NUMA_MERGE;
466+
break;
389467
default:
390468
return KSFT_FAIL;
391469
}
@@ -423,6 +501,10 @@ int main(int argc, char *argv[])
423501
ret = check_ksm_zero_page_merge(MAP_PRIVATE | MAP_ANONYMOUS, prot, page_count,
424502
ksm_scan_limit_sec, use_zero_pages, page_size);
425503
break;
504+
case CHECK_KSM_NUMA_MERGE:
505+
ret = check_ksm_numa_merge(MAP_PRIVATE | MAP_ANONYMOUS, prot, ksm_scan_limit_sec,
506+
merge_across_nodes, page_size);
507+
break;
426508
}
427509

428510
if (ksm_restore(&ksm_sysfs_old)) {

tools/testing/selftests/vm/run_vmtests.sh

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -441,6 +441,38 @@ else
441441
exitcode=1
442442
fi
443443

444+
echo "-------------------------------------------------------------"
445+
echo "running KSM test with 2 NUMA nodes and merge_across_nodes = 1"
446+
echo "-------------------------------------------------------------"
447+
./ksm_tests -N -m 1
448+
ret_val=$?
449+
450+
if [ $ret_val -eq 0 ]; then
451+
echo "[PASS]"
452+
elif [ $ret_val -eq $ksft_skip ]; then
453+
echo "[SKIP]"
454+
exitcode=$ksft_skip
455+
else
456+
echo "[FAIL]"
457+
exitcode=1
458+
fi
459+
460+
echo "-------------------------------------------------------------"
461+
echo "running KSM test with 2 NUMA nodes and merge_across_nodes = 0"
462+
echo "-------------------------------------------------------------"
463+
./ksm_tests -N -m 0
464+
ret_val=$?
465+
466+
if [ $ret_val -eq 0 ]; then
467+
echo "[PASS]"
468+
elif [ $ret_val -eq $ksft_skip ]; then
469+
echo "[SKIP]"
470+
exitcode=$ksft_skip
471+
else
472+
echo "[FAIL]"
473+
exitcode=1
474+
fi
475+
444476
exit $exitcode
445477

446478
exit $exitcode

0 commit comments

Comments
 (0)