1 // SPDX-License-Identifier: GPL-2.0 2 // Copyright (C) 2020 ARM Limited 3 4 #define _GNU_SOURCE 5 6 #include <errno.h> 7 #include <fcntl.h> 8 #include <signal.h> 9 #include <stdio.h> 10 #include <stdlib.h> 11 #include <string.h> 12 #include <ucontext.h> 13 #include <sys/mman.h> 14 15 #include "kselftest.h" 16 #include "mte_common_util.h" 17 #include "mte_def.h" 18 19 #define TEST_UNIT 10 20 #define PATH_KSM "/sys/kernel/mm/ksm/" 21 #define MAX_LOOP 4 22 23 static size_t page_sz; 24 static unsigned long ksm_sysfs[5]; 25 26 static unsigned long read_sysfs(char *str) 27 { 28 FILE *f; 29 unsigned long val = 0; 30 31 f = fopen(str, "r"); 32 if (!f) { 33 ksft_print_msg("ERR: missing %s\n", str); 34 return 0; 35 } 36 fscanf(f, "%lu", &val); 37 fclose(f); 38 return val; 39 } 40 41 static void write_sysfs(char *str, unsigned long val) 42 { 43 FILE *f; 44 45 f = fopen(str, "w"); 46 if (!f) { 47 ksft_print_msg("ERR: missing %s\n", str); 48 return; 49 } 50 fprintf(f, "%lu", val); 51 fclose(f); 52 } 53 54 static void mte_ksm_setup(void) 55 { 56 ksm_sysfs[0] = read_sysfs(PATH_KSM "merge_across_nodes"); 57 write_sysfs(PATH_KSM "merge_across_nodes", 1); 58 ksm_sysfs[1] = read_sysfs(PATH_KSM "sleep_millisecs"); 59 write_sysfs(PATH_KSM "sleep_millisecs", 0); 60 ksm_sysfs[2] = read_sysfs(PATH_KSM "run"); 61 write_sysfs(PATH_KSM "run", 1); 62 ksm_sysfs[3] = read_sysfs(PATH_KSM "max_page_sharing"); 63 write_sysfs(PATH_KSM "max_page_sharing", ksm_sysfs[3] + TEST_UNIT); 64 ksm_sysfs[4] = read_sysfs(PATH_KSM "pages_to_scan"); 65 write_sysfs(PATH_KSM "pages_to_scan", ksm_sysfs[4] + TEST_UNIT); 66 } 67 68 static void mte_ksm_restore(void) 69 { 70 write_sysfs(PATH_KSM "merge_across_nodes", ksm_sysfs[0]); 71 write_sysfs(PATH_KSM "sleep_millisecs", ksm_sysfs[1]); 72 write_sysfs(PATH_KSM "run", ksm_sysfs[2]); 73 write_sysfs(PATH_KSM "max_page_sharing", ksm_sysfs[3]); 74 write_sysfs(PATH_KSM "pages_to_scan", ksm_sysfs[4]); 75 } 76 77 static void mte_ksm_scan(void) 78 { 79 int cur_count = read_sysfs(PATH_KSM "full_scans"); 80 int scan_count = cur_count + 1; 81 int max_loop_count = MAX_LOOP; 82 83 while ((cur_count < scan_count) && max_loop_count) { 84 sleep(1); 85 cur_count = read_sysfs(PATH_KSM "full_scans"); 86 max_loop_count--; 87 } 88 #ifdef DEBUG 89 ksft_print_msg("INFO: pages_shared=%lu pages_sharing=%lu\n", 90 read_sysfs(PATH_KSM "pages_shared"), 91 read_sysfs(PATH_KSM "pages_sharing")); 92 #endif 93 } 94 95 static int check_madvise_options(int mem_type, int mode, int mapping) 96 { 97 char *ptr; 98 int err, ret; 99 100 err = KSFT_FAIL; 101 if (access(PATH_KSM, F_OK) == -1) { 102 ksft_print_msg("ERR: Kernel KSM config not enabled\n"); 103 return err; 104 } 105 106 mte_switch_mode(mode, MTE_ALLOW_NON_ZERO_TAG); 107 ptr = mte_allocate_memory(TEST_UNIT * page_sz, mem_type, mapping, true); 108 if (check_allocated_memory(ptr, TEST_UNIT * page_sz, mem_type, false) != KSFT_PASS) 109 return KSFT_FAIL; 110 111 /* Insert same data in all the pages */ 112 memset(ptr, 'A', TEST_UNIT * page_sz); 113 ret = madvise(ptr, TEST_UNIT * page_sz, MADV_MERGEABLE); 114 if (ret) { 115 ksft_print_msg("ERR: madvise failed to set MADV_UNMERGEABLE\n"); 116 goto madvise_err; 117 } 118 mte_ksm_scan(); 119 /* Tagged pages should not merge */ 120 if ((read_sysfs(PATH_KSM "pages_shared") < 1) || 121 (read_sysfs(PATH_KSM "pages_sharing") < (TEST_UNIT - 1))) 122 err = KSFT_PASS; 123 madvise_err: 124 mte_free_memory(ptr, TEST_UNIT * page_sz, mem_type, true); 125 return err; 126 } 127 128 int main(int argc, char *argv[]) 129 { 130 int err; 131 132 err = mte_default_setup(); 133 if (err) 134 return err; 135 page_sz = getpagesize(); 136 if (!page_sz) { 137 ksft_print_msg("ERR: Unable to get page size\n"); 138 return KSFT_FAIL; 139 } 140 /* Register signal handlers */ 141 mte_register_signal(SIGBUS, mte_default_handler); 142 mte_register_signal(SIGSEGV, mte_default_handler); 143 /* Enable KSM */ 144 mte_ksm_setup(); 145 146 evaluate_test(check_madvise_options(USE_MMAP, MTE_SYNC_ERR, MAP_PRIVATE), 147 "Check KSM mte page merge for private mapping, sync mode and mmap memory\n"); 148 evaluate_test(check_madvise_options(USE_MMAP, MTE_ASYNC_ERR, MAP_PRIVATE), 149 "Check KSM mte page merge for private mapping, async mode and mmap memory\n"); 150 evaluate_test(check_madvise_options(USE_MMAP, MTE_SYNC_ERR, MAP_SHARED), 151 "Check KSM mte page merge for shared mapping, sync mode and mmap memory\n"); 152 evaluate_test(check_madvise_options(USE_MMAP, MTE_ASYNC_ERR, MAP_SHARED), 153 "Check KSM mte page merge for shared mapping, async mode and mmap memory\n"); 154 155 mte_ksm_restore(); 156 mte_restore_setup(); 157 ksft_print_cnts(); 158 return ksft_get_fail_cnt() == 0 ? KSFT_PASS : KSFT_FAIL; 159 } 160