783 lines
18 KiB
C
783 lines
18 KiB
C
|
/*
|
||
|
* 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);
|
||
|
}
|