diff --git a/cmd/zdb/zdb.c b/cmd/zdb/zdb.c index d6f144c0e20..037a7681a31 100644 --- a/cmd/zdb/zdb.c +++ b/cmd/zdb/zdb.c @@ -797,8 +797,8 @@ usage(void) "[default is 200]\n"); (void) fprintf(stderr, " -K --key=KEY " "decryption key for encrypted dataset\n"); - (void) fprintf(stderr, " -o --option=\"OPTION=INTEGER\" " - "set global variable to an unsigned 32-bit integer\n"); + (void) fprintf(stderr, " -o --option=\"NAME=VALUE\" " + "set the named tunable to the given value\n"); (void) fprintf(stderr, " -p --path==PATH " "use one or more with -e to specify path to vdev dir\n"); (void) fprintf(stderr, " -P --parseable " @@ -9377,9 +9377,11 @@ main(int argc, char **argv) while (*optarg != '\0') { *optarg++ = '*'; } break; case 'o': - error = set_global_var(optarg); + dump_opt[c]++; + dump_all = 0; + error = handle_tunable_option(optarg, B_FALSE); if (error != 0) - usage(); + zdb_exit(1); break; case 'p': if (searchdirs == NULL) { @@ -9545,6 +9547,12 @@ main(int argc, char **argv) error = 0; goto fini; } + if (dump_opt['o']) + /* + * Avoid blasting tunable options off the top of the + * screen. + */ + zdb_exit(1); usage(); } diff --git a/cmd/ztest.c b/cmd/ztest.c index e334641fef1..ec1efd638f1 100644 --- a/cmd/ztest.c +++ b/cmd/ztest.c @@ -809,8 +809,8 @@ static ztest_option_t option_table[] = { { 'X', "raidz-expansion", NULL, "Perform a dedicated raidz expansion test", NO_DEFAULT, NULL}, - { 'o', "option", "\"OPTION=INTEGER\"", - "Set global variable to an unsigned 32-bit integer value", + { 'o', "option", "\"NAME=VALUE\"", + "Set the named tunable to the given value", NO_DEFAULT, NULL}, { 'G', "dump-debug-msg", NULL, "Dump zfs_dbgmsg buffer before exiting due to an error", @@ -7069,7 +7069,7 @@ ztest_set_global_vars(void) char *kv = ztest_opts.zo_gvars[i]; VERIFY3U(strlen(kv), <=, ZO_GVARS_MAX_ARGLEN); VERIFY3U(strlen(kv), >, 0); - int err = set_global_var(kv); + int err = handle_tunable_option(kv, B_TRUE); if (ztest_opts.zo_verbose > 0) { (void) printf("setting global var %s ... %s\n", kv, err ? "failed" : "ok"); diff --git a/include/sys/zfs_context.h b/include/sys/zfs_context.h index e155f2daa39..31edab919f0 100644 --- a/include/sys/zfs_context.h +++ b/include/sys/zfs_context.h @@ -661,7 +661,7 @@ extern void random_fini(void); struct spa; extern void show_pool_stats(struct spa *); -extern int set_global_var(char const *arg); +extern int handle_tunable_option(const char *, boolean_t); typedef struct callb_cpr { kmutex_t *cc_lockp; diff --git a/lib/libzpool/Makefile.am b/lib/libzpool/Makefile.am index 8553b377a76..5cdb6a3eb24 100644 --- a/lib/libzpool/Makefile.am +++ b/lib/libzpool/Makefile.am @@ -199,7 +199,7 @@ libzpool_la_LIBADD = \ libzstd.la \ libzutil.la -libzpool_la_LIBADD += $(LIBCLOCK_GETTIME) $(ZLIB_LIBS) -ldl -lm +libzpool_la_LIBADD += $(LIBCLOCK_GETTIME) $(ZLIB_LIBS) -lm libzpool_la_LDFLAGS = -pthread diff --git a/lib/libzpool/util.c b/lib/libzpool/util.c index a297daedbd4..1d0d1a1e56d 100644 --- a/lib/libzpool/util.c +++ b/lib/libzpool/util.c @@ -36,7 +36,7 @@ #include #include #include -#include +#include #include /* @@ -151,97 +151,119 @@ show_pool_stats(spa_t *spa) nvlist_free(config); } -/* *k_out must be freed by the caller */ -static int -set_global_var_parse_kv(const char *arg, char **k_out, u_longlong_t *v_out) -{ - int err; - VERIFY(arg); - char *d = strdup(arg); - - char *save = NULL; - char *k = strtok_r(d, "=", &save); - char *v_str = strtok_r(NULL, "=", &save); - char *follow = strtok_r(NULL, "=", &save); - if (k == NULL || v_str == NULL || follow != NULL) { - err = EINVAL; - goto err_free; - } - - u_longlong_t val = strtoull(v_str, NULL, 0); - if (val > UINT32_MAX) { - fprintf(stderr, "Value for global variable '%s' must " - "be a 32-bit unsigned integer, got '%s'\n", k, v_str); - err = EOVERFLOW; - goto err_free; - } - - *k_out = strdup(k); - *v_out = val; - free(d); - return (0); - -err_free: - free(d); - - return (err); -} - /* - * Sets given global variable in libzpool to given unsigned 32-bit value. - * arg: "=" + * Common helper for working with libzpool tunables from the command line. + * + * Valid inputs: + * + * show named tunable and value + * = set tunable value + * + * show show all tunables and values + * show= show named tunable and value + * info show info about all tunables + * info= show info about named tunable */ -int -set_global_var(char const *arg) + +typedef enum { SHOW, INFO, SET } tunable_mode_t; + +static int +list_tunables_cb(const zfs_tunable_t *tunable, void *arg) { - void *zpoolhdl; - char *varname; - u_longlong_t val; - int ret; + const tunable_mode_t *mode = arg; -#ifndef _ZFS_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"); - ret = ENOTSUP; - goto out_ret; -#endif - - if ((ret = set_global_var_parse_kv(arg, &varname, &val)) != 0) { - goto out_ret; - } - - 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); - ret = EINVAL; - goto out_dlclose; - } - *var = (uint32_t)val; + static const char *type[] = { + "int", "uint", "ulong", "u64", "str", + }; + static const char *perm[] = { + "rw", "rd", + }; + if (*mode == SHOW) { + char val[64]; + int err = zfs_tunable_get(tunable, val, sizeof (val)); + if (err == 0) + printf("%s: %s\n", tunable->zt_name, val); + else + printf("%s: [error getting tunable value: %s]\n", + tunable->zt_name, strerror(err)); } else { - fprintf(stderr, "Failed to open libzpool.so to set global " - "variable\n"); - ret = EIO; - goto out_free; + printf("%s [%s %s]: %s\n", tunable->zt_name, + type[tunable->zt_type], perm[tunable->zt_perm], + tunable->zt_desc); } - ret = 0; + return (0); +} +int +handle_tunable_option(const char *_arg, boolean_t quiet) +{ + int err = 0; + char *arg = strdup(_arg); + char *k, *v; -out_dlclose: - dlclose(zpoolhdl); -out_free: - free(varname); -out_ret: - return (ret); + v = arg; + k = strsep(&v, "="); + + tunable_mode_t mode; + + if (strcmp(k, "show") == 0) { + mode = SHOW; + k = v; + } else if (strcmp(k, "info") == 0) { + mode = INFO; + k = v; + } else if (v == NULL) { + mode = SHOW; + } else { + mode = SET; + } + + if (quiet && mode != SET) { + err = EINVAL; + goto out; + } + + if (mode == SET) { + const zfs_tunable_t *tunable = zfs_tunable_lookup(k); + if (tunable == NULL) { + err = ENOENT; + goto out; + } + + char vold[256], vnew[256]; + if (zfs_tunable_get(tunable, vold, sizeof (vold)) != 0) + strcpy(vold, "???"); + err = zfs_tunable_set(tunable, v); + if (err != 0) + goto out; + if (zfs_tunable_get(tunable, vnew, sizeof (vnew)) != 0) + strcpy(vnew, "???"); + + if (!quiet) + printf("%s: %s -> %s\n", k, vold, vnew); + } else if (k != NULL) { + const zfs_tunable_t *tunable = zfs_tunable_lookup(k); + if (tunable == NULL) { + err = ENOENT; + goto out; + } + list_tunables_cb(tunable, &mode); + } else { + zfs_tunable_iter(list_tunables_cb, &mode); + } + +out: + if (!quiet) { + if (err == ENOENT) + fprintf(stderr, "no such tunable: %s\n", k); + else if (err != 0) + fprintf(stderr, "couldn't set tunable '%s': %s\n", + k, strerror(err)); + } + + free(arg); + return (err); } static nvlist_t * diff --git a/man/man1/ztest.1 b/man/man1/ztest.1 index 0cbb58e40dd..febbb62b166 100644 --- a/man/man1/ztest.1 +++ b/man/man1/ztest.1 @@ -188,12 +188,8 @@ i.e. given will be loaded. .It Fl C , -vdev-class-state Ns = Ns Sy on Ns | Ns Sy off Ns | Ns Sy random No (default : Sy random ) The vdev allocation class state. -.It Fl o , -option Ns = Ns Ar variable Ns = Ns Ar value -Set global -.Ar variable -to an unsigned 32-bit integer -.Ar value -(little-endian only). +.It Fl o , -option Ns = Ns Ar var Ns = Ns Ar value Ns … +Set the given tunable to the provided value. .It Fl G , -dump-debug Dump zfs_dbgmsg buffer before exiting due to an error. .It Fl V , -verbose diff --git a/man/man8/zdb.8 b/man/man8/zdb.8 index 8bfd0dcdc38..3984aaac586 100644 --- a/man/man8/zdb.8 +++ b/man/man8/zdb.8 @@ -474,10 +474,15 @@ as it runs. Exercise extreme caution when using this option in shared or uncontrolled environments. .It Fl o , -option Ns = Ns Ar var Ns = Ns Ar value Ns … -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. +Set the given tunable to the provided value. +.It Fl o , -option Ns = Ns Ar var Ns … +Show the value of the given tunable. +.It Fl o , -option Ns = Ns show +Show all tunables and their values. +.It Fl o , -option Ns = Ns info Ns = Ns Ar value Ns … +Show info about a tunable, including their name, type and description. +.It Fl o , -option Ns = Ns info +Show info about all tunables. .It Fl P , -parseable Print numbers in an unscaled form more amenable to parsing, e.g.\& .Sy 1000000