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