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