check_ksm_options.c 4.2 KB
Newer Older
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;
}