12966Swollman// SPDX-License-Identifier: GPL-2.0
250477Speter/*
31590Srgrimes *
4274837Sbapt * A test for the patch "Allow compaction of unevictable pages".
5156813Sru * With this patch we should be able to allocate at least 1/4
64699Sjkh * of RAM in huge pages. Without the patch much less is
734706Sbde * allocated.
8213391Sgordon */
91930Swollman
101930Swollman#include <stdio.h>
11106717Smarcel#include <stdlib.h>
12286592Semaste#include <sys/mman.h>
13100200Swollman#include <sys/resource.h>
1496630Stjr#include <fcntl.h>
1591706Sobrien#include <errno.h>
1638653Sgpalmer#include <unistd.h>
1738653Sgpalmer#include <string.h>
1857013Sobrien
19299529Smm#include "../kselftest.h"
20148771Scperciva
2178562Sobrien#define MAP_SIZE_MB	100
22108667Sobrien#define MAP_SIZE	(MAP_SIZE_MB * 1024 * 1024)
2338653Sgpalmer
2438653Sgpalmerstruct map_list {
2538653Sgpalmer	void *map;
2638653Sgpalmer	struct map_list *next;
2738653Sgpalmer};
2838653Sgpalmer
29291115Sbaptint read_memory_info(unsigned long *memfree, unsigned long *hugepagesize)
3038653Sgpalmer{
3138653Sgpalmer	char  buffer[256] = {0};
3238653Sgpalmer	char *cmd = "cat /proc/meminfo | grep -i memfree | grep -o '[0-9]*'";
3338653Sgpalmer	FILE *cmdfile = popen(cmd, "r");
34176733Sjeff
3595926Stjr	if (!(fgets(buffer, sizeof(buffer), cmdfile))) {
36229997Sken		ksft_print_msg("Failed to read meminfo: %s\n", strerror(errno));
3738653Sgpalmer		return -1;
3838653Sgpalmer	}
3938653Sgpalmer
40108439Sobrien	pclose(cmdfile);
41109314Sobrien
4240826Sjoerg	*memfree = atoll(buffer);
4338653Sgpalmer	cmd = "cat /proc/meminfo | grep -i hugepagesize | grep -o '[0-9]*'";
44332946Sbenno	cmdfile = popen(cmd, "r");
4538653Sgpalmer
4638653Sgpalmer	if (!(fgets(buffer, sizeof(buffer), cmdfile))) {
4738653Sgpalmer		ksft_print_msg("Failed to read meminfo: %s\n", strerror(errno));
4838653Sgpalmer		return -1;
49203711Sdelphij	}
5038653Sgpalmer
5141036Sdima	pclose(cmdfile);
5263499Sps	*hugepagesize = atoll(buffer);
53103303Speter
5438653Sgpalmer	return 0;
55316098Svangyzen}
56101629Sjake
57158287Smaximint prereq(void)
5838653Sgpalmer{
59222273Sobrien	char allowed;
60166255Sdelphij	int fd;
6138653Sgpalmer
6238653Sgpalmer	fd = open("/proc/sys/vm/compact_unevictable_allowed",
6338653Sgpalmer		  O_RDONLY | O_NONBLOCK);
64285890Sbapt	if (fd < 0) {
6538653Sgpalmer		ksft_print_msg("Failed to open /proc/sys/vm/compact_unevictable_allowed: %s\n",
6638653Sgpalmer			       strerror(errno));
6738653Sgpalmer		return -1;
6838653Sgpalmer	}
6938653Sgpalmer
7038653Sgpalmer	if (read(fd, &allowed, sizeof(char)) != sizeof(char)) {
7138653Sgpalmer		ksft_print_msg("Failed to read from /proc/sys/vm/compact_unevictable_allowed: %s\n",
7238653Sgpalmer			       strerror(errno));
7393619Sjake		close(fd);
7438653Sgpalmer		return -1;
7538653Sgpalmer	}
7639614Sbde
7738653Sgpalmer	close(fd);
7860789Sps	if (allowed == '1')
7960789Sps		return 0;
8060789Sps
8138653Sgpalmer	ksft_print_msg("Compaction isn't allowed\n");
82116677Sphantom	return -1;
83290494Sbapt}
8438653Sgpalmer
8538653Sgpalmerint check_compaction(unsigned long mem_free, unsigned int hugepage_size)
8638653Sgpalmer{
8738653Sgpalmer	int fd, ret = -1;
88126701Sdes	int compaction_index = 0;
8938653Sgpalmer	char initial_nr_hugepages[10] = {0};
9038653Sgpalmer	char nr_hugepages[10] = {0};
9138653Sgpalmer
9238653Sgpalmer	/* We want to test with 80% of available memory. Else, OOM killer comes
93207842Smm	   in to play */
9438653Sgpalmer	mem_free = mem_free * 0.8;
95241774Suqs
9638653Sgpalmer	fd = open("/proc/sys/vm/nr_hugepages", O_RDWR | O_NONBLOCK);
97174814Sru	if (fd < 0) {
98174810Sphk		ksft_print_msg("Failed to open /proc/sys/vm/nr_hugepages: %s\n",
9938653Sgpalmer			       strerror(errno));
10038653Sgpalmer		ret = -1;
101263919Smarcel		goto out;
102291115Sbapt	}
10338653Sgpalmer
104146107Sfjoe	if (read(fd, initial_nr_hugepages, sizeof(initial_nr_hugepages)) <= 0) {
10539914Sdfr		ksft_print_msg("Failed to read from /proc/sys/vm/nr_hugepages: %s\n",
10638653Sgpalmer			       strerror(errno));
10738653Sgpalmer		goto close_fd;
10897365Stjr	}
10938653Sgpalmer
11038653Sgpalmer	/* Start with the initial condition of 0 huge pages*/
11176273Sbrian	if (write(fd, "0", sizeof(char)) != sizeof(char)) {
112285387Sadrian		ksft_print_msg("Failed to write 0 to /proc/sys/vm/nr_hugepages: %s\n",
11338653Sgpalmer			       strerror(errno));
11438653Sgpalmer		goto close_fd;
11538653Sgpalmer	}
11638653Sgpalmer
11738653Sgpalmer	lseek(fd, 0, SEEK_SET);
11838653Sgpalmer
11938653Sgpalmer	/* Request a large number of huge pages. The Kernel will allocate
120246074Sgabor	   as much as it can */
12197096Stjr	if (write(fd, "100000", (6*sizeof(char))) != (6*sizeof(char))) {
122192398Sgnn		ksft_print_msg("Failed to write 100000 to /proc/sys/vm/nr_hugepages: %s\n",
12338653Sgpalmer			       strerror(errno));
12438653Sgpalmer		goto close_fd;
12538653Sgpalmer	}
126306487Skib
127174200Srwatson	lseek(fd, 0, SEEK_SET);
128275043Sbapt
129220166Strasz	if (read(fd, nr_hugepages, sizeof(nr_hugepages)) <= 0) {
13038653Sgpalmer		ksft_print_msg("Failed to re-read from /proc/sys/vm/nr_hugepages: %s\n",
131297897Scem			       strerror(errno));
13238653Sgpalmer		goto close_fd;
133194267Sed	}
13438653Sgpalmer
13538653Sgpalmer	/* We should have been able to request at least 1/3 rd of the memory in
13638653Sgpalmer	   huge pages */
137330769Semaste	compaction_index = mem_free/(atoi(nr_hugepages) * hugepage_size);
13838653Sgpalmer
13938653Sgpalmer	lseek(fd, 0, SEEK_SET);
140330769Semaste
14138653Sgpalmer	if (write(fd, initial_nr_hugepages, strlen(initial_nr_hugepages))
142298823Sbapt	    != strlen(initial_nr_hugepages)) {
14338653Sgpalmer		ksft_print_msg("Failed to write value to /proc/sys/vm/nr_hugepages: %s\n",
144267577Sgavin			       strerror(errno));
145204103Sdelphij		goto close_fd;
14638653Sgpalmer	}
14738653Sgpalmer
14845701Sdes	ksft_print_msg("Number of huge pages allocated = %d\n",
149282318Sbapt		       atoi(nr_hugepages));
150235268Sgabor
15138653Sgpalmer	if (compaction_index > 3) {
15297955Sdougb		ksft_print_msg("ERROR: Less that 1/%d of memory is available\n"
153234772Sjlh			       "as huge pages\n", compaction_index);
154327010Semaste		goto close_fd;
15538653Sgpalmer	}
15641035Sdima
15797034Stjr	ret = 0;
15838653Sgpalmer
159127947Skientzle close_fd:
16038653Sgpalmer	close(fd);
16138653Sgpalmer out:
16238653Sgpalmer	ksft_test_result(ret == 0, "check_compaction\n");
163274837Sbapt	return ret;
16488277Smarkm}
16538653Sgpalmer
16638653Sgpalmer
16738653Sgpalmerint main(int argc, char **argv)
16838653Sgpalmer{
16938653Sgpalmer	struct rlimit lim;
17063437Ssheldonh	struct map_list *list = NULL, *entry;
17138653Sgpalmer	size_t page_size, i;
17238653Sgpalmer	void *map = NULL;
17338653Sgpalmer	unsigned long mem_free = 0;
17438653Sgpalmer	unsigned long hugepage_size = 0;
17538653Sgpalmer	long mem_fragmentable_MB = 0;
17638653Sgpalmer
177200068Srdivacky	ksft_print_header();
17838653Sgpalmer
17938653Sgpalmer	if (prereq() || geteuid())
18038653Sgpalmer		return ksft_exit_skip("Prerequisites unsatisfied\n");
18138653Sgpalmer
18238653Sgpalmer	ksft_set_plan(1);
18341062Sbde
18438653Sgpalmer	lim.rlim_cur = RLIM_INFINITY;
18538653Sgpalmer	lim.rlim_max = RLIM_INFINITY;
18638653Sgpalmer	if (setrlimit(RLIMIT_MEMLOCK, &lim))
18738653Sgpalmer		ksft_exit_fail_msg("Failed to set rlimit: %s\n", strerror(errno));
18899379Sjohan
18938653Sgpalmer	page_size = getpagesize();
19038653Sgpalmer
19138653Sgpalmer	if (read_memory_info(&mem_free, &hugepage_size) != 0)
19238653Sgpalmer		ksft_exit_fail_msg("Failed to get meminfo\n");
19338653Sgpalmer
194273568Smarcel	mem_fragmentable_MB = mem_free * 0.8 / 1024;
195207842Smm
196207842Smm	while (mem_fragmentable_MB > 0) {
197286590Semaste		map = mmap(NULL, MAP_SIZE, PROT_READ | PROT_WRITE,
1981590Srgrimes			   MAP_ANONYMOUS | MAP_PRIVATE | MAP_LOCKED, -1, 0);
199183242Ssam		if (map == MAP_FAILED)
200183242Ssam			break;
201288904Simp
202288904Simp		entry = malloc(sizeof(struct map_list));
203288904Simp		if (!entry) {
204288904Simp			munmap(map, MAP_SIZE);
205288904Simp			break;
206288904Simp		}
207319192Sngie		entry->map = map;
208288904Simp		entry->next = list;
209288904Simp		list = entry;
210288904Simp
211288904Simp		/* Write something (in this case the address of the map) to
212288904Simp		 * ensure that KSM can't merge the mapped pages
213288904Simp		 */
214288904Simp		for (i = 0; i < MAP_SIZE; i += page_size)
215288904Simp			*(unsigned long *)(map + i) = (unsigned long)map + i;
216288904Simp
217288904Simp		mem_fragmentable_MB -= MAP_SIZE_MB;
218288904Simp	}
219288904Simp
220288904Simp	for (entry = list; entry != NULL; entry = entry->next) {
221331465Sian		munmap(entry->map, MAP_SIZE);
222331465Sian		if (!entry->next)
223331465Sian			break;
224289839Sbdrewery		entry = entry->next;
225289935Stheraven	}
226289839Sbdrewery
227289839Sbdrewery	if (check_compaction(mem_free, hugepage_size) == 0)
228289935Stheraven		return ksft_exit_pass();
229288904Simp
230288904Simp	return ksft_exit_fail();
231288904Simp}
232288904Simp