1f08c3bdfSopenharmony_ci// SPDX-License-Identifier: LGPL-2.1-or-later
2f08c3bdfSopenharmony_ci/*
3f08c3bdfSopenharmony_ci * Copyright (C) 2005-2006 David Gibson & Adam Litke, IBM Corporation.
4f08c3bdfSopenharmony_ci * Author: David Gibson & Adam Litke
5f08c3bdfSopenharmony_ci */
6f08c3bdfSopenharmony_ci
7f08c3bdfSopenharmony_ci/*\
8f08c3bdfSopenharmony_ci * [Description]
9f08c3bdfSopenharmony_ci *
10f08c3bdfSopenharmony_ci * Some kernel versions after hugepage demand allocation was added used a
11f08c3bdfSopenharmony_ci * dubious heuristic to check if there was enough hugepage space available
12f08c3bdfSopenharmony_ci * for a given mapping.  The number of not-already-instantiated pages in
13f08c3bdfSopenharmony_ci * the mapping was compared against the total hugepage free pool. It was
14f08c3bdfSopenharmony_ci * very easy to confuse this heuristic into overcommitting by allocating
15f08c3bdfSopenharmony_ci * hugepage memory in chunks, each less than the total available pool size
16f08c3bdfSopenharmony_ci * but together more than available.  This would generally lead to OOM
17f08c3bdfSopenharmony_ci * SIGKILLs of one process or another when it tried to instantiate pages
18f08c3bdfSopenharmony_ci * beyond the available pool.
19f08c3bdfSopenharmony_ci */
20f08c3bdfSopenharmony_ci
21f08c3bdfSopenharmony_ci#define _GNU_SOURCE
22f08c3bdfSopenharmony_ci#include <stdio.h>
23f08c3bdfSopenharmony_ci#include <stdlib.h>
24f08c3bdfSopenharmony_ci#include <sys/mount.h>
25f08c3bdfSopenharmony_ci#include <limits.h>
26f08c3bdfSopenharmony_ci#include <sys/param.h>
27f08c3bdfSopenharmony_ci#include <sys/types.h>
28f08c3bdfSopenharmony_ci#include <sys/wait.h>
29f08c3bdfSopenharmony_ci#include <signal.h>
30f08c3bdfSopenharmony_ci
31f08c3bdfSopenharmony_ci#include "hugetlb.h"
32f08c3bdfSopenharmony_ci
33f08c3bdfSopenharmony_ci#define MNTPOINT "hugetlbfs/"
34f08c3bdfSopenharmony_ci#define WITH_OVERCOMMIT 0
35f08c3bdfSopenharmony_ci#define WITHOUT_OVERCOMMIT 1
36f08c3bdfSopenharmony_ci
37f08c3bdfSopenharmony_cistatic long hpage_size;
38f08c3bdfSopenharmony_cistatic int huge_fd = -1;
39f08c3bdfSopenharmony_ci
40f08c3bdfSopenharmony_cistatic void test_chunk_overcommit(void)
41f08c3bdfSopenharmony_ci{
42f08c3bdfSopenharmony_ci	unsigned long totpages, chunk1, chunk2;
43f08c3bdfSopenharmony_ci	void *p, *q;
44f08c3bdfSopenharmony_ci	pid_t child;
45f08c3bdfSopenharmony_ci	int status;
46f08c3bdfSopenharmony_ci
47f08c3bdfSopenharmony_ci	totpages = SAFE_READ_MEMINFO(MEMINFO_HPAGE_FREE);
48f08c3bdfSopenharmony_ci
49f08c3bdfSopenharmony_ci	chunk1 = (totpages / 2) + 1;
50f08c3bdfSopenharmony_ci	chunk2 = totpages - chunk1 + 1;
51f08c3bdfSopenharmony_ci
52f08c3bdfSopenharmony_ci	tst_res(TINFO, "Free: %ld hugepages available: "
53f08c3bdfSopenharmony_ci	       "chunk1=%ld chunk2=%ld", totpages, chunk1, chunk2);
54f08c3bdfSopenharmony_ci
55f08c3bdfSopenharmony_ci	p = SAFE_MMAP(NULL, chunk1*hpage_size, PROT_READ|PROT_WRITE, MAP_SHARED,
56f08c3bdfSopenharmony_ci		 huge_fd, 0);
57f08c3bdfSopenharmony_ci
58f08c3bdfSopenharmony_ci	q = mmap(NULL, chunk2*hpage_size, PROT_READ|PROT_WRITE, MAP_SHARED,
59f08c3bdfSopenharmony_ci		 huge_fd, chunk1*hpage_size);
60f08c3bdfSopenharmony_ci	if (q == MAP_FAILED) {
61f08c3bdfSopenharmony_ci		if (errno != ENOMEM) {
62f08c3bdfSopenharmony_ci			tst_res(TFAIL | TERRNO, "mmap() chunk2");
63f08c3bdfSopenharmony_ci			goto cleanup1;
64f08c3bdfSopenharmony_ci		} else {
65f08c3bdfSopenharmony_ci			tst_res(TPASS, "Successful without overcommit pages");
66f08c3bdfSopenharmony_ci			goto cleanup1;
67f08c3bdfSopenharmony_ci		}
68f08c3bdfSopenharmony_ci	}
69f08c3bdfSopenharmony_ci
70f08c3bdfSopenharmony_ci	tst_res(TINFO, "Looks like we've overcommitted, testing...");
71f08c3bdfSopenharmony_ci	/* Looks like we're overcommited, but we need to confirm that
72f08c3bdfSopenharmony_ci	 * this is bad.  We touch it all in a child process because an
73f08c3bdfSopenharmony_ci	 * overcommit will generally lead to a SIGKILL which we can't
74f08c3bdfSopenharmony_ci	 * handle, of course.
75f08c3bdfSopenharmony_ci	 */
76f08c3bdfSopenharmony_ci	child = SAFE_FORK();
77f08c3bdfSopenharmony_ci
78f08c3bdfSopenharmony_ci	if (child == 0) {
79f08c3bdfSopenharmony_ci		memset(p, 0, chunk1*hpage_size);
80f08c3bdfSopenharmony_ci		memset(q, 0, chunk2*hpage_size);
81f08c3bdfSopenharmony_ci		exit(0);
82f08c3bdfSopenharmony_ci	}
83f08c3bdfSopenharmony_ci
84f08c3bdfSopenharmony_ci	SAFE_WAITPID(child, &status, 0);
85f08c3bdfSopenharmony_ci
86f08c3bdfSopenharmony_ci	if (WIFSIGNALED(status)) {
87f08c3bdfSopenharmony_ci		tst_res(TFAIL, "Killed by signal '%s' due to overcommit",
88f08c3bdfSopenharmony_ci		     tst_strsig(WTERMSIG(status)));
89f08c3bdfSopenharmony_ci		goto cleanup2;
90f08c3bdfSopenharmony_ci	}
91f08c3bdfSopenharmony_ci
92f08c3bdfSopenharmony_ci	tst_res(TPASS, "Successful with overcommit pages");
93f08c3bdfSopenharmony_ci
94f08c3bdfSopenharmony_cicleanup2:
95f08c3bdfSopenharmony_ci	SAFE_MUNMAP(q, chunk2*hpage_size);
96f08c3bdfSopenharmony_ci
97f08c3bdfSopenharmony_cicleanup1:
98f08c3bdfSopenharmony_ci	SAFE_MUNMAP(p, chunk1*hpage_size);
99f08c3bdfSopenharmony_ci	SAFE_FTRUNCATE(huge_fd, 0);
100f08c3bdfSopenharmony_ci}
101f08c3bdfSopenharmony_ci
102f08c3bdfSopenharmony_cistatic void run_test(unsigned int test_type)
103f08c3bdfSopenharmony_ci{
104f08c3bdfSopenharmony_ci	switch (test_type) {
105f08c3bdfSopenharmony_ci	case WITHOUT_OVERCOMMIT:
106f08c3bdfSopenharmony_ci		tst_res(TINFO, "Without overcommit testing...");
107f08c3bdfSopenharmony_ci		SAFE_FILE_PRINTF(PATH_OC_HPAGES, "%d", 0);
108f08c3bdfSopenharmony_ci		break;
109f08c3bdfSopenharmony_ci	case WITH_OVERCOMMIT:
110f08c3bdfSopenharmony_ci		tst_res(TINFO, "With overcommit testing...");
111f08c3bdfSopenharmony_ci		SAFE_FILE_PRINTF(PATH_OC_HPAGES, "%d", 2);
112f08c3bdfSopenharmony_ci		break;
113f08c3bdfSopenharmony_ci	}
114f08c3bdfSopenharmony_ci	test_chunk_overcommit();
115f08c3bdfSopenharmony_ci}
116f08c3bdfSopenharmony_ci
117f08c3bdfSopenharmony_cistatic void setup(void)
118f08c3bdfSopenharmony_ci{
119f08c3bdfSopenharmony_ci	hpage_size = SAFE_READ_MEMINFO(MEMINFO_HPAGE_SIZE)*1024;
120f08c3bdfSopenharmony_ci	huge_fd = tst_creat_unlinked(MNTPOINT, 0);
121f08c3bdfSopenharmony_ci}
122f08c3bdfSopenharmony_ci
123f08c3bdfSopenharmony_cistatic void cleanup(void)
124f08c3bdfSopenharmony_ci{
125f08c3bdfSopenharmony_ci	SAFE_CLOSE(huge_fd);
126f08c3bdfSopenharmony_ci}
127f08c3bdfSopenharmony_ci
128f08c3bdfSopenharmony_cistatic struct tst_test test = {
129f08c3bdfSopenharmony_ci	.needs_root = 1,
130f08c3bdfSopenharmony_ci	.mntpoint = MNTPOINT,
131f08c3bdfSopenharmony_ci	.needs_hugetlbfs = 1,
132f08c3bdfSopenharmony_ci	.forks_child = 1,
133f08c3bdfSopenharmony_ci	.save_restore = (const struct tst_path_val[]) {
134f08c3bdfSopenharmony_ci		{PATH_OC_HPAGES, NULL, TST_SR_TCONF},
135f08c3bdfSopenharmony_ci		{}
136f08c3bdfSopenharmony_ci	},
137f08c3bdfSopenharmony_ci	.tcnt = 2,
138f08c3bdfSopenharmony_ci	.setup = setup,
139f08c3bdfSopenharmony_ci	.cleanup = cleanup,
140f08c3bdfSopenharmony_ci	.test = run_test,
141f08c3bdfSopenharmony_ci	.hugepages = {3, TST_NEEDS},
142f08c3bdfSopenharmony_ci};
143