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 / (unsigned long)FIVE_HUNDRED_MB + 1;
156
157	if ((pid_list = malloc(max_pids * sizeof(pid_t))) == NULL)
158		tst_brkm(TBROK | TERRNO, NULL, "malloc failed.");
159	memset(pid_list, 0, max_pids * sizeof(pid_t));
160
161	/* Currently used memory */
162	C = sstats.mem_unit * (total_ram - total_free);
163	tst_resm(TINFO, "Total memory already used on system = %llu kbytes",
164		 C / 1024);
165
166	if (maxpercent) {
167		percent = (float)maxpercent / 100.00;
168
169		/* Desired memory needed to reach maxpercent */
170		D = percent * (sstats.mem_unit * total_ram);
171		tst_resm(TINFO,
172			 "Total memory used needed to reach maximum = %llu kbytes",
173			 D / 1024);
174
175		/* Are we already using more than maxpercent? */
176		if (C > D) {
177			tst_resm(TFAIL,
178				 "More memory than the maximum amount you specified "
179				 " is already being used");
180			free(pid_list);
181			tst_exit();
182		}
183
184		/* set maxbytes to the extra amount we want to allocate */
185		maxbytes = D - C;
186		tst_resm(TINFO, "Filling up %d%% of ram which is %llu kbytes",
187			 maxpercent, maxbytes / 1024);
188	}
189	original_maxbytes = maxbytes;
190	i = 0;
191	pid_cntr = 0;
192	pid = fork();
193	if (pid < 0)
194		tst_brkm(TBROK | TERRNO, cleanup, "fork failed");
195	if (pid != 0) {
196		pid_cntr++;
197		pid_list[i] = pid;
198	}
199
200#if defined (_s390_)		/* s390's 31bit addressing requires smaller chunks */
201	while (pid != 0 && maxbytes > FIVE_HUNDRED_MB) {
202		i++;
203		maxbytes -= FIVE_HUNDRED_MB;
204		pid = fork();
205		if (pid < 0)
206			tst_brkm(TBROK | TERRNO, cleanup, "fork failed");
207		if (pid != 0) {
208			pid_cntr++;
209			pid_list[i] = pid;
210		}
211	}
212	if (maxbytes > FIVE_HUNDRED_MB)
213		alloc_bytes = FIVE_HUNDRED_MB;
214	else
215		alloc_bytes = (unsigned long)maxbytes;
216
217#elif __WORDSIZE == 32
218	while (pid != 0 && maxbytes > ONE_GB) {
219		i++;
220		maxbytes -= ONE_GB;
221		pid = fork();
222		if (pid < 0)
223		    tst_brkm(TBROK | TERRNO, cleanup, "fork failed");
224		if (pid != 0) {
225			pid_cntr++;
226			pid_list[i] = pid;
227		}
228	}
229	if (maxbytes > ONE_GB)
230		alloc_bytes = ONE_GB;
231	else
232		alloc_bytes = (unsigned long)maxbytes;
233
234#elif __WORDSIZE == 64
235	while (pid != 0 && maxbytes > THREE_GB) {
236		i++;
237		maxbytes -= THREE_GB;
238		pid = fork();
239		if (pid < 0)
240			tst_brkm(TBROK | TERRNO, cleanup, "fork failed");
241		if (pid != 0) {
242			pid_cntr++;
243			pid_list[i] = pid;
244		}
245	}
246	if (maxbytes > THREE_GB)
247		alloc_bytes = THREE_GB;
248	else
249		alloc_bytes = maxbytes;
250#endif
251
252	if (pid == 0) {
253		bytecount = chunksize;
254		while (1) {
255			if ((mem = malloc(chunksize)) == NULL) {
256				tst_resm(TBROK | TERRNO,
257					 "stopped at %lu bytes", bytecount);
258				free(pid_list);
259				tst_exit();
260			}
261			if (dowrite)
262				for (j = 0; j < chunksize; j++)
263					*(mem + j) = 'a';
264			if (verbose)
265				tst_resm(TINFO,
266					 "allocated %lu bytes chunksize is %d",
267					 bytecount, chunksize);
268			bytecount += chunksize;
269			if (alloc_bytes && bytecount >= alloc_bytes)
270				break;
271		}
272		if (dowrite)
273			tst_resm(TINFO, "... %lu bytes allocated and used.",
274				 bytecount);
275		else
276			tst_resm(TINFO, "... %lu bytes allocated only.",
277				 bytecount);
278		kill(getppid(), SIGRTMIN);
279		while (1)
280			sleep(1);
281	} else {
282		sysinfo(&sstats);
283
284		if (dowrite) {
285			/* Total Free Post-Test RAM */
286			post_mem =
287			    (unsigned long long)sstats.mem_unit *
288			    sstats.freeram;
289			post_mem =
290			    post_mem +
291			    (unsigned long long)sstats.mem_unit *
292			    sstats.freeswap;
293
294			while ((((unsigned long long)pre_mem - post_mem) <
295				(unsigned long long)original_maxbytes) &&
296			       pid_count < pid_cntr && !sigchld_count) {
297				sleep(1);
298				sysinfo(&sstats);
299				post_mem =
300				    (unsigned long long)sstats.mem_unit *
301				    sstats.freeram;
302				post_mem =
303				    post_mem +
304				    (unsigned long long)sstats.mem_unit *
305				    sstats.freeswap;
306			}
307		}
308
309		if (sigchld_count) {
310			tst_resm(TFAIL, "child process exited unexpectedly");
311		} else if (dowrite) {
312			tst_resm(TPASS, "%llu kbytes allocated and used.",
313				 original_maxbytes / 1024);
314		} else {
315			tst_resm(TPASS, "%llu kbytes allocated only.",
316				 original_maxbytes / 1024);
317		}
318
319	}
320	cleanup();
321	tst_exit();
322}
323