Skip to content

Commit a40c80e

Browse files
zhans00torvalds
authored andcommitted
selftests: vm: add KSM unmerge test
Add check_ksm_unmerge() function to verify that KSM is properly unmerging shared pages. For this, two duplicate pages are merged first and then their contents are modified. Since they are not identical anymore, the pages must be unmerged and the number of merged pages has to be 0. The test is run as follows: ./ksm_tests -U Link: https://lkml.kernel.org/r/c0f55420440d704d5b094275b4365aa1b2ad46b5.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 68d6289 commit a40c80e

File tree

2 files changed

+85
-5
lines changed

2 files changed

+85
-5
lines changed

tools/testing/selftests/vm/ksm_tests.c

Lines changed: 68 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,11 @@ struct ksm_sysfs {
2323
unsigned long use_zero_pages;
2424
};
2525

26+
enum ksm_test_name {
27+
CHECK_KSM_MERGE,
28+
CHECK_KSM_UNMERGE
29+
};
30+
2631
static int ksm_write_sysfs(const char *file_path, unsigned long val)
2732
{
2833
FILE *f = fopen(file_path, "w");
@@ -75,7 +80,12 @@ static int str_to_prot(char *prot_str)
7580

7681
static void print_help(void)
7782
{
78-
printf("usage: ksm_tests [-h] [-a prot] [-p page_count] [-l timeout]\n");
83+
printf("usage: ksm_tests [-h] <test type> [-a prot] [-p page_count] [-l timeout]\n");
84+
85+
printf("Supported <test type>:\n"
86+
" -M (page merging)\n"
87+
" -U (page unmerging)\n\n");
88+
7989
printf(" -a: specify the access protections of pages.\n"
8090
" <prot> must be of the form [rwx].\n"
8191
" Default: %s\n", KSM_PROT_STR_DEFAULT);
@@ -239,6 +249,46 @@ static int check_ksm_merge(int mapping, int prot, long page_count, int timeout,
239249
return KSFT_FAIL;
240250
}
241251

252+
static int check_ksm_unmerge(int mapping, int prot, int timeout, size_t page_size)
253+
{
254+
void *map_ptr;
255+
struct timespec start_time;
256+
int page_count = 2;
257+
258+
if (clock_gettime(CLOCK_MONOTONIC_RAW, &start_time)) {
259+
perror("clock_gettime");
260+
return KSFT_FAIL;
261+
}
262+
263+
/* fill pages with the same data and merge them */
264+
map_ptr = allocate_memory(NULL, prot, mapping, '*', page_size * page_count);
265+
if (!map_ptr)
266+
return KSFT_FAIL;
267+
268+
if (ksm_merge_pages(map_ptr, page_size * page_count, start_time, timeout))
269+
goto err_out;
270+
271+
/* change 1 byte in each of the 2 pages -- KSM must automatically unmerge them */
272+
memset(map_ptr, '-', 1);
273+
memset(map_ptr + page_size, '+', 1);
274+
275+
/* get at least 1 scan, so KSM can detect that the pages were modified */
276+
if (ksm_do_scan(1, start_time, timeout))
277+
goto err_out;
278+
279+
/* check that unmerging was successful and 0 pages are currently merged */
280+
if (assert_ksm_pages_count(0)) {
281+
printf("OK\n");
282+
munmap(map_ptr, page_size * page_count);
283+
return KSFT_PASS;
284+
}
285+
286+
err_out:
287+
printf("Not OK\n");
288+
munmap(map_ptr, page_size * page_count);
289+
return KSFT_FAIL;
290+
}
291+
242292
int main(int argc, char *argv[])
243293
{
244294
int ret, opt;
@@ -247,8 +297,9 @@ int main(int argc, char *argv[])
247297
long page_count = KSM_PAGE_COUNT_DEFAULT;
248298
size_t page_size = sysconf(_SC_PAGESIZE);
249299
struct ksm_sysfs ksm_sysfs_old;
300+
int test_name = CHECK_KSM_MERGE;
250301

251-
while ((opt = getopt(argc, argv, "ha:p:l:")) != -1) {
302+
while ((opt = getopt(argc, argv, "ha:p:l:MU")) != -1) {
252303
switch (opt) {
253304
case 'a':
254305
prot = str_to_prot(optarg);
@@ -270,6 +321,11 @@ int main(int argc, char *argv[])
270321
case 'h':
271322
print_help();
272323
break;
324+
case 'M':
325+
break;
326+
case 'U':
327+
test_name = CHECK_KSM_UNMERGE;
328+
break;
273329
default:
274330
return KSFT_FAIL;
275331
}
@@ -294,8 +350,16 @@ int main(int argc, char *argv[])
294350
ksm_write_sysfs(KSM_FP("pages_to_scan"), page_count))
295351
return KSFT_FAIL;
296352

297-
ret = check_ksm_merge(MAP_PRIVATE | MAP_ANONYMOUS, prot, page_count, ksm_scan_limit_sec,
298-
page_size);
353+
switch (test_name) {
354+
case CHECK_KSM_MERGE:
355+
ret = check_ksm_merge(MAP_PRIVATE | MAP_ANONYMOUS, prot, page_count,
356+
ksm_scan_limit_sec, page_size);
357+
break;
358+
case CHECK_KSM_UNMERGE:
359+
ret = check_ksm_unmerge(MAP_PRIVATE | MAP_ANONYMOUS, prot, ksm_scan_limit_sec,
360+
page_size);
361+
break;
362+
}
299363

300364
if (ksm_restore(&ksm_sysfs_old)) {
301365
printf("Cannot restore default tunables\n");

tools/testing/selftests/vm/run_vmtests.sh

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -380,7 +380,23 @@ fi
380380
echo "-------------------------------------------------------"
381381
echo "running KSM MADV_MERGEABLE test with 10 identical pages"
382382
echo "-------------------------------------------------------"
383-
./ksm_tests -p 10
383+
./ksm_tests -M -p 10
384+
ret_val=$?
385+
386+
if [ $ret_val -eq 0 ]; then
387+
echo "[PASS]"
388+
elif [ $ret_val -eq $ksft_skip ]; then
389+
echo "[SKIP]"
390+
exitcode=$ksft_skip
391+
else
392+
echo "[FAIL]"
393+
exitcode=1
394+
fi
395+
396+
echo "------------------------"
397+
echo "running KSM unmerge test"
398+
echo "------------------------"
399+
./ksm_tests -U
384400
ret_val=$?
385401

386402
if [ $ret_val -eq 0 ]; then

0 commit comments

Comments
 (0)