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