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