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