• 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 			while (1) {
645 				ptr = (char **)malloc(chunks * 2);
646 				for (j = 0; chunks == 0 || j < chunks; j++) {
647 					if ((ptr[j] =
648 					     (char *)malloc(bytes *
649 							    sizeof(char)))) {
650 						for (k = 0; k < bytes; k++)
651 							ptr[j][k] = 'Z';	/* Ensure that COW happens.  */
652 						dbg(stdout,
653 						    "hogvm worker malloced %lli bytes\n",
654 						    k);
655 					} else if (ignore) {
656 						++retval;
657 						wrn(stderr,
658 						    "hogvm malloc failed, continuing\n");
659 						usleep(retry);
660 						continue;
661 					} else {
662 						++retval;
663 						err(stderr,
664 						    "hogvm malloc failed\n");
665 						break;
666 					}
667 				}
668 				if (global_vmhang && retval == 0) {
669 					dbg(stdout,
670 					    "sleeping forever with allocated memory\n");
671 					while (1)
672 						sleep(1024);
673 				}
674 				if (retval == 0) {
675 					dbg(stdout,
676 					    "hogvm worker freeing memory and starting over\n");
677 					for (j = 0; chunks == 0 || j < chunks;
678 					     j++) {
679 						free(ptr[j]);
680 					}
681 					free(ptr);
682 					continue;
683 				}
684 
685 				exit(retval);
686 			}
687 
688 			/* This case never falls through; alarm signal can cause exit.  */
689 		case -1:	/* error */
690 			if (ignore) {
691 				++retval;
692 				wrn(stderr,
693 				    "hogvm worker fork failed, continuing\n");
694 				usleep(retry);
695 				continue;
696 			}
697 
698 			err(stderr, "hogvm worker fork failed\n");
699 			return 1;
700 		default:	/* parent */
701 			dbg(stdout, "--> hogvm worker forked (%i)\n", pid);
702 		}
703 	}
704 
705 	/* Wait for our children to exit.  */
706 	while (i) {
707 		int status, ret;
708 
709 		if ((pid = wait(&status)) > 0) {
710 			if ((WIFEXITED(status)) != 0) {
711 				if ((ret = WEXITSTATUS(status)) != 0) {
712 					err(stderr,
713 					    "hogvm worker %i exited %i\n", pid,
714 					    ret);
715 					retval += ret;
716 				} else {
717 					dbg(stdout,
718 					    "<-- hogvm worker exited (%i)\n",
719 					    pid);
720 				}
721 			} else {
722 				dbg(stdout, "<-- hogvm worker signalled (%i)\n",
723 				    pid);
724 			}
725 
726 			--i;
727 		} else {
728 			dbg(stdout, "wait() returned error: %s\n",
729 			    strerror(errno));
730 			err(stderr, "detected missing hogvm worker children\n");
731 			++retval;
732 			break;
733 		}
734 	}
735 
736 	return retval;
737 }
738 
hoghdd(long long forks,int clean,long long files,long long bytes)739 int hoghdd(long long forks, int clean, long long files, long long bytes)
740 {
741 	long long i, j;
742 	int fd, pid, retval = 0;
743 	int chunk = (1024 * 1024) - 1;	/* Minimize slow writing.  */
744 	char buff[chunk];
745 
746 	/* Make local copies of global variables.  */
747 	int ignore = global_ignore;
748 	int retry = global_retry;
749 	int timeout = global_timeout;
750 	long backoff = global_backoff * forks;
751 
752 	/* Initialize buffer with some random ASCII data.  */
753 	dbg(stdout, "seeding buffer with random data\n");
754 	for (i = 0; i < chunk - 1; i++) {
755 		j = rand();
756 		j = (j < 0) ? -j : j;
757 		j %= 95;
758 		j += 32;
759 		buff[i] = j;
760 	}
761 	buff[i] = '\n';
762 
763 	dbg(stdout, "using backoff sleep of %lius for hoghdd\n", backoff);
764 
765 	for (i = 0; forks == 0 || i < forks; i++) {
766 		switch (pid = fork()) {
767 		case 0:	/* child */
768 			alarm(timeout);
769 
770 			/* Use a backoff sleep to ensure we get good fork throughput.  */
771 			usleep(backoff);
772 
773 			while (1) {
774 				for (i = 0; i < files; i++) {
775 					char name[] = "./stress.XXXXXX";
776 
777 					if ((fd = mkstemp(name)) < 0) {
778 						perror("mkstemp");
779 						err(stderr, "mkstemp failed\n");
780 						exit(1);
781 					}
782 
783 					if (clean == 0) {
784 						dbg(stdout, "unlinking %s\n",
785 						    name);
786 						if (unlink(name)) {
787 							err(stderr,
788 							    "unlink failed\n");
789 							exit(1);
790 						}
791 					}
792 
793 					dbg(stdout, "fast writing to %s\n",
794 					    name);
795 					for (j = 0;
796 					     bytes == 0 || j + chunk < bytes;
797 					     j += chunk) {
798 						if (write(fd, buff, chunk) !=
799 						    chunk) {
800 							err(stderr,
801 							    "write failed\n");
802 							exit(1);
803 						}
804 					}
805 
806 					dbg(stdout, "slow writing to %s\n",
807 					    name);
808 					for (; bytes == 0 || j < bytes - 1; j++) {
809 						if (write(fd, "Z", 1) != 1) {
810 							err(stderr,
811 							    "write failed\n");
812 							exit(1);
813 						}
814 					}
815 					if (write(fd, "\n", 1) != 1) {
816 						err(stderr, "write failed\n");
817 						exit(1);
818 					}
819 					++j;
820 
821 					dbg(stdout,
822 					    "closing %s after writing %lli bytes\n",
823 					    name, j);
824 					close(fd);
825 
826 					if (clean == 1) {
827 						if (unlink(name)) {
828 							err(stderr,
829 							    "unlink failed\n");
830 							exit(1);
831 						}
832 					}
833 				}
834 				if (retval == 0) {
835 					dbg(stdout,
836 					    "hoghdd worker starting over\n");
837 					continue;
838 				}
839 
840 				exit(retval);
841 			}
842 
843 			/* This case never falls through; alarm signal can cause exit.  */
844 		case -1:	/* error */
845 			if (ignore) {
846 				++retval;
847 				wrn(stderr,
848 				    "hoghdd worker fork failed, continuing\n");
849 				usleep(retry);
850 				continue;
851 			}
852 
853 			err(stderr, "hoghdd worker fork failed\n");
854 			return 1;
855 		default:	/* parent */
856 			dbg(stdout, "--> hoghdd worker forked (%i)\n", pid);
857 		}
858 	}
859 
860 	/* Wait for our children to exit.  */
861 	while (i) {
862 		int status, ret;
863 
864 		if ((pid = wait(&status)) > 0) {
865 			if ((WIFEXITED(status)) != 0) {
866 				if ((ret = WEXITSTATUS(status)) != 0) {
867 					err(stderr,
868 					    "hoghdd worker %i exited %i\n", pid,
869 					    ret);
870 					retval += ret;
871 				} else {
872 					dbg(stdout,
873 					    "<-- hoghdd worker exited (%i)\n",
874 					    pid);
875 				}
876 			} else {
877 				dbg(stdout,
878 				    "<-- hoghdd worker signalled (%i)\n", pid);
879 			}
880 
881 			--i;
882 		} else {
883 			dbg(stdout, "wait() returned error: %s\n",
884 			    strerror(errno));
885 			err(stderr,
886 			    "detected missing hoghdd worker children\n");
887 			++retval;
888 			break;
889 		}
890 	}
891 
892 	return retval;
893 }
894