From be0769e638de9f7208df5298f84ff001eef85989 Mon Sep 17 00:00:00 2001 From: Andriy Gapon Date: Fri, 14 Apr 2017 16:53:34 +0000 Subject: [PATCH] 7280 Allow changing global libzpool variables in zdb and ztest through command line illumos/illumos-gate@0e60744c982adecd0a1f146f5637475d07ab1069 https://github.com/illumos/illumos-gate/commit/0e60744c982adecd0a1f146f5637475d07ab1069 https://www.illumos.org/issues/7280 zdb is very handy for diagnosing problems with a pool in a safe and quick way. When a pool is in a bad shape, we often want to disable some fail-safes, or adjust some tunables in order to open them. In the kernel, this is done by changing public variables in mdb. The goal of this feature is to add the same capability to zdb and ztest, so that they can change libzpool tuneables from the command line. Reviewed by: Matthew Ahrens Reviewed by: Dan Kimmel Approved by: Robert Mustacchi Author: Pavel Zakharov --- cmd/zdb/zdb.c | 12 +++++- cmd/ztest/ztest.c | 8 +++- lib/libzpool/common/sys/zfs_context.h | 3 +- lib/libzpool/common/util.c | 57 +++++++++++++++++++++++++++ man/man1m/zdb.1m | 14 ++++++- 5 files changed, 89 insertions(+), 5 deletions(-) diff --git a/cmd/zdb/zdb.c b/cmd/zdb/zdb.c index a0d834ed123c..b3b05774224d 100644 --- a/cmd/zdb/zdb.c +++ b/cmd/zdb/zdb.c @@ -118,7 +118,8 @@ usage(void) { (void) fprintf(stderr, "Usage: %s [-CumMdibcsDvhLXFPAG] [-t txg] [-e [-p path...]] " - "[-U config] [-I inflight I/Os] [-x dumpdir] poolname [object...]\n" + "[-U config] [-I inflight I/Os] [-x dumpdir] [-o var=value] " + "poolname [object...]\n" " %s [-divPA] [-e -p path...] [-U config] dataset " "[object...]\n" " %s -mM [-LXFPA] [-t txg] [-e [-p path...]] [-U config] " @@ -180,6 +181,8 @@ usage(void) "checksumming I/Os [default is 200]\n"); (void) fprintf(stderr, " -G dump zfs_dbgmsg buffer before " "exiting\n"); + (void) fprintf(stderr, " -o = set global " + "variable to an unsigned 32-bit integer value\n"); (void) fprintf(stderr, "Specify an option more than once (e.g. -bb) " "to make only that option verbose\n"); (void) fprintf(stderr, "Default is to dump everything non-verbosely\n"); @@ -3575,7 +3578,7 @@ main(int argc, char **argv) spa_config_path = spa_config_path_env; while ((c = getopt(argc, argv, - "bcdhilmMI:suCDRSAFLXx:evp:t:U:PG")) != -1) { + "bcdhilmMI:suCDRSAFLXx:evp:t:U:PGo:")) != -1) { switch (c) { case 'b': case 'c': @@ -3644,6 +3647,11 @@ main(int argc, char **argv) case 'x': vn_dumpdir = optarg; break; + case 'o': + error = set_global_var(optarg); + if (error != 0) + usage(); + break; default: usage(); break; diff --git a/cmd/ztest/ztest.c b/cmd/ztest/ztest.c index f21886b97d9f..310b5995dcda 100644 --- a/cmd/ztest/ztest.c +++ b/cmd/ztest/ztest.c @@ -579,6 +579,8 @@ usage(boolean_t requested) "\t[-F freezeloops (default: %llu)] max loops in spa_freeze()\n" "\t[-P passtime (default: %llu sec)] time per pass\n" "\t[-B alt_ztest (default: )] alternate ztest path\n" + "\t[-o variable=value] ... set global variable to an unsigned\n" + "\t 32-bit integer value\n" "\t[-h] (print help)\n" "", zo->zo_pool, @@ -614,7 +616,7 @@ process_options(int argc, char **argv) bcopy(&ztest_opts_defaults, zo, sizeof (*zo)); while ((opt = getopt(argc, argv, - "v:s:a:m:r:R:d:t:g:i:k:p:f:VET:P:hF:B:")) != EOF) { + "v:s:a:m:r:R:d:t:g:i:k:p:f:VET:P:hF:B:o:")) != EOF) { value = 0; switch (opt) { case 'v': @@ -701,6 +703,10 @@ process_options(int argc, char **argv) case 'B': (void) strlcpy(altdir, optarg, sizeof (altdir)); break; + case 'o': + if (set_global_var(optarg) != 0) + usage(B_FALSE); + break; case 'h': usage(B_TRUE); break; diff --git a/lib/libzpool/common/sys/zfs_context.h b/lib/libzpool/common/sys/zfs_context.h index 7c08c644e586..f22f364b2f9a 100644 --- a/lib/libzpool/common/sys/zfs_context.h +++ b/lib/libzpool/common/sys/zfs_context.h @@ -21,7 +21,7 @@ /* * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved. * Copyright 2011 Nexenta Systems, Inc. All rights reserved. - * Copyright (c) 2012, 2015 by Delphix. All rights reserved. + * Copyright (c) 2012, 2016 by Delphix. All rights reserved. * Copyright (c) 2012, Joyent, Inc. All rights reserved. */ @@ -541,6 +541,7 @@ extern void kernel_fini(void); struct spa; extern void nicenum(uint64_t num, char *buf); extern void show_pool_stats(struct spa *); +extern int set_global_var(char *arg); typedef struct callb_cpr { kmutex_t *cc_lockp; diff --git a/lib/libzpool/common/util.c b/lib/libzpool/common/util.c index 9b99531fd1c5..e929aa564544 100644 --- a/lib/libzpool/common/util.c +++ b/lib/libzpool/common/util.c @@ -20,6 +20,7 @@ */ /* * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016 by Delphix. All rights reserved. */ #include @@ -31,6 +32,7 @@ #include #include #include +#include /* * Routines needed by more than one client of libzpool. @@ -153,3 +155,58 @@ show_pool_stats(spa_t *spa) nvlist_free(config); } + +/* + * Sets given global variable in libzpool to given unsigned 32-bit value. + * arg: "=" + */ +int +set_global_var(char *arg) +{ + void *zpoolhdl; + char *varname = arg, *varval; + u_longlong_t val; + +#ifndef _LITTLE_ENDIAN + /* + * On big endian systems changing a 64-bit variable would set the high + * 32 bits instead of the low 32 bits, which could cause unexpected + * results. + */ + fprintf(stderr, "Setting global variables is only supported on " + "little-endian systems\n", varname); + return (ENOTSUP); +#endif + if ((varval = strchr(arg, '=')) != NULL) { + *varval = '\0'; + varval++; + val = strtoull(varval, NULL, 0); + if (val > UINT32_MAX) { + fprintf(stderr, "Value for global variable '%s' must " + "be a 32-bit unsigned integer\n", varname); + return (EOVERFLOW); + } + } else { + return (EINVAL); + } + + zpoolhdl = dlopen("libzpool.so", RTLD_LAZY); + if (zpoolhdl != NULL) { + uint32_t *var; + var = dlsym(zpoolhdl, varname); + if (var == NULL) { + fprintf(stderr, "Global variable '%s' does not exist " + "in libzpool.so\n", varname); + return (EINVAL); + } + *var = (uint32_t)val; + + dlclose(zpoolhdl); + } else { + fprintf(stderr, "Failed to open libzpool.so to set global " + "variable\n"); + return (EIO); + } + + return (0); +} diff --git a/man/man1m/zdb.1m b/man/man1m/zdb.1m index 260fa854db56..ed74d510d0dd 100644 --- a/man/man1m/zdb.1m +++ b/man/man1m/zdb.1m @@ -21,7 +21,7 @@ .SH "SYNOPSIS" \fBzdb\fR [-CumdibcsDvhLMXFPAG] [-e [-p \fIpath\fR...]] [-t \fItxg\fR] [-U \fIcache\fR] [-I \fIinflight I/Os\fR] [-x \fIdumpdir\fR] - [\fIpoolname\fR [\fIobject\fR ...]] + [-o \fIvar\fR=\fIvalue\fR] ... [\fIpoolname\fR [\fIobject\fR ...]] .P \fBzdb\fR [-divPA] [-e [-p \fIpath\fR...]] [-U \fIcache\fR] @@ -420,6 +420,18 @@ default value is 200. This option affects the performance of the \fB-c\fR option. .RE +.sp +.ne 2 +.na +\fB-o \fIvar\fR=\fIvalue\fR ... \fR +.ad +.sp .6 +.RS 4n +Set the given global libzpool variable to the provided value. The value must +be an unsigned 32-bit integer. Currently only little-endian systems are +supported to avoid accidentally setting the high 32 bits of 64-bit variables. +.RE + .sp .ne 2 .na