Linux kernel mirror (for testing)
git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel
os
linux
fork
Configure Feed
Select the types of activity you want to include in your feed.
1// SPDX-License-Identifier: GPL-2.0
2
3#include <sys/mman.h>
4#include <stdbool.h>
5#include <time.h>
6#include <string.h>
7#include <numa.h>
8#include <unistd.h>
9#include <fcntl.h>
10#include <stdint.h>
11#include <err.h>
12
13#include "../kselftest.h"
14#include "../../../../include/vdso/time64.h"
15#include "util.h"
16
17#define KSM_SYSFS_PATH "/sys/kernel/mm/ksm/"
18#define KSM_FP(s) (KSM_SYSFS_PATH s)
19#define KSM_SCAN_LIMIT_SEC_DEFAULT 120
20#define KSM_PAGE_COUNT_DEFAULT 10l
21#define KSM_PROT_STR_DEFAULT "rw"
22#define KSM_USE_ZERO_PAGES_DEFAULT false
23#define KSM_MERGE_ACROSS_NODES_DEFAULT true
24#define MB (1ul << 20)
25
26struct ksm_sysfs {
27 unsigned long max_page_sharing;
28 unsigned long merge_across_nodes;
29 unsigned long pages_to_scan;
30 unsigned long run;
31 unsigned long sleep_millisecs;
32 unsigned long stable_node_chains_prune_millisecs;
33 unsigned long use_zero_pages;
34};
35
36enum ksm_test_name {
37 CHECK_KSM_MERGE,
38 CHECK_KSM_UNMERGE,
39 CHECK_KSM_ZERO_PAGE_MERGE,
40 CHECK_KSM_NUMA_MERGE,
41 KSM_MERGE_TIME,
42 KSM_MERGE_TIME_HUGE_PAGES,
43 KSM_COW_TIME
44};
45
46static int ksm_write_sysfs(const char *file_path, unsigned long val)
47{
48 FILE *f = fopen(file_path, "w");
49
50 if (!f) {
51 fprintf(stderr, "f %s\n", file_path);
52 perror("fopen");
53 return 1;
54 }
55 if (fprintf(f, "%lu", val) < 0) {
56 perror("fprintf");
57 return 1;
58 }
59 fclose(f);
60
61 return 0;
62}
63
64static int ksm_read_sysfs(const char *file_path, unsigned long *val)
65{
66 FILE *f = fopen(file_path, "r");
67
68 if (!f) {
69 fprintf(stderr, "f %s\n", file_path);
70 perror("fopen");
71 return 1;
72 }
73 if (fscanf(f, "%lu", val) != 1) {
74 perror("fscanf");
75 return 1;
76 }
77 fclose(f);
78
79 return 0;
80}
81
82static int str_to_prot(char *prot_str)
83{
84 int prot = 0;
85
86 if ((strchr(prot_str, 'r')) != NULL)
87 prot |= PROT_READ;
88 if ((strchr(prot_str, 'w')) != NULL)
89 prot |= PROT_WRITE;
90 if ((strchr(prot_str, 'x')) != NULL)
91 prot |= PROT_EXEC;
92
93 return prot;
94}
95
96static void print_help(void)
97{
98 printf("usage: ksm_tests [-h] <test type> [-a prot] [-p page_count] [-l timeout]\n"
99 "[-z use_zero_pages] [-m merge_across_nodes] [-s size]\n");
100
101 printf("Supported <test type>:\n"
102 " -M (page merging)\n"
103 " -Z (zero pages merging)\n"
104 " -N (merging of pages in different NUMA nodes)\n"
105 " -U (page unmerging)\n"
106 " -P evaluate merging time and speed.\n"
107 " For this test, the size of duplicated memory area (in MiB)\n"
108 " must be provided using -s option\n"
109 " -H evaluate merging time and speed of area allocated mostly with huge pages\n"
110 " For this test, the size of duplicated memory area (in MiB)\n"
111 " must be provided using -s option\n"
112 " -C evaluate the time required to break COW of merged pages.\n\n");
113
114 printf(" -a: specify the access protections of pages.\n"
115 " <prot> must be of the form [rwx].\n"
116 " Default: %s\n", KSM_PROT_STR_DEFAULT);
117 printf(" -p: specify the number of pages to test.\n"
118 " Default: %ld\n", KSM_PAGE_COUNT_DEFAULT);
119 printf(" -l: limit the maximum running time (in seconds) for a test.\n"
120 " Default: %d seconds\n", KSM_SCAN_LIMIT_SEC_DEFAULT);
121 printf(" -z: change use_zero_pages tunable\n"
122 " Default: %d\n", KSM_USE_ZERO_PAGES_DEFAULT);
123 printf(" -m: change merge_across_nodes tunable\n"
124 " Default: %d\n", KSM_MERGE_ACROSS_NODES_DEFAULT);
125 printf(" -s: the size of duplicated memory area (in MiB)\n");
126
127 exit(0);
128}
129
130static void *allocate_memory(void *ptr, int prot, int mapping, char data, size_t map_size)
131{
132 void *map_ptr = mmap(ptr, map_size, PROT_WRITE, mapping, -1, 0);
133
134 if (!map_ptr) {
135 perror("mmap");
136 return NULL;
137 }
138 memset(map_ptr, data, map_size);
139 if (mprotect(map_ptr, map_size, prot)) {
140 perror("mprotect");
141 munmap(map_ptr, map_size);
142 return NULL;
143 }
144
145 return map_ptr;
146}
147
148static int ksm_do_scan(int scan_count, struct timespec start_time, int timeout)
149{
150 struct timespec cur_time;
151 unsigned long cur_scan, init_scan;
152
153 if (ksm_read_sysfs(KSM_FP("full_scans"), &init_scan))
154 return 1;
155 cur_scan = init_scan;
156
157 while (cur_scan < init_scan + scan_count) {
158 if (ksm_read_sysfs(KSM_FP("full_scans"), &cur_scan))
159 return 1;
160 if (clock_gettime(CLOCK_MONOTONIC_RAW, &cur_time)) {
161 perror("clock_gettime");
162 return 1;
163 }
164 if ((cur_time.tv_sec - start_time.tv_sec) > timeout) {
165 printf("Scan time limit exceeded\n");
166 return 1;
167 }
168 }
169
170 return 0;
171}
172
173static int ksm_merge_pages(void *addr, size_t size, struct timespec start_time, int timeout)
174{
175 if (madvise(addr, size, MADV_MERGEABLE)) {
176 perror("madvise");
177 return 1;
178 }
179 if (ksm_write_sysfs(KSM_FP("run"), 1))
180 return 1;
181
182 /* Since merging occurs only after 2 scans, make sure to get at least 2 full scans */
183 if (ksm_do_scan(2, start_time, timeout))
184 return 1;
185
186 return 0;
187}
188
189static bool assert_ksm_pages_count(long dupl_page_count)
190{
191 unsigned long max_page_sharing, pages_sharing, pages_shared;
192
193 if (ksm_read_sysfs(KSM_FP("pages_shared"), &pages_shared) ||
194 ksm_read_sysfs(KSM_FP("pages_sharing"), &pages_sharing) ||
195 ksm_read_sysfs(KSM_FP("max_page_sharing"), &max_page_sharing))
196 return false;
197
198 /*
199 * Since there must be at least 2 pages for merging and 1 page can be
200 * shared with the limited number of pages (max_page_sharing), sometimes
201 * there are 'leftover' pages that cannot be merged. For example, if there
202 * are 11 pages and max_page_sharing = 10, then only 10 pages will be
203 * merged and the 11th page won't be affected. As a result, when the number
204 * of duplicate pages is divided by max_page_sharing and the remainder is 1,
205 * pages_shared and pages_sharing values will be equal between dupl_page_count
206 * and dupl_page_count - 1.
207 */
208 if (dupl_page_count % max_page_sharing == 1 || dupl_page_count % max_page_sharing == 0) {
209 if (pages_shared == dupl_page_count / max_page_sharing &&
210 pages_sharing == pages_shared * (max_page_sharing - 1))
211 return true;
212 } else {
213 if (pages_shared == (dupl_page_count / max_page_sharing + 1) &&
214 pages_sharing == dupl_page_count - pages_shared)
215 return true;
216 }
217
218 return false;
219}
220
221static int ksm_save_def(struct ksm_sysfs *ksm_sysfs)
222{
223 if (ksm_read_sysfs(KSM_FP("max_page_sharing"), &ksm_sysfs->max_page_sharing) ||
224 ksm_read_sysfs(KSM_FP("merge_across_nodes"), &ksm_sysfs->merge_across_nodes) ||
225 ksm_read_sysfs(KSM_FP("sleep_millisecs"), &ksm_sysfs->sleep_millisecs) ||
226 ksm_read_sysfs(KSM_FP("pages_to_scan"), &ksm_sysfs->pages_to_scan) ||
227 ksm_read_sysfs(KSM_FP("run"), &ksm_sysfs->run) ||
228 ksm_read_sysfs(KSM_FP("stable_node_chains_prune_millisecs"),
229 &ksm_sysfs->stable_node_chains_prune_millisecs) ||
230 ksm_read_sysfs(KSM_FP("use_zero_pages"), &ksm_sysfs->use_zero_pages))
231 return 1;
232
233 return 0;
234}
235
236static int ksm_restore(struct ksm_sysfs *ksm_sysfs)
237{
238 if (ksm_write_sysfs(KSM_FP("max_page_sharing"), ksm_sysfs->max_page_sharing) ||
239 ksm_write_sysfs(KSM_FP("merge_across_nodes"), ksm_sysfs->merge_across_nodes) ||
240 ksm_write_sysfs(KSM_FP("pages_to_scan"), ksm_sysfs->pages_to_scan) ||
241 ksm_write_sysfs(KSM_FP("run"), ksm_sysfs->run) ||
242 ksm_write_sysfs(KSM_FP("sleep_millisecs"), ksm_sysfs->sleep_millisecs) ||
243 ksm_write_sysfs(KSM_FP("stable_node_chains_prune_millisecs"),
244 ksm_sysfs->stable_node_chains_prune_millisecs) ||
245 ksm_write_sysfs(KSM_FP("use_zero_pages"), ksm_sysfs->use_zero_pages))
246 return 1;
247
248 return 0;
249}
250
251static int check_ksm_merge(int mapping, int prot, long page_count, int timeout, size_t page_size)
252{
253 void *map_ptr;
254 struct timespec start_time;
255
256 if (clock_gettime(CLOCK_MONOTONIC_RAW, &start_time)) {
257 perror("clock_gettime");
258 return KSFT_FAIL;
259 }
260
261 /* fill pages with the same data and merge them */
262 map_ptr = allocate_memory(NULL, prot, mapping, '*', page_size * page_count);
263 if (!map_ptr)
264 return KSFT_FAIL;
265
266 if (ksm_merge_pages(map_ptr, page_size * page_count, start_time, timeout))
267 goto err_out;
268
269 /* verify that the right number of pages are merged */
270 if (assert_ksm_pages_count(page_count)) {
271 printf("OK\n");
272 munmap(map_ptr, page_size * page_count);
273 return KSFT_PASS;
274 }
275
276err_out:
277 printf("Not OK\n");
278 munmap(map_ptr, page_size * page_count);
279 return KSFT_FAIL;
280}
281
282static int check_ksm_unmerge(int mapping, int prot, int timeout, size_t page_size)
283{
284 void *map_ptr;
285 struct timespec start_time;
286 int page_count = 2;
287
288 if (clock_gettime(CLOCK_MONOTONIC_RAW, &start_time)) {
289 perror("clock_gettime");
290 return KSFT_FAIL;
291 }
292
293 /* fill pages with the same data and merge them */
294 map_ptr = allocate_memory(NULL, prot, mapping, '*', page_size * page_count);
295 if (!map_ptr)
296 return KSFT_FAIL;
297
298 if (ksm_merge_pages(map_ptr, page_size * page_count, start_time, timeout))
299 goto err_out;
300
301 /* change 1 byte in each of the 2 pages -- KSM must automatically unmerge them */
302 memset(map_ptr, '-', 1);
303 memset(map_ptr + page_size, '+', 1);
304
305 /* get at least 1 scan, so KSM can detect that the pages were modified */
306 if (ksm_do_scan(1, start_time, timeout))
307 goto err_out;
308
309 /* check that unmerging was successful and 0 pages are currently merged */
310 if (assert_ksm_pages_count(0)) {
311 printf("OK\n");
312 munmap(map_ptr, page_size * page_count);
313 return KSFT_PASS;
314 }
315
316err_out:
317 printf("Not OK\n");
318 munmap(map_ptr, page_size * page_count);
319 return KSFT_FAIL;
320}
321
322static int check_ksm_zero_page_merge(int mapping, int prot, long page_count, int timeout,
323 bool use_zero_pages, size_t page_size)
324{
325 void *map_ptr;
326 struct timespec start_time;
327
328 if (clock_gettime(CLOCK_MONOTONIC_RAW, &start_time)) {
329 perror("clock_gettime");
330 return KSFT_FAIL;
331 }
332
333 if (ksm_write_sysfs(KSM_FP("use_zero_pages"), use_zero_pages))
334 return KSFT_FAIL;
335
336 /* fill pages with zero and try to merge them */
337 map_ptr = allocate_memory(NULL, prot, mapping, 0, page_size * page_count);
338 if (!map_ptr)
339 return KSFT_FAIL;
340
341 if (ksm_merge_pages(map_ptr, page_size * page_count, start_time, timeout))
342 goto err_out;
343
344 /*
345 * verify that the right number of pages are merged:
346 * 1) if use_zero_pages is set to 1, empty pages are merged
347 * with the kernel zero page instead of with each other;
348 * 2) if use_zero_pages is set to 0, empty pages are not treated specially
349 * and merged as usual.
350 */
351 if (use_zero_pages && !assert_ksm_pages_count(0))
352 goto err_out;
353 else if (!use_zero_pages && !assert_ksm_pages_count(page_count))
354 goto err_out;
355
356 printf("OK\n");
357 munmap(map_ptr, page_size * page_count);
358 return KSFT_PASS;
359
360err_out:
361 printf("Not OK\n");
362 munmap(map_ptr, page_size * page_count);
363 return KSFT_FAIL;
364}
365
366static int get_next_mem_node(int node)
367{
368
369 long node_size;
370 int mem_node = 0;
371 int i, max_node = numa_max_node();
372
373 for (i = node + 1; i <= max_node + node; i++) {
374 mem_node = i % (max_node + 1);
375 node_size = numa_node_size(mem_node, NULL);
376 if (node_size > 0)
377 break;
378 }
379 return mem_node;
380}
381
382static int get_first_mem_node(void)
383{
384 return get_next_mem_node(numa_max_node());
385}
386
387static int check_ksm_numa_merge(int mapping, int prot, int timeout, bool merge_across_nodes,
388 size_t page_size)
389{
390 void *numa1_map_ptr, *numa2_map_ptr;
391 struct timespec start_time;
392 int page_count = 2;
393 int first_node;
394
395 if (clock_gettime(CLOCK_MONOTONIC_RAW, &start_time)) {
396 perror("clock_gettime");
397 return KSFT_FAIL;
398 }
399
400 if (numa_available() < 0) {
401 perror("NUMA support not enabled");
402 return KSFT_SKIP;
403 }
404 if (numa_num_configured_nodes() <= 1) {
405 printf("At least 2 NUMA nodes must be available\n");
406 return KSFT_SKIP;
407 }
408 if (ksm_write_sysfs(KSM_FP("merge_across_nodes"), merge_across_nodes))
409 return KSFT_FAIL;
410
411 /* allocate 2 pages in 2 different NUMA nodes and fill them with the same data */
412 first_node = get_first_mem_node();
413 numa1_map_ptr = numa_alloc_onnode(page_size, first_node);
414 numa2_map_ptr = numa_alloc_onnode(page_size, get_next_mem_node(first_node));
415 if (!numa1_map_ptr || !numa2_map_ptr) {
416 perror("numa_alloc_onnode");
417 return KSFT_FAIL;
418 }
419
420 memset(numa1_map_ptr, '*', page_size);
421 memset(numa2_map_ptr, '*', page_size);
422
423 /* try to merge the pages */
424 if (ksm_merge_pages(numa1_map_ptr, page_size, start_time, timeout) ||
425 ksm_merge_pages(numa2_map_ptr, page_size, start_time, timeout))
426 goto err_out;
427
428 /*
429 * verify that the right number of pages are merged:
430 * 1) if merge_across_nodes was enabled, 2 duplicate pages will be merged;
431 * 2) if merge_across_nodes = 0, there must be 0 merged pages, since there is
432 * only 1 unique page in each node and they can't be shared.
433 */
434 if (merge_across_nodes && !assert_ksm_pages_count(page_count))
435 goto err_out;
436 else if (!merge_across_nodes && !assert_ksm_pages_count(0))
437 goto err_out;
438
439 numa_free(numa1_map_ptr, page_size);
440 numa_free(numa2_map_ptr, page_size);
441 printf("OK\n");
442 return KSFT_PASS;
443
444err_out:
445 numa_free(numa1_map_ptr, page_size);
446 numa_free(numa2_map_ptr, page_size);
447 printf("Not OK\n");
448 return KSFT_FAIL;
449}
450
451static int ksm_merge_hugepages_time(int mapping, int prot, int timeout, size_t map_size)
452{
453 void *map_ptr, *map_ptr_orig;
454 struct timespec start_time, end_time;
455 unsigned long scan_time_ns;
456 int pagemap_fd, n_normal_pages, n_huge_pages;
457
458 map_size *= MB;
459 size_t len = map_size;
460
461 len -= len % HPAGE_SIZE;
462 map_ptr_orig = mmap(NULL, len + HPAGE_SIZE, PROT_READ | PROT_WRITE,
463 MAP_ANONYMOUS | MAP_NORESERVE | MAP_PRIVATE, -1, 0);
464 map_ptr = map_ptr_orig + HPAGE_SIZE - (uintptr_t)map_ptr_orig % HPAGE_SIZE;
465
466 if (map_ptr_orig == MAP_FAILED)
467 err(2, "initial mmap");
468
469 if (madvise(map_ptr, len + HPAGE_SIZE, MADV_HUGEPAGE))
470 err(2, "MADV_HUGEPAGE");
471
472 pagemap_fd = open("/proc/self/pagemap", O_RDONLY);
473 if (pagemap_fd < 0)
474 err(2, "open pagemap");
475
476 n_normal_pages = 0;
477 n_huge_pages = 0;
478 for (void *p = map_ptr; p < map_ptr + len; p += HPAGE_SIZE) {
479 if (allocate_transhuge(p, pagemap_fd) < 0)
480 n_normal_pages++;
481 else
482 n_huge_pages++;
483 }
484 printf("Number of normal pages: %d\n", n_normal_pages);
485 printf("Number of huge pages: %d\n", n_huge_pages);
486
487 memset(map_ptr, '*', len);
488
489 if (clock_gettime(CLOCK_MONOTONIC_RAW, &start_time)) {
490 perror("clock_gettime");
491 goto err_out;
492 }
493 if (ksm_merge_pages(map_ptr, map_size, start_time, timeout))
494 goto err_out;
495 if (clock_gettime(CLOCK_MONOTONIC_RAW, &end_time)) {
496 perror("clock_gettime");
497 goto err_out;
498 }
499
500 scan_time_ns = (end_time.tv_sec - start_time.tv_sec) * NSEC_PER_SEC +
501 (end_time.tv_nsec - start_time.tv_nsec);
502
503 printf("Total size: %lu MiB\n", map_size / MB);
504 printf("Total time: %ld.%09ld s\n", scan_time_ns / NSEC_PER_SEC,
505 scan_time_ns % NSEC_PER_SEC);
506 printf("Average speed: %.3f MiB/s\n", (map_size / MB) /
507 ((double)scan_time_ns / NSEC_PER_SEC));
508
509 munmap(map_ptr_orig, len + HPAGE_SIZE);
510 return KSFT_PASS;
511
512err_out:
513 printf("Not OK\n");
514 munmap(map_ptr_orig, len + HPAGE_SIZE);
515 return KSFT_FAIL;
516}
517
518static int ksm_merge_time(int mapping, int prot, int timeout, size_t map_size)
519{
520 void *map_ptr;
521 struct timespec start_time, end_time;
522 unsigned long scan_time_ns;
523
524 map_size *= MB;
525
526 map_ptr = allocate_memory(NULL, prot, mapping, '*', map_size);
527 if (!map_ptr)
528 return KSFT_FAIL;
529
530 if (clock_gettime(CLOCK_MONOTONIC_RAW, &start_time)) {
531 perror("clock_gettime");
532 goto err_out;
533 }
534 if (ksm_merge_pages(map_ptr, map_size, start_time, timeout))
535 goto err_out;
536 if (clock_gettime(CLOCK_MONOTONIC_RAW, &end_time)) {
537 perror("clock_gettime");
538 goto err_out;
539 }
540
541 scan_time_ns = (end_time.tv_sec - start_time.tv_sec) * NSEC_PER_SEC +
542 (end_time.tv_nsec - start_time.tv_nsec);
543
544 printf("Total size: %lu MiB\n", map_size / MB);
545 printf("Total time: %ld.%09ld s\n", scan_time_ns / NSEC_PER_SEC,
546 scan_time_ns % NSEC_PER_SEC);
547 printf("Average speed: %.3f MiB/s\n", (map_size / MB) /
548 ((double)scan_time_ns / NSEC_PER_SEC));
549
550 munmap(map_ptr, map_size);
551 return KSFT_PASS;
552
553err_out:
554 printf("Not OK\n");
555 munmap(map_ptr, map_size);
556 return KSFT_FAIL;
557}
558
559static int ksm_cow_time(int mapping, int prot, int timeout, size_t page_size)
560{
561 void *map_ptr;
562 struct timespec start_time, end_time;
563 unsigned long cow_time_ns;
564
565 /* page_count must be less than 2*page_size */
566 size_t page_count = 4000;
567
568 map_ptr = allocate_memory(NULL, prot, mapping, '*', page_size * page_count);
569 if (!map_ptr)
570 return KSFT_FAIL;
571
572 if (clock_gettime(CLOCK_MONOTONIC_RAW, &start_time)) {
573 perror("clock_gettime");
574 return KSFT_FAIL;
575 }
576 for (size_t i = 0; i < page_count - 1; i = i + 2)
577 memset(map_ptr + page_size * i, '-', 1);
578 if (clock_gettime(CLOCK_MONOTONIC_RAW, &end_time)) {
579 perror("clock_gettime");
580 return KSFT_FAIL;
581 }
582
583 cow_time_ns = (end_time.tv_sec - start_time.tv_sec) * NSEC_PER_SEC +
584 (end_time.tv_nsec - start_time.tv_nsec);
585
586 printf("Total size: %lu MiB\n\n", (page_size * page_count) / MB);
587 printf("Not merged pages:\n");
588 printf("Total time: %ld.%09ld s\n", cow_time_ns / NSEC_PER_SEC,
589 cow_time_ns % NSEC_PER_SEC);
590 printf("Average speed: %.3f MiB/s\n\n", ((page_size * (page_count / 2)) / MB) /
591 ((double)cow_time_ns / NSEC_PER_SEC));
592
593 /* Create 2000 pairs of duplicate pages */
594 for (size_t i = 0; i < page_count - 1; i = i + 2) {
595 memset(map_ptr + page_size * i, '+', i / 2 + 1);
596 memset(map_ptr + page_size * (i + 1), '+', i / 2 + 1);
597 }
598 if (ksm_merge_pages(map_ptr, page_size * page_count, start_time, timeout))
599 goto err_out;
600
601 if (clock_gettime(CLOCK_MONOTONIC_RAW, &start_time)) {
602 perror("clock_gettime");
603 goto err_out;
604 }
605 for (size_t i = 0; i < page_count - 1; i = i + 2)
606 memset(map_ptr + page_size * i, '-', 1);
607 if (clock_gettime(CLOCK_MONOTONIC_RAW, &end_time)) {
608 perror("clock_gettime");
609 goto err_out;
610 }
611
612 cow_time_ns = (end_time.tv_sec - start_time.tv_sec) * NSEC_PER_SEC +
613 (end_time.tv_nsec - start_time.tv_nsec);
614
615 printf("Merged pages:\n");
616 printf("Total time: %ld.%09ld s\n", cow_time_ns / NSEC_PER_SEC,
617 cow_time_ns % NSEC_PER_SEC);
618 printf("Average speed: %.3f MiB/s\n", ((page_size * (page_count / 2)) / MB) /
619 ((double)cow_time_ns / NSEC_PER_SEC));
620
621 munmap(map_ptr, page_size * page_count);
622 return KSFT_PASS;
623
624err_out:
625 printf("Not OK\n");
626 munmap(map_ptr, page_size * page_count);
627 return KSFT_FAIL;
628}
629
630int main(int argc, char *argv[])
631{
632 int ret, opt;
633 int prot = 0;
634 int ksm_scan_limit_sec = KSM_SCAN_LIMIT_SEC_DEFAULT;
635 long page_count = KSM_PAGE_COUNT_DEFAULT;
636 size_t page_size = sysconf(_SC_PAGESIZE);
637 struct ksm_sysfs ksm_sysfs_old;
638 int test_name = CHECK_KSM_MERGE;
639 bool use_zero_pages = KSM_USE_ZERO_PAGES_DEFAULT;
640 bool merge_across_nodes = KSM_MERGE_ACROSS_NODES_DEFAULT;
641 long size_MB = 0;
642
643 while ((opt = getopt(argc, argv, "ha:p:l:z:m:s:MUZNPCH")) != -1) {
644 switch (opt) {
645 case 'a':
646 prot = str_to_prot(optarg);
647 break;
648 case 'p':
649 page_count = atol(optarg);
650 if (page_count <= 0) {
651 printf("The number of pages must be greater than 0\n");
652 return KSFT_FAIL;
653 }
654 break;
655 case 'l':
656 ksm_scan_limit_sec = atoi(optarg);
657 if (ksm_scan_limit_sec <= 0) {
658 printf("Timeout value must be greater than 0\n");
659 return KSFT_FAIL;
660 }
661 break;
662 case 'h':
663 print_help();
664 break;
665 case 'z':
666 if (strcmp(optarg, "0") == 0)
667 use_zero_pages = 0;
668 else
669 use_zero_pages = 1;
670 break;
671 case 'm':
672 if (strcmp(optarg, "0") == 0)
673 merge_across_nodes = 0;
674 else
675 merge_across_nodes = 1;
676 break;
677 case 's':
678 size_MB = atoi(optarg);
679 if (size_MB <= 0) {
680 printf("Size must be greater than 0\n");
681 return KSFT_FAIL;
682 }
683 case 'M':
684 break;
685 case 'U':
686 test_name = CHECK_KSM_UNMERGE;
687 break;
688 case 'Z':
689 test_name = CHECK_KSM_ZERO_PAGE_MERGE;
690 break;
691 case 'N':
692 test_name = CHECK_KSM_NUMA_MERGE;
693 break;
694 case 'P':
695 test_name = KSM_MERGE_TIME;
696 break;
697 case 'H':
698 test_name = KSM_MERGE_TIME_HUGE_PAGES;
699 break;
700 case 'C':
701 test_name = KSM_COW_TIME;
702 break;
703 default:
704 return KSFT_FAIL;
705 }
706 }
707
708 if (prot == 0)
709 prot = str_to_prot(KSM_PROT_STR_DEFAULT);
710
711 if (access(KSM_SYSFS_PATH, F_OK)) {
712 printf("Config KSM not enabled\n");
713 return KSFT_SKIP;
714 }
715
716 if (ksm_save_def(&ksm_sysfs_old)) {
717 printf("Cannot save default tunables\n");
718 return KSFT_FAIL;
719 }
720
721 if (ksm_write_sysfs(KSM_FP("run"), 2) ||
722 ksm_write_sysfs(KSM_FP("sleep_millisecs"), 0) ||
723 ksm_write_sysfs(KSM_FP("merge_across_nodes"), 1) ||
724 ksm_write_sysfs(KSM_FP("pages_to_scan"), page_count))
725 return KSFT_FAIL;
726
727 switch (test_name) {
728 case CHECK_KSM_MERGE:
729 ret = check_ksm_merge(MAP_PRIVATE | MAP_ANONYMOUS, prot, page_count,
730 ksm_scan_limit_sec, page_size);
731 break;
732 case CHECK_KSM_UNMERGE:
733 ret = check_ksm_unmerge(MAP_PRIVATE | MAP_ANONYMOUS, prot, ksm_scan_limit_sec,
734 page_size);
735 break;
736 case CHECK_KSM_ZERO_PAGE_MERGE:
737 ret = check_ksm_zero_page_merge(MAP_PRIVATE | MAP_ANONYMOUS, prot, page_count,
738 ksm_scan_limit_sec, use_zero_pages, page_size);
739 break;
740 case CHECK_KSM_NUMA_MERGE:
741 ret = check_ksm_numa_merge(MAP_PRIVATE | MAP_ANONYMOUS, prot, ksm_scan_limit_sec,
742 merge_across_nodes, page_size);
743 break;
744 case KSM_MERGE_TIME:
745 if (size_MB == 0) {
746 printf("Option '-s' is required.\n");
747 return KSFT_FAIL;
748 }
749 ret = ksm_merge_time(MAP_PRIVATE | MAP_ANONYMOUS, prot, ksm_scan_limit_sec,
750 size_MB);
751 break;
752 case KSM_MERGE_TIME_HUGE_PAGES:
753 if (size_MB == 0) {
754 printf("Option '-s' is required.\n");
755 return KSFT_FAIL;
756 }
757 ret = ksm_merge_hugepages_time(MAP_PRIVATE | MAP_ANONYMOUS, prot,
758 ksm_scan_limit_sec, size_MB);
759 break;
760 case KSM_COW_TIME:
761 ret = ksm_cow_time(MAP_PRIVATE | MAP_ANONYMOUS, prot, ksm_scan_limit_sec,
762 page_size);
763 break;
764 }
765
766 if (ksm_restore(&ksm_sysfs_old)) {
767 printf("Cannot restore default tunables\n");
768 return KSFT_FAIL;
769 }
770
771 return ret;
772}