Skip to content

Commit

Permalink
selftests: vm: add KSM merging across nodes test
Browse files Browse the repository at this point in the history
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 <zhansayabagdaulet@gmail.com>
Reviewed-by: Pavel Tatashin <pasha.tatashin@soleen.com>
Reviewed-by: Tyler Hicks <tyhicks@linux.microsoft.com>
Cc: Hugh Dickins <hughd@google.com>
Cc: Shuah Khan <shuah@kernel.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
  • Loading branch information
Zhansaya Bagdauletkyzy authored and Linus Torvalds committed Sep 3, 2021
1 parent 3961998 commit 82e717a
Show file tree
Hide file tree
Showing 3 changed files with 119 additions and 3 deletions.
2 changes: 2 additions & 0 deletions tools/testing/selftests/vm/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,8 @@ $(OUTPUT)/hmm-tests: local_config.h
# HMM_EXTRA_LIBS may get set in local_config.mk, or it may be left empty.
$(OUTPUT)/hmm-tests: LDLIBS += $(HMM_EXTRA_LIBS)

$(OUTPUT)/ksm_tests: LDLIBS += -lnuma

local_config.mk local_config.h: check_config.sh
/bin/sh ./check_config.sh $(CC)

Expand Down
88 changes: 85 additions & 3 deletions tools/testing/selftests/vm/ksm_tests.c
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
#include <stdbool.h>
#include <time.h>
#include <string.h>
#include <numa.h>

#include "../kselftest.h"

Expand All @@ -13,6 +14,7 @@
#define KSM_PAGE_COUNT_DEFAULT 10l
#define KSM_PROT_STR_DEFAULT "rw"
#define KSM_USE_ZERO_PAGES_DEFAULT false
#define KSM_MERGE_ACROSS_NODES_DEFAULT true

