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 #define STOP_THRESHOLD 15 /* seconds remaining before reaching timeout */
45
46 static pid_t *pid_list;
47 static sig_atomic_t children_done;
48 static int max_pids;
49 static unsigned long long alloc_maxbytes;
50
51 static int chunksize = 1024*1024;
52 static int maxpercent = 20;
53 static long maxbytes = 0;
54 static char *dowrite;
55 static char *verbose;
56
57 static char *opt_chunksize, *opt_maxbytes, *opt_maxpercent;
58 static struct tst_option mtest_options[] = {
59 {"c:", &opt_chunksize, "-c size of chunk in bytes to malloc on each pass"},
60 {"b:", &opt_maxbytes, "-b maximum number of bytes to allocate before stopping"},
61 {"p:", &opt_maxpercent, "-p percent of total memory used at which the program stops"},
62 {"w", &dowrite, "-w write to the memory after allocating"},
63 {"v", &verbose, "-v verbose"},
64 {NULL, NULL, NULL}
65 };
66
parse_mtest_options(char * str_chunksize,int * chunksize,char * str_maxbytes,long * maxbytes,char * str_maxpercent,int * maxpercent)67 static void parse_mtest_options(char *str_chunksize, int *chunksize,
68 char *str_maxbytes, long *maxbytes,
69 char *str_maxpercent, int *maxpercent)
70 {
71 if (str_chunksize)
72 if (tst_parse_int(str_chunksize, chunksize, 1, INT_MAX))
73 tst_brk(TBROK, "Invalid chunksize '%s'", str_chunksize);
74
75 if (str_maxbytes) {
76 if (tst_parse_long(str_maxbytes, maxbytes, 1, LONG_MAX)) {
77 tst_brk(TBROK, "Invalid maxbytes '%s'", str_maxbytes);
78 } else if (str_maxpercent) {
79 tst_brk(TBROK, "ERROR: -b option cannot be used with -p "
80 "option at the same time");
81 }
82 alloc_maxbytes = (unsigned long long)maxbytes;
83 }
84
85 if (str_maxpercent) {
86 if (tst_parse_int(str_maxpercent, maxpercent, 1, 99)) {
87 tst_brk(TBROK, "Invalid maxpercent '%s'", str_maxpercent);
88 } else if (str_maxbytes) {
89 tst_brk(TBROK, "ERROR: -p option cannot be used with -b "
90 "option at the same time");
91 }
92 }
93 }
94
handler(int sig LTP_ATTRIBUTE_UNUSED)95 static void handler(int sig LTP_ATTRIBUTE_UNUSED)
96 {
97 children_done++;
98 }
99
do_write_mem(char * mem,int chunksize)100 static void do_write_mem(char *mem, int chunksize)
101 {
102 int i, pagesz = getpagesize();
103
104 for (i = 0; i < chunksize; i += pagesz)
105 *(mem + i) = 'a';
106 }
107
setup(void)108 static void setup(void)
109 {
110 struct sysinfo sstats;
111 unsigned long long total_free;
112
113 struct sigaction act;
114 act.sa_handler = handler;
115 act.sa_flags = 0;
116 sigemptyset(&act.sa_mask);
117 sigaction(SIGRTMIN, &act, 0);
118
119 parse_mtest_options(opt_chunksize, &chunksize,
120 opt_maxbytes, &maxbytes,
121 opt_maxpercent, &maxpercent);
122 sysinfo(&sstats);
123 total_free = sstats.freeram;
124
125 max_pids = total_free * sstats.mem_unit
126 / (unsigned long)ALLOC_THRESHOLD + 10;
127 pid_list = SAFE_MALLOC(max_pids * sizeof(pid_t));
128
129 if (!alloc_maxbytes) {
130 /* set alloc_maxbytes to the extra amount we want to allocate */
131 alloc_maxbytes = ((float)maxpercent / 100.00)
132 * (sstats.mem_unit * total_free);
133 tst_res(TINFO, "Filling up %d%% of free ram which is %llu kbytes",
134 maxpercent, alloc_maxbytes / 1024);
135 }
136 }
137
cleanup(void)138 static void cleanup(void)
139 {
140 if(pid_list)
141 free(pid_list);
142 }
143
child_loop_alloc(unsigned long long alloc_bytes)144 static void child_loop_alloc(unsigned long long alloc_bytes)
145 {
146 unsigned long bytecount = 0;
147 char *mem;
148
149 tst_res(TINFO, "... child %d starting", getpid());
150
151 while (1) {
152 mem = SAFE_MALLOC(chunksize);
153 if (dowrite)
154 do_write_mem(mem, chunksize);
155
156 if (verbose)
157 tst_res(TINFO,
158 "child %d allocated %lu bytes chunksize is %d",
159 getpid(), bytecount, chunksize);
160 bytecount += chunksize;
161 if (bytecount >= alloc_bytes)
162 break;
163 }
164 if (dowrite)
165 tst_res(TINFO, "... [t=%d] %lu bytes allocated and used in child %d",
166 tst_timeout_remaining(), bytecount, getpid());
167 else
168 tst_res(TINFO, "... [t=%d] %lu bytes allocated only in child %d",
169 tst_timeout_remaining(), bytecount, getpid());
170
171 kill(getppid(), SIGRTMIN);
172 raise(SIGSTOP);
173 exit(0);
174 }
175
mem_test(void)176 static void mem_test(void)
177 {
178 pid_t pid;
179 int i = 0, pid_cntr = 0;
180 unsigned long long alloc_bytes = alloc_maxbytes;
181 const char *write_msg = "";
182
183 if (dowrite)
184 write_msg = "(and written to) ";
185
186 /* to make mtest01 support -i N */
187 children_done = 0;
188
189 do {
190 pid = SAFE_FORK();
191 if (pid == 0) {
192 alloc_bytes = MIN(ALLOC_THRESHOLD, alloc_bytes);
193 child_loop_alloc(alloc_bytes);
194 }
195
196 pid_list[pid_cntr++] = pid;
197
198 if (alloc_bytes <= ALLOC_THRESHOLD)
199 break;
200
201 alloc_bytes -= ALLOC_THRESHOLD;
202 } while (pid_cntr < max_pids);
203
204 /* wait in the loop for all children finish allocating */
205 while (children_done < pid_cntr) {
206 if (tst_timeout_remaining() < STOP_THRESHOLD) {
207 tst_res(TWARN,
208 "the remaininig time is not enough for testing");
209
210 break;
211 }
212
213 usleep(100000);
214 }
215
216 if (children_done < pid_cntr) {
217 tst_res(TFAIL, "kbytes allocated %sless than expected %llu",
218 write_msg, alloc_maxbytes / 1024);
219
220 for (i = 0; i < pid_cntr; i++)
221 kill(pid_list[i], SIGKILL);
222
223 return;
224 }
225
226 tst_res(TPASS, "%llu kbytes allocated %s",
227 alloc_maxbytes / 1024, write_msg);
228
229 for (i = 0; i < pid_cntr; i++) {
230 TST_PROCESS_STATE_WAIT(pid_list[i], 'T');
231 kill(pid_list[i], SIGCONT);
232 }
233 }
234
235 static struct tst_test test = {
236 .forks_child = 1,
237 .options = mtest_options,
238 .setup = setup,
239 .cleanup = cleanup,
240 .test_all = mem_test,
241 };
242