| /* |
| * CDDL HEADER START |
| * |
| * The contents of this file are subject to the terms of the |
| * Common Development and Distribution License (the "License"). |
| * You may not use this file except in compliance with the License. |
| * |
| * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE |
| * or http://www.opensolaris.org/os/licensing. |
| * See the License for the specific language governing permissions |
| * and limitations under the License. |
| * |
| * When distributing Covered Code, include this CDDL HEADER in each |
| * file and include the License file at usr/src/OPENSOLARIS.LICENSE. |
| * If applicable, add the following below this CDDL HEADER, with the |
| * fields enclosed by brackets "[]" replaced with your own identifying |
| * information: Portions Copyright [yyyy] [name of copyright owner] |
| * |
| * CDDL HEADER END |
| */ |
| |
| /* |
| * Copyright (C) 2016 Gvozden Nešković. All rights reserved. |
| */ |
| |
| #include <sys/zfs_context.h> |
| #include <sys/time.h> |
| #include <sys/wait.h> |
| #include <sys/zio.h> |
| #include <umem.h> |
| #include <sys/vdev_raidz.h> |
| #include <sys/vdev_raidz_impl.h> |
| #include <assert.h> |
| #include <stdio.h> |
| #include "raidz_test.h" |
| |
| static int *rand_data; |
| raidz_test_opts_t rto_opts; |
| |
| static char gdb[256]; |
| static const char gdb_tmpl[] = "gdb -ex \"set pagination 0\" -p %d"; |
| |
| static void sig_handler(int signo) |
| { |
| struct sigaction action; |
| /* |
| * Restore default action and re-raise signal so SIGSEGV and |
| * SIGABRT can trigger a core dump. |
| */ |
| action.sa_handler = SIG_DFL; |
| sigemptyset(&action.sa_mask); |
| action.sa_flags = 0; |
| (void) sigaction(signo, &action, NULL); |
| |
| if (rto_opts.rto_gdb) |
| if (system(gdb)) { } |
| |
| raise(signo); |
| } |
| |
| static void print_opts(raidz_test_opts_t *opts, boolean_t force) |
| { |
| char *verbose; |
| switch (opts->rto_v) { |
| case 0: |
| verbose = "no"; |
| break; |
| case 1: |
| verbose = "info"; |
| break; |
| default: |
| verbose = "debug"; |
| break; |
| } |
| |
| if (force || opts->rto_v >= D_INFO) { |
| (void) fprintf(stdout, DBLSEP "Running with options:\n" |
| " (-a) zio ashift : %zu\n" |
| " (-o) zio offset : 1 << %zu\n" |
| " (-d) number of raidz data columns : %zu\n" |
| " (-s) size of DATA : 1 << %zu\n" |
| " (-S) sweep parameters : %s \n" |
| " (-v) verbose : %s \n\n", |
| opts->rto_ashift, /* -a */ |
| ilog2(opts->rto_offset), /* -o */ |
| opts->rto_dcols, /* -d */ |
| ilog2(opts->rto_dsize), /* -s */ |
| opts->rto_sweep ? "yes" : "no", /* -S */ |
| verbose); /* -v */ |
| } |
| } |
| |
| static void usage(boolean_t requested) |
| { |
| const raidz_test_opts_t *o = &rto_opts_defaults; |
| |
| FILE *fp = requested ? stdout : stderr; |
| |
| (void) fprintf(fp, "Usage:\n" |
| "\t[-a zio ashift (default: %zu)]\n" |
| "\t[-o zio offset, exponent radix 2 (default: %zu)]\n" |
| "\t[-d number of raidz data columns (default: %zu)]\n" |
| "\t[-s zio size, exponent radix 2 (default: %zu)]\n" |
| "\t[-S parameter sweep (default: %s)]\n" |
| "\t[-t timeout for parameter sweep test]\n" |
| "\t[-B benchmark all raidz implementations]\n" |
| "\t[-v increase verbosity (default: %zu)]\n" |
| "\t[-h (print help)]\n" |
| "\t[-T test the test, see if failure would be detected]\n" |
| "\t[-D debug (attach gdb on SIGSEGV)]\n" |
| "", |
| o->rto_ashift, /* -a */ |
| ilog2(o->rto_offset), /* -o */ |
| o->rto_dcols, /* -d */ |
| ilog2(o->rto_dsize), /* -s */ |
| rto_opts.rto_sweep ? "yes" : "no", /* -S */ |
| o->rto_v); /* -d */ |
| |
| exit(requested ? 0 : 1); |
| } |
| |
| static void process_options(int argc, char **argv) |
| { |
| size_t value; |
| int opt; |
| |
| raidz_test_opts_t *o = &rto_opts; |
| |
| bcopy(&rto_opts_defaults, o, sizeof (*o)); |
| |
| while ((opt = getopt(argc, argv, "TDBSvha:o:d:s:t:")) != -1) { |
| value = 0; |
| |
| switch (opt) { |
| case 'a': |
| value = strtoull(optarg, NULL, 0); |
| o->rto_ashift = MIN(13, MAX(9, value)); |
| break; |
| case 'o': |
| value = strtoull(optarg, NULL, 0); |
| o->rto_offset = ((1ULL << MIN(12, value)) >> 9) << 9; |
| break; |
| case 'd': |
| value = strtoull(optarg, NULL, 0); |
| o->rto_dcols = MIN(255, MAX(1, value)); |
| break; |
| case 's': |
| value = strtoull(optarg, NULL, 0); |
| o->rto_dsize = 1ULL << MIN(SPA_MAXBLOCKSHIFT, |
| MAX(SPA_MINBLOCKSHIFT, value)); |
| break; |
| case 't': |
| value = strtoull(optarg, NULL, 0); |
| o->rto_sweep_timeout = value; |
| break; |
| case 'v': |
| o->rto_v++; |
| break; |
| case 'S': |
| o->rto_sweep = 1; |
| break; |
| case 'B': |
| o->rto_benchmark = 1; |
| break; |
| case 'D': |
| o->rto_gdb = 1; |
| break; |
| case 'T': |
| o->rto_sanity = 1; |
| break; |
| case 'h': |
| usage(B_TRUE); |
| break; |
| case '?': |
| default: |
| usage(B_FALSE); |
| break; |
| } |
| } |
| } |
| |
| #define DATA_COL(rm, i) ((rm)->rm_col[raidz_parity(rm) + (i)].rc_abd) |
| #define DATA_COL_SIZE(rm, i) ((rm)->rm_col[raidz_parity(rm) + (i)].rc_size) |
| |
| #define CODE_COL(rm, i) ((rm)->rm_col[(i)].rc_abd) |
| #define CODE_COL_SIZE(rm, i) ((rm)->rm_col[(i)].rc_size) |
| |
| static int |
| cmp_code(raidz_test_opts_t *opts, const raidz_map_t *rm, const int parity) |
| { |
| int i, ret = 0; |
| |
| VERIFY(parity >= 1 && parity <= 3); |
| |
| for (i = 0; i < parity; i++) { |
| if (abd_cmp(CODE_COL(rm, i), CODE_COL(opts->rm_golden, i)) |
| != 0) { |
| ret++; |
| LOG_OPT(D_DEBUG, opts, |
| "\nParity block [%d] different!\n", i); |
| } |
| } |
| return (ret); |
| } |
| |
| static int |
| cmp_data(raidz_test_opts_t *opts, raidz_map_t *rm) |
| { |
| int i, ret = 0; |
| int dcols = opts->rm_golden->rm_cols - raidz_parity(opts->rm_golden); |
| |
| for (i = 0; i < dcols; i++) { |
| if (abd_cmp(DATA_COL(opts->rm_golden, i), DATA_COL(rm, i)) |
| != 0) { |
| ret++; |
| |
| LOG_OPT(D_DEBUG, opts, |
| "\nData block [%d] different!\n", i); |
| } |
| } |
| return (ret); |
| } |
| |
| static int |
| init_rand(void *data, size_t size, void *private) |
| { |
| int i; |
| int *dst = (int *)data; |
| |
| for (i = 0; i < size / sizeof (int); i++) |
| dst[i] = rand_data[i]; |
| |
| return (0); |
| } |
| |
| static void |
| corrupt_colums(raidz_map_t *rm, const int *tgts, const int cnt) |
| { |
| int i; |
| raidz_col_t *col; |
| |
| for (i = 0; i < cnt; i++) { |
| col = &rm->rm_col[tgts[i]]; |
| abd_iterate_func(col->rc_abd, 0, col->rc_size, init_rand, NULL); |
| } |
| } |
| |
| void |
| init_zio_abd(zio_t *zio) |
| { |
| abd_iterate_func(zio->io_abd, 0, zio->io_size, init_rand, NULL); |
| } |
| |
| static void |
| fini_raidz_map(zio_t **zio, raidz_map_t **rm) |
| { |
| vdev_raidz_map_free(*rm); |
| raidz_free((*zio)->io_abd, (*zio)->io_size); |
| umem_free(*zio, sizeof (zio_t)); |
| |
| *zio = NULL; |
| *rm = NULL; |
| } |
| |
| static int |
| init_raidz_golden_map(raidz_test_opts_t *opts, const int parity) |
| { |
| int err = 0; |
| zio_t *zio_test; |
| raidz_map_t *rm_test; |
| const size_t total_ncols = opts->rto_dcols + parity; |
| |
| if (opts->rm_golden) { |
| fini_raidz_map(&opts->zio_golden, &opts->rm_golden); |
| } |
| |
| opts->zio_golden = umem_zalloc(sizeof (zio_t), UMEM_NOFAIL); |
| zio_test = umem_zalloc(sizeof (zio_t), UMEM_NOFAIL); |
| |
| opts->zio_golden->io_offset = zio_test->io_offset = opts->rto_offset; |
| opts->zio_golden->io_size = zio_test->io_size = opts->rto_dsize; |
| |
| opts->zio_golden->io_abd = raidz_alloc(opts->rto_dsize); |
| zio_test->io_abd = raidz_alloc(opts->rto_dsize); |
| |
| init_zio_abd(opts->zio_golden); |
| init_zio_abd(zio_test); |
| |
| VERIFY0(vdev_raidz_impl_set("original")); |
| |
| opts->rm_golden = vdev_raidz_map_alloc(opts->zio_golden, |
| opts->rto_ashift, total_ncols, parity); |
| rm_test = vdev_raidz_map_alloc(zio_test, |
| opts->rto_ashift, total_ncols, parity); |
| |
| VERIFY(opts->zio_golden); |
| VERIFY(opts->rm_golden); |
| |
| vdev_raidz_generate_parity(opts->rm_golden); |
| vdev_raidz_generate_parity(rm_test); |
| |
| /* sanity check */ |
| err |= cmp_data(opts, rm_test); |
| err |= cmp_code(opts, rm_test, parity); |
| |
| if (err) |
| ERR("initializing the golden copy ... [FAIL]!\n"); |
| |
| /* tear down raidz_map of test zio */ |
| fini_raidz_map(&zio_test, &rm_test); |
| |
| return (err); |
| } |
| |
| static raidz_map_t * |
| init_raidz_map(raidz_test_opts_t *opts, zio_t **zio, const int parity) |
| { |
| raidz_map_t *rm = NULL; |
| const size_t alloc_dsize = opts->rto_dsize; |
| const size_t total_ncols = opts->rto_dcols + parity; |
| const int ccols[] = { 0, 1, 2 }; |
| |
| VERIFY(zio); |
| VERIFY(parity <= 3 && parity >= 1); |
| |
| *zio = umem_zalloc(sizeof (zio_t), UMEM_NOFAIL); |
| |
| (*zio)->io_offset = 0; |
| (*zio)->io_size = alloc_dsize; |
| (*zio)->io_abd = raidz_alloc(alloc_dsize); |
| init_zio_abd(*zio); |
| |
| rm = vdev_raidz_map_alloc(*zio, opts->rto_ashift, |
| total_ncols, parity); |
| VERIFY(rm); |
| |
| /* Make sure code columns are destroyed */ |
| corrupt_colums(rm, ccols, parity); |
| |
| return (rm); |
| } |
| |
| static int |
| run_gen_check(raidz_test_opts_t *opts) |
| { |
| char **impl_name; |
| int fn, err = 0; |
| zio_t *zio_test; |
| raidz_map_t *rm_test; |
| |
| err = init_raidz_golden_map(opts, PARITY_PQR); |
| if (0 != err) |
| return (err); |
| |
| LOG(D_INFO, DBLSEP); |
| LOG(D_INFO, "Testing parity generation...\n"); |
| |
| for (impl_name = (char **)raidz_impl_names+1; *impl_name != NULL; |
| impl_name++) { |
| |
| LOG(D_INFO, SEP); |
| LOG(D_INFO, "\tTesting [%s] implementation...", *impl_name); |
| |
| if (0 != vdev_raidz_impl_set(*impl_name)) { |
| LOG(D_INFO, "[SKIP]\n"); |
| continue; |
| } else { |
| LOG(D_INFO, "[SUPPORTED]\n"); |
| } |
| |
| for (fn = 0; fn < RAIDZ_GEN_NUM; fn++) { |
| |
| /* Check if should stop */ |
| if (rto_opts.rto_should_stop) |
| return (err); |
| |
| /* create suitable raidz_map */ |
| rm_test = init_raidz_map(opts, &zio_test, fn+1); |
| VERIFY(rm_test); |
| |
| LOG(D_INFO, "\t\tTesting method [%s] ...", |
| raidz_gen_name[fn]); |
| |
| if (!opts->rto_sanity) |
| vdev_raidz_generate_parity(rm_test); |
| |
| if (cmp_code(opts, rm_test, fn+1) != 0) { |
| LOG(D_INFO, "[FAIL]\n"); |
| err++; |
| } else |
| LOG(D_INFO, "[PASS]\n"); |
| |
| fini_raidz_map(&zio_test, &rm_test); |
| } |
| } |
| |
| fini_raidz_map(&opts->zio_golden, &opts->rm_golden); |
| |
| return (err); |
| } |
| |
| static int |
| run_rec_check_impl(raidz_test_opts_t *opts, raidz_map_t *rm, const int fn) |
| { |
| int x0, x1, x2; |
| int tgtidx[3]; |
| int err = 0; |
| static const int rec_tgts[7][3] = { |
| {1, 2, 3}, /* rec_p: bad QR & D[0] */ |
| {0, 2, 3}, /* rec_q: bad PR & D[0] */ |
| {0, 1, 3}, /* rec_r: bad PQ & D[0] */ |
| {2, 3, 4}, /* rec_pq: bad R & D[0][1] */ |
| {1, 3, 4}, /* rec_pr: bad Q & D[0][1] */ |
| {0, 3, 4}, /* rec_qr: bad P & D[0][1] */ |
| {3, 4, 5} /* rec_pqr: bad & D[0][1][2] */ |
| }; |
| |
| memcpy(tgtidx, rec_tgts[fn], sizeof (tgtidx)); |
| |
| if (fn < RAIDZ_REC_PQ) { |
| /* can reconstruct 1 failed data disk */ |
| for (x0 = 0; x0 < opts->rto_dcols; x0++) { |
| if (x0 >= rm->rm_cols - raidz_parity(rm)) |
| continue; |
| |
| /* Check if should stop */ |
| if (rto_opts.rto_should_stop) |
| return (err); |
| |
| LOG(D_DEBUG, "[%d] ", x0); |
| |
| tgtidx[2] = x0 + raidz_parity(rm); |
| |
| corrupt_colums(rm, tgtidx+2, 1); |
| |
| if (!opts->rto_sanity) |
| vdev_raidz_reconstruct(rm, tgtidx, 3); |
| |
| if (cmp_data(opts, rm) != 0) { |
| err++; |
| LOG(D_DEBUG, "\nREC D[%d]... [FAIL]\n", x0); |
| } |
| } |
| |
| } else if (fn < RAIDZ_REC_PQR) { |
| /* can reconstruct 2 failed data disk */ |
| for (x0 = 0; x0 < opts->rto_dcols; x0++) { |
| if (x0 >= rm->rm_cols - raidz_parity(rm)) |
| continue; |
| for (x1 = x0 + 1; x1 < opts->rto_dcols; x1++) { |
| if (x1 >= rm->rm_cols - raidz_parity(rm)) |
| continue; |
| |
| /* Check if should stop */ |
| if (rto_opts.rto_should_stop) |
| return (err); |
| |
| LOG(D_DEBUG, "[%d %d] ", x0, x1); |
| |
| tgtidx[1] = x0 + raidz_parity(rm); |
| tgtidx[2] = x1 + raidz_parity(rm); |
| |
| corrupt_colums(rm, tgtidx+1, 2); |
| |
| if (!opts->rto_sanity) |
| vdev_raidz_reconstruct(rm, tgtidx, 3); |
| |
| if (cmp_data(opts, rm) != 0) { |
| err++; |
| LOG(D_DEBUG, "\nREC D[%d %d]... " |
| "[FAIL]\n", x0, x1); |
| } |
| } |
| } |
| } else { |
| /* can reconstruct 3 failed data disk */ |
| for (x0 = 0; x0 < opts->rto_dcols; x0++) { |
| if (x0 >= rm->rm_cols - raidz_parity(rm)) |
| continue; |
| for (x1 = x0 + 1; x1 < opts->rto_dcols; x1++) { |
| if (x1 >= rm->rm_cols - raidz_parity(rm)) |
| continue; |
| for (x2 = x1 + 1; x2 < opts->rto_dcols; x2++) { |
| if (x2 >= |
| rm->rm_cols - raidz_parity(rm)) |
| continue; |
| |
| /* Check if should stop */ |
| if (rto_opts.rto_should_stop) |
| return (err); |
| |
| LOG(D_DEBUG, "[%d %d %d]", x0, x1, x2); |
| |
| tgtidx[0] = x0 + raidz_parity(rm); |
| tgtidx[1] = x1 + raidz_parity(rm); |
| tgtidx[2] = x2 + raidz_parity(rm); |
| |
| corrupt_colums(rm, tgtidx, 3); |
| |
| if (!opts->rto_sanity) |
| vdev_raidz_reconstruct(rm, |
| tgtidx, 3); |
| |
| if (cmp_data(opts, rm) != 0) { |
| err++; |
| LOG(D_DEBUG, |
| "\nREC D[%d %d %d]... " |
| "[FAIL]\n", x0, x1, x2); |
| } |
| } |
| } |
| } |
| } |
| return (err); |
| } |
| |
| static int |
| run_rec_check(raidz_test_opts_t *opts) |
| { |
| char **impl_name; |
| unsigned fn, err = 0; |
| zio_t *zio_test; |
| raidz_map_t *rm_test; |
| |
| err = init_raidz_golden_map(opts, PARITY_PQR); |
| if (0 != err) |
| return (err); |
| |
| LOG(D_INFO, DBLSEP); |
| LOG(D_INFO, "Testing data reconstruction...\n"); |
| |
| for (impl_name = (char **)raidz_impl_names+1; *impl_name != NULL; |
| impl_name++) { |
| |
| LOG(D_INFO, SEP); |
| LOG(D_INFO, "\tTesting [%s] implementation...", *impl_name); |
| |
| if (vdev_raidz_impl_set(*impl_name) != 0) { |
| LOG(D_INFO, "[SKIP]\n"); |
| continue; |
| } else |
| LOG(D_INFO, "[SUPPORTED]\n"); |
| |
| |
| /* create suitable raidz_map */ |
| rm_test = init_raidz_map(opts, &zio_test, PARITY_PQR); |
| /* generate parity */ |
| vdev_raidz_generate_parity(rm_test); |
| |
| for (fn = 0; fn < RAIDZ_REC_NUM; fn++) { |
| |
| LOG(D_INFO, "\t\tTesting method [%s] ...", |
| raidz_rec_name[fn]); |
| |
| if (run_rec_check_impl(opts, rm_test, fn) != 0) { |
| LOG(D_INFO, "[FAIL]\n"); |
| err++; |
| |
| } else |
| LOG(D_INFO, "[PASS]\n"); |
| |
| } |
| /* tear down test raidz_map */ |
| fini_raidz_map(&zio_test, &rm_test); |
| } |
| |
| fini_raidz_map(&opts->zio_golden, &opts->rm_golden); |
| |
| return (err); |
| } |
| |
| static int |
| run_test(raidz_test_opts_t *opts) |
| { |
| int err = 0; |
| |
| if (opts == NULL) |
| opts = &rto_opts; |
| |
| print_opts(opts, B_FALSE); |
| |
| err |= run_gen_check(opts); |
| err |= run_rec_check(opts); |
| |
| return (err); |
| } |
| |
| #define SWEEP_RUNNING 0 |
| #define SWEEP_FINISHED 1 |
| #define SWEEP_ERROR 2 |
| #define SWEEP_TIMEOUT 3 |
| |
| static int sweep_state = 0; |
| static raidz_test_opts_t failed_opts; |
| |
| static kmutex_t sem_mtx; |
| static kcondvar_t sem_cv; |
| static int max_free_slots; |
| static int free_slots; |
| |
| static void |
| sweep_thread(void *arg) |
| { |
| int err = 0; |
| raidz_test_opts_t *opts = (raidz_test_opts_t *)arg; |
| VERIFY(opts != NULL); |
| |
| err = run_test(opts); |
| |
| if (rto_opts.rto_sanity) { |
| /* 25% chance that a sweep test fails */ |
| if (rand() < (RAND_MAX/4)) |
| err = 1; |
| } |
| |
| if (0 != err) { |
| mutex_enter(&sem_mtx); |
| memcpy(&failed_opts, opts, sizeof (raidz_test_opts_t)); |
| sweep_state = SWEEP_ERROR; |
| mutex_exit(&sem_mtx); |
| } |
| |
| umem_free(opts, sizeof (raidz_test_opts_t)); |
| |
| /* signal the next thread */ |
| mutex_enter(&sem_mtx); |
| free_slots++; |
| cv_signal(&sem_cv); |
| mutex_exit(&sem_mtx); |
| |
| thread_exit(); |
| } |
| |
| static int |
| run_sweep(void) |
| { |
| static const size_t dcols_v[] = { 1, 2, 3, 4, 5, 6, 7, 8, 12, 15, 16 }; |
| static const size_t ashift_v[] = { 9, 12, 14 }; |
| static const size_t size_v[] = { 1 << 9, 21 * (1 << 9), 13 * (1 << 12), |
| 1 << 17, (1 << 20) - (1 << 12), SPA_MAXBLOCKSIZE }; |
| |
| (void) setvbuf(stdout, NULL, _IONBF, 0); |
| |
| ulong_t total_comb = ARRAY_SIZE(size_v) * ARRAY_SIZE(ashift_v) * |
| ARRAY_SIZE(dcols_v); |
| ulong_t tried_comb = 0; |
| hrtime_t time_diff, start_time = gethrtime(); |
| raidz_test_opts_t *opts; |
| int a, d, s; |
| |
| max_free_slots = free_slots = MAX(2, boot_ncpus); |
| |
| mutex_init(&sem_mtx, NULL, MUTEX_DEFAULT, NULL); |
| cv_init(&sem_cv, NULL, CV_DEFAULT, NULL); |
| |
| for (s = 0; s < ARRAY_SIZE(size_v); s++) |
| for (a = 0; a < ARRAY_SIZE(ashift_v); a++) |
| for (d = 0; d < ARRAY_SIZE(dcols_v); d++) { |
| |
| if (size_v[s] < (1 << ashift_v[a])) { |
| total_comb--; |
| continue; |
| } |
| |
| if (++tried_comb % 20 == 0) |
| LOG(D_ALL, "%lu/%lu... ", tried_comb, total_comb); |
| |
| /* wait for signal to start new thread */ |
| mutex_enter(&sem_mtx); |
| while (cv_timedwait_sig(&sem_cv, &sem_mtx, |
| ddi_get_lbolt() + hz)) { |
| |
| /* check if should stop the test (timeout) */ |
| time_diff = (gethrtime() - start_time) / NANOSEC; |
| if (rto_opts.rto_sweep_timeout > 0 && |
| time_diff >= rto_opts.rto_sweep_timeout) { |
| sweep_state = SWEEP_TIMEOUT; |
| rto_opts.rto_should_stop = B_TRUE; |
| mutex_exit(&sem_mtx); |
| goto exit; |
| } |
| |
| /* check if should stop the test (error) */ |
| if (sweep_state != SWEEP_RUNNING) { |
| mutex_exit(&sem_mtx); |
| goto exit; |
| } |
| |
| /* exit loop if a slot is available */ |
| if (free_slots > 0) { |
| break; |
| } |
| } |
| |
| free_slots--; |
| mutex_exit(&sem_mtx); |
| |
| opts = umem_zalloc(sizeof (raidz_test_opts_t), UMEM_NOFAIL); |
| opts->rto_ashift = ashift_v[a]; |
| opts->rto_dcols = dcols_v[d]; |
| opts->rto_offset = (1 << ashift_v[a]) * rand(); |
| opts->rto_dsize = size_v[s]; |
| opts->rto_v = 0; /* be quiet */ |
| |
| VERIFY3P(thread_create(NULL, 0, sweep_thread, (void *) opts, |
| 0, NULL, TS_RUN, defclsyspri), !=, NULL); |
| } |
| |
| exit: |
| LOG(D_ALL, "\nWaiting for test threads to finish...\n"); |
| mutex_enter(&sem_mtx); |
| VERIFY(free_slots <= max_free_slots); |
| while (free_slots < max_free_slots) { |
| (void) cv_wait(&sem_cv, &sem_mtx); |
| } |
| mutex_exit(&sem_mtx); |
| |
| if (sweep_state == SWEEP_ERROR) { |
| ERR("Sweep test failed! Failed option: \n"); |
| print_opts(&failed_opts, B_TRUE); |
| } else { |
| if (sweep_state == SWEEP_TIMEOUT) |
| LOG(D_ALL, "Test timeout (%lus). Stopping...\n", |
| (ulong_t)rto_opts.rto_sweep_timeout); |
| |
| LOG(D_ALL, "Sweep test succeeded on %lu raidz maps!\n", |
| (ulong_t)tried_comb); |
| } |
| |
| mutex_destroy(&sem_mtx); |
| |
| return (sweep_state == SWEEP_ERROR ? SWEEP_ERROR : 0); |
| } |
| |
| int |
| main(int argc, char **argv) |
| { |
| size_t i; |
| struct sigaction action; |
| int err = 0; |
| |
| /* init gdb string early */ |
| (void) sprintf(gdb, gdb_tmpl, getpid()); |
| |
| action.sa_handler = sig_handler; |
| sigemptyset(&action.sa_mask); |
| action.sa_flags = 0; |
| |
| if (sigaction(SIGSEGV, &action, NULL) < 0) { |
| ERR("raidz_test: cannot catch SIGSEGV: %s.\n", strerror(errno)); |
| exit(EXIT_FAILURE); |
| } |
| |
| (void) setvbuf(stdout, NULL, _IOLBF, 0); |
| |
| dprintf_setup(&argc, argv); |
| |
| process_options(argc, argv); |
| |
| kernel_init(FREAD); |
| |
| /* setup random data because rand() is not reentrant */ |
| rand_data = (int *)umem_alloc(SPA_MAXBLOCKSIZE, UMEM_NOFAIL); |
| srand((unsigned)time(NULL) * getpid()); |
| for (i = 0; i < SPA_MAXBLOCKSIZE / sizeof (int); i++) |
| rand_data[i] = rand(); |
| |
| mprotect(rand_data, SPA_MAXBLOCKSIZE, PROT_READ); |
| |
| if (rto_opts.rto_benchmark) { |
| run_raidz_benchmark(); |
| } else if (rto_opts.rto_sweep) { |
| err = run_sweep(); |
| } else { |
| err = run_test(NULL); |
| } |
| |
| umem_free(rand_data, SPA_MAXBLOCKSIZE); |
| kernel_fini(); |
| |
| return (err); |
| } |