xref: /linux/tools/testing/selftests/arm64/mte/check_ksm_options.c (revision 67f8bc848ee31831336bd478e57d2f993551902e)
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