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

hugetlb-madvise.c (10576B)


      1// SPDX-License-Identifier: GPL-2.0
      2/*
      3 * hugepage-madvise:
      4 *
      5 * Basic functional testing of madvise MADV_DONTNEED and MADV_REMOVE
      6 * on hugetlb mappings.
      7 *
      8 * Before running this test, make sure the administrator has pre-allocated
      9 * at least MIN_FREE_PAGES hugetlb pages and they are free.  In addition,
     10 * the test takes an argument that is the path to a file in a hugetlbfs
     11 * filesystem.  Therefore, a hugetlbfs filesystem must be mounted on some
     12 * directory.
     13 */
     14
     15#include <stdlib.h>
     16#include <stdio.h>
     17#include <unistd.h>
     18#include <sys/mman.h>
     19#define __USE_GNU
     20#include <fcntl.h>
     21
     22#define USAGE	"USAGE: %s <hugepagefile_name>\n"
     23#define MIN_FREE_PAGES	20
     24#define NR_HUGE_PAGES	10	/* common number of pages to map/allocate */
     25
     26#define validate_free_pages(exp_free)					\
     27	do {								\
     28		int fhp = get_free_hugepages();				\
     29		if (fhp != (exp_free)) {				\
     30			printf("Unexpected number of free huge "	\
     31				"pages line %d\n", __LINE__);		\
     32			exit(1);					\
     33		}							\
     34	} while (0)
     35
     36unsigned long huge_page_size;
     37unsigned long base_page_size;
     38
     39/*
     40 * default_huge_page_size copied from mlock2-tests.c
     41 */
     42unsigned long default_huge_page_size(void)
     43{
     44	unsigned long hps = 0;
     45	char *line = NULL;
     46	size_t linelen = 0;
     47	FILE *f = fopen("/proc/meminfo", "r");
     48
     49	if (!f)
     50		return 0;
     51	while (getline(&line, &linelen, f) > 0) {
     52		if (sscanf(line, "Hugepagesize:       %lu kB", &hps) == 1) {
     53			hps <<= 10;
     54			break;
     55		}
     56	}
     57
     58	free(line);
     59	fclose(f);
     60	return hps;
     61}
     62
     63unsigned long get_free_hugepages(void)
     64{
     65	unsigned long fhp = 0;
     66	char *line = NULL;
     67	size_t linelen = 0;
     68	FILE *f = fopen("/proc/meminfo", "r");
     69
     70	if (!f)
     71		return fhp;
     72	while (getline(&line, &linelen, f) > 0) {
     73		if (sscanf(line, "HugePages_Free:      %lu", &fhp) == 1)
     74			break;
     75	}
     76
     77	free(line);
     78	fclose(f);
     79	return fhp;
     80}
     81
     82void write_fault_pages(void *addr, unsigned long nr_pages)
     83{
     84	unsigned long i;
     85
     86	for (i = 0; i < nr_pages; i++)
     87		*((unsigned long *)(addr + (i * huge_page_size))) = i;
     88}
     89
     90void read_fault_pages(void *addr, unsigned long nr_pages)
     91{
     92	unsigned long i, tmp;
     93
     94	for (i = 0; i < nr_pages; i++)
     95		tmp += *((unsigned long *)(addr + (i * huge_page_size)));
     96}
     97
     98int main(int argc, char **argv)
     99{
    100	unsigned long free_hugepages;
    101	void *addr, *addr2;
    102	int fd;
    103	int ret;
    104
    105	if (argc != 2) {
    106		printf(USAGE, argv[0]);
    107		exit(1);
    108	}
    109
    110	huge_page_size = default_huge_page_size();
    111	if (!huge_page_size) {
    112		printf("Unable to determine huge page size, exiting!\n");
    113		exit(1);
    114	}
    115	base_page_size = sysconf(_SC_PAGE_SIZE);
    116	if (!huge_page_size) {
    117		printf("Unable to determine base page size, exiting!\n");
    118		exit(1);
    119	}
    120
    121	free_hugepages = get_free_hugepages();
    122	if (free_hugepages < MIN_FREE_PAGES) {
    123		printf("Not enough free huge pages to test, exiting!\n");
    124		exit(1);
    125	}
    126
    127	fd = open(argv[1], O_CREAT | O_RDWR, 0755);
    128	if (fd < 0) {
    129		perror("Open failed");
    130		exit(1);
    131	}
    132
    133	/*
    134	 * Test validity of MADV_DONTNEED addr and length arguments.  mmap
    135	 * size is NR_HUGE_PAGES + 2.  One page at the beginning and end of
    136	 * the mapping will be unmapped so we KNOW there is nothing mapped
    137	 * there.
    138	 */
    139	addr = mmap(NULL, (NR_HUGE_PAGES + 2) * huge_page_size,
    140			PROT_READ | PROT_WRITE,
    141			MAP_PRIVATE | MAP_ANONYMOUS | MAP_HUGETLB,
    142			-1, 0);
    143	if (addr == MAP_FAILED) {
    144		perror("mmap");
    145		exit(1);
    146	}
    147	if (munmap(addr, huge_page_size) ||
    148			munmap(addr + (NR_HUGE_PAGES + 1) * huge_page_size,
    149				huge_page_size)) {
    150		perror("munmap");
    151		exit(1);
    152	}
    153	addr = addr + huge_page_size;
    154
    155	write_fault_pages(addr, NR_HUGE_PAGES);
    156	validate_free_pages(free_hugepages - NR_HUGE_PAGES);
    157
    158	/* addr before mapping should fail */
    159	ret = madvise(addr - base_page_size, NR_HUGE_PAGES * huge_page_size,
    160		MADV_DONTNEED);
    161	if (!ret) {
    162		printf("Unexpected success of madvise call with invalid addr line %d\n",
    163				__LINE__);
    164			exit(1);
    165	}
    166
    167	/* addr + length after mapping should fail */
    168	ret = madvise(addr, (NR_HUGE_PAGES * huge_page_size) + base_page_size,
    169		MADV_DONTNEED);
    170	if (!ret) {
    171		printf("Unexpected success of madvise call with invalid length line %d\n",
    172				__LINE__);
    173			exit(1);
    174	}
    175
    176	(void)munmap(addr, NR_HUGE_PAGES * huge_page_size);
    177
    178	/*
    179	 * Test alignment of MADV_DONTNEED addr and length arguments
    180	 */
    181	addr = mmap(NULL, NR_HUGE_PAGES * huge_page_size,
    182			PROT_READ | PROT_WRITE,
    183			MAP_PRIVATE | MAP_ANONYMOUS | MAP_HUGETLB,
    184			-1, 0);
    185	if (addr == MAP_FAILED) {
    186		perror("mmap");
    187		exit(1);
    188	}
    189	write_fault_pages(addr, NR_HUGE_PAGES);
    190	validate_free_pages(free_hugepages - NR_HUGE_PAGES);
    191
    192	/* addr is not huge page size aligned and should fail */
    193	ret = madvise(addr + base_page_size,
    194			NR_HUGE_PAGES * huge_page_size - base_page_size,
    195			MADV_DONTNEED);
    196	if (!ret) {
    197		printf("Unexpected success of madvise call with unaligned start address %d\n",
    198				__LINE__);
    199			exit(1);
    200	}
    201
    202	/* addr + length should be aligned up to huge page size */
    203	if (madvise(addr,
    204			((NR_HUGE_PAGES - 1) * huge_page_size) + base_page_size,
    205			MADV_DONTNEED)) {
    206		perror("madvise");
    207		exit(1);
    208	}
    209
    210	/* should free all pages in mapping */
    211	validate_free_pages(free_hugepages);
    212
    213	(void)munmap(addr, NR_HUGE_PAGES * huge_page_size);
    214
    215	/*
    216	 * Test MADV_DONTNEED on anonymous private mapping
    217	 */
    218	addr = mmap(NULL, NR_HUGE_PAGES * huge_page_size,
    219			PROT_READ | PROT_WRITE,
    220			MAP_PRIVATE | MAP_ANONYMOUS | MAP_HUGETLB,
    221			-1, 0);
    222	if (addr == MAP_FAILED) {
    223		perror("mmap");
    224		exit(1);
    225	}
    226	write_fault_pages(addr, NR_HUGE_PAGES);
    227	validate_free_pages(free_hugepages - NR_HUGE_PAGES);
    228
    229	if (madvise(addr, NR_HUGE_PAGES * huge_page_size, MADV_DONTNEED)) {
    230		perror("madvise");
    231		exit(1);
    232	}
    233
    234	/* should free all pages in mapping */
    235	validate_free_pages(free_hugepages);
    236
    237	(void)munmap(addr, NR_HUGE_PAGES * huge_page_size);
    238
    239	/*
    240	 * Test MADV_DONTNEED on private mapping of hugetlb file
    241	 */
    242	if (fallocate(fd, 0, 0, NR_HUGE_PAGES * huge_page_size)) {
    243		perror("fallocate");
    244		exit(1);
    245	}
    246	validate_free_pages(free_hugepages - NR_HUGE_PAGES);
    247
    248	addr = mmap(NULL, NR_HUGE_PAGES * huge_page_size,
    249			PROT_READ | PROT_WRITE,
    250			MAP_PRIVATE, fd, 0);
    251	if (addr == MAP_FAILED) {
    252		perror("mmap");
    253		exit(1);
    254	}
    255
    256	/* read should not consume any pages */
    257	read_fault_pages(addr, NR_HUGE_PAGES);
    258	validate_free_pages(free_hugepages - NR_HUGE_PAGES);
    259
    260	/* madvise should not free any pages */
    261	if (madvise(addr, NR_HUGE_PAGES * huge_page_size, MADV_DONTNEED)) {
    262		perror("madvise");
    263		exit(1);
    264	}
    265	validate_free_pages(free_hugepages - NR_HUGE_PAGES);
    266
    267	/* writes should allocate private pages */
    268	write_fault_pages(addr, NR_HUGE_PAGES);
    269	validate_free_pages(free_hugepages - (2 * NR_HUGE_PAGES));
    270
    271	/* madvise should free private pages */
    272	if (madvise(addr, NR_HUGE_PAGES * huge_page_size, MADV_DONTNEED)) {
    273		perror("madvise");
    274		exit(1);
    275	}
    276	validate_free_pages(free_hugepages - NR_HUGE_PAGES);
    277
    278	/* writes should allocate private pages */
    279	write_fault_pages(addr, NR_HUGE_PAGES);
    280	validate_free_pages(free_hugepages - (2 * NR_HUGE_PAGES));
    281
    282	/*
    283	 * The fallocate below certainly should free the pages associated
    284	 * with the file.  However, pages in the private mapping are also
    285	 * freed.  This is not the 'correct' behavior, but is expected
    286	 * because this is how it has worked since the initial hugetlb
    287	 * implementation.
    288	 */
    289	if (fallocate(fd, FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE,
    290					0, NR_HUGE_PAGES * huge_page_size)) {
    291		perror("fallocate");
    292		exit(1);
    293	}
    294	validate_free_pages(free_hugepages);
    295
    296	(void)munmap(addr, NR_HUGE_PAGES * huge_page_size);
    297
    298	/*
    299	 * Test MADV_DONTNEED on shared mapping of hugetlb file
    300	 */
    301	if (fallocate(fd, 0, 0, NR_HUGE_PAGES * huge_page_size)) {
    302		perror("fallocate");
    303		exit(1);
    304	}
    305	validate_free_pages(free_hugepages - NR_HUGE_PAGES);
    306
    307	addr = mmap(NULL, NR_HUGE_PAGES * huge_page_size,
    308			PROT_READ | PROT_WRITE,
    309			MAP_SHARED, fd, 0);
    310	if (addr == MAP_FAILED) {
    311		perror("mmap");
    312		exit(1);
    313	}
    314
    315	/* write should not consume any pages */
    316	write_fault_pages(addr, NR_HUGE_PAGES);
    317	validate_free_pages(free_hugepages - NR_HUGE_PAGES);
    318
    319	/* madvise should not free any pages */
    320	if (madvise(addr, NR_HUGE_PAGES * huge_page_size, MADV_DONTNEED)) {
    321		perror("madvise");
    322		exit(1);
    323	}
    324	validate_free_pages(free_hugepages - NR_HUGE_PAGES);
    325
    326	/*
    327	 * Test MADV_REMOVE on shared mapping of hugetlb file
    328	 *
    329	 * madvise is same as hole punch and should free all pages.
    330	 */
    331	if (madvise(addr, NR_HUGE_PAGES * huge_page_size, MADV_REMOVE)) {
    332		perror("madvise");
    333		exit(1);
    334	}
    335	validate_free_pages(free_hugepages);
    336	(void)munmap(addr, NR_HUGE_PAGES * huge_page_size);
    337
    338	/*
    339	 * Test MADV_REMOVE on shared and private mapping of hugetlb file
    340	 */
    341	if (fallocate(fd, 0, 0, NR_HUGE_PAGES * huge_page_size)) {
    342		perror("fallocate");
    343		exit(1);
    344	}
    345	validate_free_pages(free_hugepages - NR_HUGE_PAGES);
    346
    347	addr = mmap(NULL, NR_HUGE_PAGES * huge_page_size,
    348			PROT_READ | PROT_WRITE,
    349			MAP_SHARED, fd, 0);
    350	if (addr == MAP_FAILED) {
    351		perror("mmap");
    352		exit(1);
    353	}
    354
    355	/* shared write should not consume any additional pages */
    356	write_fault_pages(addr, NR_HUGE_PAGES);
    357	validate_free_pages(free_hugepages - NR_HUGE_PAGES);
    358
    359	addr2 = mmap(NULL, NR_HUGE_PAGES * huge_page_size,
    360			PROT_READ | PROT_WRITE,
    361			MAP_PRIVATE, fd, 0);
    362	if (addr2 == MAP_FAILED) {
    363		perror("mmap");
    364		exit(1);
    365	}
    366
    367	/* private read should not consume any pages */
    368	read_fault_pages(addr2, NR_HUGE_PAGES);
    369	validate_free_pages(free_hugepages - NR_HUGE_PAGES);
    370
    371	/* private write should consume additional pages */
    372	write_fault_pages(addr2, NR_HUGE_PAGES);
    373	validate_free_pages(free_hugepages - (2 * NR_HUGE_PAGES));
    374
    375	/* madvise of shared mapping should not free any pages */
    376	if (madvise(addr, NR_HUGE_PAGES * huge_page_size, MADV_DONTNEED)) {
    377		perror("madvise");
    378		exit(1);
    379	}
    380	validate_free_pages(free_hugepages - (2 * NR_HUGE_PAGES));
    381
    382	/* madvise of private mapping should free private pages */
    383	if (madvise(addr2, NR_HUGE_PAGES * huge_page_size, MADV_DONTNEED)) {
    384		perror("madvise");
    385		exit(1);
    386	}
    387	validate_free_pages(free_hugepages - NR_HUGE_PAGES);
    388
    389	/* private write should consume additional pages again */
    390	write_fault_pages(addr2, NR_HUGE_PAGES);
    391	validate_free_pages(free_hugepages - (2 * NR_HUGE_PAGES));
    392
    393	/*
    394	 * madvise should free both file and private pages although this is
    395	 * not correct.  private pages should not be freed, but this is
    396	 * expected.  See comment associated with FALLOC_FL_PUNCH_HOLE call.
    397	 */
    398	if (madvise(addr, NR_HUGE_PAGES * huge_page_size, MADV_REMOVE)) {
    399		perror("madvise");
    400		exit(1);
    401	}
    402	validate_free_pages(free_hugepages);
    403
    404	(void)munmap(addr, NR_HUGE_PAGES * huge_page_size);
    405	(void)munmap(addr2, NR_HUGE_PAGES * huge_page_size);
    406
    407	close(fd);
    408	unlink(argv[1]);
    409	return 0;
    410}