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