ksm_tests.c 26 KB

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