cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

split_huge_page_test.c (7038B)


      1// SPDX-License-Identifier: GPL-2.0
      2/*
      3 * A test of splitting PMD THPs and PTE-mapped THPs from a specified virtual
      4 * address range in a process via <debugfs>/split_huge_pages interface.
      5 */
      6
      7#define _GNU_SOURCE
      8#include <stdio.h>
      9#include <stdlib.h>
     10#include <stdarg.h>
     11#include <unistd.h>
     12#include <inttypes.h>
     13#include <string.h>
     14#include <fcntl.h>
     15#include <sys/mman.h>
     16#include <sys/mount.h>
     17#include <malloc.h>
     18#include <stdbool.h>
     19#include "vm_util.h"
     20
     21uint64_t pagesize;
     22unsigned int pageshift;
     23uint64_t pmd_pagesize;
     24
     25#define SPLIT_DEBUGFS "/sys/kernel/debug/split_huge_pages"
     26#define INPUT_MAX 80
     27
     28#define PID_FMT "%d,0x%lx,0x%lx"
     29#define PATH_FMT "%s,0x%lx,0x%lx"
     30
     31#define PFN_MASK     ((1UL<<55)-1)
     32#define KPF_THP      (1UL<<22)
     33
     34int is_backed_by_thp(char *vaddr, int pagemap_file, int kpageflags_file)
     35{
     36	uint64_t paddr;
     37	uint64_t page_flags;
     38
     39	if (pagemap_file) {
     40		pread(pagemap_file, &paddr, sizeof(paddr),
     41			((long)vaddr >> pageshift) * sizeof(paddr));
     42
     43		if (kpageflags_file) {
     44			pread(kpageflags_file, &page_flags, sizeof(page_flags),
     45				(paddr & PFN_MASK) * sizeof(page_flags));
     46
     47			return !!(page_flags & KPF_THP);
     48		}
     49	}
     50	return 0;
     51}
     52
     53static int write_file(const char *path, const char *buf, size_t buflen)
     54{
     55	int fd;
     56	ssize_t numwritten;
     57
     58	fd = open(path, O_WRONLY);
     59	if (fd == -1)
     60		return 0;
     61
     62	numwritten = write(fd, buf, buflen - 1);
     63	close(fd);
     64	if (numwritten < 1)
     65		return 0;
     66
     67	return (unsigned int) numwritten;
     68}
     69
     70static void write_debugfs(const char *fmt, ...)
     71{
     72	char input[INPUT_MAX];
     73	int ret;
     74	va_list argp;
     75
     76	va_start(argp, fmt);
     77	ret = vsnprintf(input, INPUT_MAX, fmt, argp);
     78	va_end(argp);
     79
     80	if (ret >= INPUT_MAX) {
     81		printf("%s: Debugfs input is too long\n", __func__);
     82		exit(EXIT_FAILURE);
     83	}
     84
     85	if (!write_file(SPLIT_DEBUGFS, input, ret + 1)) {
     86		perror(SPLIT_DEBUGFS);
     87		exit(EXIT_FAILURE);
     88	}
     89}
     90
     91void split_pmd_thp(void)
     92{
     93	char *one_page;
     94	size_t len = 4 * pmd_pagesize;
     95	uint64_t thp_size;
     96	size_t i;
     97
     98	one_page = memalign(pmd_pagesize, len);
     99
    100	if (!one_page) {
    101		printf("Fail to allocate memory\n");
    102		exit(EXIT_FAILURE);
    103	}
    104
    105	madvise(one_page, len, MADV_HUGEPAGE);
    106
    107	for (i = 0; i < len; i++)
    108		one_page[i] = (char)i;
    109
    110	thp_size = check_huge(one_page);
    111	if (!thp_size) {
    112		printf("No THP is allocated\n");
    113		exit(EXIT_FAILURE);
    114	}
    115
    116	/* split all THPs */
    117	write_debugfs(PID_FMT, getpid(), (uint64_t)one_page,
    118		(uint64_t)one_page + len);
    119
    120	for (i = 0; i < len; i++)
    121		if (one_page[i] != (char)i) {
    122			printf("%ld byte corrupted\n", i);
    123			exit(EXIT_FAILURE);
    124		}
    125
    126
    127	thp_size = check_huge(one_page);
    128	if (thp_size) {
    129		printf("Still %ld kB AnonHugePages not split\n", thp_size);
    130		exit(EXIT_FAILURE);
    131	}
    132
    133	printf("Split huge pages successful\n");
    134	free(one_page);
    135}
    136
    137void split_pte_mapped_thp(void)
    138{
    139	char *one_page, *pte_mapped, *pte_mapped2;
    140	size_t len = 4 * pmd_pagesize;
    141	uint64_t thp_size;
    142	size_t i;
    143	const char *pagemap_template = "/proc/%d/pagemap";
    144	const char *kpageflags_proc = "/proc/kpageflags";
    145	char pagemap_proc[255];
    146	int pagemap_fd;
    147	int kpageflags_fd;
    148
    149	if (snprintf(pagemap_proc, 255, pagemap_template, getpid()) < 0) {
    150		perror("get pagemap proc error");
    151		exit(EXIT_FAILURE);
    152	}
    153	pagemap_fd = open(pagemap_proc, O_RDONLY);
    154
    155	if (pagemap_fd == -1) {
    156		perror("read pagemap:");
    157		exit(EXIT_FAILURE);
    158	}
    159
    160	kpageflags_fd = open(kpageflags_proc, O_RDONLY);
    161
    162	if (kpageflags_fd == -1) {
    163		perror("read kpageflags:");
    164		exit(EXIT_FAILURE);
    165	}
    166
    167	one_page = mmap((void *)(1UL << 30), len, PROT_READ | PROT_WRITE,
    168			MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
    169
    170	madvise(one_page, len, MADV_HUGEPAGE);
    171
    172	for (i = 0; i < len; i++)
    173		one_page[i] = (char)i;
    174
    175	thp_size = check_huge(one_page);
    176	if (!thp_size) {
    177		printf("No THP is allocated\n");
    178		exit(EXIT_FAILURE);
    179	}
    180
    181	/* remap the first pagesize of first THP */
    182	pte_mapped = mremap(one_page, pagesize, pagesize, MREMAP_MAYMOVE);
    183
    184	/* remap the Nth pagesize of Nth THP */
    185	for (i = 1; i < 4; i++) {
    186		pte_mapped2 = mremap(one_page + pmd_pagesize * i + pagesize * i,
    187				     pagesize, pagesize,
    188				     MREMAP_MAYMOVE|MREMAP_FIXED,
    189				     pte_mapped + pagesize * i);
    190		if (pte_mapped2 == (char *)-1) {
    191			perror("mremap failed");
    192			exit(EXIT_FAILURE);
    193		}
    194	}
    195
    196	/* smap does not show THPs after mremap, use kpageflags instead */
    197	thp_size = 0;
    198	for (i = 0; i < pagesize * 4; i++)
    199		if (i % pagesize == 0 &&
    200		    is_backed_by_thp(&pte_mapped[i], pagemap_fd, kpageflags_fd))
    201			thp_size++;
    202
    203	if (thp_size != 4) {
    204		printf("Some THPs are missing during mremap\n");
    205		exit(EXIT_FAILURE);
    206	}
    207
    208	/* split all remapped THPs */
    209	write_debugfs(PID_FMT, getpid(), (uint64_t)pte_mapped,
    210		      (uint64_t)pte_mapped + pagesize * 4);
    211
    212	/* smap does not show THPs after mremap, use kpageflags instead */
    213	thp_size = 0;
    214	for (i = 0; i < pagesize * 4; i++) {
    215		if (pte_mapped[i] != (char)i) {
    216			printf("%ld byte corrupted\n", i);
    217			exit(EXIT_FAILURE);
    218		}
    219		if (i % pagesize == 0 &&
    220		    is_backed_by_thp(&pte_mapped[i], pagemap_fd, kpageflags_fd))
    221			thp_size++;
    222	}
    223
    224	if (thp_size) {
    225		printf("Still %ld THPs not split\n", thp_size);
    226		exit(EXIT_FAILURE);
    227	}
    228
    229	printf("Split PTE-mapped huge pages successful\n");
    230	munmap(one_page, len);
    231	close(pagemap_fd);
    232	close(kpageflags_fd);
    233}
    234
    235void split_file_backed_thp(void)
    236{
    237	int status;
    238	int fd;
    239	ssize_t num_written;
    240	char tmpfs_template[] = "/tmp/thp_split_XXXXXX";
    241	const char *tmpfs_loc = mkdtemp(tmpfs_template);
    242	char testfile[INPUT_MAX];
    243	uint64_t pgoff_start = 0, pgoff_end = 1024;
    244
    245	printf("Please enable pr_debug in split_huge_pages_in_file() if you need more info.\n");
    246
    247	status = mount("tmpfs", tmpfs_loc, "tmpfs", 0, "huge=always,size=4m");
    248
    249	if (status) {
    250		printf("Unable to create a tmpfs for testing\n");
    251		exit(EXIT_FAILURE);
    252	}
    253
    254	status = snprintf(testfile, INPUT_MAX, "%s/thp_file", tmpfs_loc);
    255	if (status >= INPUT_MAX) {
    256		printf("Fail to create file-backed THP split testing file\n");
    257		goto cleanup;
    258	}
    259
    260	fd = open(testfile, O_CREAT|O_WRONLY);
    261	if (fd == -1) {
    262		perror("Cannot open testing file\n");
    263		goto cleanup;
    264	}
    265
    266	/* write something to the file, so a file-backed THP can be allocated */
    267	num_written = write(fd, tmpfs_loc, strlen(tmpfs_loc) + 1);
    268	close(fd);
    269
    270	if (num_written < 1) {
    271		printf("Fail to write data to testing file\n");
    272		goto cleanup;
    273	}
    274
    275	/* split the file-backed THP */
    276	write_debugfs(PATH_FMT, testfile, pgoff_start, pgoff_end);
    277
    278	status = unlink(testfile);
    279	if (status)
    280		perror("Cannot remove testing file\n");
    281
    282cleanup:
    283	status = umount(tmpfs_loc);
    284	if (status) {
    285		printf("Unable to umount %s\n", tmpfs_loc);
    286		exit(EXIT_FAILURE);
    287	}
    288	status = rmdir(tmpfs_loc);
    289	if (status) {
    290		perror("cannot remove tmp dir");
    291		exit(EXIT_FAILURE);
    292	}
    293
    294	printf("file-backed THP split test done, please check dmesg for more information\n");
    295}
    296
    297int main(int argc, char **argv)
    298{
    299	if (geteuid() != 0) {
    300		printf("Please run the benchmark as root\n");
    301		exit(EXIT_FAILURE);
    302	}
    303
    304	pagesize = getpagesize();
    305	pageshift = ffs(pagesize) - 1;
    306	pmd_pagesize = read_pmd_pagesize();
    307
    308	split_pmd_thp();
    309	split_pte_mapped_thp();
    310	split_file_backed_thp();
    311
    312	return 0;
    313}