• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* A program to put stress on a POSIX system (stress).
2  *
3  * Copyright (C) 2001, 2002 Amos Waterland <awaterl@yahoo.com>
4  *
5  * This program is free software; you can redistribute it and/or modify it
6  * under the terms of the GNU General Public License as published by the Free
7  * Software Foundation; either version 2 of the License, or (at your option)
8  * any later version.
9  *
10  * This program is distributed in the hope that it will be useful, but WITHOUT
11  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
12  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
13  * more details.
14  *
15  * You should have received a copy of the GNU General Public License along
16  * with this program; if not, write to the Free Software Foundation, Inc.,
17  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18  */
19 
20 #include <ctype.h>
21 #include <errno.h>
22 #include <libgen.h>
23 #include <math.h>
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <string.h>
27 #include <signal.h>
28 #include <time.h>
29 #include <unistd.h>
30 #include <sys/wait.h>
31 
32 /* By default, print all messages of severity info and above.  */
33 static int global_debug = 2;
34 
35 /* By default, just print warning for non-critical errors.  */
36 static int global_ignore = 1;
37 
38 /* By default, retry on non-critical errors every 50ms.  */
39 static int global_retry = 50000;
40 
41 /* By default, use this as backoff coefficient for good fork throughput.  */
42 static int global_backoff = 3000;
43 
44 /* By default, do not timeout.  */
45 static int global_timeout = 0;
46 
47 /* Name of this program */
48 static char *global_progname = PACKAGE;
49 
50 /* By default, do not hang after allocating memory.  */
51 static int global_vmhang = 0;
52 
53 /* Implemention of runtime-selectable severity message printing.  */
54 #define dbg if (global_debug >= 3) \
55             fprintf (stdout, "%s: debug: (%d) ", global_progname, __LINE__), \
56             fprintf
57 #define out if (global_debug >= 2) \
58             fprintf (stdout, "%s: info: ", global_progname), \
59             fprintf
60 #define wrn if (global_debug >= 1) \
61             fprintf (stderr, "%s: warn: (%d) ", global_progname, __LINE__), \
62             fprintf
63 #define err if (global_debug >= 0) \
64             fprintf (stderr, "%s: error: (%d) ", global_progname, __LINE__), \
65             fprintf
66 
67 /* Implementation of check for option argument correctness.  */
68 #define assert_arg(A) \
69           if (++i == argc || ((arg = argv[i])[0] == '-' && \
70               !isdigit ((int)arg[1]) )) \
71             { \
72               err (stderr, "missing argument to option '%s'\n", A); \
73               exit (1); \
74             }
75 
76 /* Prototypes for utility functions.  */
77 int usage(int status);
78 int version(int status);
79 long long atoll_s(const char *nptr);
80 long long atoll_b(const char *nptr);
81 
82 /* Prototypes for the worker functions.  */
83 int hogcpu(long long forks);
84 int hogio(long long forks);
85 int hogvm(long long forks, long long chunks, long long bytes);
86 int hoghdd(long long forks, int clean, long long files, long long bytes);
87 
main(int argc,char ** argv)88 int main(int argc, char **argv)
89 {
90 	int i, pid, children = 0, retval = 0;
91 	long starttime, stoptime, runtime;
92 
93 	/* Variables that indicate which options have been selected.  */
94 	int do_dryrun = 0;
95 	int do_timeout = 0;
96 	int do_cpu = 0;		/* Default to 1 fork. */
97 	long long do_cpu_forks = 1;
98 	int do_io = 0;		/* Default to 1 fork. */
99 	long long do_io_forks = 1;
100 	int do_vm = 0;		/* Default to 1 fork, 1 chunk of 256MB.  */
101 	long long do_vm_forks = 1;
102 	long long do_vm_chunks = 1;
103 	long long do_vm_bytes = 256 * 1024 * 1024;
104 	int do_hdd = 0;		/* Default to 1 fork, clean, 1 file of 1GB.  */
105 	long long do_hdd_forks = 1;
106 	int do_hdd_clean = 0;
107 	long long do_hdd_files = 1;
108 	long long do_hdd_bytes = 1024 * 1024 * 1024;
109 
110 	/* Record our start time.  */
111 	if ((starttime = time(NULL)) == -1) {
112 		err(stderr, "failed to acquire current time\n");
113 		exit(1);
114 	}
115 
116 	/* SuSv3 does not define any error conditions for this function.  */
117 	global_progname = basename(argv[0]);
118 
119 	/* For portability, parse command line options without getopt_long.  */
120 	for (i = 1; i < argc; i++) {
121 		char *arg = argv[i];
122 
123 		if (strcmp(arg, "--help") == 0 || strcmp(arg, "-?") == 0) {
124 			usage(0);
125 		} else if (strcmp(arg, "--version") == 0) {
126 			version(0);
127 		} else if (strcmp(arg, "--verbose") == 0
128 			   || strcmp(arg, "-v") == 0) {
129 			global_debug = 3;
130 		} else if (strcmp(arg, "--quiet") == 0
131 			   || strcmp(arg, "-q") == 0) {
132 			global_debug = 0;
133 		} else if (strcmp(arg, "--dry-run") == 0
134 			   || strcmp(arg, "-n") == 0) {
135 			do_dryrun = 1;
136 		} else if (strcmp(arg, "--no-retry") == 0) {
137 			global_ignore = 0;
138 			dbg(stdout,
139 			    "turning off ignore of non-critical errors");
140 		} else if (strcmp(arg, "--retry-delay") == 0) {
141 			assert_arg("--retry-delay");
142 			global_retry = atoll(arg);
143 			dbg(stdout, "setting retry delay to %dus\n",
144 			    global_retry);
145 		} else if (strcmp(arg, "--backoff") == 0) {
146 			assert_arg("--backoff");
147 			global_backoff = atoll(arg);
148 			if (global_backoff < 0) {
149 				err(stderr, "invalid backoff factor: %i\n",
150 				    global_backoff);
151 				exit(1);
152 			}
153 			dbg(stdout, "setting backoff coeffient to %dus\n",
154 			    global_backoff);
155 		} else if (strcmp(arg, "--timeout") == 0
156 			   || strcmp(arg, "-t") == 0) {
157 			do_timeout = 1;
158 			assert_arg("--timeout");
159 			global_timeout = atoll_s(arg);
160 			dbg(stdout, "setting timeout to %ds\n", global_timeout);
161 		} else if (strcmp(arg, "--cpu") == 0 || strcmp(arg, "-c") == 0) {
162 			do_cpu = 1;
163 			assert_arg("--cpu");
164 			do_cpu_forks = atoll_b(arg);
165 		} else if (strcmp(arg, "--io") == 0 || strcmp(arg, "-i") == 0) {
166 			do_io = 1;
167 			assert_arg("--io");
168 			do_io_forks = atoll_b(arg);
169 		} else if (strcmp(arg, "--vm") == 0 || strcmp(arg, "-m") == 0) {
170 			do_vm = 1;
171 			assert_arg("--vm");
172 			do_vm_forks = atoll_b(arg);
173 		} else if (strcmp(arg, "--vm-chunks") == 0) {
174 			assert_arg("--vm-chunks");
175 			do_vm_chunks = atoll_b(arg);
176 		} else if (strcmp(arg, "--vm-bytes") == 0) {
177 			assert_arg("--vm-bytes");
178 			do_vm_bytes = atoll_b(arg);
179 		} else if (strcmp(arg, "--vm-hang") == 0) {
180 			global_vmhang = 1;
181 		} else if (strcmp(arg, "--hdd") == 0 || strcmp(arg, "-d") == 0) {
182 			do_hdd = 1;
183 			assert_arg("--hdd");
184 			do_hdd_forks = atoll_b(arg);
185 		} else if (strcmp(arg, "--hdd-noclean") == 0) {
186 			do_hdd_clean = 2;
187 		} else if (strcmp(arg, "--hdd-files") == 0) {
188 			assert_arg("--hdd-files");
189 			do_hdd_files = atoll_b(arg);
190 		} else if (strcmp(arg, "--hdd-bytes") == 0) {
191 			assert_arg("--hdd-bytes");
192 			do_hdd_bytes = atoll_b(arg);
193 		} else {
194 			err(stderr, "unrecognized option: %s\n", arg);
195 			exit(1);
196 		}
197 	}
198 
199 	/* Hog CPU option.  */
200 	if (do_cpu) {
201 		out(stdout, "dispatching %lli hogcpu forks\n", do_cpu_forks);
202 
203 		switch (pid = fork()) {
204 		case 0:	/* child */
205 			if (do_dryrun)
206 				exit(0);
207 			exit(hogcpu(do_cpu_forks));
208 		case -1:	/* error */
209 			err(stderr, "hogcpu dispatcher fork failed\n");
210 			exit(1);
211 		default:	/* parent */
212 			children++;
213 			dbg(stdout, "--> hogcpu dispatcher forked (%i)\n", pid);
214 		}
215 	}
216 
217 	/* Hog I/O option.  */
218 	if (do_io) {
219 		out(stdout, "dispatching %lli hogio forks\n", do_io_forks);
220 
221 		switch (pid = fork()) {
222 		case 0:	/* child */
223 			if (do_dryrun)
224 				exit(0);
225 			exit(hogio(do_io_forks));
226 		case -1:	/* error */
227 			err(stderr, "hogio dispatcher fork failed\n");
228 			exit(1);
229 		default:	/* parent */
230 			children++;
231 			dbg(stdout, "--> hogio dispatcher forked (%i)\n", pid);
232 		}
233 	}
234 
235 	/* Hog VM option.  */
236 	if (do_vm) {
237 		out(stdout,
238 		    "dispatching %lli hogvm forks, each %lli chunks of %lli bytes\n",
239 		    do_vm_forks, do_vm_chunks, do_vm_bytes);
240 
241 		switch (pid = fork()) {
242 		case 0:	/* child */
243 			if (do_dryrun)
244 				exit(0);
245 			exit(hogvm(do_vm_forks, do_vm_chunks, do_vm_bytes));
246 		case -1:	/* error */
247 			err(stderr, "hogvm dispatcher fork failed\n");
248 			exit(1);
249 		default:	/* parent */
250 			children++;
251 			dbg(stdout, "--> hogvm dispatcher forked (%i)\n", pid);
252 		}
253 	}
254 
255 	/* Hog HDD option.  */
256 	if (do_hdd) {
257 		out(stdout, "dispatching %lli hoghdd forks, each %lli files of "
258 		    "%lli bytes\n", do_hdd_forks, do_hdd_files, do_hdd_bytes);
259 
260 		switch (pid = fork()) {
261 		case 0:	/* child */
262 			if (do_dryrun)
263 				exit(0);
264 			exit(hoghdd
265 			     (do_hdd_forks, do_hdd_clean, do_hdd_files,
266 			      do_hdd_bytes));
267 		case -1:	/* error */
268 			err(stderr, "hoghdd dispatcher fork failed\n");
269 			exit(1);
270 		default:	/* parent */
271 			children++;
272 			dbg(stdout, "--> hoghdd dispatcher forked (%i)\n", pid);
273 		}
274 	}
275 
276 	/* We have no work to do, so bail out.  */
277 	if (children == 0)
278 		usage(0);
279 
280 	/* Wait for our children to exit.  */
281 	while (children) {
282 		int status, ret;
283 
284 		if ((pid = wait(&status)) > 0) {
285 			if ((WIFEXITED(status)) != 0) {
286 				if ((ret = WEXITSTATUS(status)) != 0) {
287 					err(stderr,
288 					    "dispatcher %i returned error %i\n",
289 					    pid, ret);
290 					retval += ret;
291 				} else {
292 					dbg(stdout,
293 					    "<-- dispatcher return (%i)\n",
294 					    pid);
295 				}
296 			} else {
297 				err(stderr,
298 				    "dispatcher did not exit normally\n");
299 				++retval;
300 			}
301 
302 			--children;
303 		} else {
304 			dbg(stdout, "wait() returned error: %s\n",
305 			    strerror(errno));
306 			err(stderr, "detected missing dispatcher children\n");
307 			++retval;
308 			break;
309 		}
310 	}
311 
312 	/* Record our stop time.  */
313 	if ((stoptime = time(NULL)) == -1) {
314 		err(stderr, "failed to acquire current time\n");
315 		exit(1);
316 	}
317 
318 	/* Calculate our runtime.  */
319 	runtime = stoptime - starttime;
320 
321 	/* Print final status message.  */
322 	if (retval) {
323 		err(stderr, "failed run completed in %lis\n", runtime);
324 	} else {
325 		out(stdout, "successful run completed in %lis\n", runtime);
326 	}
327 
328 	exit(retval);
329 }
330 
usage(int status)331 int usage(int status)
332 {
333 	char *mesg =
334 	    "`%s' imposes certain types of compute stress on your system\n\n"
335 	    "Usage: %s [OPTION [ARG]] ...\n\n"
336 	    " -?, --help            show this help statement\n"
337 	    "     --version         show version statement\n"
338 	    " -v, --verbose         be verbose\n"
339 	    " -q, --quiet           be quiet\n"
340 	    " -n, --dry-run         show what would have been done\n"
341 	    "     --no-retry        exit rather than retry non-critical errors\n"
342 	    "     --retry-delay n   wait n us before continuing past error\n"
343 	    " -t, --timeout n       timeout after n seconds\n"
344 	    "     --backoff n       wait for factor of n us before starting work\n"
345 	    " -c, --cpu n           spawn n procs spinning on sqrt()\n"
346 	    " -i, --io n            spawn n procs spinning on sync()\n"
347 	    " -m, --vm n            spawn n procs spinning on malloc()\n"
348 	    "     --vm-chunks c     malloc c chunks (default is 1)\n"
349 	    "     --vm-bytes b      malloc chunks of b bytes (default is 256MB)\n"
350 	    "     --vm-hang         hang in a sleep loop after memory allocated\n"
351 	    " -d, --hdd n           spawn n procs spinning on write()\n"
352 	    "     --hdd-noclean     do not unlink file to which random data written\n"
353 	    "     --hdd-files f     write to f files (default is 1)\n"
354 	    "     --hdd-bytes b     write b bytes (default is 1GB)\n\n"
355 	    "Infinity is denoted with 0.  For -m, -d: n=0 means infinite redo,\n"
356 	    "n<0 means redo abs(n) times. Valid suffixes are m,h,d,y for time;\n"
357 	    "k,m,g for size.\n\n";
358 
359 	fprintf(stdout, mesg, global_progname, global_progname);
360 
361 	if (status <= 0)
362 		exit(-1 * status);
363 
364 	return 0;
365 }
366 
version(int status)367 int version(int status)
368 {
369 	char *mesg = "%s %s\n";
370 
371 	fprintf(stdout, mesg, global_progname, VERSION);
372 
373 	if (status <= 0)
374 		exit(-1 * status);
375 
376 	return 0;
377 }
378 
379 /* Convert a string representation of a number with an optional size suffix
380  * to a long long.
381  */
atoll_b(const char * nptr)382 long long atoll_b(const char *nptr)
383 {
384 	int pos;
385 	char suffix;
386 	long long factor = 1;
387 
388 	if ((pos = strlen(nptr) - 1) < 0) {
389 		err(stderr, "invalid string\n");
390 		exit(1);
391 	}
392 
393 	switch (suffix = nptr[pos]) {
394 	case 'k':
395 	case 'K':
396 		factor = 1024;
397 		break;
398 	case 'm':
399 	case 'M':
400 		factor = 1024 * 1024;
401 		break;
402 	case 'g':
403 	case 'G':
404 		factor = 1024 * 1024 * 1024;
405 		break;
406 	default:
407 		if (suffix < '0' || suffix > '9') {
408 			err(stderr, "unrecognized suffix: %c\n", suffix);
409 			exit(1);
410 		}
411 	}
412 
413 	factor = atoll(nptr) * factor;
414 
415 	return factor;
416 }
417 
418 /* Convert a string representation of a number with an optional time suffix
419  * to a long long.
420  */
atoll_s(const char * nptr)421 long long atoll_s(const char *nptr)
422 {
423 	int pos;
424 	char suffix;
425 	long long factor = 1;
426 
427 	if ((pos = strlen(nptr) - 1) < 0) {
428 		err(stderr, "invalid string\n");
429 		exit(1);
430 	}
431 
432 	switch (suffix = nptr[pos]) {
433 	case 's':
434 	case 'S':
435 		factor = 1;
436 		break;
437 	case 'm':
438 	case 'M':
439 		factor = 60;
440 		break;
441 	case 'h':
442 	case 'H':
443 		factor = 60 * 60;
444 		break;
445 	case 'd':
446 	case 'D':
447 		factor = 60 * 60 * 24;
448 		break;
449 	case 'y':
450 	case 'Y':
451 		factor = 60 * 60 * 24 * 360;
452 		break;
453 	default:
454 		if (suffix < '0' || suffix > '9') {
455 			err(stderr, "unrecognized suffix: %c\n", suffix);
456 			exit(1);
457 		}
458 	}
459 
460 	factor = atoll(nptr) * factor;
461 
462 	return factor;
463 }
464 
hogcpu(long long forks)465 int hogcpu(long long forks)
466 {
467 	long long i;
468 	double d;
469 	int pid, retval = 0;
470 
471 	/* Make local copies of global variables.  */
472 	int ignore = global_ignore;
473 	int retry = global_retry;
474 	int timeout = global_timeout;
475 	long backoff = global_backoff * forks;
476 
477 	dbg(stdout, "using backoff sleep of %lius for hogcpu\n", backoff);
478 
479 	for (i = 0; forks == 0 || i < forks; i++) {
480 		switch (pid = fork()) {
481 		case 0:	/* child */
482 			alarm(timeout);
483 
484 			/* Use a backoff sleep to ensure we get good fork throughput.  */
485 			usleep(backoff);
486 
487 			while (1)
488 				d = sqrt(rand());
489 
490 			/* This case never falls through; alarm signal can cause exit.  */
491 		case -1:	/* error */
492 			if (ignore) {
493 				++retval;
494 				wrn(stderr,
495 				    "hogcpu worker fork failed, continuing\n");
496 				usleep(retry);
497 				continue;
498 			}
499 
500 			err(stderr, "hogcpu worker fork failed\n");
501 			return 1;
502 		default:	/* parent */
503 			dbg(stdout, "--> hogcpu worker forked (%i)\n", pid);
504 		}
505 	}
506 
507 	/* Wait for our children to exit.  */
508 	while (i) {
509 		int status, ret;
510 
511 		if ((pid = wait(&status)) > 0) {
512 			if ((WIFEXITED(status)) != 0) {
513 				if ((ret = WEXITSTATUS(status)) != 0) {
514 					err(stderr,
515 					    "hogcpu worker %i exited %i\n", pid,
516 					    ret);
517 					retval += ret;
518 				} else {
519 					dbg(stdout,
520 					    "<-- hogcpu worker exited (%i)\n",
521 					    pid);
522 				}
523 			} else {
524 				dbg(stdout,
525 				    "<-- hogcpu worker signalled (%i)\n", pid);
526 			}
527 
528 			--i;
529 		} else {
530 			dbg(stdout, "wait() returned error: %s\n",
531 			    strerror(errno));
532 			err(stderr,
533 			    "detected missing hogcpu worker children\n");
534 			++retval;
535 			break;
536 		}
537 	}
538 
539 	return retval;
540 }
541 
hogio(long long forks)542 int hogio(long long forks)
543 {
544 	long long i;
545 	int pid, retval = 0;
546 
547 	/* Make local copies of global variables.  */
548 	int ignore = global_ignore;
549 	int retry = global_retry;
550 	int timeout = global_timeout;
551 	long backoff = global_backoff * forks;
552 
553 	dbg(stdout, "using backoff sleep of %lius for hogio\n", backoff);
554 
555 	for (i = 0; forks == 0 || i < forks; i++) {
556 		switch (pid = fork()) {
557 		case 0:	/* child */
558 			alarm(timeout);
559 
560 			/* Use a backoff sleep to ensure we get good fork throughput.  */
561 			usleep(backoff);
562 
563 			while (1)
564 				sync();
565 
566 			/* This case never falls through; alarm signal can cause exit.  */
567 		case -1:	/* error */
568 			if (ignore) {
569 				++retval;
570 				wrn(stderr,
571 				    "hogio worker fork failed, continuing\n");
572 				usleep(retry);
573 				continue;
574 			}
575 
576 			err(stderr, "hogio worker fork failed\n");
577 			return 1;
578 		default:	/* parent */
579 			dbg(stdout, "--> hogio worker forked (%i)\n", pid);
580 		}
581 	}
582 
583 	/* Wait for our children to exit.  */
584 	while (i) {
585 		int status, ret;
586 
587 		if ((pid = wait(&status)) > 0) {
588 			if ((WIFEXITED(status)) != 0) {
589 				if ((ret = WEXITSTATUS(status)) != 0) {
590 					err(stderr,
591 					    "hogio worker %i exited %i\n", pid,
592 					    ret);
593 					retval += ret;
594 				} else {
595 					dbg(stdout,
596 					    "<-- hogio worker exited (%i)\n",
597 					    pid);
598 				}
599 			} else {
600 				dbg(stdout, "<-- hogio worker signalled (%i)\n",
601 				    pid);
602 			}
603 
604 			--i;
605 		} else {
606 			dbg(stdout, "wait() returned error: %s\n",
607 			    strerror(errno));
608 			err(stderr, "detected missing hogio worker children\n");
609 			++retval;
610 			break;
611 		}
612 	}
613 
614 	return retval;
615 }
616 
hogvm(long long forks,long long chunks,long long bytes)617 int hogvm(long long forks, long long chunks, long long bytes)
618 {
619 	long long i, j, k;
620 	int pid, retval = 0;
621 	char **ptr;
622 
623 	/* Make local copies of global variables.  */
624 	int ignore = global_ignore;
625 	int retry = global_retry;
626 	int timeout = global_timeout;
627 	long backoff = global_backoff * forks;
628 
629 	dbg(stdout, "using backoff sleep of %lius for hogvm\n", backoff);
630 
631 	if (bytes == 0) {
632 		/* 512MB is guess at the largest value can than be malloced at once.  */
633 		bytes = 512 * 1024 * 1024;
634 	}
635 
636 	for (i = 0; forks == 0 || i < forks; i++) {
637 		switch (pid = fork()) {
638 		case 0:	/* child */
639 			alarm(timeout);
640 
641 			/* Use a backoff sleep to ensure we get good fork throughput.  */
642 			usleep(backoff);
643 
644 			/* If chunks is 0, ptr will allocate 0 bytes's
645 			 * memory, it will cause the process to crash
646 			 * during runtime, so adjust to 1 */
647 			if (chunks == 0)
648 				chunks = 1;
649 
650 			while (1) {
651 				ptr = (char **)malloc(chunks *
652 						sizeof(char *));
653 				for (j = 0; j < chunks; j++) {
654 					if ((ptr[j] =
655 					     (char *)malloc(bytes *
656 							    sizeof(char)))) {
657 						for (k = 0; k < bytes; k++)
658 							ptr[j][k] = 'Z';	/* Ensure that COW happens.  */
659 						dbg(stdout,
660 						    "hogvm worker malloced %lli bytes\n",
661 						    k);
662 					} else if (ignore) {
663 						++retval;
664 						wrn(stderr,
665 						    "hogvm malloc failed, continuing\n");
666 						usleep(retry);
667 						continue;
668 					} else {
669 						++retval;
670 						err(stderr,
671 						    "hogvm malloc failed\n");
672 						break;
673 					}
674 				}
675 				if (global_vmhang && retval == 0) {
676 					dbg(stdout,
677 					    "sleeping forever with allocated memory\n");
678 					while (1)
679 						sleep(1024);
680 				}
681 				if (retval == 0) {
682 					dbg(stdout,
683 					    "hogvm worker freeing memory and starting over\n");
684 					for (j = 0; j < chunks; j++)
685 						free(ptr[j]);
686 					free(ptr);
687 					continue;
688 				}
689 
690 				exit(retval);
691 			}
692 
693 			/* This case never falls through; alarm signal can cause exit.  */
694 		case -1:	/* error */
695 			if (ignore) {
696 				++retval;
697 				wrn(stderr,
698 				    "hogvm worker fork failed, continuing\n");
699 				usleep(retry);
700 				continue;
701 			}
702 
703 			err(stderr, "hogvm worker fork failed\n");
704 			return 1;
705 		default:	/* parent */
706 			dbg(stdout, "--> hogvm worker forked (%i)\n", pid);
707 		}
708 	}
709 
710 	/* Wait for our children to exit.  */
711 	while (i) {
712 		int status, ret;
713 
714 		if ((pid = wait(&status)) > 0) {
715 			if ((WIFEXITED(status)) != 0) {
716 				if ((ret = WEXITSTATUS(status)) != 0) {
717 					err(stderr,
718 					    "hogvm worker %i exited %i\n", pid,
719 					    ret);
720 					retval += ret;
721 				} else {
722 					dbg(stdout,
723 					    "<-- hogvm worker exited (%i)\n",
724 					    pid);
725 				}
726 			} else {
727 				dbg(stdout, "<-- hogvm worker signalled (%i)\n",
728 				    pid);
729 			}
730 
731 			--i;
732 		} else {
733 			dbg(stdout, "wait() returned error: %s\n",
734 			    strerror(errno));
735 			err(stderr, "detected missing hogvm worker children\n");
736 			++retval;
737 			break;
738 		}
739 	}
740 
741 	return retval;
742 }
743 
hoghdd(long long forks,int clean,long long files,long long bytes)744 int hoghdd(long long forks, int clean, long long files, long long bytes)
745 {
746 	long long i, j;
747 	int fd, pid, retval = 0;
748 	int chunk = (1024 * 1024) - 1;	/* Minimize slow writing.  */
749 	char buff[chunk];
750 
751 	/* Make local copies of global variables.  */
752 	int ignore = global_ignore;
753 	int retry = global_retry;
754 	int timeout = global_timeout;
755 	long backoff = global_backoff * forks;
756 
757 	/* Initialize buffer with some random ASCII data.  */
758 	dbg(stdout, "seeding buffer with random data\n");
759 	for (i = 0; i < chunk - 1; i++) {
760 		j = rand();
761 		j = (j < 0) ? -j : j;
762 		j %= 95;
763 		j += 32;
764 		buff[i] = j;
765 	}
766 	buff[i] = '\n';
767 
768 	dbg(stdout, "using backoff sleep of %lius for hoghdd\n", backoff);
769 
770 	for (i = 0; forks == 0 || i < forks; i++) {
771 		switch (pid = fork()) {
772 		case 0:	/* child */
773 			alarm(timeout);
774 
775 			/* Use a backoff sleep to ensure we get good fork throughput.  */
776 			usleep(backoff);
777 
778 			while (1) {
779 				for (i = 0; i < files; i++) {
780 					char name[] = "./stress.XXXXXX";
781 
782 					if ((fd = mkstemp(name)) < 0) {
783 						perror("mkstemp");
784 						err(stderr, "mkstemp failed\n");
785 						exit(1);
786 					}
787 
788 					if (clean == 0) {
789 						dbg(stdout, "unlinking %s\n",
790 						    name);
791 						if (unlink(name)) {
792 							err(stderr,
793 							    "unlink failed\n");
794 							exit(1);
795 						}
796 					}
797 
798 					dbg(stdout, "fast writing to %s\n",
799 					    name);
800 					for (j = 0;
801 					     bytes == 0 || j + chunk < bytes;
802 					     j += chunk) {
803 						if (write(fd, buff, chunk) !=
804 						    chunk) {
805 							err(stderr,
806 							    "write failed\n");
807 							exit(1);
808 						}
809 					}
810 
811 					dbg(stdout, "slow writing to %s\n",
812 					    name);
813 					for (; bytes == 0 || j < bytes - 1; j++) {
814 						if (write(fd, "Z", 1) != 1) {
815 							err(stderr,
816 							    "write failed\n");
817 							exit(1);
818 						}
819 					}
820 					if (write(fd, "\n", 1) != 1) {
821 						err(stderr, "write failed\n");
822 						exit(1);
823 					}
824 					++j;
825 
826 					dbg(stdout,
827 					    "closing %s after writing %lli bytes\n",
828 					    name, j);
829 					close(fd);
830 
831 					if (clean == 1) {
832 						if (unlink(name)) {
833 							err(stderr,
834 							    "unlink failed\n");
835 							exit(1);
836 						}
837 					}
838 				}
839 				if (retval == 0) {
840 					dbg(stdout,
841 					    "hoghdd worker starting over\n");
842 					continue;
843 				}
844 
845 				exit(retval);
846 			}
847 
848 			/* This case never falls through; alarm signal can cause exit.  */
849 		case -1:	/* error */
850 			if (ignore) {
851 				++retval;
852 				wrn(stderr,
853 				    "hoghdd worker fork failed, continuing\n");
854 				usleep(retry);
855 				continue;
856 			}
857 
858 			err(stderr, "hoghdd worker fork failed\n");
859 			return 1;
860 		default:	/* parent */
861 			dbg(stdout, "--> hoghdd worker forked (%i)\n", pid);
862 		}
863 	}
864 
865 	/* Wait for our children to exit.  */
866 	while (i) {
867 		int status, ret;
868 
869 		if ((pid = wait(&status)) > 0) {
870 			if ((WIFEXITED(status)) != 0) {
871 				if ((ret = WEXITSTATUS(status)) != 0) {
872 					err(stderr,
873 					    "hoghdd worker %i exited %i\n", pid,
874 					    ret);
875 					retval += ret;
876 				} else {
877 					dbg(stdout,
878 					    "<-- hoghdd worker exited (%i)\n",
879 					    pid);
880 				}
881 			} else {
882 				dbg(stdout,
883 				    "<-- hoghdd worker signalled (%i)\n", pid);
884 			}
885 
886 			--i;
887 		} else {
888 			dbg(stdout, "wait() returned error: %s\n",
889 			    strerror(errno));
890 			err(stderr,
891 			    "detected missing hoghdd worker children\n");
892 			++retval;
893 			break;
894 		}
895 	}
896 
897 	return retval;
898 }
899