1f08c3bdfSopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
2f08c3bdfSopenharmony_ci/*
3f08c3bdfSopenharmony_ci *  Copyright (c) International Business Machines Corp., 2001
4f08c3bdfSopenharmony_ci *  Copyright (c) Linux Test Project., 2019
5f08c3bdfSopenharmony_ci *
6f08c3bdfSopenharmony_ci *  DESCRIPTION:
7f08c3bdfSopenharmony_ci *
8f08c3bdfSopenharmony_ci *  mtest01 mallocs memory <chunksize> at a time until malloc fails.
9f08c3bdfSopenharmony_ci *
10f08c3bdfSopenharmony_ci *  Parent process starts several child processes (each child process is
11f08c3bdfSopenharmony_ci *  tasked with allocating some amount of memory), it waits until all child
12f08c3bdfSopenharmony_ci *  processes send SIGRTMIN signal and resumes all children by sending the
13f08c3bdfSopenharmony_ci *  SIGCONT signal.
14f08c3bdfSopenharmony_ci *
15f08c3bdfSopenharmony_ci *  Child process allocates certain amount of memory and fills it with some
16f08c3bdfSopenharmony_ci *  data (the '-w' option) so the pages are actually allocated when the desired
17f08c3bdfSopenharmony_ci *  amount of memory is allocated then it sends SIGRTMIN signal to the parent
18f08c3bdfSopenharmony_ci *  process, it pauses itself by raise SIGSTOP until get parent SIGCONT signal
19f08c3bdfSopenharmony_ci *  to continue and exit.
20f08c3bdfSopenharmony_ci */
21f08c3bdfSopenharmony_ci
22f08c3bdfSopenharmony_ci#include <sys/types.h>
23f08c3bdfSopenharmony_ci#include <sys/sysinfo.h>
24f08c3bdfSopenharmony_ci#include <sys/wait.h>
25f08c3bdfSopenharmony_ci#include <limits.h>
26f08c3bdfSopenharmony_ci#include <signal.h>
27f08c3bdfSopenharmony_ci#include <stdio.h>
28f08c3bdfSopenharmony_ci#include <stdlib.h>
29f08c3bdfSopenharmony_ci#include <unistd.h>
30f08c3bdfSopenharmony_ci
31f08c3bdfSopenharmony_ci#include "lapi/abisize.h"
32f08c3bdfSopenharmony_ci#include "tst_test.h"
33f08c3bdfSopenharmony_ci
34f08c3bdfSopenharmony_ci#define FIVE_HUNDRED_MB         (500ULL*1024*1024)
35f08c3bdfSopenharmony_ci
36f08c3bdfSopenharmony_ci#if defined(__s390__) || defined(__s390x__)
37f08c3bdfSopenharmony_ci#define ALLOC_THRESHOLD		FIVE_HUNDRED_MB
38f08c3bdfSopenharmony_ci#elif defined(TST_ABI32)
39f08c3bdfSopenharmony_ci#define ALLOC_THRESHOLD		(2*FIVE_HUNDRED_MB)
40f08c3bdfSopenharmony_ci#elif defined(TST_ABI64)
41f08c3bdfSopenharmony_ci#define ALLOC_THRESHOLD		(6*FIVE_HUNDRED_MB)
42f08c3bdfSopenharmony_ci#endif
43f08c3bdfSopenharmony_ci
44f08c3bdfSopenharmony_cistatic pid_t *pid_list;
45f08c3bdfSopenharmony_cistatic sig_atomic_t children_done;
46f08c3bdfSopenharmony_cistatic int max_pids;
47f08c3bdfSopenharmony_cistatic unsigned long long alloc_maxbytes;
48f08c3bdfSopenharmony_ci
49f08c3bdfSopenharmony_cistatic int chunksize = 1024*1024;
50f08c3bdfSopenharmony_cistatic int maxpercent = 20;
51f08c3bdfSopenharmony_cistatic long maxbytes = 0;
52f08c3bdfSopenharmony_cistatic char *dowrite;
53f08c3bdfSopenharmony_cistatic char *verbose;
54f08c3bdfSopenharmony_ci
55f08c3bdfSopenharmony_cistatic char *opt_chunksize, *opt_maxbytes, *opt_maxpercent;
56f08c3bdfSopenharmony_ci
57f08c3bdfSopenharmony_cistatic void parse_mtest_options(char *str_chunksize, int *chunksize,
58f08c3bdfSopenharmony_ci		char *str_maxbytes, long *maxbytes,
59f08c3bdfSopenharmony_ci		char *str_maxpercent, int *maxpercent)
60f08c3bdfSopenharmony_ci{
61f08c3bdfSopenharmony_ci	if (str_chunksize)
62f08c3bdfSopenharmony_ci		if (tst_parse_int(str_chunksize, chunksize, 1, INT_MAX))
63f08c3bdfSopenharmony_ci			tst_brk(TBROK, "Invalid chunksize '%s'", str_chunksize);
64f08c3bdfSopenharmony_ci
65f08c3bdfSopenharmony_ci	if (str_maxbytes) {
66f08c3bdfSopenharmony_ci		if (tst_parse_long(str_maxbytes, maxbytes, 1, LONG_MAX)) {
67f08c3bdfSopenharmony_ci			tst_brk(TBROK, "Invalid maxbytes '%s'", str_maxbytes);
68f08c3bdfSopenharmony_ci		} else if (str_maxpercent) {
69f08c3bdfSopenharmony_ci			tst_brk(TBROK, "ERROR: -b option cannot be used with -p "
70f08c3bdfSopenharmony_ci					"option at the same time");
71f08c3bdfSopenharmony_ci		}
72f08c3bdfSopenharmony_ci		alloc_maxbytes = (unsigned long long)maxbytes;
73f08c3bdfSopenharmony_ci	}
74f08c3bdfSopenharmony_ci
75f08c3bdfSopenharmony_ci	if (str_maxpercent) {
76f08c3bdfSopenharmony_ci		if (tst_parse_int(str_maxpercent, maxpercent, 1, 99)) {
77f08c3bdfSopenharmony_ci			tst_brk(TBROK, "Invalid maxpercent '%s'", str_maxpercent);
78f08c3bdfSopenharmony_ci		} else if (str_maxbytes) {
79f08c3bdfSopenharmony_ci			tst_brk(TBROK, "ERROR: -p option cannot be used with -b "
80f08c3bdfSopenharmony_ci					"option at the same time");
81f08c3bdfSopenharmony_ci		}
82f08c3bdfSopenharmony_ci	}
83f08c3bdfSopenharmony_ci}
84f08c3bdfSopenharmony_ci
85f08c3bdfSopenharmony_cistatic void handler(int sig LTP_ATTRIBUTE_UNUSED)
86f08c3bdfSopenharmony_ci{
87f08c3bdfSopenharmony_ci        children_done++;
88f08c3bdfSopenharmony_ci}
89f08c3bdfSopenharmony_ci
90f08c3bdfSopenharmony_cistatic void do_write_mem(char *mem, int chunksize)
91f08c3bdfSopenharmony_ci{
92f08c3bdfSopenharmony_ci	int i, pagesz = getpagesize();
93f08c3bdfSopenharmony_ci
94f08c3bdfSopenharmony_ci	for (i = 0; i < chunksize; i += pagesz)
95f08c3bdfSopenharmony_ci		*(mem + i) = 'a';
96f08c3bdfSopenharmony_ci}
97f08c3bdfSopenharmony_ci
98f08c3bdfSopenharmony_cistatic void setup(void)
99f08c3bdfSopenharmony_ci{
100f08c3bdfSopenharmony_ci	struct sysinfo sstats;
101f08c3bdfSopenharmony_ci	unsigned long long total_free;
102f08c3bdfSopenharmony_ci
103f08c3bdfSopenharmony_ci	struct sigaction act;
104f08c3bdfSopenharmony_ci	act.sa_handler = handler;
105f08c3bdfSopenharmony_ci	act.sa_flags = 0;
106f08c3bdfSopenharmony_ci	sigemptyset(&act.sa_mask);
107f08c3bdfSopenharmony_ci	sigaction(SIGRTMIN, &act, 0);
108f08c3bdfSopenharmony_ci
109f08c3bdfSopenharmony_ci	parse_mtest_options(opt_chunksize, &chunksize,
110f08c3bdfSopenharmony_ci			opt_maxbytes, &maxbytes,
111f08c3bdfSopenharmony_ci			opt_maxpercent, &maxpercent);
112f08c3bdfSopenharmony_ci	sysinfo(&sstats);
113f08c3bdfSopenharmony_ci	total_free = sstats.freeram;
114f08c3bdfSopenharmony_ci
115f08c3bdfSopenharmony_ci	max_pids = total_free * sstats.mem_unit
116f08c3bdfSopenharmony_ci		/ (unsigned long)ALLOC_THRESHOLD + 10;
117f08c3bdfSopenharmony_ci	pid_list = SAFE_MALLOC(max_pids * sizeof(pid_t));
118f08c3bdfSopenharmony_ci
119f08c3bdfSopenharmony_ci	if (!alloc_maxbytes) {
120f08c3bdfSopenharmony_ci		/* set alloc_maxbytes to the extra amount we want to allocate */
121f08c3bdfSopenharmony_ci		alloc_maxbytes = ((float)maxpercent / 100.00)
122f08c3bdfSopenharmony_ci			* (sstats.mem_unit * total_free);
123f08c3bdfSopenharmony_ci		tst_res(TINFO, "Filling up %d%% of free ram which is %llu kbytes",
124f08c3bdfSopenharmony_ci			 maxpercent, alloc_maxbytes / 1024);
125f08c3bdfSopenharmony_ci	}
126f08c3bdfSopenharmony_ci}
127f08c3bdfSopenharmony_ci
128f08c3bdfSopenharmony_cistatic void cleanup(void)
129f08c3bdfSopenharmony_ci{
130f08c3bdfSopenharmony_ci	if(pid_list)
131f08c3bdfSopenharmony_ci		free(pid_list);
132f08c3bdfSopenharmony_ci}
133f08c3bdfSopenharmony_ci
134f08c3bdfSopenharmony_cistatic void child_loop_alloc(unsigned long long alloc_bytes)
135f08c3bdfSopenharmony_ci{
136f08c3bdfSopenharmony_ci	unsigned long bytecount = 0;
137f08c3bdfSopenharmony_ci	char *mem;
138f08c3bdfSopenharmony_ci	int runtime_rem;
139f08c3bdfSopenharmony_ci
140f08c3bdfSopenharmony_ci	tst_res(TINFO, "... child %d starting", getpid());
141f08c3bdfSopenharmony_ci
142f08c3bdfSopenharmony_ci	while (1) {
143f08c3bdfSopenharmony_ci		mem = SAFE_MALLOC(chunksize);
144f08c3bdfSopenharmony_ci		if (dowrite)
145f08c3bdfSopenharmony_ci			do_write_mem(mem, chunksize);
146f08c3bdfSopenharmony_ci
147f08c3bdfSopenharmony_ci		if (verbose)
148f08c3bdfSopenharmony_ci			tst_res(TINFO,
149f08c3bdfSopenharmony_ci				"child %d allocated %lu bytes chunksize is %d",
150f08c3bdfSopenharmony_ci				getpid(), bytecount, chunksize);
151f08c3bdfSopenharmony_ci		bytecount += chunksize;
152f08c3bdfSopenharmony_ci		if (bytecount >= alloc_bytes)
153f08c3bdfSopenharmony_ci			break;
154f08c3bdfSopenharmony_ci	}
155f08c3bdfSopenharmony_ci
156f08c3bdfSopenharmony_ci	runtime_rem = tst_remaining_runtime();
157f08c3bdfSopenharmony_ci
158f08c3bdfSopenharmony_ci	if (dowrite)
159f08c3bdfSopenharmony_ci		tst_res(TINFO, "... [t=%d] %lu bytes allocated and used in child %d",
160f08c3bdfSopenharmony_ci				runtime_rem, bytecount, getpid());
161f08c3bdfSopenharmony_ci	else
162f08c3bdfSopenharmony_ci		tst_res(TINFO, "... [t=%d] %lu bytes allocated only in child %d",
163f08c3bdfSopenharmony_ci				runtime_rem, bytecount, getpid());
164f08c3bdfSopenharmony_ci
165f08c3bdfSopenharmony_ci	kill(getppid(), SIGRTMIN);
166f08c3bdfSopenharmony_ci	raise(SIGSTOP);
167f08c3bdfSopenharmony_ci	exit(0);
168f08c3bdfSopenharmony_ci}
169f08c3bdfSopenharmony_ci
170f08c3bdfSopenharmony_cistatic void mem_test(void)
171f08c3bdfSopenharmony_ci{
172f08c3bdfSopenharmony_ci	pid_t pid;
173f08c3bdfSopenharmony_ci	int i = 0, pid_cntr = 0;
174f08c3bdfSopenharmony_ci	unsigned long long alloc_bytes = alloc_maxbytes;
175f08c3bdfSopenharmony_ci	const char *write_msg = "";
176f08c3bdfSopenharmony_ci
177f08c3bdfSopenharmony_ci	if (dowrite)
178f08c3bdfSopenharmony_ci		write_msg = "(and written to) ";
179f08c3bdfSopenharmony_ci
180f08c3bdfSopenharmony_ci	/* to make mtest01 support -i N */
181f08c3bdfSopenharmony_ci	children_done = 0;
182f08c3bdfSopenharmony_ci
183f08c3bdfSopenharmony_ci	do {
184f08c3bdfSopenharmony_ci		pid = SAFE_FORK();
185f08c3bdfSopenharmony_ci		if (pid == 0) {
186f08c3bdfSopenharmony_ci			alloc_bytes = MIN(ALLOC_THRESHOLD, alloc_bytes);
187f08c3bdfSopenharmony_ci			child_loop_alloc(alloc_bytes);
188f08c3bdfSopenharmony_ci		}
189f08c3bdfSopenharmony_ci
190f08c3bdfSopenharmony_ci		pid_list[pid_cntr++] = pid;
191f08c3bdfSopenharmony_ci
192f08c3bdfSopenharmony_ci		if (alloc_bytes <= ALLOC_THRESHOLD)
193f08c3bdfSopenharmony_ci			break;
194f08c3bdfSopenharmony_ci
195f08c3bdfSopenharmony_ci		alloc_bytes -= ALLOC_THRESHOLD;
196f08c3bdfSopenharmony_ci	} while (pid_cntr < max_pids);
197f08c3bdfSopenharmony_ci
198f08c3bdfSopenharmony_ci	/* wait in the loop for all children finish allocating */
199f08c3bdfSopenharmony_ci	while (children_done < pid_cntr) {
200f08c3bdfSopenharmony_ci		if (!tst_remaining_runtime()) {
201f08c3bdfSopenharmony_ci			tst_res(TWARN,
202f08c3bdfSopenharmony_ci				"the remaininig time is not enough for testing");
203f08c3bdfSopenharmony_ci			break;
204f08c3bdfSopenharmony_ci		}
205f08c3bdfSopenharmony_ci
206f08c3bdfSopenharmony_ci		usleep(100000);
207f08c3bdfSopenharmony_ci	}
208f08c3bdfSopenharmony_ci
209f08c3bdfSopenharmony_ci	if (children_done < pid_cntr) {
210f08c3bdfSopenharmony_ci		tst_res(TFAIL, "kbytes allocated %sless than expected %llu",
211f08c3bdfSopenharmony_ci				write_msg, alloc_maxbytes / 1024);
212f08c3bdfSopenharmony_ci
213f08c3bdfSopenharmony_ci		for (i = 0; i < pid_cntr; i++)
214f08c3bdfSopenharmony_ci			kill(pid_list[i], SIGKILL);
215f08c3bdfSopenharmony_ci
216f08c3bdfSopenharmony_ci		return;
217f08c3bdfSopenharmony_ci	}
218f08c3bdfSopenharmony_ci
219f08c3bdfSopenharmony_ci	tst_res(TPASS, "%llu kbytes allocated %s",
220f08c3bdfSopenharmony_ci			alloc_maxbytes / 1024, write_msg);
221f08c3bdfSopenharmony_ci
222f08c3bdfSopenharmony_ci	for (i = 0; i < pid_cntr; i++) {
223f08c3bdfSopenharmony_ci		TST_PROCESS_STATE_WAIT(pid_list[i], 'T', 0);
224f08c3bdfSopenharmony_ci		kill(pid_list[i], SIGCONT);
225f08c3bdfSopenharmony_ci	}
226f08c3bdfSopenharmony_ci}
227f08c3bdfSopenharmony_ci
228f08c3bdfSopenharmony_cistatic struct tst_test test = {
229f08c3bdfSopenharmony_ci	.forks_child = 1,
230f08c3bdfSopenharmony_ci	.options = (struct tst_option[]) {
231f08c3bdfSopenharmony_ci		{"c:", &opt_chunksize,	"Size of chunk in bytes to malloc on each pass"},
232f08c3bdfSopenharmony_ci		{"b:", &opt_maxbytes,	"Maximum number of bytes to allocate before stopping"},
233f08c3bdfSopenharmony_ci		{"p:", &opt_maxpercent, "Percent of total memory used at which the program stops"},
234f08c3bdfSopenharmony_ci		{"w",  &dowrite,   	"Write to the memory after allocating"},
235f08c3bdfSopenharmony_ci		{"v",  &verbose,     	"Verbose"},
236f08c3bdfSopenharmony_ci		{}
237f08c3bdfSopenharmony_ci	},
238f08c3bdfSopenharmony_ci	.max_runtime = 300,
239f08c3bdfSopenharmony_ci	.setup = setup,
240f08c3bdfSopenharmony_ci	.cleanup = cleanup,
241f08c3bdfSopenharmony_ci	.test_all = mem_test,
242f08c3bdfSopenharmony_ci};
243