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
23static size_t page_sz;
24static unsigned long ksm_sysfs[5];
25
26static 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
44static 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
57static 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
71static 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
80static 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
98static 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;
126madvise_err:
127	mte_free_memory(ptr, TEST_UNIT * page_sz, mem_type, true);
128	return err;
129}
130
131int 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