Home | History | Annotate | Download | only in genload
      1 /* A program to put stress on a POSIX system (stress).
      2  *
      3  * Copyright (C) 2001, 2002 Amos Waterland <awaterl (at) 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 
     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 
    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 
    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  */
    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  */
    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 
    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 
    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 
    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 
    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