1/*
2 *
3 *   Copyright (c) International Business Machines  Corp., 2001
4 *
5 *   This program is free software;  you can redistribute it and/or modify
6 *   it under the terms of the GNU General Public License as published by
7 *   the Free Software Foundation; either version 2 of the License, or
8 *   (at your option) any later version.
9 *
10 *   This program is distributed in the hope that it will be useful,
11 *   but WITHOUT ANY WARRANTY;  without even the implied warranty of
12 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See
13 *   the GNU General Public License for more details.
14 *
15 *   You should have received a copy of the GNU General Public License
16 *   along with this program;  if not, write to the Free Software
17 *   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18 */
19
20/*
21 *  FILE		: mtest01.c
22 *  DESCRIPTION : mallocs memory <chunksize> at a time until malloc fails.
23 *  HISTORY:
24 *	04/10/2001 Paul Larson (plars@us.ibm.com)
25 *	  written
26 *	11/09/2001 Manoj Iyer (manjo@austin.ibm.com)
27 *	  Modified.
28 *	  - Removed compile warnings.
29 *	  - Added header file #include <unistd.h> definition for getopt()
30 *	05/13/2003 Robbie Williamson (robbiew@us.ibm.com)
31 *	  Modified.
32 *	  - Rewrote the test to be able to execute on large memory machines.
33 *
34 */
35
36#include <sys/types.h>
37#include <sys/sysinfo.h>
38#include <sys/wait.h>
39#include <limits.h>
40#include <signal.h>
41#include <stdio.h>
42#include <stdlib.h>
43#include <unistd.h>
44
45#include "test.h"
46
47#define FIVE_HUNDRED_MB (unsigned long long)(500*1024*1024)
48#define ONE_GB	(unsigned long long)(1024*1024*1024)
49#define THREE_GB (unsigned long long)(3*ONE_GB)
50
51char *TCID = "mtest01";
52int TST_TOTAL = 1;
53static sig_atomic_t pid_count;
54static sig_atomic_t sigchld_count;
55static pid_t *pid_list;
56
57static void handler(int signo)
58{
59	if (signo == SIGCHLD)
60		sigchld_count++;
61	pid_count++;
62}
63
64static void cleanup(void)
65{
66	int i = 0;
67
68	while (pid_list[i] > 0) {
69		kill(pid_list[i], SIGKILL);
70		i++;
71	}
72
73	free(pid_list);
74}
75
76int main(int argc, char *argv[])
77{
78	int c;
79	char *mem;
80	float percent;
81	unsigned int maxpercent = 0, dowrite = 0, verbose = 0, j;
82	unsigned long bytecount, alloc_bytes, max_pids;
83	unsigned long long original_maxbytes, maxbytes = 0;
84	unsigned long long pre_mem = 0, post_mem = 0;
85	unsigned long long total_ram, total_free, D, C;
86	int chunksize = 1024 * 1024;	/* one meg at a time by default */
87	struct sysinfo sstats;
88	int i, pid_cntr;
89	pid_t pid;
90	struct sigaction act;
91
92	act.sa_handler = handler;
93	act.sa_flags = 0;
94	sigemptyset(&act.sa_mask);
95	sigaction(SIGRTMIN, &act, 0);
96	sigaction(SIGCHLD, &act, 0);
97
98	while ((c = getopt(argc, argv, "c:b:p:wvh")) != -1) {
99		switch (c) {
100		case 'c':
101			chunksize = atoi(optarg);
102			break;
103		case 'b':
104			if (maxpercent != 0)
105				tst_brkm(TBROK, NULL,
106					 "ERROR: -b option cannot be used with -p "
107					 "option at the same time");
108			maxbytes = atoll(optarg);
109			break;
110		case 'p':
111			if (maxbytes != 0)
112				tst_brkm(TBROK, NULL,
113					 "ERROR: -p option cannot be used with -b "
114					 "option at the same time");
115			maxpercent = atoi(optarg);
116			if (maxpercent <= 0)
117				tst_brkm(TBROK, NULL,
118					 "ERROR: -p option requires number greater "
119					 "than 0");
120			if (maxpercent > 99)
121				tst_brkm(TBROK, NULL,
122					 "ERROR: -p option cannot be greater than "
123					 "99");
124			break;
125		case 'w':
126			dowrite = 1;
127			break;
128		case 'v':
129			verbose = 1;
130			break;
131		case 'h':
132		default:
133			printf
134			    ("usage: %s [-c <bytes>] [-b <bytes>|-p <percent>] [-v]\n",
135			     argv[0]);
136			printf
137			    ("\t-c <num>\tsize of chunk in bytes to malloc on each pass\n");
138			printf
139			    ("\t-b <bytes>\tmaximum number of bytes to allocate before stopping\n");
140			printf
141			    ("\t-p <bytes>\tpercent of total memory used at which the program stops\n");
142			printf
143			    ("\t-w\t\twrite to the memory after allocating\n");
144			printf("\t-v\t\tverbose\n");
145			printf("\t-h\t\tdisplay usage\n");
146			exit(1);
147		}
148	}
149
150	sysinfo(&sstats);
151	total_ram = sstats.totalram + sstats.totalswap;
152	total_free = sstats.freeram + sstats.freeswap;
153	/* Total Free Pre-Test RAM */
154	pre_mem = sstats.mem_unit * total_free;
155	max_pids = total_ram * sstats.mem_unit
156		/ (unsigned long)FIVE_HUNDRED_MB + 10;
157
158	if ((pid_list = malloc(max_pids * sizeof(pid_t))) == NULL)
159		tst_brkm(TBROK | TERRNO, NULL, "malloc failed.");
160	memset(pid_list, 0, max_pids * sizeof(pid_t));
161
162	/* Currently used memory */
163	C = sstats.mem_unit * (total_ram - total_free);
164	tst_resm(TINFO, "Total memory already used on system = %llu kbytes",
165		 C / 1024);
166
167	if (maxpercent) {
168		percent = (float)maxpercent / 100.00;
169
170		/* Desired memory needed to reach maxpercent */
171		D = percent * (sstats.mem_unit * total_ram);
172		tst_resm(TINFO,
173			 "Total memory used needed to reach maximum = %llu kbytes",
174			 D / 1024);
175
176		/* Are we already using more than maxpercent? */
177		if (C > D) {
178			tst_resm(TFAIL,
179				 "More memory than the maximum amount you specified "
180				 " is already being used");
181			free(pid_list);
182			tst_exit();
183		}
184
185		/* set maxbytes to the extra amount we want to allocate */
186		maxbytes = D - C;
187		tst_resm(TINFO, "Filling up %d%% of ram which is %llu kbytes",
188			 maxpercent, maxbytes / 1024);
189	}
190	original_maxbytes = maxbytes;
191	i = 0;
192	pid_cntr = 0;
193	pid = fork();
194	if (pid < 0)
195		tst_brkm(TBROK | TERRNO, cleanup, "fork failed");
196	if (pid != 0) {
197		pid_cntr++;
198		pid_list[i] = pid;
199	}
200
201#if defined (_s390_)		/* s390's 31bit addressing requires smaller chunks */
202	while (pid != 0 && maxbytes > FIVE_HUNDRED_MB) {
203		i++;
204		if (i >= max_pids)
205			tst_brkm(TBROK, cleanup, "max_pids needs to be increased");
206		maxbytes -= FIVE_HUNDRED_MB;
207		pid = fork();
208		if (pid < 0)
209			tst_brkm(TBROK | TERRNO, cleanup, "fork failed");
210		if (pid != 0) {
211			pid_cntr++;
212			pid_list[i] = pid;
213		}
214	}
215	if (maxbytes > FIVE_HUNDRED_MB)
216		alloc_bytes = FIVE_HUNDRED_MB;
217	else
218		alloc_bytes = (unsigned long)maxbytes;
219
220#elif __WORDSIZE == 32
221	while (pid != 0 && maxbytes > ONE_GB) {
222		i++;
223		if (i >= max_pids)
224			tst_brkm(TBROK, cleanup, "max_pids needs to be increased");
225		maxbytes -= ONE_GB;
226		pid = fork();
227		if (pid < 0)
228		    tst_brkm(TBROK | TERRNO, cleanup, "fork failed");
229		if (pid != 0) {
230			pid_cntr++;
231			pid_list[i] = pid;
232		}
233	}
234	if (maxbytes > ONE_GB)
235		alloc_bytes = ONE_GB;
236	else
237		alloc_bytes = (unsigned long)maxbytes;
238
239#elif __WORDSIZE == 64
240	while (pid != 0 && maxbytes > THREE_GB) {
241		i++;
242		if (i >= max_pids)
243			tst_brkm(TBROK, cleanup, "max_pids needs to be increased");
244		maxbytes -= THREE_GB;
245		pid = fork();
246		if (pid < 0)
247			tst_brkm(TBROK | TERRNO, cleanup, "fork failed");
248		if (pid != 0) {
249			pid_cntr++;
250			pid_list[i] = pid;
251		}
252	}
253	if (maxbytes > THREE_GB)
254		alloc_bytes = THREE_GB;
255	else
256		alloc_bytes = maxbytes;
257#endif
258
259	if (pid == 0) {
260		bytecount = chunksize;
261		while (1) {
262			if ((mem = malloc(chunksize)) == NULL) {
263				tst_resm(TBROK | TERRNO,
264					 "stopped at %lu bytes", bytecount);
265				free(pid_list);
266				tst_exit();
267			}
268			if (dowrite)
269				for (j = 0; j < chunksize; j++)
270					*(mem + j) = 'a';
271			if (verbose)
272				tst_resm(TINFO,
273					 "allocated %lu bytes chunksize is %d",
274					 bytecount, chunksize);
275			bytecount += chunksize;
276			if (alloc_bytes && bytecount >= alloc_bytes)
277				break;
278		}
279		if (dowrite)
280			tst_resm(TINFO, "... %lu bytes allocated and used.",
281				 bytecount);
282		else
283			tst_resm(TINFO, "... %lu bytes allocated only.",
284				 bytecount);
285		kill(getppid(), SIGRTMIN);
286		while (1)
287			sleep(1);
288	} else {
289		sysinfo(&sstats);
290
291		if (dowrite) {
292			/* Total Free Post-Test RAM */
293			post_mem =
294			    (unsigned long long)sstats.mem_unit *
295			    sstats.freeram;
296			post_mem =
297			    post_mem +
298			    (unsigned long long)sstats.mem_unit *
299			    sstats.freeswap;
300
301			while ((((unsigned long long)pre_mem - post_mem) <
302				(unsigned long long)original_maxbytes) &&
303			       pid_count < pid_cntr && !sigchld_count) {
304				sleep(1);
305				sysinfo(&sstats);
306				post_mem =
307				    (unsigned long long)sstats.mem_unit *
308				    sstats.freeram;
309				post_mem =
310				    post_mem +
311				    (unsigned long long)sstats.mem_unit *
312				    sstats.freeswap;
313			}
314		}
315
316		if (sigchld_count) {
317			tst_resm(TFAIL, "child process exited unexpectedly");
318		} else if (dowrite) {
319			tst_resm(TPASS, "%llu kbytes allocated and used.",
320				 original_maxbytes / 1024);
321		} else {
322			tst_resm(TPASS, "%llu kbytes allocated only.",
323				 original_maxbytes / 1024);
324		}
325
326	}
327	cleanup();
328	tst_exit();
329}
330