struct ksm_sysfs {
unsigned long max_page_sharing;
Expand All @@ -27,7 +29,8 @@ struct ksm_sysfs {
enum ksm_test_name {
CHECK_KSM_MERGE,
CHECK_KSM_UNMERGE,
CHECK_KSM_ZERO_PAGE_MERGE
CHECK_KSM_ZERO_PAGE_MERGE,
CHECK_KSM_NUMA_MERGE
};

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

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

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

exit(0);
}
Expand Down Expand Up @@ -339,6 +345,68 @@ static int check_ksm_zero_page_merge(int mapping, int prot, long page_count, int
return KSFT_FAIL;
}

static int check_ksm_numa_merge(int mapping, int prot, int timeout, bool merge_across_nodes,
size_t page_size)
{
void *numa1_map_ptr, *numa2_map_ptr;
struct timespec start_time;
int page_count = 2;

if (clock_gettime(CLOCK_MONOTONIC_RAW, &start_time)) {
perror("clock_gettime");
return KSFT_FAIL;
}

if (numa_available() < 0) {
perror("NUMA support not enabled");
return KSFT_SKIP;
}
if (numa_max_node() < 1) {
printf("At least 2 NUMA nodes must be available\n");
return KSFT_SKIP;
}
if (ksm_write_sysfs(KSM_FP("merge_across_nodes"), merge_across_nodes))
return KSFT_FAIL;

/* allocate 2 pages in 2 different NUMA nodes and fill them with the same data */
numa1_map_ptr = numa_alloc_onnode(page_size, 0);
numa2_map_ptr = numa_alloc_onnode(page_size, 1);
if (!numa1_map_ptr || !numa2_map_ptr) {
perror("numa_alloc_onnode");
return KSFT_FAIL;
}

memset(numa1_map_ptr, '*', page_size);
memset(numa2_map_ptr, '*', page_size);

/* try to merge the pages */
if (ksm_merge_pages(numa1_map_ptr, page_size, start_time, timeout) ||
ksm_merge_pages(numa2_map_ptr, page_size, start_time, timeout))
goto err_out;

/*
* verify that the right number of pages are merged:
* 1) if merge_across_nodes was enabled, 2 duplicate pages will be merged;
* 2) if merge_across_nodes = 0, there must be 0 merged pages, since there is
* only 1 unique page in each node and they can't be shared.
*/
if (merge_across_nodes && !assert_ksm_pages_count(page_count))
goto err_out;
else if (!merge_across_nodes && !assert_ksm_pages_count(0))
goto err_out;

numa_free(numa1_map_ptr, page_size);
numa_free(numa2_map_ptr, page_size);
printf("OK\n");
return KSFT_PASS;

err_out:
numa_free(numa1_map_ptr, page_size);
numa_free(numa2_map_ptr, page_size);
printf("Not OK\n");
return KSFT_FAIL;
}

int main(int argc, char *argv[])
{
int ret, opt;
Expand All @@ -349,8 +417,9 @@ int main(int argc, char *argv[])
struct ksm_sysfs ksm_sysfs_old;
int test_name = CHECK_KSM_MERGE;
bool use_zero_pages = KSM_USE_ZERO_PAGES_DEFAULT;
bool merge_across_nodes = KSM_MERGE_ACROSS_NODES_DEFAULT;

while ((opt = getopt(argc, argv, "ha:p:l:z:MUZ")) != -1) {
while ((opt = getopt(argc, argv, "ha:p:l:z:m:MUZN")) != -1) {
switch (opt) {
case 'a':
prot = str_to_prot(optarg);
Expand Down Expand Up @@ -378,6 +447,12 @@ int main(int argc, char *argv[])
else
use_zero_pages = 1;
break;
case 'm':
if (strcmp(optarg, "0") == 0)
merge_across_nodes = 0;
else
merge_across_nodes = 1;
break;
case 'M':
break;
case 'U':
Expand All @@ -386,6 +461,9 @@ int main(int argc, char *argv[])
case 'Z':
test_name = CHECK_KSM_ZERO_PAGE_MERGE;
break;
case 'N':
test_name = CHECK_KSM_NUMA_MERGE;
break;
default:
return KSFT_FAIL;
}
Expand Down Expand Up @@ -423,6 +501,10 @@ int main(int argc, char *argv[])
ret = check_ksm_zero_page_merge(MAP_PRIVATE | MAP_ANONYMOUS, prot, page_count,
ksm_scan_limit_sec, use_zero_pages, page_size);
break;
case CHECK_KSM_NUMA_MERGE:
ret = check_ksm_numa_merge(MAP_PRIVATE | MAP_ANONYMOUS, prot, ksm_scan_limit_sec,
merge_across_nodes, page_size);
break;
}

if (ksm_restore(&ksm_sysfs_old)) {
Expand Down
32 changes: 32 additions & 0 deletions tools/testing/selftests/vm/run_vmtests.sh
Original file line number Diff line number Diff line change
Expand Up @@ -441,6 +441,38 @@ else
exitcode=1
fi

echo "-------------------------------------------------------------"
echo "running KSM test with 2 NUMA nodes and merge_across_nodes = 1"
echo "-------------------------------------------------------------"
./ksm_tests -N -m 1
ret_val=$?

if [ $ret_val -eq 0 ]; then
echo "[PASS]"
elif [ $ret_val -eq $ksft_skip ]; then
echo "[SKIP]"
exitcode=$ksft_skip
else
echo "[FAIL]"
exitcode=1
fi

echo "-------------------------------------------------------------"
echo "running KSM test with 2 NUMA nodes and merge_across_nodes = 0"
echo "-------------------------------------------------------------"
./ksm_tests -N -m 0
ret_val=$?

if [ $ret_val -eq 0 ]; then
echo "[PASS]"
elif [ $ret_val -eq $ksft_skip ]; then
echo "[SKIP]"
exitcode=$ksft_skip
else
echo "[FAIL]"
exitcode=1
fi

exit $exitcode

exit $exitcode

0 comments on commit 82e717a

Please sign in to comment.