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