summaryrefslogtreecommitdiff
path: root/tools/testing/selftests/arm64/mte/check_ksm_options.c
blob: bc41ae630c86c5c5b521772fdff9d6702f9c28e0 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
// SPDX-License-Identifier: GPL-2.0
// Copyright (C) 2020 ARM Limited

#define _GNU_SOURCE

#include <errno.h>
#include <fcntl.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ucontext.h>
#include <sys/mman.h>

#include "kselftest.h"
#include "mte_common_util.h"
#include "mte_def.h"

#define TEST_UNIT	10
#define PATH_KSM	"/sys/kernel/mm/ksm/"
#define MAX_LOOP	4

static size_t page_sz;
static unsigned long ksm_sysfs[5];

static unsigned long read_sysfs(char *str)
{
	FILE *f;
	unsigned long val = 0;

	f = fopen(str, "r");
	if (!f) {
		ksft_print_msg("ERR: missing %s\n", str);
		return 0;
	}
	fscanf(f, "%lu", &val);
	fclose(f);
	return val;
}

static void write_sysfs(char *str, unsigned long val)
{
	FILE *f;

	f = fopen(str, "w");
	if (!f) {
		ksft_print_msg("ERR: missing %s\n", str);
		return;
	}
	fprintf(f, "%lu", val);
	fclose(f);
}

static void mte_ksm_setup(void)
{
	ksm_sysfs[0] = read_sysfs(PATH_KSM "merge_across_nodes");
	write_sysfs(PATH_KSM "merge_across_nodes", 1);
	ksm_sysfs[1] = read_sysfs(PATH_KSM "sleep_millisecs");
	write_sysfs(PATH_KSM "sleep_millisecs", 0);
	ksm_sysfs[2] = read_sysfs(PATH_KSM "run");
	write_sysfs(PATH_KSM "run", 1);
	ksm_sysfs[3] = read_sysfs(PATH_KSM "max_page_sharing");
	write_sysfs(PATH_KSM "max_page_sharing", ksm_sysfs[3] + TEST_UNIT);
	ksm_sysfs[4] = read_sysfs(PATH_KSM "pages_to_scan");
	write_sysfs(PATH_KSM "pages_to_scan", ksm_sysfs[4] + TEST_UNIT);
}

static void mte_ksm_restore(void)
{
	write_sysfs(PATH_KSM "merge_across_nodes", ksm_sysfs[0]);
	write_sysfs(PATH_KSM "sleep_millisecs", ksm_sysfs[1]);
	write_sysfs(PATH_KSM "run", ksm_sysfs[2]);
	write_sysfs(PATH_KSM "max_page_sharing", ksm_sysfs[3]);
	write_sysfs(PATH_KSM "pages_to_scan", ksm_sysfs[4]);
}

static void mte_ksm_scan(void)
{
	int cur_count = read_sysfs(PATH_KSM "full_scans");
	int scan_count = cur_count + 1;
	int max_loop_count = MAX_LOOP;

	while ((cur_count < scan_count) && max_loop_count) {
		sleep(1);
		cur_count = read_sysfs(PATH_KSM "full_scans");
		max_loop_count--;
	}
#ifdef DEBUG
	ksft_print_msg("INFO: pages_shared=%lu pages_sharing=%lu\n",
			read_sysfs(PATH_KSM "pages_shared"),
			read_sysfs(PATH_KSM "pages_sharing"));
#endif
}

static int check_madvise_options(int mem_type, int mode, int mapping)
{
	char *ptr;
	int err, ret;

	err = KSFT_FAIL;
	if (access(PATH_KSM, F_OK) == -1) {
		ksft_print_msg("ERR: Kernel KSM config not enabled\n");
		return err;
	}

	mte_switch_mode(mode, MTE_ALLOW_NON_ZERO_TAG);
	ptr = mte_allocate_memory(TEST_UNIT * page_sz, mem_type, mapping, true);
	if (check_allocated_memory(ptr, TEST_UNIT * page_sz, mem_type, false) != KSFT_PASS)
		return KSFT_FAIL;

	/* Insert same data in all the pages */
	memset(ptr, 'A', TEST_UNIT * page_sz);
	ret = madvise(ptr, TEST_UNIT * page_sz, MADV_MERGEABLE);
	if (ret) {
		ksft_print_msg("ERR: madvise failed to set MADV_UNMERGEABLE\n");
		goto madvise_err;
	}
	mte_ksm_scan();
	/* Tagged pages should not merge */
	if ((read_sysfs(PATH_KSM "pages_shared") < 1) ||
	    (read_sysfs(PATH_KSM "pages_sharing") < (TEST_UNIT - 1)))
		err = KSFT_PASS;
madvise_err:
	mte_free_memory(ptr, TEST_UNIT * page_sz, mem_type, true);
	return err;
}

int main(int argc, char *argv[])
{
	int err;

	err = mte_default_setup();
	if (err)
		return err;
	page_sz = getpagesize();
	if (!page_sz) {
		ksft_print_msg("ERR: Unable to get page size\n");
		return KSFT_FAIL;
	}
	/* Register signal handlers */
	mte_register_signal(SIGBUS, mte_default_handler);
	mte_register_signal(SIGSEGV, mte_default_handler);
	/* Enable KSM */
	mte_ksm_setup();

	evaluate_test(check_madvise_options(USE_MMAP, MTE_SYNC_ERR, MAP_PRIVATE),
		"Check KSM mte page merge for private mapping, sync mode and mmap memory\n");
	evaluate_test(check_madvise_options(USE_MMAP, MTE_ASYNC_ERR, MAP_PRIVATE),
		"Check KSM mte page merge for private mapping, async mode and mmap memory\n");
	evaluate_test(check_madvise_options(USE_MMAP, MTE_SYNC_ERR, MAP_SHARED),
		"Check KSM mte page merge for shared mapping, sync mode and mmap memory\n");
	evaluate_test(check_madvise_options(USE_MMAP, MTE_ASYNC_ERR, MAP_SHARED),
		"Check KSM mte page merge for shared mapping, async mode and mmap memory\n");

	mte_ksm_restore();
	mte_restore_setup();
	ksft_print_cnts();
	return ksft_get_fail_cnt() == 0 ? KSFT_PASS : KSFT_FAIL;
}