202 lines · 4.7 KB · C
RawDownload
// SPDX-License-Identifier: GPL-2.0/* * * A test for the patch "Allow compaction of unevictable pages". * With this patch we should be able to allocate at least 1/4 * of RAM in huge pages. Without the patch much less is * allocated. */ #include <stdio.h>#include <stdlib.h>#include <sys/mman.h>#include <sys/resource.h>#include <fcntl.h>#include <errno.h>#include <unistd.h>#include <string.h> #include "kselftest.h"#include "hugepage_settings.h" #define MAP_SIZE_MB	100#define MAP_SIZE	(MAP_SIZE_MB * 1024 * 1024) struct map_list {	void *map;	struct map_list *next;}; int read_memory_info(unsigned long *memfree, unsigned long *hugepagesize){	char buffer[256];	int found = 0;	FILE *file;	int ret = -1; 	file = fopen("/proc/meminfo", "r");	if (!file) {		ksft_print_msg("Failed to open /proc/meminfo: %s\n",			       strerror(errno));		return -1;	} 	while (fgets(buffer, sizeof(buffer), file) && found != 2) {		if (sscanf(buffer, "MemFree: %lu kB", memfree) == 1 ||		    sscanf(buffer, "Hugepagesize: %lu kB", hugepagesize) == 1)			found++;	} 	if (ferror(file))		ksft_print_msg("Failed to read /proc/meminfo: %s\n",			       strerror(errno));	else if (found != 2)		ksft_print_msg("Failed to parse /proc/meminfo\n");	else		ret = 0; 	fclose(file);	return ret;} int prereq(void){	char allowed;	int fd; 	fd = open("/proc/sys/vm/compact_unevictable_allowed",		  O_RDONLY | O_NONBLOCK);	if (fd < 0) {		ksft_print_msg("Failed to open /proc/sys/vm/compact_unevictable_allowed: %s\n",			       strerror(errno));		return -1;	} 	if (read(fd, &allowed, sizeof(char)) != sizeof(char)) {		ksft_print_msg("Failed to read from /proc/sys/vm/compact_unevictable_allowed: %s\n",			       strerror(errno));		close(fd);		return -1;	} 	close(fd);	if (allowed == '1')		return 0; 	ksft_print_msg("Compaction isn't allowed\n");	return -1;} int check_compaction(unsigned long mem_free, unsigned long hugepage_size){	unsigned long nr_hugepages;	int compaction_index = 0;	int ret = -1; 	/* We want to test with 80% of available memory. Else, OOM killer comes	   in to play */	mem_free = mem_free * 0.8; 	/*	 * Request huge pages for about half of the free memory. The Kernel	 * will allocate as much as it can, and we expect it will get at least 1/3	 */	nr_hugepages = mem_free / hugepage_size / 2;	hugetlb_set_nr_default_pages(nr_hugepages); 	/* We should have been able to request at least 1/3 rd of the memory in	   huge pages */	nr_hugepages = hugetlb_nr_default_pages();	if (!nr_hugepages) {		ksft_print_msg("ERROR: No memory is available as huge pages\n");		goto out;	}	compaction_index = mem_free/(nr_hugepages * hugepage_size); 	ksft_print_msg("Number of huge pages allocated = %lu\n", nr_hugepages); 	if (compaction_index > 3) {		ksft_print_msg("ERROR: Less than 1/%d of memory is available\n"			       "as huge pages\n", compaction_index);		goto out;	} 	ret = 0;  out:	ksft_test_result(ret == 0, "check_compaction\n");	return ret;} int main(int argc, char **argv){	struct rlimit lim;	struct map_list *list = NULL, *entry;	size_t page_size, i;	void *map = NULL;	unsigned long mem_free = 0;	unsigned long hugepage_size = 0;	long mem_fragmentable_MB = 0; 	ksft_print_header(); 	if (prereq() || geteuid())		ksft_exit_skip("Prerequisites unsatisfied\n"); 	/* Start the test without hugepages reducing mem_free */	if (!hugetlb_setup_default_exact(0))		ksft_exit_skip("Could not reset nr_hugepages\n"); 	ksft_set_plan(1); 	lim.rlim_cur = RLIM_INFINITY;	lim.rlim_max = RLIM_INFINITY;	if (setrlimit(RLIMIT_MEMLOCK, &lim))		ksft_exit_fail_msg("Failed to set rlimit: %s\n", strerror(errno)); 	page_size = getpagesize(); 	if (read_memory_info(&mem_free, &hugepage_size) != 0)		ksft_exit_fail_msg("Failed to get meminfo\n"); 	mem_fragmentable_MB = mem_free * 0.8 / 1024; 	while (mem_fragmentable_MB > 0) {		map = mmap(NULL, MAP_SIZE, PROT_READ | PROT_WRITE,			   MAP_ANONYMOUS | MAP_PRIVATE | MAP_LOCKED, -1, 0);		if (map == MAP_FAILED)			break; 		entry = malloc(sizeof(struct map_list));		if (!entry) {			munmap(map, MAP_SIZE);			break;		}		entry->map = map;		entry->next = list;		list = entry; 		/* Write something (in this case the address of the map) to		 * ensure that KSM can't merge the mapped pages		 */		for (i = 0; i < MAP_SIZE; i += page_size)			*(unsigned long *)(map + i) = (unsigned long)map + i; 		mem_fragmentable_MB -= MAP_SIZE_MB;	} 	/* Unmap every other entry in the list to create fragmentation with	 * locked pages before invoking check_compaction().	 */	for (entry = list; entry != NULL; entry = entry->next) {		munmap(entry->map, MAP_SIZE);		if (!entry->next)			break;		entry = entry->next;	} 	if (check_compaction(mem_free, hugepage_size) == 0)		ksft_exit_pass(); 	ksft_exit_fail();}