xref: /illumos-gate/usr/src/cmd/raidz_test/raidz_test.c (revision 45ede40b2394db7967e59f19288fae9b62efd4aa)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 
22 /*
23  * Copyright (C) 2016 Gvozden Nešković. All rights reserved.
24  * Copyright 2020 Joyent, Inc.
25  */
26 
27 #include <sys/zfs_context.h>
28 #include <sys/time.h>
29 #include <sys/wait.h>
30 #include <sys/zio.h>
31 #include <umem.h>
32 #include <sys/vdev_raidz.h>
33 #include <sys/vdev_raidz_impl.h>
34 #include <assert.h>
35 #include <stdio.h>
36 #include <strings.h>
37 #include <unistd.h>
38 #include "raidz_test.h"
39 
40 static int *rand_data;
41 raidz_test_opts_t rto_opts;
42 
43 static char gdb[256];
44 static const char gdb_tmpl[] = "gdb -ex \"set pagination 0\" -p %d";
45 
46 #define	boot_ncpus	(sysconf(_SC_NPROCESSORS_ONLN))
47 
48 static void print_opts(raidz_test_opts_t *opts, boolean_t force)
49 {
50 	char *verbose;
51 	switch (opts->rto_v) {
52 		case 0:
53 			verbose = "no";
54 			break;
55 		case 1:
56 			verbose = "info";
57 			break;
58 		default:
59 			verbose = "debug";
60 			break;
61 	}
62 
63 	if (force || opts->rto_v >= D_INFO) {
64 		(void) fprintf(stdout, DBLSEP "Running with options:\n"
65 		    "  (-a) zio ashift                   : %zu\n"
66 		    "  (-o) zio offset                   : 1 << %zu\n"
67 		    "  (-d) number of raidz data columns : %zu\n"
68 		    "  (-s) size of DATA                 : 1 << %zu\n"
69 		    "  (-S) sweep parameters             : %s \n"
70 		    "  (-v) verbose                      : %s \n\n",
71 		    opts->rto_ashift,			/* -a */
72 		    ilog2(opts->rto_offset),		/* -o */
73 		    opts->rto_dcols,			/* -d */
74 		    ilog2(opts->rto_dsize),		/* -s */
75 		    opts->rto_sweep ? "yes" : "no",	/* -S */
76 		    verbose);				/* -v */
77 	}
78 }
79 
80 static void usage(boolean_t requested)
81 {
82 	const raidz_test_opts_t *o = &rto_opts_defaults;
83 
84 	FILE *fp = requested ? stdout : stderr;
85 
86 	(void) fprintf(fp, "Usage:\n"
87 	    "\t[-a zio ashift (default: %zu)]\n"
88 	    "\t[-o zio offset, exponent radix 2 (default: %zu)]\n"
89 	    "\t[-d number of raidz data columns (default: %zu)]\n"
90 	    "\t[-s zio size, exponent radix 2 (default: %zu)]\n"
91 	    "\t[-S parameter sweep (default: %s)]\n"
92 	    "\t[-t timeout for parameter sweep test]\n"
93 	    "\t[-B benchmark all raidz implementations]\n"
94 	    "\t[-v increase verbosity (default: %zu)]\n"
95 	    "\t[-h (print help)]\n"
96 	    "\t[-T test the test, see if failure would be detected]\n"
97 	    "\t[-D debug (attach gdb on SIGSEGV)]\n"
98 	    "",
99 	    o->rto_ashift,				/* -a */
100 	    ilog2(o->rto_offset),			/* -o */
101 	    o->rto_dcols,				/* -d */
102 	    ilog2(o->rto_dsize),			/* -s */
103 	    rto_opts.rto_sweep ? "yes" : "no",		/* -S */
104 	    o->rto_v);					/* -d */
105 
106 	exit(requested ? 0 : 1);
107 }
108 
109 static void process_options(int argc, char **argv)
110 {
111 	size_t value;
112 	int opt;
113 
114 	raidz_test_opts_t *o = &rto_opts;
115 
116 	bcopy(&rto_opts_defaults, o, sizeof (*o));
117 
118 	while ((opt = getopt(argc, argv, "TDBSvha:o:d:s:t:")) != -1) {
119 		value = 0;
120 
121 		switch (opt) {
122 		case 'a':
123 			value = strtoull(optarg, NULL, 0);
124 			o->rto_ashift = MIN(13, MAX(9, value));
125 			break;
126 		case 'o':
127 			value = strtoull(optarg, NULL, 0);
128 			o->rto_offset = ((1ULL << MIN(12, value)) >> 9) << 9;
129 			break;
130 		case 'd':
131 			value = strtoull(optarg, NULL, 0);
132 			o->rto_dcols = MIN(255, MAX(1, value));
133 			break;
134 		case 's':
135 			value = strtoull(optarg, NULL, 0);
136 			o->rto_dsize = 1ULL <<  MIN(SPA_MAXBLOCKSHIFT,
137 			    MAX(SPA_MINBLOCKSHIFT, value));
138 			break;
139 		case 't':
140 			value = strtoull(optarg, NULL, 0);
141 			o->rto_sweep_timeout = value;
142 			break;
143 		case 'v':
144 			o->rto_v++;
145 			break;
146 		case 'S':
147 			o->rto_sweep = 1;
148 			break;
149 		case 'B':
150 			o->rto_benchmark = 1;
151 			break;
152 		case 'D':
153 			o->rto_gdb = 1;
154 			break;
155 		case 'T':
156 			o->rto_sanity = 1;
157 			break;
158 		case 'h':
159 			usage(B_TRUE);
160 			break;
161 		case '?':
162 		default:
163 			usage(B_FALSE);
164 			break;
165 		}
166 	}
167 }
168 
169 #define	DATA_COL(rm, i) ((rm)->rm_col[raidz_parity(rm) + (i)].rc_abd)
170 #define	DATA_COL_SIZE(rm, i) ((rm)->rm_col[raidz_parity(rm) + (i)].rc_size)
171 
172 #define	CODE_COL(rm, i) ((rm)->rm_col[(i)].rc_abd)
173 #define	CODE_COL_SIZE(rm, i) ((rm)->rm_col[(i)].rc_size)
174 
175 static int
176 cmp_code(raidz_test_opts_t *opts, const raidz_map_t *rm, const int parity)
177 {
178 	int i, ret = 0;
179 
180 	VERIFY(parity >= 1 && parity <= 3);
181 
182 	for (i = 0; i < parity; i++) {
183 		if (abd_cmp(CODE_COL(rm, i), CODE_COL(opts->rm_golden, i),
184 		    CODE_COL(rm, i)->abd_size) != 0) {
185 			ret++;
186 			LOG_OPT(D_DEBUG, opts,
187 			    "\nParity block [%d] different!\n", i);
188 		}
189 	}
190 	return (ret);
191 }
192 
193 static int
194 cmp_data(raidz_test_opts_t *opts, raidz_map_t *rm)
195 {
196 	int i, ret = 0;
197 	int dcols = opts->rm_golden->rm_cols - raidz_parity(opts->rm_golden);
198 
199 	for (i = 0; i < dcols; i++) {
200 		if (abd_cmp(DATA_COL(opts->rm_golden, i), DATA_COL(rm, i),
201 		    DATA_COL(opts->rm_golden, i)->abd_size) != 0) {
202 			ret++;
203 
204 			LOG_OPT(D_DEBUG, opts,
205 			    "\nData block [%d] different!\n", i);
206 		}
207 	}
208 	return (ret);
209 }
210 
211 static int
212 init_rand(void *data, size_t size, void *private)
213 {
214 	int i;
215 	int *dst = (int *)data;
216 
217 	for (i = 0; i < size / sizeof (int); i++)
218 		dst[i] = rand_data[i];
219 
220 	return (0);
221 }
222 
223 static void
224 corrupt_colums(raidz_map_t *rm, const int *tgts, const int cnt)
225 {
226 	int i;
227 	raidz_col_t *col;
228 
229 	for (i = 0; i < cnt; i++) {
230 		col = &rm->rm_col[tgts[i]];
231 		(void) abd_iterate_func(col->rc_abd, 0, col->rc_size,
232 		    init_rand, NULL);
233 	}
234 }
235 
236 void
237 init_zio_abd(zio_t *zio)
238 {
239 	(void) abd_iterate_func(zio->io_abd, 0, zio->io_size, init_rand, NULL);
240 }
241 
242 static void
243 fini_raidz_map(zio_t **zio, raidz_map_t **rm)
244 {
245 	vdev_raidz_map_free(*rm);
246 	raidz_free((*zio)->io_abd, (*zio)->io_size);
247 	umem_free(*zio, sizeof (zio_t));
248 
249 	*zio = NULL;
250 	*rm = NULL;
251 }
252 
253 static int
254 init_raidz_golden_map(raidz_test_opts_t *opts, const int parity)
255 {
256 	int err = 0;
257 	zio_t *zio_test;
258 	raidz_map_t *rm_test;
259 	const size_t total_ncols = opts->rto_dcols + parity;
260 
261 	if (opts->rm_golden) {
262 		fini_raidz_map(&opts->zio_golden, &opts->rm_golden);
263 	}
264 
265 	opts->zio_golden = umem_zalloc(sizeof (zio_t), UMEM_NOFAIL);
266 	zio_test = umem_zalloc(sizeof (zio_t), UMEM_NOFAIL);
267 
268 	opts->zio_golden->io_offset = zio_test->io_offset = opts->rto_offset;
269 	opts->zio_golden->io_size = zio_test->io_size = opts->rto_dsize;
270 
271 	opts->zio_golden->io_abd = raidz_alloc(opts->rto_dsize);
272 	zio_test->io_abd = raidz_alloc(opts->rto_dsize);
273 
274 	init_zio_abd(opts->zio_golden);
275 	init_zio_abd(zio_test);
276 
277 	VERIFY0(vdev_raidz_impl_set("original"));
278 
279 	opts->rm_golden = vdev_raidz_map_alloc(opts->zio_golden,
280 	    opts->rto_ashift, total_ncols, parity);
281 	rm_test = vdev_raidz_map_alloc(zio_test,
282 	    opts->rto_ashift, total_ncols, parity);
283 
284 	VERIFY(opts->zio_golden);
285 	VERIFY(opts->rm_golden);
286 
287 	vdev_raidz_generate_parity(opts->rm_golden);
288 	vdev_raidz_generate_parity(rm_test);
289 
290 	/* sanity check */
291 	err |= cmp_data(opts, rm_test);
292 	err |= cmp_code(opts, rm_test, parity);
293 
294 	if (err)
295 		ERRMSG("initializing the golden copy ... [FAIL]!\n");
296 
297 	/* tear down raidz_map of test zio */
298 	fini_raidz_map(&zio_test, &rm_test);
299 
300 	return (err);
301 }
302 
303 static raidz_map_t *
304 init_raidz_map(raidz_test_opts_t *opts, zio_t **zio, const int parity)
305 {
306 	raidz_map_t *rm = NULL;
307 	const size_t alloc_dsize = opts->rto_dsize;
308 	const size_t total_ncols = opts->rto_dcols + parity;
309 	const int ccols[] = { 0, 1, 2 };
310 
311 	VERIFY(zio);
312 	VERIFY(parity <= 3 && parity >= 1);
313 
314 	*zio = umem_zalloc(sizeof (zio_t), UMEM_NOFAIL);
315 
316 	(*zio)->io_offset = 0;
317 	(*zio)->io_size = alloc_dsize;
318 	(*zio)->io_abd = raidz_alloc(alloc_dsize);
319 	init_zio_abd(*zio);
320 
321 	rm = vdev_raidz_map_alloc(*zio, opts->rto_ashift,
322 	    total_ncols, parity);
323 	VERIFY(rm);
324 
325 	/* Make sure code columns are destroyed */
326 	corrupt_colums(rm, ccols, parity);
327 
328 	return (rm);
329 }
330 
331 static int
332 run_gen_check(raidz_test_opts_t *opts)
333 {
334 	char **impl_name;
335 	int fn, err = 0;
336 	zio_t *zio_test;
337 	raidz_map_t *rm_test;
338 
339 	err = init_raidz_golden_map(opts, PARITY_PQR);
340 	if (0 != err)
341 		return (err);
342 
343 	LOG(D_INFO, DBLSEP);
344 	LOG(D_INFO, "Testing parity generation...\n");
345 
346 	for (impl_name = (char **)raidz_impl_names+1; *impl_name != NULL;
347 	    impl_name++) {
348 
349 		LOG(D_INFO, SEP);
350 		LOG(D_INFO, "\tTesting [%s] implementation...", *impl_name);
351 
352 		if (0 != vdev_raidz_impl_set(*impl_name)) {
353 			LOG(D_INFO, "[SKIP]\n");
354 			continue;
355 		} else {
356 			LOG(D_INFO, "[SUPPORTED]\n");
357 		}
358 
359 		for (fn = 0; fn < RAIDZ_GEN_NUM; fn++) {
360 
361 			/* Check if should stop */
362 			if (rto_opts.rto_should_stop)
363 				return (err);
364 
365 			/* create suitable raidz_map */
366 			rm_test = init_raidz_map(opts, &zio_test, fn+1);
367 			VERIFY(rm_test);
368 
369 			LOG(D_INFO, "\t\tTesting method [%s] ...",
370 			    raidz_gen_name[fn]);
371 
372 			if (!opts->rto_sanity)
373 				vdev_raidz_generate_parity(rm_test);
374 
375 			if (cmp_code(opts, rm_test, fn+1) != 0) {
376 				LOG(D_INFO, "[FAIL]\n");
377 				err++;
378 			} else
379 				LOG(D_INFO, "[PASS]\n");
380 
381 			fini_raidz_map(&zio_test, &rm_test);
382 		}
383 	}
384 
385 	fini_raidz_map(&opts->zio_golden, &opts->rm_golden);
386 
387 	return (err);
388 }
389 
390 static int
391 run_rec_check_impl(raidz_test_opts_t *opts, raidz_map_t *rm, const int fn)
392 {
393 	int x0, x1, x2;
394 	int tgtidx[3];
395 	int err = 0;
396 	static const int rec_tgts[7][3] = {
397 		{1, 2, 3},	/* rec_p:   bad QR & D[0]	*/
398 		{0, 2, 3},	/* rec_q:   bad PR & D[0]	*/
399 		{0, 1, 3},	/* rec_r:   bad PQ & D[0]	*/
400 		{2, 3, 4},	/* rec_pq:  bad R  & D[0][1]	*/
401 		{1, 3, 4},	/* rec_pr:  bad Q  & D[0][1]	*/
402 		{0, 3, 4},	/* rec_qr:  bad P  & D[0][1]	*/
403 		{3, 4, 5}	/* rec_pqr: bad    & D[0][1][2] */
404 	};
405 
406 	memcpy(tgtidx, rec_tgts[fn], sizeof (tgtidx));
407 
408 	if (fn < RAIDZ_REC_PQ) {
409 		/* can reconstruct 1 failed data disk */
410 		for (x0 = 0; x0 < opts->rto_dcols; x0++) {
411 			if (x0 >= rm->rm_cols - raidz_parity(rm))
412 				continue;
413 
414 			/* Check if should stop */
415 			if (rto_opts.rto_should_stop)
416 				return (err);
417 
418 			LOG(D_DEBUG, "[%d] ", x0);
419 
420 			tgtidx[2] = x0 + raidz_parity(rm);
421 
422 			corrupt_colums(rm, tgtidx+2, 1);
423 
424 			if (!opts->rto_sanity)
425 				(void) vdev_raidz_reconstruct(rm, tgtidx, 3);
426 
427 			if (cmp_data(opts, rm) != 0) {
428 				err++;
429 				LOG(D_DEBUG, "\nREC D[%d]... [FAIL]\n", x0);
430 			}
431 		}
432 
433 	} else if (fn < RAIDZ_REC_PQR) {
434 		/* can reconstruct 2 failed data disk */
435 		for (x0 = 0; x0 < opts->rto_dcols; x0++) {
436 			if (x0 >= rm->rm_cols - raidz_parity(rm))
437 				continue;
438 			for (x1 = x0 + 1; x1 < opts->rto_dcols; x1++) {
439 				if (x1 >= rm->rm_cols - raidz_parity(rm))
440 					continue;
441 
442 				/* Check if should stop */
443 				if (rto_opts.rto_should_stop)
444 					return (err);
445 
446 				LOG(D_DEBUG, "[%d %d] ", x0, x1);
447 
448 				tgtidx[1] = x0 + raidz_parity(rm);
449 				tgtidx[2] = x1 + raidz_parity(rm);
450 
451 				corrupt_colums(rm, tgtidx+1, 2);
452 
453 				if (!opts->rto_sanity)
454 					(void) vdev_raidz_reconstruct(rm,
455 					    tgtidx, 3);
456 
457 				if (cmp_data(opts, rm) != 0) {
458 					err++;
459 					LOG(D_DEBUG, "\nREC D[%d %d]... "
460 					    "[FAIL]\n", x0, x1);
461 				}
462 			}
463 		}
464 	} else {
465 		/* can reconstruct 3 failed data disk */
466 		for (x0 = 0; x0 < opts->rto_dcols; x0++) {
467 			if (x0 >= rm->rm_cols - raidz_parity(rm))
468 				continue;
469 			for (x1 = x0 + 1; x1 < opts->rto_dcols; x1++) {
470 				if (x1 >= rm->rm_cols - raidz_parity(rm))
471 					continue;
472 				for (x2 = x1 + 1; x2 < opts->rto_dcols; x2++) {
473 					if (x2 >=
474 					    rm->rm_cols - raidz_parity(rm))
475 						continue;
476 
477 					/* Check if should stop */
478 					if (rto_opts.rto_should_stop)
479 						return (err);
480 
481 					LOG(D_DEBUG, "[%d %d %d]", x0, x1, x2);
482 
483 					tgtidx[0] = x0 + raidz_parity(rm);
484 					tgtidx[1] = x1 + raidz_parity(rm);
485 					tgtidx[2] = x2 + raidz_parity(rm);
486 
487 					corrupt_colums(rm, tgtidx, 3);
488 
489 					if (!opts->rto_sanity)
490 						(void) vdev_raidz_reconstruct(
491 						    rm, tgtidx, 3);
492 
493 					if (cmp_data(opts, rm) != 0) {
494 						err++;
495 						LOG(D_DEBUG,
496 						    "\nREC D[%d %d %d]... "
497 						    "[FAIL]\n", x0, x1, x2);
498 					}
499 				}
500 			}
501 		}
502 	}
503 	return (err);
504 }
505 
506 static int
507 run_rec_check(raidz_test_opts_t *opts)
508 {
509 	char **impl_name;
510 	unsigned fn, err = 0;
511 	zio_t *zio_test;
512 	raidz_map_t *rm_test;
513 
514 	err = init_raidz_golden_map(opts, PARITY_PQR);
515 	if (0 != err)
516 		return (err);
517 
518 	LOG(D_INFO, DBLSEP);
519 	LOG(D_INFO, "Testing data reconstruction...\n");
520 
521 	for (impl_name = (char **)raidz_impl_names+1; *impl_name != NULL;
522 	    impl_name++) {
523 
524 		LOG(D_INFO, SEP);
525 		LOG(D_INFO, "\tTesting [%s] implementation...", *impl_name);
526 
527 		if (vdev_raidz_impl_set(*impl_name) != 0) {
528 			LOG(D_INFO, "[SKIP]\n");
529 			continue;
530 		} else
531 			LOG(D_INFO, "[SUPPORTED]\n");
532 
533 
534 		/* create suitable raidz_map */
535 		rm_test = init_raidz_map(opts, &zio_test, PARITY_PQR);
536 		/* generate parity */
537 		vdev_raidz_generate_parity(rm_test);
538 
539 		for (fn = 0; fn < RAIDZ_REC_NUM; fn++) {
540 
541 			LOG(D_INFO, "\t\tTesting method [%s] ...",
542 			    raidz_rec_name[fn]);
543 
544 			if (run_rec_check_impl(opts, rm_test, fn) != 0) {
545 				LOG(D_INFO, "[FAIL]\n");
546 				err++;
547 
548 			} else
549 				LOG(D_INFO, "[PASS]\n");
550 
551 		}
552 		/* tear down test raidz_map */
553 		fini_raidz_map(&zio_test, &rm_test);
554 	}
555 
556 	fini_raidz_map(&opts->zio_golden, &opts->rm_golden);
557 
558 	return (err);
559 }
560 
561 static int
562 run_test(raidz_test_opts_t *opts)
563 {
564 	int err = 0;
565 
566 	if (opts == NULL)
567 		opts = &rto_opts;
568 
569 	print_opts(opts, B_FALSE);
570 
571 	err |= run_gen_check(opts);
572 	err |= run_rec_check(opts);
573 
574 	return (err);
575 }
576 
577 #define	SWEEP_RUNNING	0
578 #define	SWEEP_FINISHED	1
579 #define	SWEEP_ERROR	2
580 #define	SWEEP_TIMEOUT	3
581 
582 static int sweep_state = 0;
583 static raidz_test_opts_t failed_opts;
584 
585 static kmutex_t sem_mtx;
586 static kcondvar_t sem_cv;
587 static int max_free_slots;
588 static int free_slots;
589 
590 static void
591 sweep_thread(void *arg)
592 {
593 	int err = 0;
594 	raidz_test_opts_t *opts = (raidz_test_opts_t *)arg;
595 	VERIFY(opts != NULL);
596 
597 	err = run_test(opts);
598 
599 	if (rto_opts.rto_sanity) {
600 		/* 25% chance that a sweep test fails */
601 		if (rand() < (RAND_MAX/4))
602 			err = 1;
603 	}
604 
605 	if (0 != err) {
606 		mutex_enter(&sem_mtx);
607 		memcpy(&failed_opts, opts, sizeof (raidz_test_opts_t));
608 		sweep_state = SWEEP_ERROR;
609 		mutex_exit(&sem_mtx);
610 	}
611 
612 	umem_free(opts, sizeof (raidz_test_opts_t));
613 
614 	/* signal the next thread */
615 	mutex_enter(&sem_mtx);
616 	free_slots++;
617 	cv_signal(&sem_cv);
618 	mutex_exit(&sem_mtx);
619 
620 	thread_exit();
621 }
622 
623 static int
624 run_sweep(void)
625 {
626 	static const size_t dcols_v[] = { 1, 2, 3, 4, 5, 6, 7, 8, 12, 15, 16 };
627 	static const size_t ashift_v[] = { 9, 12, 14 };
628 	static const size_t size_v[] = { 1 << 9, 21 * (1 << 9), 13 * (1 << 12),
629 		1 << 17, (1 << 20) - (1 << 12), SPA_MAXBLOCKSIZE };
630 
631 	(void) setvbuf(stdout, NULL, _IONBF, 0);
632 
633 	ulong_t total_comb = ARRAY_SIZE(size_v) * ARRAY_SIZE(ashift_v) *
634 	    ARRAY_SIZE(dcols_v);
635 	ulong_t tried_comb = 0;
636 	hrtime_t time_diff, start_time = gethrtime();
637 	raidz_test_opts_t *opts;
638 	int a, d, s;
639 
640 	max_free_slots = free_slots = MAX(2, boot_ncpus);
641 
642 	mutex_init(&sem_mtx, NULL, MUTEX_DEFAULT, NULL);
643 	cv_init(&sem_cv, NULL, CV_DEFAULT, NULL);
644 
645 	for (s = 0; s < ARRAY_SIZE(size_v); s++)
646 	for (a = 0; a < ARRAY_SIZE(ashift_v); a++)
647 	for (d = 0; d < ARRAY_SIZE(dcols_v); d++) {
648 
649 		if (size_v[s] < (1 << ashift_v[a])) {
650 			total_comb--;
651 			continue;
652 		}
653 
654 		if (++tried_comb % 20 == 0)
655 			LOG(D_ALL, "%lu/%lu... ", tried_comb, total_comb);
656 
657 		/* wait for signal to start new thread */
658 		mutex_enter(&sem_mtx);
659 		while (cv_timedwait_sig(&sem_cv, &sem_mtx,
660 		    ddi_get_lbolt() + hz)) {
661 
662 			/* check if should stop the test (timeout) */
663 			time_diff = (gethrtime() - start_time) / NANOSEC;
664 			if (rto_opts.rto_sweep_timeout > 0 &&
665 			    time_diff >= rto_opts.rto_sweep_timeout) {
666 				sweep_state = SWEEP_TIMEOUT;
667 				rto_opts.rto_should_stop = B_TRUE;
668 				mutex_exit(&sem_mtx);
669 				goto exit;
670 			}
671 
672 			/* check if should stop the test (error) */
673 			if (sweep_state != SWEEP_RUNNING) {
674 				mutex_exit(&sem_mtx);
675 				goto exit;
676 			}
677 
678 			/* exit loop if a slot is available */
679 			if (free_slots > 0) {
680 				break;
681 			}
682 		}
683 
684 		free_slots--;
685 		mutex_exit(&sem_mtx);
686 
687 		opts = umem_zalloc(sizeof (raidz_test_opts_t), UMEM_NOFAIL);
688 		opts->rto_ashift = ashift_v[a];
689 		opts->rto_dcols = dcols_v[d];
690 		opts->rto_offset = (1 << ashift_v[a]) * rand();
691 		opts->rto_dsize = size_v[s];
692 		opts->rto_v = 0; /* be quiet */
693 
694 		VERIFY3P(thread_create(NULL, 0, sweep_thread, (void *) opts,
695 		    0, NULL, TS_RUN, maxclsyspri), !=, NULL);
696 	}
697 
698 exit:
699 	LOG(D_ALL, "\nWaiting for test threads to finish...\n");
700 	mutex_enter(&sem_mtx);
701 	VERIFY(free_slots <= max_free_slots);
702 	while (free_slots < max_free_slots) {
703 		(void) cv_wait(&sem_cv, &sem_mtx);
704 	}
705 	mutex_exit(&sem_mtx);
706 
707 	if (sweep_state == SWEEP_ERROR) {
708 		ERRMSG("Sweep test failed! Failed option: \n");
709 		print_opts(&failed_opts, B_TRUE);
710 	} else {
711 		if (sweep_state == SWEEP_TIMEOUT)
712 			LOG(D_ALL, "Test timeout (%lus). Stopping...\n",
713 			    (ulong_t)rto_opts.rto_sweep_timeout);
714 
715 		LOG(D_ALL, "Sweep test succeeded on %lu raidz maps!\n",
716 		    (ulong_t)tried_comb);
717 	}
718 
719 	mutex_destroy(&sem_mtx);
720 
721 	return (sweep_state == SWEEP_ERROR ? SWEEP_ERROR : 0);
722 }
723 
724 int
725 main(int argc, char **argv)
726 {
727 	size_t i;
728 	int err = 0;
729 
730 	/* init gdb string early */
731 	(void) sprintf(gdb, gdb_tmpl, getpid());
732 
733 	(void) setvbuf(stdout, NULL, _IOLBF, 0);
734 
735 	dprintf_setup(&argc, argv);
736 
737 	process_options(argc, argv);
738 
739 	kernel_init(FREAD);
740 
741 	/* setup random data because rand() is not reentrant */
742 	rand_data = (int *)umem_alloc(SPA_MAXBLOCKSIZE, UMEM_NOFAIL);
743 	srand((unsigned)time(NULL) * getpid());
744 	for (i = 0; i < SPA_MAXBLOCKSIZE / sizeof (int); i++)
745 		rand_data[i] = rand();
746 
747 	mprotect((void *)rand_data, SPA_MAXBLOCKSIZE, PROT_READ);
748 
749 	if (rto_opts.rto_benchmark) {
750 		run_raidz_benchmark();
751 	} else if (rto_opts.rto_sweep) {
752 		err = run_sweep();
753 	} else {
754 		err = run_test(NULL);
755 	}
756 
757 	umem_free(rand_data, SPA_MAXBLOCKSIZE);
758 	kernel_fini();
759 
760 	return (err);
761 }
762