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 if (fscanf(f, "%lu", &val) != 1) { 37 ksft_print_msg("ERR: parsing %s\n", str); 38 val = 0; 39 } 40 fclose(f); 41 return val; 42 } 43 44 static void write_sysfs(char *str, unsigned long val) 45 { 46 FILE *f; 47 48 f = fopen(str, "w"); 49 if (!f) { 50 ksft_print_msg("ERR: missing %s\n", str); 51 return; 52 } 53 fprintf(f, "%lu", val); 54 fclose(f); 55 } 56 57 static void mte_ksm_setup(void) 58 { 59 ksm_sysfs[0] = read_sysfs(PATH_KSM "merge_across_nodes"); 60 write_sysfs(PATH_KSM "merge_across_nodes", 1); 61 ksm_sysfs[1] = read_sysfs(PATH_KSM "sleep_millisecs"); 62 write_sysfs(PATH_KSM "sleep_millisecs", 0); 63 ksm_sysfs[2] = read_sysfs(PATH_KSM "run"); 64 write_sysfs(PATH_KSM "run", 1); 65 ksm_sysfs[3] = read_sysfs(PATH_KSM "max_page_sharing"); 66 write_sysfs(PATH_KSM "max_page_sharing", ksm_sysfs[3] + TEST_UNIT); 67 ksm_sysfs[4] = read_sysfs(PATH_KSM "pages_to_scan"); 68 write_sysfs(PATH_KSM "pages_to_scan", ksm_sysfs[4] + TEST_UNIT); 69 } 70 71 static void mte_ksm_restore(void) 72 { 73 write_sysfs(PATH_KSM "merge_across_nodes", ksm_sysfs[0]); 74 write_sysfs(PATH_KSM "sleep_millisecs", ksm_sysfs[1]); 75 write_sysfs(PATH_KSM "run", ksm_sysfs[2]); 76 write_sysfs(PATH_KSM "max_page_sharing", ksm_sysfs[3]); 77 write_sysfs(PATH_KSM "pages_to_scan", ksm_sysfs[4]); 78 } 79 80 static void mte_ksm_scan(void) 81 { 82 int cur_count = read_sysfs(PATH_KSM "full_scans"); 83 int scan_count = cur_count + 1; 84 int max_loop_count = MAX_LOOP; 85 86 while ((cur_count < scan_count) && max_loop_count) { 87 sleep(1); 88 cur_count = read_sysfs(PATH_KSM "full_scans"); 89 max_loop_count--; 90 } 91 #ifdef DEBUG 92 ksft_print_msg("INFO: pages_shared=%lu pages_sharing=%lu\n", 93 read_sysfs(PATH_KSM "pages_shared"), 94 read_sysfs(PATH_KSM "pages_sharing")); 95 #endif 96 } 97 98 static int check_madvise_options(int mem_type, int mode, int mapping) 99 { 100 char *ptr; 101 int err, ret; 102 103 err = KSFT_FAIL; 104 if (access(PATH_KSM, F_OK) == -1) { 105 ksft_print_msg("ERR: Kernel KSM config not enabled\n"); 106 return err; 107 } 108 109 mte_switch_mode(mode, MTE_ALLOW_NON_ZERO_TAG); 110 ptr = mte_allocate_memory(TEST_UNIT * page_sz, mem_type, mapping, true); 111 if (check_allocated_memory(ptr, TEST_UNIT * page_sz, mem_type, false) != KSFT_PASS) 112 return KSFT_FAIL; 113 114 /* Insert same data in all the pages */ 115 memset(ptr, 'A', TEST_UNIT * page_sz); 116 ret = madvise(ptr, TEST_UNIT * page_sz, MADV_MERGEABLE); 117 if (ret) { 118 ksft_print_msg("ERR: madvise failed to set MADV_UNMERGEABLE\n"); 119 goto madvise_err; 120 } 121 mte_ksm_scan(); 122 /* Tagged pages should not merge */ 123 if ((read_sysfs(PATH_KSM "pages_shared") < 1) || 124 (read_sysfs(PATH_KSM "pages_sharing") < (TEST_UNIT - 1))) 125 err = KSFT_PASS; 126 madvise_err: 127 mte_free_memory(ptr, TEST_UNIT * page_sz, mem_type, true); 128 return err; 129 } 130 131 int main(int argc, char *argv[]) 132 { 133 int err; 134 135 err = mte_default_setup(); 136 if (err) 137 return err; 138 page_sz = getpagesize(); 139 if (!page_sz) { 140 ksft_print_msg("ERR: Unable to get page size\n"); 141 return KSFT_FAIL; 142 } 143 /* Register signal handlers */ 144 mte_register_signal(SIGBUS, mte_default_handler); 145 mte_register_signal(SIGSEGV, mte_default_handler); 146 147 /* Set test plan */ 148 ksft_set_plan(4); 149 150 /* Enable KSM */ 151 mte_ksm_setup(); 152 153 evaluate_test(check_madvise_options(USE_MMAP, MTE_SYNC_ERR, MAP_PRIVATE), 154 "Check KSM mte page merge for private mapping, sync mode and mmap memory\n"); 155 evaluate_test(check_madvise_options(USE_MMAP, MTE_ASYNC_ERR, MAP_PRIVATE), 156 "Check KSM mte page merge for private mapping, async mode and mmap memory\n"); 157 evaluate_test(check_madvise_options(USE_MMAP, MTE_SYNC_ERR, MAP_SHARED), 158 "Check KSM mte page merge for shared mapping, sync mode and mmap memory\n"); 159 evaluate_test(check_madvise_options(USE_MMAP, MTE_ASYNC_ERR, MAP_SHARED), 160 "Check KSM mte page merge for shared mapping, async mode and mmap memory\n"); 161 162 mte_ksm_restore(); 163 mte_restore_setup(); 164 ksft_print_cnts(); 165 return ksft_get_fail_cnt() == 0 ? KSFT_PASS : KSFT_FAIL; 166 } 167