From 1bc2923155cb9be3d40e6f62a5706d0a23e198eb Mon Sep 17 00:00:00 2001 From: Allan Jude Date: Fri, 18 Sep 2020 19:30:36 +0000 Subject: [PATCH 1/7] WIP: vdev properties Signed-off-by: Allan Jude --- cmd/zhack/zhack.c | 34 ++ cmd/zpool/zpool_iter.c | 17 +- cmd/zpool/zpool_main.c | 388 +++++++++++++---- cmd/zpool/zpool_util.h | 5 +- include/libzfs.h | 30 +- include/libzfs_core.h | 4 + include/sys/fs/zfs.h | 83 +++- include/sys/vdev.h | 3 + include/zfs_prop.h | 8 + lib/libzfs/libzfs_pool.c | 469 +++++++++++++++++++- lib/libzfs/libzfs_util.c | 33 +- lib/libzfs/os/freebsd/libzfs_compat.c | 4 + lib/libzfs_core/libzfs_core.c | 12 + module/zcommon/zfs_prop.c | 14 +- module/zcommon/zpool_prop.c | 245 +++++++++++ module/zcommon/zprop_common.c | 19 + module/zfs/spa.c | 15 +- module/zfs/spa_misc.c | 1 + module/zfs/vdev.c | 606 ++++++++++++++++++++++++++ module/zfs/zfs_ioctl.c | 96 +++- 20 files changed, 1963 insertions(+), 123 deletions(-) diff --git a/cmd/zhack/zhack.c b/cmd/zhack/zhack.c index 08263120c7c4..b5ac5dcf9da2 100644 --- a/cmd/zhack/zhack.c +++ b/cmd/zhack/zhack.c @@ -471,6 +471,38 @@ zhack_do_feature(int argc, char **argv) return (0); } +static int +zhack_do_zap(int argc, char **argv) +{ + spa_t *spa; + objset_t *os; + uint64_t obj; + char *target; + + argc--; + argv++; + + if (argc < 1) { + (void) fprintf(stderr, "error: missing pool name\n"); + usage(); + } + if (argc < 2) { + (void) fprintf(stderr, "error: missing object id\n"); + usage(); + } + target = argv[0]; + obj = strtoull(argv[1], NULL, 0); + + zhack_spa_open(target, B_TRUE, FTAG, &spa); + os = spa->spa_meta_objset; + + dump_obj(os, obj, argv[1]); + + spa_close(spa, FTAG); + + return (0); +} + #define MAX_NUM_PATHS 1024 int @@ -516,6 +548,8 @@ main(int argc, char **argv) if (strcmp(subcommand, "feature") == 0) { rv = zhack_do_feature(argc, argv); + } else if (strcmp(subcommand, "zap") == 0) { + rv = zhack_do_zap(argc, argv); } else { (void) fprintf(stderr, "error: unknown subcommand: %s\n", subcommand); diff --git a/cmd/zpool/zpool_iter.c b/cmd/zpool/zpool_iter.c index d70d266699cf..02767d24076b 100644 --- a/cmd/zpool/zpool_iter.c +++ b/cmd/zpool/zpool_iter.c @@ -60,6 +60,7 @@ struct zpool_list { uu_avl_t *zl_avl; uu_avl_pool_t *zl_pool; zprop_list_t **zl_proplist; + zfs_type_t zl_type; }; /* ARGSUSED */ @@ -90,8 +91,7 @@ add_pool(zpool_handle_t *zhp, void *data) if (uu_avl_find(zlp->zl_avl, node, NULL, &idx) == NULL) { if (zlp->zl_proplist && zpool_expand_proplist(zhp, zlp->zl_proplist, - zlp->zl_literal) - != 0) { + zlp->zl_type, zlp->zl_literal) != 0) { zpool_close(zhp); free(node); return (-1); @@ -113,7 +113,7 @@ add_pool(zpool_handle_t *zhp, void *data) * line. */ zpool_list_t * -pool_list_get(int argc, char **argv, zprop_list_t **proplist, +pool_list_get(int argc, char **argv, zprop_list_t **proplist, zfs_type_t type, boolean_t literal, int *err) { zpool_list_t *zlp; @@ -131,6 +131,7 @@ pool_list_get(int argc, char **argv, zprop_list_t **proplist, zpool_no_memory(); zlp->zl_proplist = proplist; + zlp->zl_type = type; zlp->zl_literal = literal; @@ -248,12 +249,14 @@ pool_list_count(zpool_list_t *zlp) */ int for_each_pool(int argc, char **argv, boolean_t unavail, - zprop_list_t **proplist, boolean_t literal, zpool_iter_f func, void *data) + zprop_list_t **proplist, zfs_type_t type, boolean_t literal, + zpool_iter_f func, void *data) { zpool_list_t *list; int ret = 0; - if ((list = pool_list_get(argc, argv, proplist, literal, &ret)) == NULL) + if ((list = pool_list_get(argc, argv, proplist, type, literal, + &ret)) == NULL) return (1); if (pool_list_iter(list, unavail, func, data) != 0) @@ -717,8 +720,8 @@ all_pools_for_each_vdev_run(int argc, char **argv, char *cmd, vcdl->g_zfs = g_zfs; /* Gather our list of all vdevs in all pools */ - for_each_pool(argc, argv, B_TRUE, NULL, B_FALSE, - all_pools_for_each_vdev_gather_cb, vcdl); + for_each_pool(argc, argv, B_TRUE, NULL, ZFS_TYPE_POOL, + B_FALSE, all_pools_for_each_vdev_gather_cb, vcdl); /* Run command on all vdevs in all pools */ all_pools_for_each_vdev_run_vcdl(vcdl); diff --git a/cmd/zpool/zpool_main.c b/cmd/zpool/zpool_main.c index 8d809acb2146..94a24e55a370 100644 --- a/cmd/zpool/zpool_main.c +++ b/cmd/zpool/zpool_main.c @@ -32,6 +32,7 @@ * Copyright (c) 2017, Intel Corporation. * Copyright (c) 2019, loli10K * Copyright (c) 2021, Colm Buckley + * Copyright (c) 2021, Klara Inc. */ #include @@ -329,6 +330,7 @@ static zpool_command_t command_table[] = { #define VDEV_ALLOC_CLASS_LOGS "logs" static zpool_command_t *current_command; +static zfs_type_t current_prop_type = ZFS_TYPE_POOL; static char history_str[HIS_MAX_RECORD_LEN]; static boolean_t log_history = B_TRUE; static uint_t timestamp_fmt = NODATE; @@ -484,6 +486,29 @@ print_prop_cb(int prop, void *cb) return (ZPROP_CONT); } +/* + * Callback routine that will print out a pool property value. + */ +static int +print_vdev_prop_cb(int prop, void *cb) +{ + FILE *fp = cb; + + (void) fprintf(fp, "\t%-19s ", vdev_prop_to_name(prop)); + + if (vdev_prop_readonly(prop)) + (void) fprintf(fp, " NO "); + else + (void) fprintf(fp, " YES "); + + if (vdev_prop_values(prop) == NULL) + (void) fprintf(fp, "-\n"); + else + (void) fprintf(fp, "%s\n", vdev_prop_values(prop)); + + return (ZPROP_CONT); +} + /* * Display usage message. If we're inside a command, display only the usage for * that command. Otherwise, iterate over the entire command table and display @@ -525,14 +550,21 @@ usage(boolean_t requested) "PROPERTY", "EDIT", "VALUES"); /* Iterate over all properties */ - (void) zprop_iter(print_prop_cb, fp, B_FALSE, B_TRUE, - ZFS_TYPE_POOL); + if (current_prop_type == ZFS_TYPE_POOL) { + (void) zprop_iter(print_prop_cb, fp, B_FALSE, + B_TRUE, current_prop_type); - (void) fprintf(fp, "\t%-19s ", "feature@..."); - (void) fprintf(fp, "YES disabled | enabled | active\n"); + (void) fprintf(fp, "\t%-19s ", "feature@..."); + (void) fprintf(fp, "YES " + "disabled | enabled | active\n"); - (void) fprintf(fp, gettext("\nThe feature@ properties must be " - "appended with a feature name.\nSee zpool-features(5).\n")); + (void) fprintf(fp, gettext("\nThe feature@ properties " + "must be appended with a feature name.\n" + "See zpool-features(5).\n")); + } else if (current_prop_type == ZFS_TYPE_VDEV) { + (void) zprop_iter(print_vdev_prop_cb, fp, B_FALSE, + B_TRUE, current_prop_type); + } } /* @@ -790,7 +822,8 @@ add_prop_list(const char *propname, char *propval, nvlist_t **props, zpool_prop_to_name(ZPOOL_PROP_COMPATIBILITY); if ((prop = zpool_name_to_prop(propname)) == ZPOOL_PROP_INVAL && - !zpool_prop_feature(propname)) { + (!zpool_prop_feature(propname) || + zpool_prop_vdev(propname))) { (void) fprintf(stderr, gettext("property '%s' is " "not a valid pool property\n"), propname); return (2); @@ -827,7 +860,7 @@ add_prop_list(const char *propname, char *propval, nvlist_t **props, return (2); } - if (zpool_prop_feature(propname)) + if (zpool_prop_feature(propname) || zpool_prop_vdev(propname)) normnm = propname; else normnm = zpool_prop_to_name(prop); @@ -1891,7 +1924,7 @@ zpool_do_export(int argc, char **argv) } return (for_each_pool(argc, argv, B_TRUE, NULL, - B_FALSE, zpool_export_one, &cb)); + ZFS_TYPE_POOL, B_FALSE, zpool_export_one, &cb)); } /* check arguments */ @@ -1900,8 +1933,8 @@ zpool_do_export(int argc, char **argv) usage(B_FALSE); } - ret = for_each_pool(argc, argv, B_TRUE, NULL, B_FALSE, zpool_export_one, - &cb); + ret = for_each_pool(argc, argv, B_TRUE, NULL, ZFS_TYPE_POOL, + B_FALSE, zpool_export_one, &cb); return (ret); } @@ -3828,24 +3861,22 @@ zpool_do_sync(int argc, char **argv) argv += optind; /* if argc == 0 we will execute zpool_sync_one on all pools */ - ret = for_each_pool(argc, argv, B_FALSE, NULL, B_FALSE, zpool_sync_one, - &force); + ret = for_each_pool(argc, argv, B_FALSE, NULL, ZFS_TYPE_POOL, + B_FALSE, zpool_sync_one, &force); return (ret); } typedef struct iostat_cbdata { uint64_t cb_flags; - int cb_name_flags; int cb_namewidth; int cb_iteration; - char **cb_vdev_names; /* Only show these vdevs */ - unsigned int cb_vdev_names_count; boolean_t cb_verbose; boolean_t cb_literal; boolean_t cb_scripted; zpool_list_t *cb_list; vdev_cmd_data_list_t *vcdl; + vdev_cbdata_t cb_vdevs; } iostat_cbdata_t; /* iostat labels */ @@ -4095,7 +4126,7 @@ print_iostat_dashes(iostat_cbdata_t *cb, unsigned int force_column_width, if (cb->cb_flags & IOS_ANYHISTO_M) { title = histo_to_title[IOS_HISTO_IDX(cb->cb_flags)]; - } else if (cb->cb_vdev_names_count) { + } else if (cb->cb_vdevs.cb_names_count) { title = "vdev"; } else { title = "pool"; @@ -4155,7 +4186,7 @@ print_iostat_header_impl(iostat_cbdata_t *cb, unsigned int force_column_width, if (cb->cb_flags & IOS_ANYHISTO_M) { title = histo_to_title[IOS_HISTO_IDX(cb->cb_flags)]; - } else if (cb->cb_vdev_names_count) { + } else if (cb->cb_vdevs.cb_names_count) { title = "vdev"; } else { title = "pool"; @@ -4660,9 +4691,9 @@ print_vdev_stats(zpool_handle_t *zhp, const char *name, nvlist_t *oldnv, } /* Do we only want to see a specific vdev? */ - for (i = 0; i < cb->cb_vdev_names_count; i++) { + for (i = 0; i < cb->cb_vdevs.cb_names_count; i++) { /* Yes we do. Is this the vdev? */ - if (strcmp(name, cb->cb_vdev_names[i]) == 0) { + if (strcmp(name, cb->cb_vdevs.cb_names[i]) == 0) { /* * This is our vdev. Since it is the only vdev we * will be displaying, make depth = 0 so that it @@ -4673,7 +4704,7 @@ print_vdev_stats(zpool_handle_t *zhp, const char *name, nvlist_t *oldnv, } } - if (cb->cb_vdev_names_count && (i == cb->cb_vdev_names_count)) { + if (cb->cb_vdevs.cb_names_count && (i == cb->cb_vdevs.cb_names_count)) { /* Couldn't match the name */ goto children; } @@ -4780,7 +4811,7 @@ print_vdev_stats(zpool_handle_t *zhp, const char *name, nvlist_t *oldnv, continue; vname = zpool_vdev_name(g_zfs, zhp, newchild[c], - cb->cb_name_flags); + cb->cb_vdevs.cb_name_flags); ret += print_vdev_stats(zhp, vname, oldnv ? oldchild[c] : NULL, newchild[c], cb, depth + 2); free(vname); @@ -4814,7 +4845,8 @@ print_vdev_stats(zpool_handle_t *zhp, const char *name, nvlist_t *oldnv, if (!printed) { if ((!(cb->cb_flags & IOS_ANYHISTO_M)) && - !cb->cb_scripted && !cb->cb_vdev_names) { + !cb->cb_scripted && + !cb->cb_vdevs.cb_names) { print_iostat_dashes(cb, 0, class_name[n]); } @@ -4823,7 +4855,7 @@ print_vdev_stats(zpool_handle_t *zhp, const char *name, nvlist_t *oldnv, } vname = zpool_vdev_name(g_zfs, zhp, newchild[c], - cb->cb_name_flags); + cb->cb_vdevs.cb_name_flags); ret += print_vdev_stats(zhp, vname, oldnv ? oldchild[c] : NULL, newchild[c], cb, depth + 2); free(vname); @@ -4847,14 +4879,14 @@ print_vdev_stats(zpool_handle_t *zhp, const char *name, nvlist_t *oldnv, if (children > 0) { if ((!(cb->cb_flags & IOS_ANYHISTO_M)) && !cb->cb_scripted && - !cb->cb_vdev_names) { + !cb->cb_vdevs.cb_names) { print_iostat_dashes(cb, 0, "cache"); } printf("\n"); for (c = 0; c < children; c++) { vname = zpool_vdev_name(g_zfs, zhp, newchild[c], - cb->cb_name_flags); + cb->cb_vdevs.cb_name_flags); ret += print_vdev_stats(zhp, vname, oldnv ? oldchild[c] : NULL, newchild[c], cb, depth + 2); free(vname); @@ -4910,7 +4942,8 @@ print_iostat(zpool_handle_t *zhp, void *data) ret = print_vdev_stats(zhp, zpool_get_name(zhp), oldnvroot, newnvroot, cb, 0); if ((ret != 0) && !(cb->cb_flags & IOS_ANYHISTO_M) && - !cb->cb_scripted && cb->cb_verbose && !cb->cb_vdev_names_count) { + !cb->cb_scripted && cb->cb_verbose && + !cb->cb_vdevs.cb_names_count) { print_iostat_separator(cb); if (cb->vcdl != NULL) { print_cmd_columns(cb->vcdl, 1); @@ -5117,18 +5150,26 @@ get_stat_flags(zpool_list_t *list) } /* - * Return 1 if cb_data->cb_vdev_names[0] is this vdev's name, 0 otherwise. + * Return 1 if cb_data->cb_names[0] is this vdev's name, 0 otherwise. */ static int is_vdev_cb(zpool_handle_t *zhp, nvlist_t *nv, void *cb_data) { - iostat_cbdata_t *cb = cb_data; + vdev_cbdata_t *cb = cb_data; char *name = NULL; int ret = 0; name = zpool_vdev_name(g_zfs, zhp, nv, cb->cb_name_flags); - if (strcmp(name, cb->cb_vdev_names[0]) == 0) + if (strcmp(name, cb->cb_names[0]) == 0) + ret = 1; /* match */ + free(name); + + if (ret) + return (ret); + + name = zpool_vdev_name(g_zfs, zhp, nv, VDEV_NAME_GUID); + if (strcmp(name, cb->cb_names[0]) == 0) ret = 1; /* match */ free(name); @@ -5136,7 +5177,7 @@ is_vdev_cb(zpool_handle_t *zhp, nvlist_t *nv, void *cb_data) } /* - * Returns 1 if cb_data->cb_vdev_names[0] is a vdev name, 0 otherwise. + * Returns 1 if cb_data->cb_names[0] is a vdev name, 0 otherwise. */ static int is_vdev(zpool_handle_t *zhp, void *cb_data) @@ -5152,7 +5193,7 @@ is_vdev(zpool_handle_t *zhp, void *cb_data) */ static int are_vdevs_in_pool(int argc, char **argv, char *pool_name, - iostat_cbdata_t *cb) + vdev_cbdata_t *cb) { char **tmp_name; int ret = 0; @@ -5165,23 +5206,23 @@ are_vdevs_in_pool(int argc, char **argv, char *pool_name, if (pool_name) pool_count = 1; - /* Temporarily hijack cb_vdev_names for a second... */ - tmp_name = cb->cb_vdev_names; + /* Temporarily hijack cb_names for a second... */ + tmp_name = cb->cb_names; /* Go though our list of prospective vdev names */ for (i = 0; i < argc; i++) { - cb->cb_vdev_names = argv + i; + cb->cb_names = argv + i; /* Is this name a vdev in our pools? */ ret = for_each_pool(pool_count, &pool_name, B_TRUE, NULL, - B_FALSE, is_vdev, cb); + ZFS_TYPE_POOL, B_FALSE, is_vdev, cb); if (!ret) { /* No match */ break; } } - cb->cb_vdev_names = tmp_name; + cb->cb_names = tmp_name; return (ret); } @@ -5202,8 +5243,8 @@ is_pool_cb(zpool_handle_t *zhp, void *data) static int is_pool(char *name) { - return (for_each_pool(0, NULL, B_TRUE, NULL, B_FALSE, is_pool_cb, - name)); + return (for_each_pool(0, NULL, B_TRUE, NULL, ZFS_TYPE_POOL, B_FALSE, + is_pool_cb, name)); } /* Are all our argv[] strings pool names? If so return 1, 0 otherwise. */ @@ -5226,7 +5267,7 @@ are_all_pools(int argc, char **argv) */ static void error_list_unresolved_vdevs(int argc, char **argv, char *pool_name, - iostat_cbdata_t *cb) + vdev_cbdata_t *cb) { int i; char *name; @@ -5250,7 +5291,7 @@ error_list_unresolved_vdevs(int argc, char **argv, char *pool_name, /* * Same as get_interval_count(), but with additional checks to not misinterpret * guids as interval/count values. Assumes VDEV_NAME_GUID is set in - * cb.cb_name_flags. + * cb.cb_vdevs.cb_name_flags. */ static void get_interval_count_filter_guids(int *argc, char **argv, float *interval, @@ -5260,7 +5301,8 @@ get_interval_count_filter_guids(int *argc, char **argv, float *interval, int argc_for_interval = 0; /* Is the last arg an interval value? Or a guid? */ - if (*argc >= 1 && !are_vdevs_in_pool(1, &argv[*argc - 1], NULL, cb)) { + if (*argc >= 1 && !are_vdevs_in_pool(1, &argv[*argc - 1], NULL, + &cb->cb_vdevs)) { /* * The last arg is not a guid, so it's probably an * interval value. @@ -5268,7 +5310,8 @@ get_interval_count_filter_guids(int *argc, char **argv, float *interval, argc_for_interval++; if (*argc >= 2 && - !are_vdevs_in_pool(1, &argv[*argc - 2], NULL, cb)) { + !are_vdevs_in_pool(1, &argv[*argc - 2], NULL, + &cb->cb_vdevs)) { /* * The 2nd to last arg is not a guid, so it's probably * an interval value. @@ -5412,7 +5455,7 @@ get_namewidth_iostat(zpool_handle_t *zhp, void *data) * get_namewidth() returns the maximum width of any name in that column * for any pool/vdev/device line that will be output. */ - width = get_namewidth(zhp, cb->cb_namewidth, cb->cb_name_flags, + width = get_namewidth(zhp, cb->cb_namewidth, cb->cb_vdevs.cb_name_flags, cb->cb_verbose); /* @@ -5590,11 +5633,11 @@ zpool_do_iostat(int argc, char **argv) cb.cb_scripted = scripted; if (guid) - cb.cb_name_flags |= VDEV_NAME_GUID; + cb.cb_vdevs.cb_name_flags |= VDEV_NAME_GUID; if (follow_links) - cb.cb_name_flags |= VDEV_NAME_FOLLOW_LINKS; + cb.cb_vdevs.cb_name_flags |= VDEV_NAME_FOLLOW_LINKS; if (full_name) - cb.cb_name_flags |= VDEV_NAME_PATH; + cb.cb_vdevs.cb_name_flags |= VDEV_NAME_PATH; cb.cb_iteration = 0; cb.cb_namewidth = 0; cb.cb_verbose = verbose; @@ -5611,17 +5654,18 @@ zpool_do_iostat(int argc, char **argv) /* No args, so just print the defaults. */ } else if (are_all_pools(argc, argv)) { /* All the args are pool names */ - } else if (are_vdevs_in_pool(argc, argv, NULL, &cb)) { + } else if (are_vdevs_in_pool(argc, argv, NULL, &cb.cb_vdevs)) { /* All the args are vdevs */ - cb.cb_vdev_names = argv; - cb.cb_vdev_names_count = argc; + cb.cb_vdevs.cb_names = argv; + cb.cb_vdevs.cb_names_count = argc; argc = 0; /* No pools to process */ } else if (are_all_pools(1, argv)) { /* The first arg is a pool name */ - if (are_vdevs_in_pool(argc - 1, argv + 1, argv[0], &cb)) { + if (are_vdevs_in_pool(argc - 1, argv + 1, argv[0], + &cb.cb_vdevs)) { /* ...and the rest are vdev names */ - cb.cb_vdev_names = argv + 1; - cb.cb_vdev_names_count = argc - 1; + cb.cb_vdevs.cb_names = argv + 1; + cb.cb_vdevs.cb_names_count = argc - 1; argc = 1; /* One pool to process */ } else { fprintf(stderr, gettext("Expected either a list of ")); @@ -5629,7 +5673,7 @@ zpool_do_iostat(int argc, char **argv) fprintf(stderr, " \"%s\", ", argv[0]); fprintf(stderr, gettext("but got:\n")); error_list_unresolved_vdevs(argc - 1, argv + 1, - argv[0], &cb); + argv[0], &cb.cb_vdevs); fprintf(stderr, "\n"); usage(B_FALSE); return (1); @@ -5644,7 +5688,7 @@ zpool_do_iostat(int argc, char **argv) return (1); } - if (cb.cb_vdev_names_count != 0) { + if (cb.cb_vdevs.cb_names_count != 0) { /* * If user specified vdevs, it implies verbose. */ @@ -5655,7 +5699,8 @@ zpool_do_iostat(int argc, char **argv) * Construct the list of all interesting pools. */ ret = 0; - if ((list = pool_list_get(argc, argv, NULL, parsable, &ret)) == NULL) + if ((list = pool_list_get(argc, argv, NULL, ZFS_TYPE_POOL, parsable, + &ret)) == NULL) return (1); if (pool_list_count(list) == 0 && argc != 0) { @@ -5763,8 +5808,9 @@ zpool_do_iostat(int argc, char **argv) if (cmd != NULL && cb.cb_verbose && !(cb.cb_flags & IOS_ANYHISTO_M)) { cb.vcdl = all_pools_for_each_vdev_run(argc, - argv, cmd, g_zfs, cb.cb_vdev_names, - cb.cb_vdev_names_count, cb.cb_name_flags); + argv, cmd, g_zfs, cb.cb_vdevs.cb_names, + cb.cb_vdevs.cb_names_count, + cb.cb_vdevs.cb_name_flags); } else { cb.vcdl = NULL; } @@ -5816,7 +5862,7 @@ zpool_do_iostat(int argc, char **argv) if (((npools > 1 && !verbose && !(cb.cb_flags & IOS_ANYHISTO_M)) || (!(cb.cb_flags & IOS_ANYHISTO_M) && - cb.cb_vdev_names_count)) && + cb.cb_vdevs.cb_names_count)) && !cb.cb_scripted) { print_iostat_separator(&cb); if (cb.vcdl != NULL) @@ -6329,7 +6375,7 @@ zpool_do_list(int argc, char **argv) for (;;) { if ((list = pool_list_get(argc, argv, &cb.cb_proplist, - cb.cb_literal, &ret)) == NULL) + ZFS_TYPE_POOL, cb.cb_literal, &ret)) == NULL) return (1); if (pool_list_count(list) == 0) @@ -7085,8 +7131,8 @@ zpool_do_reopen(int argc, char **argv) argv += optind; /* if argc == 0 we will execute zpool_reopen_one on all pools */ - ret = for_each_pool(argc, argv, B_TRUE, NULL, B_FALSE, zpool_reopen_one, - &scrub_restart); + ret = for_each_pool(argc, argv, B_TRUE, NULL, ZFS_TYPE_POOL, + B_FALSE, zpool_reopen_one, &scrub_restart); return (ret); } @@ -7215,13 +7261,13 @@ zpool_do_scrub(int argc, char **argv) usage(B_FALSE); } - error = for_each_pool(argc, argv, B_TRUE, NULL, B_FALSE, - scrub_callback, &cb); + error = for_each_pool(argc, argv, B_TRUE, NULL, ZFS_TYPE_POOL, + B_FALSE, scrub_callback, &cb); if (wait && !error) { zpool_wait_activity_t act = ZPOOL_WAIT_SCRUB; - error = for_each_pool(argc, argv, B_TRUE, NULL, B_FALSE, - wait_callback, &act); + error = for_each_pool(argc, argv, B_TRUE, NULL, ZFS_TYPE_POOL, + B_FALSE, wait_callback, &act); } return (error); @@ -7259,8 +7305,8 @@ zpool_do_resilver(int argc, char **argv) usage(B_FALSE); } - return (for_each_pool(argc, argv, B_TRUE, NULL, B_FALSE, - scrub_callback, &cb)); + return (for_each_pool(argc, argv, B_TRUE, NULL, ZFS_TYPE_POOL, + B_FALSE, scrub_callback, &cb)); } /* @@ -8682,8 +8728,8 @@ zpool_do_status(int argc, char **argv) cb.vcdl = all_pools_for_each_vdev_run(argc, argv, cmd, NULL, NULL, 0, 0); - ret = for_each_pool(argc, argv, B_TRUE, NULL, cb.cb_literal, - status_callback, &cb); + ret = for_each_pool(argc, argv, B_TRUE, NULL, ZFS_TYPE_POOL, + cb.cb_literal, status_callback, &cb); if (cb.vcdl != NULL) free_vdev_cmd_data_list(cb.vcdl); @@ -9240,8 +9286,8 @@ zpool_do_upgrade(int argc, char **argv) (void) printf(gettext("\n")); } } else { - ret = for_each_pool(argc, argv, B_FALSE, NULL, B_FALSE, - upgrade_one, &cb); + ret = for_each_pool(argc, argv, B_FALSE, NULL, ZFS_TYPE_POOL, + B_FALSE, upgrade_one, &cb); } return (ret); @@ -9437,8 +9483,8 @@ zpool_do_history(int argc, char **argv) argc -= optind; argv += optind; - ret = for_each_pool(argc, argv, B_FALSE, NULL, B_FALSE, get_history_one, - &cbdata); + ret = for_each_pool(argc, argv, B_FALSE, NULL, ZFS_TYPE_POOL, + B_FALSE, get_history_one, &cbdata); if (argc == 0 && cbdata.first == B_TRUE) { (void) fprintf(stderr, gettext("no pools available\n")); @@ -9836,7 +9882,7 @@ zpool_do_events(int argc, char **argv) } static int -get_callback(zpool_handle_t *zhp, void *data) +get_callback_vdev(zpool_handle_t *zhp, char *vdevname, void *data) { zprop_get_cbdata_t *cbp = (zprop_get_cbdata_t *)data; char value[MAXNAMELEN]; @@ -9844,7 +9890,99 @@ get_callback(zpool_handle_t *zhp, void *data) zprop_list_t *pl; for (pl = cbp->cb_proplist; pl != NULL; pl = pl->pl_next) { + /* + * Skip the special fake placeholder. This will also skip + * over the name property when 'all' is specified. + */ + if (pl->pl_prop == ZPOOL_PROP_NAME && + pl == cbp->cb_proplist) + continue; + + if (pl->pl_prop == ZPROP_INVAL) { + /* User Properties */ + if (zpool_get_vdev_prop(zhp, vdevname, pl->pl_prop, + pl->pl_user_prop, value, sizeof (value), &srctype, + cbp->cb_literal) == 0) { + zprop_print_one_property( + vdevname, cbp, pl->pl_user_prop, value, + srctype, NULL, NULL); + } + } else { + if (zpool_get_vdev_prop(zhp, vdevname, pl->pl_prop, + NULL, value, sizeof (value), &srctype, + cbp->cb_literal) != 0) + continue; + + zprop_print_one_property( + vdevname, cbp, vdev_prop_to_name(pl->pl_prop), + value, srctype, NULL, NULL); + } + } + + return (0); +} + +static int +get_callback_vdev_width_cb(zpool_handle_t *zhp, nvlist_t *nv, void *data) +{ + zprop_get_cbdata_t *cbp = (zprop_get_cbdata_t *)data; + char *vdevname = zpool_vdev_name(g_zfs, zhp, nv, + cbp->cb_vdevs.cb_name_flags); + int ret; + + /* Adjust the column widths for the vdev properties */ + ret = vdev_expand_proplist(zhp, vdevname, &cbp->cb_proplist); + + return (ret); +} + +static int +get_callback_vdev_cb(zpool_handle_t *zhp, nvlist_t *nv, void *data) +{ + zprop_get_cbdata_t *cbp = (zprop_get_cbdata_t *)data; + char *vdevname = zpool_vdev_name(g_zfs, zhp, nv, + cbp->cb_vdevs.cb_name_flags); + int ret; + + /* Display the properties */ + ret = get_callback_vdev(zhp, vdevname, data); + return (ret); +} + +static int +get_callback(zpool_handle_t *zhp, void *data) +{ + zprop_get_cbdata_t *cbp = (zprop_get_cbdata_t *)data; + char value[MAXNAMELEN]; + zprop_source_t srctype; + zprop_list_t *pl; + int vid; + + if (cbp->cb_type == ZFS_TYPE_VDEV) { + if (strcmp(cbp->cb_vdevs.cb_names[0], "all") == 0) { + for_each_vdev(zhp, get_callback_vdev_width_cb, data); + for_each_vdev(zhp, get_callback_vdev_cb, data); + } else { + for (vid = 0; vid < cbp->cb_vdevs.cb_names_count; + vid++) { + /* Adjust column widths for vdev properties */ + vdev_expand_proplist(zhp, + cbp->cb_vdevs.cb_names[vid], + &cbp->cb_proplist); + } + for (vid = 0; vid < cbp->cb_vdevs.cb_names_count; + vid++) { + /* Display the properties */ + get_callback_vdev(zhp, + cbp->cb_vdevs.cb_names[vid], data); + } + } + + return (0); + } + + for (pl = cbp->cb_proplist; pl != NULL; pl = pl->pl_next) { /* * Skip the special fake placeholder. This will also skip * over the name property when 'all' is specified. @@ -9874,6 +10012,7 @@ get_callback(zpool_handle_t *zhp, void *data) NULL, NULL); } } + return (0); } @@ -9897,6 +10036,7 @@ zpool_do_get(int argc, char **argv) int ret; int c, i; char *value; + char *propstr = NULL; cb.cb_first = B_TRUE; @@ -9909,6 +10049,7 @@ zpool_do_get(int argc, char **argv) cb.cb_columns[2] = GET_COL_VALUE; cb.cb_columns[3] = GET_COL_SOURCE; cb.cb_type = ZFS_TYPE_POOL; + cb.cb_vdevs.cb_name_flags |= VDEV_NAME_TYPE_ID; /* check options */ while ((c = getopt(argc, argv, ":Hpo:")) != -1) { @@ -9986,13 +10127,59 @@ zpool_do_get(int argc, char **argv) usage(B_FALSE); } - if (zprop_get_list(g_zfs, argv[0], &cb.cb_proplist, - ZFS_TYPE_POOL) != 0) - usage(B_FALSE); + /* Save the comma separated list of properties for later */ + propstr = argv[0]; argc--; argv++; + if (argc == 0) { + /* No args, so just print the defaults. */ + } else if (are_all_pools(argc, argv)) { + /* All the args are pool names */ + } else if (are_vdevs_in_pool(argc, argv, NULL, &cb.cb_vdevs)) { + /* All the args are vdevs */ + cb.cb_vdevs.cb_names = argv; + cb.cb_vdevs.cb_names_count = argc; + cb.cb_type = ZFS_TYPE_VDEV; + argc = 0; /* No pools to process */ + } else if (are_all_pools(1, argv)) { + /* The first arg is a pool name */ + if (are_vdevs_in_pool(argc - 1, argv + 1, argv[0], + &cb.cb_vdevs) || (strcmp(argv[1], "all") == 0)) { + /* ...and the rest are vdev names */ + cb.cb_vdevs.cb_names = argv + 1; + cb.cb_vdevs.cb_names_count = argc - 1; + cb.cb_type = ZFS_TYPE_VDEV; + argc = 1; /* One pool to process */ + } else { + fprintf(stderr, gettext("Expected either a list of ")); + fprintf(stderr, gettext("pools, or list of vdevs in")); + fprintf(stderr, " \"%s\", ", argv[0]); + fprintf(stderr, gettext("but got:\n")); + error_list_unresolved_vdevs(argc - 1, argv + 1, + argv[0], &cb.cb_vdevs); + fprintf(stderr, "\n"); + usage(B_FALSE); + return (1); + } + } else { + /* + * The args don't make sense. The first arg isn't a pool name, + * nor are all the args vdevs. + */ + fprintf(stderr, gettext("Unable to parse pools/vdevs list.\n")); + fprintf(stderr, "\n"); + return (1); + } + + if (zprop_get_list(g_zfs, propstr, &cb.cb_proplist, + cb.cb_type) != 0) { + /* Use correct list of valid properties (pool or vdev) */ + current_prop_type = cb.cb_type; + usage(B_FALSE); + } + if (cb.cb_proplist != NULL) { fake_name.pl_prop = ZPOOL_PROP_NAME; fake_name.pl_width = strlen(gettext("NAME")); @@ -10000,8 +10187,8 @@ zpool_do_get(int argc, char **argv) cb.cb_proplist = &fake_name; } - ret = for_each_pool(argc, argv, B_TRUE, &cb.cb_proplist, cb.cb_literal, - get_callback, &cb); + ret = for_each_pool(argc, argv, B_TRUE, &cb.cb_proplist, cb.cb_type, + cb.cb_literal, get_callback, &cb); if (cb.cb_proplist == &fake_name) zprop_free_list(fake_name.pl_next); @@ -10014,6 +10201,8 @@ zpool_do_get(int argc, char **argv) typedef struct set_cbdata { char *cb_propname; char *cb_value; + zfs_type_t cb_type; + vdev_cbdata_t cb_vdevs; boolean_t cb_any_successful; } set_cbdata_t; @@ -10023,6 +10212,7 @@ set_callback(zpool_handle_t *zhp, void *data) int error; set_cbdata_t *cb = (set_cbdata_t *)data; +<<<<<<< HEAD /* Check if we have out-of-bounds features */ if (strcmp(cb->cb_propname, ZPOOL_CONFIG_COMPATIBILITY) == 0) { boolean_t features[SPA_FEATURES]; @@ -10080,7 +10270,11 @@ set_callback(zpool_handle_t *zhp, void *data) } } - error = zpool_set_prop(zhp, cb->cb_propname, cb->cb_value); + if (cb->cb_type == ZFS_TYPE_VDEV) + error = zpool_set_vdev_prop(zhp, *cb->cb_vdevs.cb_names, + cb->cb_propname, cb->cb_value); + else + error = zpool_set_prop(zhp, cb->cb_propname, cb->cb_value); if (!error) cb->cb_any_successful = B_TRUE; @@ -10111,12 +10305,14 @@ zpool_do_set(int argc, char **argv) usage(B_FALSE); } - if (argc > 3) { + if (argc > 4) { (void) fprintf(stderr, gettext("too many pool names\n")); usage(B_FALSE); } cb.cb_propname = argv[1]; + cb.cb_type = ZFS_TYPE_POOL; + cb.cb_vdevs.cb_name_flags |= VDEV_NAME_TYPE_ID; cb.cb_value = strchr(cb.cb_propname, '='); if (cb.cb_value == NULL) { (void) fprintf(stderr, gettext("missing value in " @@ -10126,9 +10322,33 @@ zpool_do_set(int argc, char **argv) *(cb.cb_value) = '\0'; cb.cb_value++; + argc -= 2; + argv += 2; + + if (are_vdevs_in_pool(argc, argv, NULL, &cb.cb_vdevs)) { + /* Argument is a vdev */ + cb.cb_vdevs.cb_names = argv; + cb.cb_vdevs.cb_names_count = 1; + cb.cb_type = ZFS_TYPE_VDEV; + argc = 0; /* No pools to process */ + } else if (are_all_pools(1, argv)) { + /* The first arg is a pool name */ + if (are_vdevs_in_pool(argc - 1, argv + 1, argv[0], + &cb.cb_vdevs)) { + /* 2nd argument is a vdev */ + cb.cb_vdevs.cb_names = argv + 1; + cb.cb_vdevs.cb_names_count = 1; + cb.cb_type = ZFS_TYPE_VDEV; + argc = 1; /* One pool to process */ + } else if (argc > 1) { + (void) fprintf(stderr, + gettext("too many pool names\n")); + usage(B_FALSE); + } + } - error = for_each_pool(argc - 2, argv + 2, B_TRUE, NULL, B_FALSE, - set_callback, &cb); + error = for_each_pool(argc, argv, B_TRUE, NULL, ZFS_TYPE_POOL, + B_FALSE, set_callback, &cb); return (error); } diff --git a/cmd/zpool/zpool_util.h b/cmd/zpool/zpool_util.h index abaa22d78c20..6ebbd3f8797e 100644 --- a/cmd/zpool/zpool_util.h +++ b/cmd/zpool/zpool_util.h @@ -63,7 +63,7 @@ nvlist_t *split_mirror_vdev(zpool_handle_t *zhp, char *newname, /* * Pool list functions */ -int for_each_pool(int, char **, boolean_t unavail, zprop_list_t **, +int for_each_pool(int, char **, boolean_t unavail, zprop_list_t **, zfs_type_t, boolean_t, zpool_iter_f, void *); /* Vdev list functions */ @@ -72,7 +72,8 @@ int for_each_vdev(zpool_handle_t *zhp, pool_vdev_iter_f func, void *data); typedef struct zpool_list zpool_list_t; -zpool_list_t *pool_list_get(int, char **, zprop_list_t **, boolean_t, int *); +zpool_list_t *pool_list_get(int, char **, zprop_list_t **, zfs_type_t, + boolean_t, int *); void pool_list_update(zpool_list_t *); int pool_list_iter(zpool_list_t *, int unavail, zpool_iter_f, void *); void pool_list_free(zpool_list_t *); diff --git a/include/libzfs.h b/include/libzfs.h index eeb4daae723b..0b5509d52f62 100644 --- a/include/libzfs.h +++ b/include/libzfs.h @@ -333,6 +333,24 @@ extern int zpool_props_refresh(zpool_handle_t *); extern const char *zpool_prop_to_name(zpool_prop_t); extern const char *zpool_prop_values(zpool_prop_t); +/* + * Functions to manage vdev properties + */ +extern int zpool_get_vdev_prop_value(nvlist_t *, vdev_prop_t, char *, char *, + size_t, zprop_source_t *, boolean_t); +extern int zpool_get_vdev_prop(zpool_handle_t *, const char *, vdev_prop_t, + char *, char *, size_t, zprop_source_t *, boolean_t); +extern int zpool_get_all_vdev_props(zpool_handle_t *, const char *, + nvlist_t **); +extern int zpool_set_vdev_prop(zpool_handle_t *, const char *, const char *, + const char *); + +extern const char *vdev_prop_to_name(vdev_prop_t); +extern const char *vdev_prop_values(vdev_prop_t); +extern boolean_t vdev_prop_user(const char *name); +extern const char *vdev_prop_column_name(vdev_prop_t); +extern boolean_t vdev_prop_align_right(vdev_prop_t); + /* * Pool health statistics. */ @@ -546,6 +564,8 @@ typedef struct zprop_list { extern int zfs_expand_proplist(zfs_handle_t *, zprop_list_t **, boolean_t, boolean_t); extern void zfs_prune_proplist(zfs_handle_t *, uint8_t *); +extern int vdev_expand_proplist(zpool_handle_t *, const char *, + zprop_list_t **); #define ZFS_MOUNTPOINT_NONE "none" #define ZFS_MOUNTPOINT_LEGACY "legacy" @@ -560,7 +580,8 @@ extern void zfs_prune_proplist(zfs_handle_t *, uint8_t *); /* * zpool property management */ -extern int zpool_expand_proplist(zpool_handle_t *, zprop_list_t **, boolean_t); +extern int zpool_expand_proplist(zpool_handle_t *, zprop_list_t **, + zfs_type_t, boolean_t); extern int zpool_prop_get_feature(zpool_handle_t *, const char *, char *, size_t); extern const char *zpool_prop_default_string(zpool_prop_t); @@ -591,6 +612,12 @@ typedef enum { /* * Functions for printing zfs or zpool properties */ +typedef struct vdev_cbdata { + int cb_name_flags; + char **cb_names; + unsigned int cb_names_count; +} vdev_cbdata_t; + typedef struct zprop_get_cbdata { int cb_sources; zfs_get_column_t cb_columns[ZFS_GET_NCOLS]; @@ -600,6 +627,7 @@ typedef struct zprop_get_cbdata { boolean_t cb_first; zprop_list_t *cb_proplist; zfs_type_t cb_type; + vdev_cbdata_t cb_vdevs; } zprop_get_cbdata_t; void zprop_print_one_property(const char *, zprop_get_cbdata_t *, diff --git a/include/libzfs_core.h b/include/libzfs_core.h index 34161a06fb45..b63e42da6115 100644 --- a/include/libzfs_core.h +++ b/include/libzfs_core.h @@ -137,6 +137,10 @@ int lzc_wait_fs(const char *, zfs_wait_activity_t, boolean_t *); int lzc_set_bootenv(const char *, const nvlist_t *); int lzc_get_bootenv(const char *, nvlist_t **); + +int lzc_get_vdev_prop(const char *, nvlist_t *, nvlist_t **); +int lzc_set_vdev_prop(const char *, nvlist_t *, nvlist_t **); + #ifdef __cplusplus } #endif diff --git a/include/sys/fs/zfs.h b/include/sys/fs/zfs.h index 71d736d5cc97..cc727527241b 100644 --- a/include/sys/fs/zfs.h +++ b/include/sys/fs/zfs.h @@ -54,7 +54,8 @@ typedef enum { ZFS_TYPE_SNAPSHOT = (1 << 1), ZFS_TYPE_VOLUME = (1 << 2), ZFS_TYPE_POOL = (1 << 3), - ZFS_TYPE_BOOKMARK = (1 << 4) + ZFS_TYPE_BOOKMARK = (1 << 4), + ZFS_TYPE_VDEV = (1 << 5), } zfs_type_t; /* @@ -298,6 +299,58 @@ typedef int (*zprop_func)(int, void *); */ #define ZFS_WRITTEN_PROP_PREFIX_LEN 8 +/* + * VDEV properties are identified by these constants and must be added to the + * end of this list to ensure that external consumers are not affected + * by the change. If you make any changes to this list, be sure to update + * the property table in usr/src/common/zfs/zpool_prop.c. + */ +typedef enum { + VDEV_PROP_INVAL = -1, + VDEV_PROP_NAME, + VDEV_PROP_CAPACITY, + VDEV_PROP_STATE, + VDEV_PROP_GUID, + VDEV_PROP_ASIZE, + VDEV_PROP_PSIZE, + VDEV_PROP_ASHIFT, + VDEV_PROP_ROTATION_RATE, + VDEV_PROP_SIZE, + VDEV_PROP_FREE, + VDEV_PROP_ALLOCATED, + VDEV_PROP_COMMENT, + VDEV_PROP_EXPANDSZ, + VDEV_PROP_FRAGMENTATION, + VDEV_PROP_BOOTSIZE, + VDEV_PROP_PARITY, + VDEV_PROP_PATH, + VDEV_PROP_DEVID, + VDEV_PROP_PHYS_PATH, + VDEV_PROP_ENC_PATH, + VDEV_PROP_FRU, + VDEV_PROP_PARENT, + VDEV_PROP_CHILDREN, + VDEV_PROP_NUMCHILDREN, + VDEV_PROP_READ_ERRORS, + VDEV_PROP_WRITE_ERRORS, + VDEV_PROP_CHECKSUM_ERRORS, + VDEV_PROP_INITIALIZE_ERRORS, + VDEV_PROP_OPS_NULL, + VDEV_PROP_OPS_READ, + VDEV_PROP_OPS_WRITE, + VDEV_PROP_OPS_FREE, + VDEV_PROP_OPS_CLAIM, + VDEV_PROP_OPS_TRIM, + VDEV_PROP_BYTES_NULL, + VDEV_PROP_BYTES_READ, + VDEV_PROP_BYTES_WRITE, + VDEV_PROP_BYTES_FREE, + VDEV_PROP_BYTES_CLAIM, + VDEV_PROP_BYTES_TRIM, + VDEV_PROP_NOALLOC, + VDEV_NUM_PROPS +} vdev_prop_t; + /* * Dataset property functions shared between libzfs and kernel. */ @@ -334,6 +387,20 @@ int zpool_prop_index_to_string(zpool_prop_t, uint64_t, const char **); int zpool_prop_string_to_index(zpool_prop_t, const char *, uint64_t *); uint64_t zpool_prop_random_value(zpool_prop_t, uint64_t seed); +/* + * VDEV property functions shared between libzfs and kernel. + */ +vdev_prop_t vdev_name_to_prop(const char *); +boolean_t vdev_prop_user(const char *name); +const char *vdev_prop_to_name(vdev_prop_t); +const char *vdev_prop_default_string(vdev_prop_t); +uint64_t vdev_prop_default_numeric(vdev_prop_t); +boolean_t vdev_prop_readonly(vdev_prop_t prop); +int vdev_prop_index_to_string(vdev_prop_t, uint64_t, const char **); +int vdev_prop_string_to_index(vdev_prop_t, const char *, uint64_t *); +boolean_t zpool_prop_vdev(const char *name); +uint64_t vdev_prop_random_value(vdev_prop_t prop, uint64_t seed); + /* * Definitions for the Delegation. */ @@ -1354,6 +1421,8 @@ typedef enum zfs_ioc { ZFS_IOC_GET_BOOKMARK_PROPS, /* 0x5a52 */ ZFS_IOC_WAIT, /* 0x5a53 */ ZFS_IOC_WAIT_FS, /* 0x5a54 */ + ZFS_IOC_VDEV_GET_PROPS, /* 0x5a55 */ + ZFS_IOC_VDEV_SET_PROPS, /* 0x5a56 */ /* * Per-platform (Optional) - 8/128 numbers reserved. @@ -1500,6 +1569,18 @@ typedef enum { #define ZPOOL_WAIT_TAG "wait_tag" #define ZPOOL_WAIT_WAITED "wait_waited" +/* + * The following are names used when invoking ZFS_IOC_VDEV_GET_PROP. + */ +#define ZPOOL_VDEV_GET_PROPS_VDEV "vdevprops_get_vdev" +#define ZPOOL_VDEV_GET_PROPS_PROPS "vdevprops_get_props" + +/* + * The following are names used when invoking ZFS_IOC_VDEV_SET_PROP. + */ +#define ZPOOL_VDEV_SET_PROPS_VDEV "vdevprops_set_vdev" +#define ZPOOL_VDEV_SET_PROPS_PROPS "vdevprops_set_props" + /* * The following are names used when invoking ZFS_IOC_WAIT_FS. */ diff --git a/include/sys/vdev.h b/include/sys/vdev.h index f235bfc8cc19..6b1f8d755ece 100644 --- a/include/sys/vdev.h +++ b/include/sys/vdev.h @@ -218,6 +218,9 @@ typedef enum { extern int vdev_label_init(vdev_t *vd, uint64_t txg, vdev_labeltype_t reason); +extern int vdev_prop_set(vdev_t *vd, nvlist_t *innvl, nvlist_t *outnvl); +extern int vdev_prop_get(vdev_t *vd, nvlist_t *nvprops, nvlist_t *outnvl); + #ifdef __cplusplus } #endif diff --git a/include/zfs_prop.h b/include/zfs_prop.h index 89b6a20243fb..52313cea7849 100644 --- a/include/zfs_prop.h +++ b/include/zfs_prop.h @@ -99,6 +99,13 @@ void zpool_prop_init(void); zprop_type_t zpool_prop_get_type(zpool_prop_t); zprop_desc_t *zpool_prop_get_table(void); +/* + * vdev property functions + */ +void vdev_prop_init(void); +zprop_type_t vdev_prop_get_type(vdev_prop_t prop); +zprop_desc_t *vdev_prop_get_table(void); + /* * Common routines to initialize property tables */ @@ -125,6 +132,7 @@ uint64_t zprop_random_value(int, uint64_t, zfs_type_t); const char *zprop_values(int, zfs_type_t); size_t zprop_width(int, boolean_t *, zfs_type_t); boolean_t zprop_valid_for_type(int, zfs_type_t, boolean_t); +int zprop_valid_char(char c); #ifdef __cplusplus } diff --git a/lib/libzfs/libzfs_pool.c b/lib/libzfs/libzfs_pool.c index b4c9d7df90b6..19720d639409 100644 --- a/lib/libzfs/libzfs_pool.c +++ b/lib/libzfs/libzfs_pool.c @@ -29,6 +29,7 @@ * Copyright (c) 2017, Intel Corporation. * Copyright (c) 2018, loli10K * Copyright (c) 2021, Colm Buckley + * Copyright (c) 2021, Klara Inc. */ #include @@ -61,6 +62,7 @@ static boolean_t zpool_vdev_is_interior(const char *name); typedef struct prop_flags { int create:1; /* Validate property on creation */ int import:1; /* Validate property on import */ + int vdevprop:1; /* Validate property as a VDEV property */ } prop_flags_t; /* @@ -478,6 +480,35 @@ zpool_valid_proplist(libzfs_handle_t *hdl, const char *poolname, while ((elem = nvlist_next_nvpair(props, elem)) != NULL) { const char *propname = nvpair_name(elem); + if (flags.vdevprop && zpool_prop_vdev(propname)) { + vdev_prop_t vprop = vdev_name_to_prop(propname); + + if (vdev_prop_readonly(vprop)) { + zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "'%s' " + "is readonly"), propname); + (void) zfs_error(hdl, EZFS_PROPREADONLY, + errbuf); + goto error; + } + + if (zprop_parse_value(hdl, elem, vprop, ZFS_TYPE_VDEV, + retprops, &strval, &intval, errbuf) != 0) + goto error; + + continue; + } else if (flags.vdevprop && vdev_prop_user(propname)) { + if (nvlist_add_nvpair(retprops, elem) != 0) { + (void) no_memory(hdl); + goto error; + } + continue; + } else if (flags.vdevprop) { + zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, + "invalid property: '%s'"), propname); + (void) zfs_error(hdl, EZFS_BADPROP, errbuf); + goto error; + } + prop = zpool_name_to_prop(propname); if (prop == ZPOOL_PROP_INVAL && zpool_prop_feature(propname)) { int err; @@ -805,7 +836,7 @@ zpool_set_prop(zpool_handle_t *zhp, const char *propname, const char *propval) int zpool_expand_proplist(zpool_handle_t *zhp, zprop_list_t **plp, - boolean_t literal) + zfs_type_t type, boolean_t literal) { libzfs_handle_t *hdl = zhp->zpool_hdl; zprop_list_t *entry; @@ -816,9 +847,12 @@ zpool_expand_proplist(zpool_handle_t *zhp, zprop_list_t **plp, boolean_t firstexpand = (NULL == *plp); int i; - if (zprop_expand_list(hdl, plp, ZFS_TYPE_POOL) != 0) + if (zprop_expand_list(hdl, plp, type) != 0) return (-1); + if (type == ZFS_TYPE_VDEV) + return (0); + last = plp; while (*last != NULL) last = &(*last)->pl_next; @@ -898,6 +932,76 @@ zpool_expand_proplist(zpool_handle_t *zhp, zprop_list_t **plp, return (0); } +int +vdev_expand_proplist(zpool_handle_t *zhp, const char *vdevname, + zprop_list_t **plp) +{ + zprop_list_t *entry; + char buf[ZFS_MAXPROPLEN]; + char *strval = NULL; + int err = 0; + nvpair_t *elem = NULL; + nvlist_t *vprops = NULL; + nvlist_t *propval = NULL; + const char *propname; + vdev_prop_t prop; + zprop_list_t **last; + + for (entry = *plp; entry != NULL; entry = entry->pl_next) { + if (entry->pl_fixed) + continue; + + if (zpool_get_vdev_prop(zhp, vdevname, entry->pl_prop, + entry->pl_user_prop, buf, sizeof (buf), NULL, + B_FALSE) == 0) { + if (strlen(buf) > entry->pl_width) + entry->pl_width = strlen(buf); + } + if (entry->pl_prop == VDEV_PROP_NAME && + strlen(vdevname) > entry->pl_width) + entry->pl_width = strlen(vdevname); + } + + /* Handle the all properties case */ + last = plp; + if (*last != NULL && (*last)->pl_all == B_TRUE) { + while (*last != NULL) + last = &(*last)->pl_next; + + err = zpool_get_all_vdev_props(zhp, vdevname, &vprops); + if (err != 0) + return (err); + + while ((elem = nvlist_next_nvpair(vprops, elem)) != NULL) { + propname = nvpair_name(elem); + + /* Skip properties that are not user defined */ + if ((prop = vdev_name_to_prop(propname)) != ZPROP_INVAL) + continue; + + if (nvpair_value_nvlist(elem, &propval) != 0) + continue; + + verify(nvlist_lookup_string(propval, ZPROP_VALUE, + &strval) == 0); + + if ((entry = zfs_alloc(zhp->zpool_hdl, + sizeof (zprop_list_t))) == NULL) + return (ZPROP_INVAL); + + entry->pl_prop = prop; + entry->pl_user_prop = zfs_strdup(zhp->zpool_hdl, + propname); + entry->pl_width = strlen(strval); + entry->pl_all = B_TRUE; + *last = entry; + last = &entry->pl_next; + } + } + + return (0); +} + /* * Get the state for the given feature on the given ZFS pool. */ @@ -4936,3 +5040,364 @@ zpool_load_compat(const char *compat, boolean_t *features, char *report, strlcpy(report, gettext("compatibility set ok"), rlen); return (ZPOOL_COMPATIBILITY_OK); } + +/* + * Get a vdev property value for 'prop' and return the value in + * a pre-allocated buffer. + */ +int +zpool_get_vdev_prop_value(nvlist_t *nvprop, vdev_prop_t prop, char *prop_name, + char *buf, size_t len, zprop_source_t *srctype, boolean_t literal) +{ + nvlist_t *nv; + uint64_t intval; + char *strval; + zprop_source_t src = ZPROP_SRC_NONE; + + if (prop == VDEV_PROP_INVAL) { + assert(prop_name != NULL); + if (nvlist_lookup_nvlist(nvprop, prop_name, &nv) == 0) { + verify(nvlist_lookup_uint64(nv, ZPROP_SOURCE, + &intval) == 0); + src = intval; + verify(nvlist_lookup_string(nv, ZPROP_VALUE, + &strval) == 0); + } else { + src = ZPROP_SRC_NONE; + strval = "-"; + } + (void) strlcpy(buf, strval, len); + if (srctype) + *srctype = src; + return (0); + } + + if (prop_name == NULL) + prop_name = (char *)vdev_prop_to_name(prop); + + switch (vdev_prop_get_type(prop)) { + case PROP_TYPE_STRING: + if (nvlist_lookup_nvlist(nvprop, prop_name, &nv) == 0) { + verify(nvlist_lookup_uint64(nv, ZPROP_SOURCE, + &intval) == 0); + src = intval; + verify(nvlist_lookup_string(nv, ZPROP_VALUE, + &strval) == 0); + } else { + src = ZPROP_SRC_DEFAULT; + if ((strval = (char *)vdev_prop_default_string(prop)) + == NULL) + strval = "-"; + } + (void) strlcpy(buf, strval, len); + break; + + case PROP_TYPE_NUMBER: + if (nvlist_lookup_nvlist(nvprop, prop_name, &nv) == 0) { + verify(nvlist_lookup_uint64(nv, ZPROP_SOURCE, + &intval) == 0); + src = intval; + verify(nvlist_lookup_uint64(nv, ZPROP_VALUE, + &intval) == 0); + } else { + src = ZPROP_SRC_DEFAULT; + intval = vdev_prop_default_numeric(prop); + } + + switch (prop) { + case VDEV_PROP_ASIZE: + case VDEV_PROP_PSIZE: + case VDEV_PROP_SIZE: + case VDEV_PROP_ALLOCATED: + case VDEV_PROP_FREE: + case VDEV_PROP_READ_ERRORS: + case VDEV_PROP_WRITE_ERRORS: + case VDEV_PROP_CHECKSUM_ERRORS: + case VDEV_PROP_INITIALIZE_ERRORS: + case VDEV_PROP_OPS_NULL: + case VDEV_PROP_OPS_READ: + case VDEV_PROP_OPS_WRITE: + case VDEV_PROP_OPS_FREE: + case VDEV_PROP_OPS_CLAIM: + case VDEV_PROP_OPS_TRIM: + case VDEV_PROP_BYTES_NULL: + case VDEV_PROP_BYTES_READ: + case VDEV_PROP_BYTES_WRITE: + case VDEV_PROP_BYTES_FREE: + case VDEV_PROP_BYTES_CLAIM: + case VDEV_PROP_BYTES_TRIM: + if (literal) { + (void) snprintf(buf, len, "%llu", + (u_longlong_t)intval); + } else { + (void) zfs_nicenum(intval, buf, len); + } + break; + case VDEV_PROP_EXPANDSZ: + if (intval == 0) { + (void) strlcpy(buf, "-", len); + } else if (literal) { + (void) snprintf(buf, len, "%llu", + (u_longlong_t)intval); + } else { + (void) zfs_nicenum(intval, buf, len); + } + break; + case VDEV_PROP_CAPACITY: + if (literal) { + (void) snprintf(buf, len, "%llu", + (u_longlong_t)intval); + } else { + (void) snprintf(buf, len, "%llu%%", + (u_longlong_t)intval); + } + break; + case VDEV_PROP_FRAGMENTATION: + if (intval == UINT64_MAX) { + (void) strlcpy(buf, "-", len); + } else { + (void) snprintf(buf, len, "%llu%%", + (u_longlong_t)intval); + } + break; + case VDEV_PROP_STATE: + if (literal) { + (void) snprintf(buf, len, "%llu", + (u_longlong_t)intval); + } else { + (void) strlcpy(buf, zpool_state_to_name(intval, + VDEV_AUX_NONE), len); + } + break; + case VDEV_PROP_ROTATION_RATE: + if (intval == 0) /* VDEV_RATE_UNKNOWN */ + (void) snprintf(buf, len, "Unknown"); + else if (intval == 1) /* VDEV_RATE_NON_ROTATING */ + (void) snprintf(buf, len, "Non-Rotating"); + else + (void) snprintf(buf, len, "%zu RPM", intval); + default: + (void) snprintf(buf, len, "%zu", intval); + } + break; + + case PROP_TYPE_INDEX: + if (nvlist_lookup_nvlist(nvprop, prop_name, &nv) == 0) { + verify(nvlist_lookup_uint64(nv, ZPROP_SOURCE, + &intval) == 0); + src = intval; + verify(nvlist_lookup_uint64(nv, ZPROP_VALUE, + &intval) == 0); + } else { + src = ZPROP_SRC_DEFAULT; + intval = vdev_prop_default_numeric(prop); + } + if (vdev_prop_index_to_string(prop, intval, + (const char **)&strval) != 0) + return (-1); + (void) strlcpy(buf, strval, len); + break; + + default: + abort(); + } + + if (srctype) + *srctype = src; + + return (0); +} + +/* + * Get a vdev property value for 'prop_name' and return the value in + * a pre-allocated buffer. + */ +int +zpool_get_vdev_prop(zpool_handle_t *zhp, const char *vdevname, vdev_prop_t prop, + char *prop_name, char *buf, size_t len, zprop_source_t *srctype, + boolean_t literal) +{ + nvlist_t *tgt, *reqnvl, *reqprops, *retprops; + uint64_t vdev_guid; + boolean_t avail_spare, l2cache; + char errbuf[1024]; + int ret = -1; + + verify(zhp != NULL); + if (zpool_get_state(zhp) == POOL_STATE_UNAVAIL) { + (void) snprintf(errbuf, sizeof (errbuf), + dgettext(TEXT_DOMAIN, "pool is in an unavailable state")); + return (zfs_error(zhp->zpool_hdl, EZFS_POOLUNAVAIL, errbuf)); + } + + if ((tgt = zpool_find_vdev(zhp, vdevname, &avail_spare, &l2cache, + NULL)) == NULL) + return (-1); + + verify(nvlist_lookup_uint64(tgt, ZPOOL_CONFIG_GUID, &vdev_guid) == 0); + + if (nvlist_alloc(&reqnvl, NV_UNIQUE_NAME, 0) != 0) + return (no_memory(zhp->zpool_hdl)); + if (nvlist_alloc(&reqprops, NV_UNIQUE_NAME, 0) != 0) + return (no_memory(zhp->zpool_hdl)); + + fnvlist_add_uint64(reqnvl, ZPOOL_VDEV_GET_PROPS_VDEV, vdev_guid); + + if (prop != VDEV_PROP_INVAL && prop_name == NULL) + prop_name = (char *)vdev_prop_to_name(prop); + + assert(prop_name != NULL); + if (nvlist_add_uint64(reqprops, prop_name, prop) != 0) { + nvlist_free(reqnvl); + nvlist_free(reqprops); + return (no_memory(zhp->zpool_hdl)); + } + + fnvlist_add_nvlist(reqnvl, ZPOOL_VDEV_GET_PROPS_PROPS, reqprops); + + (void) snprintf(errbuf, sizeof (errbuf), + dgettext(TEXT_DOMAIN, "cannot get vdev property %s from %s in %s"), + prop_name, vdevname, zhp->zpool_name); + + ret = lzc_get_vdev_prop(zhp->zpool_name, reqnvl, &retprops); + + if (ret) + (void) zpool_standard_error(zhp->zpool_hdl, ret, errbuf); + + nvlist_free(reqnvl); + nvlist_free(reqprops); + + if (ret == 0) + ret = zpool_get_vdev_prop_value(retprops, prop, prop_name, buf, + len, srctype, literal); + + nvlist_free(retprops); + + return (ret); +} + +/* + * Get all vdev properties + */ +int +zpool_get_all_vdev_props(zpool_handle_t *zhp, const char *vdevname, + nvlist_t **outnvl) +{ + nvlist_t *tgt; + nvlist_t *nvl = NULL; + uint64_t vdev_guid; + boolean_t avail_spare, l2cache; + char errbuf[1024]; + int ret = -1; + + verify(zhp != NULL); + if (zpool_get_state(zhp) == POOL_STATE_UNAVAIL) { + (void) snprintf(errbuf, sizeof (errbuf), + dgettext(TEXT_DOMAIN, "pool is in an unavailable state")); + return (zfs_error(zhp->zpool_hdl, EZFS_POOLUNAVAIL, errbuf)); + } + + if ((tgt = zpool_find_vdev(zhp, vdevname, &avail_spare, &l2cache, + NULL)) == NULL) + return (-1); + + verify(nvlist_lookup_uint64(tgt, ZPOOL_CONFIG_GUID, &vdev_guid) == 0); + + (void) snprintf(errbuf, sizeof (errbuf), + dgettext(TEXT_DOMAIN, "cannot get vdev properties for %s in %s"), + vdevname, zhp->zpool_name); + + if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0) != 0) + return (no_memory(zhp->zpool_hdl)); + + fnvlist_add_uint64(nvl, ZPOOL_VDEV_GET_PROPS_VDEV, vdev_guid); + + ret = lzc_get_vdev_prop(zhp->zpool_name, nvl, outnvl); + + nvlist_free(nvl); + + /* XXX: Allan: Errors */ + if (ret) + (void) zpool_standard_error(zhp->zpool_hdl, errno, errbuf); + + return (ret); +} + +/* + * Set vdev property + */ +int +zpool_set_vdev_prop(zpool_handle_t *zhp, const char *vdevname, + const char *propname, const char *propval) +{ + int ret = -1; + char errbuf[1024]; + vdev_prop_t vprop; + nvlist_t *nvl = NULL; + nvlist_t *outnvl = NULL; + nvlist_t *props; + nvlist_t *realprops; + nvlist_t *tgt; + prop_flags_t flags = { 0 }; + boolean_t avail_spare, l2cache; + uint64_t version; + uint64_t vdev_guid; + + verify(zhp != NULL); + if (zpool_get_state(zhp) == POOL_STATE_UNAVAIL) { + (void) snprintf(errbuf, sizeof (errbuf), + dgettext(TEXT_DOMAIN, "pool is in an unavailable state")); + return (zfs_error(zhp->zpool_hdl, EZFS_POOLUNAVAIL, errbuf)); + } + + vprop = vdev_name_to_prop(propname); + + if ((tgt = zpool_find_vdev(zhp, vdevname, &avail_spare, &l2cache, + NULL)) == NULL) + return (-1); + + verify(nvlist_lookup_uint64(tgt, ZPOOL_CONFIG_GUID, &vdev_guid) == 0); + + (void) snprintf(errbuf, sizeof (errbuf), + dgettext(TEXT_DOMAIN, "cannot set property %s for %s on %s"), + propname, vdevname, zhp->zpool_name); + + if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0) != 0) + return (no_memory(zhp->zpool_hdl)); + if (nvlist_alloc(&props, NV_UNIQUE_NAME, 0) != 0) + return (no_memory(zhp->zpool_hdl)); + + fnvlist_add_uint64(nvl, ZPOOL_VDEV_SET_PROPS_VDEV, vdev_guid); + + if (nvlist_add_string(props, propname, propval) != 0) { + nvlist_free(props); + return (no_memory(zhp->zpool_hdl)); + } + + flags.vdevprop = 1; + version = zpool_get_prop_int(zhp, ZPOOL_PROP_VERSION, NULL); + if ((realprops = zpool_valid_proplist(zhp->zpool_hdl, + zhp->zpool_name, props, version, flags, errbuf)) == NULL) { + nvlist_free(props); + nvlist_free(nvl); + return (-1); + } + + nvlist_free(props); + props = realprops; + + fnvlist_add_nvlist(nvl, ZPOOL_VDEV_SET_PROPS_PROPS, props); + + ret = lzc_set_vdev_prop(zhp->zpool_name, nvl, &outnvl); + +/* XXX: Allan: check for errors! */ + + nvlist_free(props); + nvlist_free(nvl); + nvlist_free(outnvl); + + if (ret) + (void) zpool_standard_error(zhp->zpool_hdl, errno, errbuf); + + return (ret); +} diff --git a/lib/libzfs/libzfs_util.c b/lib/libzfs/libzfs_util.c index 8038f7fa343e..baca9d915efe 100644 --- a/lib/libzfs/libzfs_util.c +++ b/lib/libzfs/libzfs_util.c @@ -1034,6 +1034,7 @@ libzfs_init(void) zfs_prop_init(); zpool_prop_init(); zpool_feature_init(); + vdev_prop_init(); libzfs_mnttab_init(hdl); fletcher_4_init(); @@ -1262,7 +1263,8 @@ zprop_print_headers(zprop_get_cbdata_t *cbp, zfs_type_t type) /* first property is always NAME */ assert(cbp->cb_proplist->pl_prop == - ((type == ZFS_TYPE_POOL) ? ZPOOL_PROP_NAME : ZFS_PROP_NAME)); + ((type == ZFS_TYPE_POOL) ? ZPOOL_PROP_NAME : + ((type == ZFS_TYPE_VDEV) ? VDEV_PROP_NAME : ZFS_PROP_NAME))); /* * Go through and calculate the widths for each column. For the @@ -1277,14 +1279,18 @@ zprop_print_headers(zprop_get_cbdata_t *cbp, zfs_type_t type) * 'PROPERTY' column */ if (pl->pl_prop != ZPROP_INVAL) { - const char *propname = (type == ZFS_TYPE_POOL) ? + const char *propname = ((type == ZFS_TYPE_POOL) ? zpool_prop_to_name(pl->pl_prop) : - zfs_prop_to_name(pl->pl_prop); + ((type == ZFS_TYPE_VDEV) ? + vdev_prop_to_name(pl->pl_prop) : + zfs_prop_to_name(pl->pl_prop))); + assert(propname != NULL); len = strlen(propname); if (len > cbp->cb_colwidths[GET_COL_PROPERTY]) cbp->cb_colwidths[GET_COL_PROPERTY] = len; } else { + assert(pl->pl_user_prop != NULL); len = strlen(pl->pl_user_prop); if (len > cbp->cb_colwidths[GET_COL_PROPERTY]) cbp->cb_colwidths[GET_COL_PROPERTY] = len; @@ -1309,9 +1315,10 @@ zprop_print_headers(zprop_get_cbdata_t *cbp, zfs_type_t type) /* * 'NAME' and 'SOURCE' columns */ - if (pl->pl_prop == (type == ZFS_TYPE_POOL ? ZPOOL_PROP_NAME : - ZFS_PROP_NAME) && - pl->pl_width > cbp->cb_colwidths[GET_COL_NAME]) { + if (pl->pl_prop == ((type == ZFS_TYPE_POOL) ? ZPOOL_PROP_NAME : + ((type == ZFS_TYPE_VDEV) ? VDEV_PROP_NAME : + ZFS_PROP_NAME)) && pl->pl_width > + cbp->cb_colwidths[GET_COL_NAME]) { cbp->cb_colwidths[GET_COL_NAME] = pl->pl_width; cbp->cb_colwidths[GET_COL_SOURCE] = pl->pl_width + strlen(dgettext(TEXT_DOMAIN, "inherited from")); @@ -1592,6 +1599,9 @@ zprop_parse_value(libzfs_handle_t *hdl, nvpair_t *elem, int prop, if (type == ZFS_TYPE_POOL) { proptype = zpool_prop_get_type(prop); propname = zpool_prop_to_name(prop); + } else if (type == ZFS_TYPE_VDEV) { + proptype = vdev_prop_get_type(prop); + propname = vdev_prop_to_name(prop); } else { proptype = zfs_prop_get_type(prop); propname = zfs_prop_to_name(prop); @@ -1743,14 +1753,15 @@ addlist(libzfs_handle_t *hdl, char *propname, zprop_list_t **listp, /* * When no property table entry can be found, return failure if - * this is a pool property or if this isn't a user-defined - * dataset property, + * this is a valid property for the type, or if this isn't a + * user-defined dataset property. */ if (prop == ZPROP_INVAL && ((type == ZFS_TYPE_POOL && !zpool_prop_feature(propname) && !zpool_prop_unsupported(propname)) || (type == ZFS_TYPE_DATASET && !zfs_prop_user(propname) && - !zfs_prop_userquota(propname) && !zfs_prop_written(propname)))) { + !zfs_prop_userquota(propname) && !zfs_prop_written(propname)) || + (type == ZFS_TYPE_VDEV && !vdev_prop_user(propname)))) { zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "invalid property '%s'"), propname); return (zfs_error(hdl, EZFS_BADPROP, @@ -1933,8 +1944,8 @@ zprop_expand_list(libzfs_handle_t *hdl, zprop_list_t **plp, zfs_type_t type) if ((entry = zfs_alloc(hdl, sizeof (zprop_list_t))) == NULL) return (-1); - entry->pl_prop = (type == ZFS_TYPE_POOL) ? ZPOOL_PROP_NAME : - ZFS_PROP_NAME; + entry->pl_prop = ((type == ZFS_TYPE_POOL) ? ZPOOL_PROP_NAME : + ((type == ZFS_TYPE_VDEV) ? VDEV_PROP_NAME : ZFS_PROP_NAME)); entry->pl_width = zprop_width(entry->pl_prop, &entry->pl_fixed, type); entry->pl_all = B_TRUE; diff --git a/lib/libzfs/os/freebsd/libzfs_compat.c b/lib/libzfs/os/freebsd/libzfs_compat.c index 0e8a3b12176b..63ef483c9fb0 100644 --- a/lib/libzfs/os/freebsd/libzfs_compat.c +++ b/lib/libzfs/os/freebsd/libzfs_compat.c @@ -277,6 +277,10 @@ zfs_jail(zfs_handle_t *zhp, int jailid, int attach) zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "bookmarks can not be jailed")); return (zfs_error(hdl, EZFS_BADTYPE, errbuf)); + case ZFS_TYPE_VDEV: + zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, + "vdevs can not be jailed")); + return (zfs_error(hdl, EZFS_BADTYPE, errbuf)); case ZFS_TYPE_POOL: case ZFS_TYPE_FILESYSTEM: /* OK */ diff --git a/lib/libzfs_core/libzfs_core.c b/lib/libzfs_core/libzfs_core.c index 1e6bbcd561fa..f07b6b62763f 100644 --- a/lib/libzfs_core/libzfs_core.c +++ b/lib/libzfs_core/libzfs_core.c @@ -1392,6 +1392,18 @@ lzc_channel_program_nosync(const char *pool, const char *program, memlimit, argnvl, outnvl)); } +int +lzc_get_vdev_prop(const char *poolname, nvlist_t *innvl, nvlist_t **outnvl) +{ + return (lzc_ioctl(ZFS_IOC_VDEV_GET_PROPS, poolname, innvl, outnvl)); +} + +int +lzc_set_vdev_prop(const char *poolname, nvlist_t *innvl, nvlist_t **outnvl) +{ + return (lzc_ioctl(ZFS_IOC_VDEV_SET_PROPS, poolname, innvl, outnvl)); +} + /* * Performs key management functions * diff --git a/module/zcommon/zfs_prop.c b/module/zcommon/zfs_prop.c index 402d749c1aeb..bce25d26c7b1 100644 --- a/module/zcommon/zfs_prop.c +++ b/module/zcommon/zfs_prop.c @@ -737,18 +737,6 @@ zfs_name_to_prop(const char *propname) return (zprop_name_to_prop(propname, ZFS_TYPE_DATASET)); } -/* - * For user property names, we allow all lowercase alphanumeric characters, plus - * a few useful punctuation characters. - */ -static int -valid_char(char c) -{ - return ((c >= 'a' && c <= 'z') || - (c >= '0' && c <= '9') || - c == '-' || c == '_' || c == '.' || c == ':'); -} - /* * Returns true if this is a valid user-defined property (one with a ':'). */ @@ -761,7 +749,7 @@ zfs_prop_user(const char *name) for (i = 0; i < strlen(name); i++) { c = name[i]; - if (!valid_char(c)) + if (!zprop_valid_char(c)) return (B_FALSE); if (c == ':') foundsep = B_TRUE; diff --git a/module/zcommon/zpool_prop.c b/module/zcommon/zpool_prop.c index 6299d371f25d..ad924b15a061 100644 --- a/module/zcommon/zpool_prop.c +++ b/module/zcommon/zpool_prop.c @@ -23,6 +23,7 @@ * Copyright 2011 Nexenta Systems, Inc. All rights reserved. * Copyright (c) 2012, 2018 by Delphix. All rights reserved. * Copyright (c) 2021, Colm Buckley + * Copyright (c) 2021, Klara Inc. */ #include @@ -40,6 +41,7 @@ #endif static zprop_desc_t zpool_prop_table[ZPOOL_NUM_PROPS]; +static zprop_desc_t vdev_prop_table[VDEV_NUM_PROPS]; zprop_desc_t * zpool_prop_get_table(void) @@ -260,12 +262,244 @@ zpool_prop_align_right(zpool_prop_t prop) } #endif +zprop_desc_t * +vdev_prop_get_table(void) +{ + return (vdev_prop_table); +} + +void +vdev_prop_init(void) +{ + static zprop_index_t boolean_table[] = { + { "off", 0}, + { "on", 1}, + { NULL } + }; + + /* string properties */ + zprop_register_string(VDEV_PROP_COMMENT, "comment", NULL, + PROP_DEFAULT, ZFS_TYPE_VDEV, "", "COMMENT"); + zprop_register_string(VDEV_PROP_PATH, "path", NULL, + PROP_DEFAULT, ZFS_TYPE_VDEV, "", "PATH"); + zprop_register_string(VDEV_PROP_DEVID, "devid", NULL, + PROP_READONLY, ZFS_TYPE_VDEV, "", "DEVID"); + zprop_register_string(VDEV_PROP_PHYS_PATH, "physpath", NULL, + PROP_READONLY, ZFS_TYPE_VDEV, "", "PHYSPATH"); + zprop_register_string(VDEV_PROP_ENC_PATH, "encpath", NULL, + PROP_READONLY, ZFS_TYPE_VDEV, "", "ENCPATH"); + zprop_register_string(VDEV_PROP_FRU, "fru", NULL, + PROP_READONLY, ZFS_TYPE_VDEV, "", "FRU"); + zprop_register_string(VDEV_PROP_PARENT, "parent", NULL, + PROP_READONLY, ZFS_TYPE_VDEV, "", "PARENT"); + zprop_register_string(VDEV_PROP_CHILDREN, "children", NULL, + PROP_READONLY, ZFS_TYPE_VDEV, "", "CHILDREN"); + + /* readonly number properties */ + zprop_register_number(VDEV_PROP_SIZE, "size", 0, PROP_READONLY, + ZFS_TYPE_VDEV, "", "SIZE"); + zprop_register_number(VDEV_PROP_FREE, "free", 0, PROP_READONLY, + ZFS_TYPE_VDEV, "", "FREE"); + zprop_register_number(VDEV_PROP_ALLOCATED, "allocated", 0, + PROP_READONLY, ZFS_TYPE_VDEV, "", "ALLOC"); + zprop_register_number(VDEV_PROP_EXPANDSZ, "expandsize", 0, + PROP_READONLY, ZFS_TYPE_VDEV, "", "EXPANDSZ"); + zprop_register_number(VDEV_PROP_FRAGMENTATION, "fragmentation", 0, + PROP_READONLY, ZFS_TYPE_VDEV, "", "FRAG"); + zprop_register_number(VDEV_PROP_CAPACITY, "capacity", 0, PROP_READONLY, + ZFS_TYPE_VDEV, "", "CAP"); + zprop_register_number(VDEV_PROP_GUID, "guid", 0, PROP_READONLY, + ZFS_TYPE_VDEV, "", "GUID"); + zprop_register_number(VDEV_PROP_STATE, "state", 0, PROP_READONLY, + ZFS_TYPE_VDEV, "", "STATE"); + zprop_register_number(VDEV_PROP_BOOTSIZE, "bootsize", 0, PROP_READONLY, + ZFS_TYPE_VDEV, "", "BOOTSIZE"); + zprop_register_number(VDEV_PROP_ASIZE, "asize", 0, PROP_READONLY, + ZFS_TYPE_VDEV, "", "ASIZE"); + zprop_register_number(VDEV_PROP_PSIZE, "psize", 0, PROP_READONLY, + ZFS_TYPE_VDEV, "", "PSIZE"); + zprop_register_number(VDEV_PROP_ASHIFT, "ashift", 0, PROP_READONLY, + ZFS_TYPE_VDEV, "", "ASHIFT"); + zprop_register_number(VDEV_PROP_ROTATION_RATE, "rotation_rate", 0, + PROP_READONLY, ZFS_TYPE_VDEV, "", "ROTRATE"); + zprop_register_number(VDEV_PROP_PARITY, "parity", 0, PROP_READONLY, + ZFS_TYPE_VDEV, "", "PARITY"); + zprop_register_number(VDEV_PROP_NUMCHILDREN, "numchildren", 0, + PROP_READONLY, ZFS_TYPE_VDEV, "", "NUMCHILD"); + zprop_register_number(VDEV_PROP_READ_ERRORS, "read_errors", 0, + PROP_READONLY, ZFS_TYPE_VDEV, "", "RDERR"); + zprop_register_number(VDEV_PROP_WRITE_ERRORS, "write_errors", 0, + PROP_READONLY, ZFS_TYPE_VDEV, "", "WRERR"); + zprop_register_number(VDEV_PROP_CHECKSUM_ERRORS, "checksum_errors", 0, + PROP_READONLY, ZFS_TYPE_VDEV, "", "CKERR"); + zprop_register_number(VDEV_PROP_INITIALIZE_ERRORS, + "initialize_errors", 0, PROP_READONLY, ZFS_TYPE_VDEV, "", + "INITERR"); + zprop_register_number(VDEV_PROP_OPS_NULL, "null_ops", 0, + PROP_READONLY, ZFS_TYPE_VDEV, "", "NULLOP"); + zprop_register_number(VDEV_PROP_OPS_READ, "read_ops", 0, + PROP_READONLY, ZFS_TYPE_VDEV, "", "READOP"); + zprop_register_number(VDEV_PROP_OPS_WRITE, "write_ops", 0, + PROP_READONLY, ZFS_TYPE_VDEV, "", "WRITEOP"); + zprop_register_number(VDEV_PROP_OPS_FREE, "free_ops", 0, + PROP_READONLY, ZFS_TYPE_VDEV, "", "FREEOP"); + zprop_register_number(VDEV_PROP_OPS_CLAIM, "claim_ops", 0, + PROP_READONLY, ZFS_TYPE_VDEV, "", "CLAIMOP"); + zprop_register_number(VDEV_PROP_OPS_TRIM, "trim_ops", 0, + PROP_READONLY, ZFS_TYPE_VDEV, "", "TRIMOP"); + zprop_register_number(VDEV_PROP_BYTES_NULL, "null_bytes", 0, + PROP_READONLY, ZFS_TYPE_VDEV, "", "NULLBYTE"); + zprop_register_number(VDEV_PROP_BYTES_READ, "read_bytes", 0, + PROP_READONLY, ZFS_TYPE_VDEV, "", "READBYTE"); + zprop_register_number(VDEV_PROP_BYTES_WRITE, "write_bytes", 0, + PROP_READONLY, ZFS_TYPE_VDEV, "", "WRITEBYTE"); + zprop_register_number(VDEV_PROP_BYTES_FREE, "free_bytes", 0, + PROP_READONLY, ZFS_TYPE_VDEV, "", "FREEBYTE"); + zprop_register_number(VDEV_PROP_BYTES_CLAIM, "claim_bytes", 0, + PROP_READONLY, ZFS_TYPE_VDEV, "", "CLAIMBYTE"); + zprop_register_number(VDEV_PROP_BYTES_TRIM, "trim_bytes", 0, + PROP_READONLY, ZFS_TYPE_VDEV, "", "TRIMBYTE"); + + /* default numeric properties */ + + /* default index (boolean) properties */ + zprop_register_index(VDEV_PROP_NOALLOC, "noalloc", 0, + PROP_DEFAULT, ZFS_TYPE_VDEV, "on | off", "NOALLOC", boolean_table); + + /* default index properties */ + + /* hidden properties */ + zprop_register_hidden(VDEV_PROP_NAME, "name", PROP_TYPE_STRING, + PROP_READONLY, ZFS_TYPE_VDEV, "NAME"); +} + +/* + * Given a property name and its type, returns the corresponding property ID. + */ +vdev_prop_t +vdev_name_to_prop(const char *propname) +{ + return (zprop_name_to_prop(propname, ZFS_TYPE_VDEV)); +} + +/* + * Returns true if this is a valid user-defined property (one with a ':'). + */ +boolean_t +vdev_prop_user(const char *name) +{ + int i; + char c; + boolean_t foundsep = B_FALSE; + + for (i = 0; i < strlen(name); i++) { + c = name[i]; + if (!zprop_valid_char(c)) + return (B_FALSE); + if (c == ':') + foundsep = B_TRUE; + } + + if (!foundsep) + return (B_FALSE); + + return (B_TRUE); +} + +/* + * Given a pool property ID, returns the corresponding name. + * Assuming the pool propety ID is valid. + */ +const char * +vdev_prop_to_name(vdev_prop_t prop) +{ + return (vdev_prop_table[prop].pd_name); +} + +zprop_type_t +vdev_prop_get_type(vdev_prop_t prop) +{ + return (vdev_prop_table[prop].pd_proptype); +} + +boolean_t +vdev_prop_readonly(vdev_prop_t prop) +{ + return (vdev_prop_table[prop].pd_attr == PROP_READONLY); +} + +const char * +vdev_prop_default_string(vdev_prop_t prop) +{ + return (vdev_prop_table[prop].pd_strdefault); +} + +uint64_t +vdev_prop_default_numeric(vdev_prop_t prop) +{ + return (vdev_prop_table[prop].pd_numdefault); +} + +int +vdev_prop_string_to_index(vdev_prop_t prop, const char *string, + uint64_t *index) +{ + return (zprop_string_to_index(prop, string, index, ZFS_TYPE_VDEV)); +} + +int +vdev_prop_index_to_string(vdev_prop_t prop, uint64_t index, + const char **string) +{ + return (zprop_index_to_string(prop, index, string, ZFS_TYPE_VDEV)); +} + +/* + * Returns true if this is a valid vdev property. + */ +boolean_t +zpool_prop_vdev(const char *name) +{ + return (vdev_name_to_prop(name) != VDEV_PROP_INVAL); +} + +uint64_t +vdev_prop_random_value(vdev_prop_t prop, uint64_t seed) +{ + return (zprop_random_value(prop, seed, ZFS_TYPE_VDEV)); +} + +#ifndef _KERNEL +const char * +vdev_prop_values(vdev_prop_t prop) +{ + return (vdev_prop_table[prop].pd_values); +} + +const char * +vdev_prop_column_name(vdev_prop_t prop) +{ + return (vdev_prop_table[prop].pd_colname); +} + +boolean_t +vdev_prop_align_right(vdev_prop_t prop) +{ + return (vdev_prop_table[prop].pd_rightalign); +} +#endif + #if defined(_KERNEL) /* zpool property functions */ EXPORT_SYMBOL(zpool_prop_init); EXPORT_SYMBOL(zpool_prop_get_type); EXPORT_SYMBOL(zpool_prop_get_table); +/* vdev property functions */ +EXPORT_SYMBOL(vdev_prop_init); +EXPORT_SYMBOL(vdev_prop_get_type); +EXPORT_SYMBOL(vdev_prop_get_table); + /* Pool property functions shared between libzfs and kernel. */ EXPORT_SYMBOL(zpool_name_to_prop); EXPORT_SYMBOL(zpool_prop_to_name); @@ -276,4 +510,15 @@ EXPORT_SYMBOL(zpool_prop_feature); EXPORT_SYMBOL(zpool_prop_unsupported); EXPORT_SYMBOL(zpool_prop_index_to_string); EXPORT_SYMBOL(zpool_prop_string_to_index); +EXPORT_SYMBOL(zpool_prop_vdev); + +/* vdev property functions shared between libzfs and kernel. */ +EXPORT_SYMBOL(vdev_name_to_prop); +EXPORT_SYMBOL(vdev_prop_user); +EXPORT_SYMBOL(vdev_prop_to_name); +EXPORT_SYMBOL(vdev_prop_default_string); +EXPORT_SYMBOL(vdev_prop_default_numeric); +EXPORT_SYMBOL(vdev_prop_readonly); +EXPORT_SYMBOL(vdev_prop_index_to_string); +EXPORT_SYMBOL(vdev_prop_string_to_index); #endif diff --git a/module/zcommon/zprop_common.c b/module/zcommon/zprop_common.c index faab9d9a74fd..bf87d37f02cc 100644 --- a/module/zcommon/zprop_common.c +++ b/module/zcommon/zprop_common.c @@ -53,6 +53,8 @@ zprop_get_proptable(zfs_type_t type) { if (type == ZFS_TYPE_POOL) return (zpool_prop_get_table()); + else if (type == ZFS_TYPE_VDEV) + return (vdev_prop_get_table()); else return (zfs_prop_get_table()); } @@ -62,6 +64,8 @@ zprop_get_numprops(zfs_type_t type) { if (type == ZFS_TYPE_POOL) return (ZPOOL_NUM_PROPS); + else if (type == ZFS_TYPE_VDEV) + return (VDEV_NUM_PROPS); else return (ZFS_NUM_PROPS); } @@ -235,6 +239,8 @@ propname_match(const char *p, size_t len, zprop_desc_t *prop_entry) int c; #endif + ASSERT(propname != NULL); + if (len == strlen(propname) && strncmp(p, propname, len) == 0) return (B_TRUE); @@ -391,6 +397,18 @@ zprop_valid_for_type(int prop, zfs_type_t type, boolean_t headcheck) return ((prop_tbl[prop].pd_types & type) != 0); } +/* + * For user property names, we allow all lowercase alphanumeric characters, plus + * a few useful punctuation characters. + */ +int +zprop_valid_char(char c) +{ + return ((c >= 'a' && c <= 'z') || + (c >= '0' && c <= '9') || + c == '-' || c == '_' || c == '.' || c == ':'); +} + #ifndef _KERNEL /* @@ -477,4 +495,5 @@ EXPORT_SYMBOL(zprop_index_to_string); EXPORT_SYMBOL(zprop_random_value); EXPORT_SYMBOL(zprop_values); EXPORT_SYMBOL(zprop_valid_for_type); +EXPORT_SYMBOL(zprop_valid_char); #endif diff --git a/module/zfs/spa.c b/module/zfs/spa.c index a30821e045fa..3c3ff01dc8a7 100644 --- a/module/zfs/spa.c +++ b/module/zfs/spa.c @@ -539,6 +539,11 @@ spa_prop_validate(spa_t *spa, nvlist_t *props) switch (prop) { case ZPOOL_PROP_INVAL: + if (zpool_prop_vdev(propname)) { + /* XXX: Allan: todo */ +printf("ALLAN: spa_prop_validate TODO: %s\n", propname); + break; + } if (!zpool_prop_feature(propname)) { error = SET_ERROR(EINVAL); break; @@ -776,10 +781,13 @@ spa_prop_set(spa_t *spa, nvlist_t *nvp) continue; if (prop == ZPOOL_PROP_VERSION || prop == ZPOOL_PROP_INVAL) { - uint64_t ver; + uint64_t ver = 0; if (prop == ZPOOL_PROP_VERSION) { VERIFY(nvpair_value_uint64(elem, &ver) == 0); + } else if (zpool_prop_vdev(nvpair_name(elem))) { + /* XXX: Allan: todo */ +printf("ALLAN: spa_prop_set TODO: %s\n", nvpair_name(elem)); } else { ASSERT(zpool_prop_feature(nvpair_name(elem))); ver = SPA_VERSION_FEATURES; @@ -8631,6 +8639,11 @@ spa_sync_props(void *arg, dmu_tx_t *tx) switch (prop = zpool_name_to_prop(nvpair_name(elem))) { case ZPOOL_PROP_INVAL: + if (zpool_prop_vdev(nvpair_name(elem))) { + /* XXX: Allan: todo */ +printf("ALLAN: spa_sync_props TODO: %s\n", nvpair_name(elem)); + break; + } /* * We checked this earlier in spa_prop_validate(). */ diff --git a/module/zfs/spa_misc.c b/module/zfs/spa_misc.c index 1a2e5abc5335..f86f755a46ec 100644 --- a/module/zfs/spa_misc.c +++ b/module/zfs/spa_misc.c @@ -2425,6 +2425,7 @@ spa_init(spa_mode_t mode) zpool_prop_init(); zpool_feature_init(); spa_config_load(); + vdev_prop_init(); l2arc_start(); scan_init(); qat_init(); diff --git a/module/zfs/vdev.c b/module/zfs/vdev.c index d00782d934ff..42b731bab2d1 100644 --- a/module/zfs/vdev.c +++ b/module/zfs/vdev.c @@ -28,6 +28,7 @@ * Copyright 2017 Joyent, Inc. * Copyright (c) 2017, Intel Corporation. * Copyright (c) 2019, Datto Inc. All rights reserved. + * Copyright (c) 2021, Klara Inc. */ #include @@ -58,6 +59,7 @@ #include #include #include +#include "zfs_prop.h" /* * One metaslab from each (normal-class) vdev is used by the ZIL. These are @@ -5346,6 +5348,29 @@ vdev_xlate_walk(vdev_t *vd, const range_seg64_t *logical_rs, } } +static char * +vdev_name(vdev_t *vd) +{ + char *ret; + + ret = vd->vdev_path; + if (ret == NULL) { + if (strcmp(vd->vdev_ops->vdev_op_type, "root") == 0) { + ret = vd->vdev_spa->spa_name; + } else if (!vd->vdev_ops->vdev_op_leaf) { + char namestr[64] = { 0 }; + + snprintf((char *)&namestr, + sizeof (namestr), "%s-%llu", + vd->vdev_ops->vdev_op_type, + (u_longlong_t)vd->vdev_id); + ret = (char *)&namestr; + } + } + + return (ret); +} + /* * Look at the vdev tree and determine whether any devices are currently being * replaced. @@ -5375,6 +5400,587 @@ vdev_replace_in_progress(vdev_t *vdev) return (B_FALSE); } +/* + * Add a (source=src, propname=propval) list to an nvlist. + */ +static void +vdev_prop_add_list(nvlist_t *nvl, const char *propname, char *strval, + uint64_t intval, zprop_source_t src) +{ + nvlist_t *propval; + + if (nvl == NULL) + return; + + VERIFY(nvlist_alloc(&propval, NV_UNIQUE_NAME, KM_SLEEP) == 0); + VERIFY(nvlist_add_uint64(propval, ZPROP_SOURCE, src) == 0); + + if (strval != NULL) + VERIFY(nvlist_add_string(propval, ZPROP_VALUE, strval) == 0); + else + VERIFY(nvlist_add_uint64(propval, ZPROP_VALUE, intval) == 0); + + VERIFY(nvlist_add_nvlist(nvl, propname, propval) == 0); + nvlist_free(propval); +} + +static void +vdev_sync_props(void *arg, dmu_tx_t *tx) +{ + vdev_t *vd; + nvlist_t *nvp = arg; + spa_t *spa = dmu_tx_pool(tx)->dp_spa; + objset_t *mos = spa->spa_meta_objset; + nvpair_t *elem = NULL; + uint64_t vdev_guid; + nvlist_t *nvprops; + + if (nvlist_lookup_uint64(nvp, ZPOOL_VDEV_SET_PROPS_VDEV, + &vdev_guid) != 0) + return; + + if ((vd = spa_lookup_by_guid(spa, vdev_guid, B_TRUE)) == NULL) + return; + + (void) nvlist_lookup_nvlist(nvp, ZPOOL_VDEV_SET_PROPS_PROPS, &nvprops); + + mutex_enter(&spa->spa_props_lock); + + while ((elem = nvlist_next_nvpair(nvprops, elem)) != NULL) { + uint64_t intval, objid = 0; + char *strval; + vdev_prop_t prop; + const char *propname = nvpair_name(elem); + zprop_type_t proptype; + + /* + * Set vdev property values in the vdev props mos object. + */ + if (vd->vdev_top_zap != 0) { + objid = vd->vdev_top_zap; + } else if (vd->vdev_leaf_zap != 0) { + objid = vd->vdev_leaf_zap; + } else { + /* XXX: what do we do here? */ + ASSERT(0); + } + + switch (prop = vdev_name_to_prop(propname)) { + case VDEV_PROP_INVAL: + if (vdev_prop_user(propname)) { + strval = fnvpair_value_string(elem); + VERIFY0(zap_update(mos, objid, propname, + 1, strlen(strval) + 1, strval, tx)); + spa_history_log_internal(spa, "vdev set", tx, + "vdev_guid=%llu: %s=%s", + (u_longlong_t)vdev_guid, nvpair_name(elem), + strval); + } + break; + default: + /* normalize the property name */ + propname = vdev_prop_to_name(prop); + proptype = vdev_prop_get_type(prop); + + if (nvpair_type(elem) == DATA_TYPE_STRING) { + ASSERT(proptype == PROP_TYPE_STRING); + strval = fnvpair_value_string(elem); + VERIFY0(zap_update(mos, objid, propname, + 1, strlen(strval) + 1, strval, tx)); + spa_history_log_internal(spa, "vdev set", tx, + "vdev_guid=%llu: %s=%s", + (u_longlong_t)vdev_guid, nvpair_name(elem), + strval); + } else if (nvpair_type(elem) == DATA_TYPE_UINT64) { + intval = fnvpair_value_uint64(elem); + + if (proptype == PROP_TYPE_INDEX) { + const char *unused; + VERIFY0(vdev_prop_index_to_string( + prop, intval, &unused)); + } + VERIFY0(zap_update(mos, objid, propname, + sizeof (uint64_t), 1, &intval, tx)); + spa_history_log_internal(spa, "vdev set", tx, + "vdev_guid=%llu: %s=%lld", + (u_longlong_t)vdev_guid, + nvpair_name(elem), (longlong_t)intval); + } else { + ASSERT(0); /* not allowed */ + } + } + + } + + mutex_exit(&spa->spa_props_lock); +} + +int +vdev_prop_set(vdev_t *vd, nvlist_t *innvl, nvlist_t *outnvl) +{ + spa_t *spa = vd->vdev_spa; + nvpair_t *elem = NULL; + uint64_t vdev_guid; + nvlist_t *nvprops; + + ASSERT(vd != NULL); + + if (nvlist_lookup_uint64(innvl, ZPOOL_VDEV_SET_PROPS_VDEV, + &vdev_guid) != 0) + return (SET_ERROR(EINVAL)); + + (void) nvlist_lookup_nvlist(innvl, ZPOOL_VDEV_SET_PROPS_PROPS, + &nvprops); + +#if 0 + if ((error = vdev_prop_validate(spa, nvprops)) != 0) + return; +#endif + + while ((elem = nvlist_next_nvpair(nvprops, elem)) != NULL) { + char *propname = nvpair_name(elem); + vdev_prop_t prop = vdev_name_to_prop(propname); + uint64_t intval = 0; + char *strval = NULL; + + if (prop == VDEV_PROP_INVAL && !vdev_prop_user(propname)) { + intval = EINVAL; + vdev_prop_add_list(outnvl, propname, strval, intval, 0); + continue; + } + + if (vdev_prop_readonly(prop) == B_TRUE) { + intval = EROFS; + vdev_prop_add_list(outnvl, propname, strval, intval, 0); + continue; + } + + /* Special Processing */ + switch (prop) { + case VDEV_PROP_PATH: + strval = vd->vdev_path; + if (strval == NULL) + intval = EROFS; + if (nvpair_type(elem) != DATA_TYPE_STRING) + intval = EINVAL; + if (intval == 0) + strval = fnvpair_value_string(elem); + if (strval == NULL) + intval = EINVAL; + if (intval != 0) { + vdev_prop_add_list(outnvl, propname, strval, + intval, 0); + continue; + } + spa_strfree(vd->vdev_path); + vd->vdev_path = spa_strdup(strval); + spa_config_enter(spa, SCL_CONFIG, FTAG, RW_WRITER); + vdev_config_dirty(vd->vdev_top); + spa_config_exit(spa, SCL_CONFIG, FTAG); + break; + default: + /* Most processing is done in vdev_sync_props */ + break; + } + } + + return (dsl_sync_task(spa->spa_name, NULL, vdev_sync_props, + innvl, 6, ZFS_SPACE_CHECK_RESERVED)); +} + +int +vdev_prop_get(vdev_t *vd, nvlist_t *innvl, nvlist_t *outnvl) +{ + spa_t *spa = vd->vdev_spa; + objset_t *mos = spa->spa_meta_objset; + zap_cursor_t zc; + zap_attribute_t za; + int err = 0; + uint64_t objid; + uint64_t vdev_guid; + nvpair_t *elem = NULL; + nvlist_t *nvprops = NULL; + uint64_t intval = 0; + char *strval = NULL; + const char *propname = NULL; + vdev_prop_t prop; + + ASSERT(vd != NULL); + + if (nvlist_lookup_uint64(innvl, ZPOOL_VDEV_GET_PROPS_VDEV, + &vdev_guid) != 0) + return (SET_ERROR(EINVAL)); + + (void) nvlist_lookup_nvlist(innvl, ZPOOL_VDEV_GET_PROPS_PROPS, + &nvprops); + + /* + * XXX todo: check: + * zap_lookup(mos, DMU_POOL_DIRECTORY_OBJECT, DMU_POOL_VDEV_ZAP_MAP); + */ + + if (vd->vdev_top_zap != 0) { + objid = vd->vdev_top_zap; + } else if (vd->vdev_leaf_zap != 0) { + objid = vd->vdev_leaf_zap; + } else { + /* XXX: what do we do here? */ + return (SET_ERROR(EINVAL)); + } + + /* If no vdev property object, no more prop to get. */ + if (mos == NULL || objid == 0) { + return (SET_ERROR(EINVAL)); + } + + mutex_enter(&spa->spa_props_lock); + + if (nvprops != NULL) { + while ((elem = nvlist_next_nvpair(nvprops, elem)) != NULL) { + intval = 0; + strval = NULL; + propname = nvpair_name(elem); + prop = vdev_name_to_prop(propname); + zprop_source_t src = ZPROP_SRC_DEFAULT; + + switch (prop) { + /* Special Read-only Properties */ + case VDEV_PROP_NAME: + strval = vdev_name(vd); + if (strval == NULL) + continue; + vdev_prop_add_list(outnvl, propname, strval, 0, + ZPROP_SRC_NONE); + continue; + case VDEV_PROP_CAPACITY: + /* percent used */ + intval = (vd->vdev_stat.vs_dspace == 0) ? 0 : + (vd->vdev_stat.vs_alloc * 100 / + vd->vdev_stat.vs_dspace); + vdev_prop_add_list(outnvl, propname, NULL, + intval, ZPROP_SRC_NONE); + continue; + case VDEV_PROP_STATE: + vdev_prop_add_list(outnvl, propname, NULL, + vd->vdev_state, ZPROP_SRC_NONE); + continue; + case VDEV_PROP_GUID: + vdev_prop_add_list(outnvl, propname, NULL, + vd->vdev_guid, ZPROP_SRC_NONE); + continue; + case VDEV_PROP_ASIZE: + vdev_prop_add_list(outnvl, propname, NULL, + vd->vdev_asize, ZPROP_SRC_NONE); + continue; + case VDEV_PROP_PSIZE: + vdev_prop_add_list(outnvl, propname, NULL, + vd->vdev_psize, ZPROP_SRC_NONE); + continue; + case VDEV_PROP_ASHIFT: + vdev_prop_add_list(outnvl, propname, NULL, + vd->vdev_ashift, ZPROP_SRC_NONE); + continue; + case VDEV_PROP_ROTATION_RATE: + /* XXX: Allan TODO: was removed from vdev_t */ + vdev_prop_add_list(outnvl, propname, NULL, + 0, ZPROP_SRC_NONE); + continue; + case VDEV_PROP_SIZE: + vdev_prop_add_list(outnvl, propname, NULL, + vd->vdev_stat.vs_dspace, ZPROP_SRC_NONE); + continue; + case VDEV_PROP_FREE: + vdev_prop_add_list(outnvl, propname, NULL, + vd->vdev_stat.vs_dspace - + vd->vdev_stat.vs_alloc, ZPROP_SRC_NONE); + continue; + case VDEV_PROP_ALLOCATED: + vdev_prop_add_list(outnvl, propname, NULL, + vd->vdev_stat.vs_alloc, ZPROP_SRC_NONE); + continue; + case VDEV_PROP_EXPANDSZ: + vdev_prop_add_list(outnvl, propname, NULL, + vd->vdev_stat.vs_esize, ZPROP_SRC_NONE); + continue; + case VDEV_PROP_FRAGMENTATION: + vdev_prop_add_list(outnvl, propname, NULL, + vd->vdev_stat.vs_fragmentation, + ZPROP_SRC_NONE); + continue; + case VDEV_PROP_BOOTSIZE: + /* XXX: Allan TODO */ + continue; + case VDEV_PROP_PARITY: + vdev_prop_add_list(outnvl, propname, NULL, + vdev_get_nparity(vd), ZPROP_SRC_NONE); + continue; + case VDEV_PROP_PATH: + if (vd->vdev_path == NULL) + continue; + vdev_prop_add_list(outnvl, propname, + vd->vdev_path, 0, ZPROP_SRC_NONE); + continue; + case VDEV_PROP_DEVID: + if (vd->vdev_devid == NULL) + continue; + vdev_prop_add_list(outnvl, propname, + vd->vdev_devid, 0, ZPROP_SRC_NONE); + continue; + case VDEV_PROP_PHYS_PATH: + if (vd->vdev_physpath == NULL) + continue; + vdev_prop_add_list(outnvl, propname, + vd->vdev_physpath, 0, ZPROP_SRC_NONE); + continue; + case VDEV_PROP_ENC_PATH: + if (vd->vdev_enc_sysfs_path == NULL) + continue; + vdev_prop_add_list(outnvl, propname, + vd->vdev_enc_sysfs_path, 0, ZPROP_SRC_NONE); + continue; + case VDEV_PROP_FRU: + if (vd->vdev_fru == NULL) + continue; + vdev_prop_add_list(outnvl, propname, + vd->vdev_fru, 0, ZPROP_SRC_NONE); + continue; + case VDEV_PROP_PARENT: + if (vd->vdev_parent != NULL) { + strval = vdev_name(vd->vdev_parent); + vdev_prop_add_list(outnvl, propname, + strval, 0, ZPROP_SRC_NONE); + } + continue; + case VDEV_PROP_CHILDREN: + if (vd->vdev_children > 0) + strval = kmem_zalloc(ZAP_MAXVALUELEN, + KM_SLEEP); + for (uint64_t i = 0; i < vd->vdev_children; + i++) { + char *vname; + + vname = vdev_name(vd->vdev_child[i]); + if (vname == NULL) + vname = "(unknown)"; + if (strlen(strval) > 0) + strlcat(strval, " ", + ZAP_MAXVALUELEN); + strlcat(strval, vname, ZAP_MAXVALUELEN); + } + if (strval != NULL) { + vdev_prop_add_list(outnvl, propname, + strval, 0, ZPROP_SRC_NONE); + kmem_free(strval, ZAP_MAXVALUELEN); + } + continue; + case VDEV_PROP_NUMCHILDREN: + vdev_prop_add_list(outnvl, propname, NULL, + vd->vdev_children, ZPROP_SRC_NONE); + continue; + case VDEV_PROP_READ_ERRORS: + vdev_prop_add_list(outnvl, propname, NULL, + vd->vdev_stat.vs_read_errors, + ZPROP_SRC_NONE); + continue; + case VDEV_PROP_WRITE_ERRORS: + vdev_prop_add_list(outnvl, propname, NULL, + vd->vdev_stat.vs_write_errors, + ZPROP_SRC_NONE); + continue; + case VDEV_PROP_CHECKSUM_ERRORS: + vdev_prop_add_list(outnvl, propname, NULL, + vd->vdev_stat.vs_checksum_errors, + ZPROP_SRC_NONE); + continue; + case VDEV_PROP_INITIALIZE_ERRORS: + vdev_prop_add_list(outnvl, propname, NULL, + vd->vdev_stat.vs_initialize_errors, + ZPROP_SRC_NONE); + continue; + case VDEV_PROP_OPS_NULL: + vdev_prop_add_list(outnvl, propname, NULL, + vd->vdev_stat.vs_ops[ZIO_TYPE_NULL], + ZPROP_SRC_NONE); + continue; + case VDEV_PROP_OPS_READ: + vdev_prop_add_list(outnvl, propname, NULL, + vd->vdev_stat.vs_ops[ZIO_TYPE_READ], + ZPROP_SRC_NONE); + continue; + case VDEV_PROP_OPS_WRITE: + vdev_prop_add_list(outnvl, propname, NULL, + vd->vdev_stat.vs_ops[ZIO_TYPE_WRITE], + ZPROP_SRC_NONE); + continue; + case VDEV_PROP_OPS_FREE: + vdev_prop_add_list(outnvl, propname, NULL, + vd->vdev_stat.vs_ops[ZIO_TYPE_FREE], + ZPROP_SRC_NONE); + continue; + case VDEV_PROP_OPS_CLAIM: + vdev_prop_add_list(outnvl, propname, NULL, + vd->vdev_stat.vs_ops[ZIO_TYPE_CLAIM], + ZPROP_SRC_NONE); + continue; + case VDEV_PROP_OPS_TRIM: + /* + * TRIM ops and bytes are reported to user + * space as ZIO_TYPE_IOCTL. This is done to + * preserve the vdev_stat_t structure layout + * for user space. + */ + vdev_prop_add_list(outnvl, propname, NULL, + vd->vdev_stat.vs_ops[ZIO_TYPE_IOCTL], + ZPROP_SRC_NONE); + continue; + case VDEV_PROP_BYTES_NULL: + vdev_prop_add_list(outnvl, propname, NULL, + vd->vdev_stat.vs_bytes[ZIO_TYPE_NULL], + ZPROP_SRC_NONE); + continue; + case VDEV_PROP_BYTES_READ: + vdev_prop_add_list(outnvl, propname, NULL, + vd->vdev_stat.vs_bytes[ZIO_TYPE_READ], + ZPROP_SRC_NONE); + continue; + case VDEV_PROP_BYTES_WRITE: + vdev_prop_add_list(outnvl, propname, NULL, + vd->vdev_stat.vs_bytes[ZIO_TYPE_WRITE], + ZPROP_SRC_NONE); + continue; + case VDEV_PROP_BYTES_FREE: + vdev_prop_add_list(outnvl, propname, NULL, + vd->vdev_stat.vs_bytes[ZIO_TYPE_FREE], + ZPROP_SRC_NONE); + continue; + case VDEV_PROP_BYTES_CLAIM: + vdev_prop_add_list(outnvl, propname, NULL, + vd->vdev_stat.vs_bytes[ZIO_TYPE_CLAIM], + ZPROP_SRC_NONE); + continue; + case VDEV_PROP_BYTES_TRIM: + /* + * TRIM ops and bytes are reported to user + * space as ZIO_TYPE_IOCTL. This is done to + * preserve the vdev_stat_t structure layout + * for user space. + */ + vdev_prop_add_list(outnvl, propname, NULL, + vd->vdev_stat.vs_bytes[ZIO_TYPE_IOCTL], + ZPROP_SRC_NONE); + continue; + /* Numeric Properites */ + case VDEV_PROP_NOALLOC: + src = ZPROP_SRC_LOCAL; + strval = NULL; + + err = zap_lookup(mos, objid, nvpair_name(elem), + sizeof (uint64_t), 1, &intval); + if (intval == vdev_prop_default_numeric(prop)) + src = ZPROP_SRC_DEFAULT; + vdev_prop_add_list(outnvl, propname, strval, + intval, src); + break; + /* Text Properties */ + case VDEV_PROP_COMMENT: + /* Exists in the ZAP below */ + /* FALLTHRU */ + case VDEV_PROP_INVAL: + /* User Properites */ + src = ZPROP_SRC_LOCAL; + + /* + * za.za_integer_length is an int, but + * zap_length() expects a uint64_t. + * Use za_first_integer instead. + */ + err = zap_length(mos, objid, nvpair_name(elem), + &za.za_first_integer, &za.za_num_integers); + if (err) + break; + + za.za_integer_length = za.za_first_integer; + za.za_first_integer = 0; + + switch (za.za_integer_length) { + case 8: + /* User properties cannot be integers */ + err = EINVAL; + break; + case 1: + /* string property */ + strval = kmem_alloc(za.za_num_integers, + KM_SLEEP); + err = zap_lookup(mos, objid, + nvpair_name(elem), 1, + za.za_num_integers, strval); + if (err) { + kmem_free(strval, + za.za_num_integers); + break; + } + vdev_prop_add_list(outnvl, propname, + strval, 0, src); + kmem_free(strval, za.za_num_integers); + break; + } + break; + default: + err = ENOENT; + break; + } + if (err) + break; + } + } else { + /* + * Get all properties from the MOS vdev property object. + */ + for (zap_cursor_init(&zc, mos, objid); + (err = zap_cursor_retrieve(&zc, &za)) == 0; + zap_cursor_advance(&zc)) { + intval = 0; + strval = NULL; + zprop_source_t src = ZPROP_SRC_DEFAULT; + propname = za.za_name; + prop = vdev_name_to_prop(propname); + + switch (za.za_integer_length) { + case 8: + /* We do not allow integer user properties */ + /* This is likely an internal value */ + continue; + break; + case 1: + /* string property */ + strval = kmem_alloc(za.za_num_integers, + KM_SLEEP); + err = zap_lookup(mos, objid, za.za_name, 1, + za.za_num_integers, strval); + if (err) { + kmem_free(strval, za.za_num_integers); + break; + } + vdev_prop_add_list(outnvl, propname, strval, 0, + src); + kmem_free(strval, za.za_num_integers); + break; + + default: + break; + } + } + zap_cursor_fini(&zc); + } + + mutex_exit(&spa->spa_props_lock); + if (err && err != ENOENT) { + return (err); + } + + return (0); +} + EXPORT_SYMBOL(vdev_fault); EXPORT_SYMBOL(vdev_degrade); EXPORT_SYMBOL(vdev_online); diff --git a/module/zfs/zfs_ioctl.c b/module/zfs/zfs_ioctl.c index df96378e2c0a..e7eddede1530 100644 --- a/module/zfs/zfs_ioctl.c +++ b/module/zfs/zfs_ioctl.c @@ -38,7 +38,7 @@ * Copyright (c) 2017 Open-E, Inc. All Rights Reserved. * Copyright (c) 2019 Datto Inc. * Copyright (c) 2019, 2020 by Christian Schwarz. All rights reserved. - * Copyright (c) 2019, Klara Inc. + * Copyright (c) 2019, 2021, Klara Inc. * Copyright (c) 2019, Allan Jude */ @@ -2954,6 +2954,90 @@ zfs_ioc_pool_get_props(zfs_cmd_t *zc) return (error); } +/* + * innvl: { + * "vdevprops_set_vdev" -> guid + * "vdevprops_set_props" -> { prop -> value } + * } + * + * outnvl: propname -> error code (int32) + */ +static const zfs_ioc_key_t zfs_keys_vdev_set_props[] = { + {ZPOOL_VDEV_SET_PROPS_VDEV, DATA_TYPE_UINT64, 0}, + {ZPOOL_VDEV_SET_PROPS_PROPS, DATA_TYPE_NVLIST, 0} +}; + +static int +zfs_ioc_vdev_set_props(const char *poolname, nvlist_t *innvl, nvlist_t *outnvl) +{ + spa_t *spa; + int error; + vdev_t *vd; + uint64_t vdev_guid; + + /* Early validation */ + if (nvlist_lookup_uint64(innvl, ZPOOL_VDEV_SET_PROPS_VDEV, + &vdev_guid) != 0) + return (SET_ERROR(EINVAL)); + + if ((error = spa_open(poolname, &spa, FTAG)) != 0) + return (error); + + ASSERT(spa_writeable(spa)); + + if ((vd = spa_lookup_by_guid(spa, vdev_guid, B_TRUE)) == NULL) { + spa_close(spa, FTAG); + return (ENOENT); + } + + error = vdev_prop_set(vd, innvl, outnvl); + + spa_close(spa, FTAG); + + return (error); +} + +/* + * innvl: { + * "vdevprops_get_vdev" -> guid + * (optional) "vdevprops_get_props" -> { propname -> propid } + * } + * + * outnvl: propname -> value + */ +static const zfs_ioc_key_t zfs_keys_vdev_get_props[] = { + {ZPOOL_VDEV_GET_PROPS_VDEV, DATA_TYPE_UINT64, 0}, + {ZPOOL_VDEV_GET_PROPS_PROPS, DATA_TYPE_NVLIST, ZK_OPTIONAL} +}; + +static int +zfs_ioc_vdev_get_props(const char *poolname, nvlist_t *innvl, nvlist_t *outnvl) +{ + spa_t *spa; + int error; + vdev_t *vd; + uint64_t vdev_guid; + + /* Early validation */ + if (nvlist_lookup_uint64(innvl, ZPOOL_VDEV_GET_PROPS_VDEV, + &vdev_guid) != 0) + return (SET_ERROR(EINVAL)); + + if ((error = spa_open(poolname, &spa, FTAG)) != 0) + return (error); + + if ((vd = spa_lookup_by_guid(spa, vdev_guid, B_TRUE)) == NULL) { + spa_close(spa, FTAG); + return (ENOENT); + } + + error = vdev_prop_get(vd, innvl, outnvl); + + spa_close(spa, FTAG); + + return (error); +} + /* * inputs: * zc_name name of filesystem @@ -7078,6 +7162,16 @@ zfs_ioctl_init(void) POOL_CHECK_SUSPENDED, B_FALSE, B_TRUE, zfs_keys_get_bootenv, ARRAY_SIZE(zfs_keys_get_bootenv)); + zfs_ioctl_register("zpool_vdev_get_props", ZFS_IOC_VDEV_GET_PROPS, + zfs_ioc_vdev_get_props, zfs_secpolicy_read, POOL_NAME, + POOL_CHECK_NONE, B_FALSE, B_FALSE, zfs_keys_vdev_get_props, + ARRAY_SIZE(zfs_keys_vdev_get_props)); + + zfs_ioctl_register("zpool_vdev_set_props", ZFS_IOC_VDEV_SET_PROPS, + zfs_ioc_vdev_set_props, zfs_secpolicy_config, POOL_NAME, + POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY, B_FALSE, B_FALSE, + zfs_keys_vdev_set_props, ARRAY_SIZE(zfs_keys_vdev_set_props)); + /* IOCTLS that use the legacy function signature */ zfs_ioctl_register_legacy(ZFS_IOC_POOL_FREEZE, zfs_ioc_pool_freeze, From bc93ace273bda1fccabc4349e15f89f0192a5bd8 Mon Sep 17 00:00:00 2001 From: Allan Jude Date: Tue, 23 Mar 2021 22:12:25 +0000 Subject: [PATCH 2/7] Remove old debug printfs that Linux doesn't like Signed-off-by: Allan Jude --- module/zfs/spa.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/module/zfs/spa.c b/module/zfs/spa.c index 3c3ff01dc8a7..60312ab154e8 100644 --- a/module/zfs/spa.c +++ b/module/zfs/spa.c @@ -541,7 +541,6 @@ spa_prop_validate(spa_t *spa, nvlist_t *props) case ZPOOL_PROP_INVAL: if (zpool_prop_vdev(propname)) { /* XXX: Allan: todo */ -printf("ALLAN: spa_prop_validate TODO: %s\n", propname); break; } if (!zpool_prop_feature(propname)) { @@ -787,7 +786,6 @@ spa_prop_set(spa_t *spa, nvlist_t *nvp) VERIFY(nvpair_value_uint64(elem, &ver) == 0); } else if (zpool_prop_vdev(nvpair_name(elem))) { /* XXX: Allan: todo */ -printf("ALLAN: spa_prop_set TODO: %s\n", nvpair_name(elem)); } else { ASSERT(zpool_prop_feature(nvpair_name(elem))); ver = SPA_VERSION_FEATURES; @@ -8641,7 +8639,6 @@ spa_sync_props(void *arg, dmu_tx_t *tx) case ZPOOL_PROP_INVAL: if (zpool_prop_vdev(nvpair_name(elem))) { /* XXX: Allan: todo */ -printf("ALLAN: spa_sync_props TODO: %s\n", nvpair_name(elem)); break; } /* From 46b8a635eb0ba63937e62be515128d3a95932e2b Mon Sep 17 00:00:00 2001 From: Mark Maybee Date: Mon, 29 Mar 2021 09:49:56 -0600 Subject: [PATCH 3/7] Fixes to get bits running on Linux: 1. Fix type in expression 2. Fix up vdev_name() 3. Add support for properties.vdev in SYSFS Signed-off-by: Allan Jude --- include/sys/zfs_sysfs.h | 1 + lib/libzfs/libzfs_pool.c | 3 ++- module/os/linux/zfs/zfs_sysfs.c | 33 +++++++++++++++++++++++++++++++++ module/zcommon/zprop_common.c | 3 ++- module/zfs/vdev.c | 29 +++++++++++++---------------- 5 files changed, 51 insertions(+), 18 deletions(-) diff --git a/include/sys/zfs_sysfs.h b/include/sys/zfs_sysfs.h index 925d7ad542f7..6b5e33560209 100644 --- a/include/sys/zfs_sysfs.h +++ b/include/sys/zfs_sysfs.h @@ -39,6 +39,7 @@ boolean_t zfs_mod_supported(const char *, const char *); #endif #define ZFS_SYSFS_POOL_PROPERTIES "properties.pool" +#define ZFS_SYSFS_VDEV_PROPERTIES "properties.vdev" #define ZFS_SYSFS_DATASET_PROPERTIES "properties.dataset" #define ZFS_SYSFS_KERNEL_FEATURES "features.kernel" #define ZFS_SYSFS_POOL_FEATURES "features.pool" diff --git a/lib/libzfs/libzfs_pool.c b/lib/libzfs/libzfs_pool.c index 19720d639409..0714efc8bd2f 100644 --- a/lib/libzfs/libzfs_pool.c +++ b/lib/libzfs/libzfs_pool.c @@ -976,7 +976,8 @@ vdev_expand_proplist(zpool_handle_t *zhp, const char *vdevname, propname = nvpair_name(elem); /* Skip properties that are not user defined */ - if ((prop = vdev_name_to_prop(propname)) != ZPROP_INVAL) + if ((prop = vdev_name_to_prop(propname)) != + (vdev_prop_t)ZPROP_INVAL) continue; if (nvpair_value_nvlist(elem, &propval) != 0) diff --git a/module/os/linux/zfs/zfs_sysfs.c b/module/os/linux/zfs/zfs_sysfs.c index fb7c68987360..4cade1cc5fd9 100644 --- a/module/os/linux/zfs/zfs_sysfs.c +++ b/module/os/linux/zfs/zfs_sysfs.c @@ -90,6 +90,7 @@ struct zfs_mod_kobj { static zfs_mod_kobj_t kernel_features_kobj; static zfs_mod_kobj_t pool_features_kobj; static zfs_mod_kobj_t dataset_props_kobj; +static zfs_mod_kobj_t vdev_props_kobj; static zfs_mod_kobj_t pool_props_kobj; /* @@ -333,6 +334,20 @@ dataset_property_show(struct kobject *kobj, struct attribute *attr, char *buf) return (len); } +static ssize_t +vdev_property_show(struct kobject *kobj, struct attribute *attr, char *buf) +{ + vdev_prop_t prop = vdev_name_to_prop(kobject_name(kobj)); + zprop_desc_t *prop_tbl = vdev_prop_get_table(); + ssize_t len; + + ASSERT3U(prop, <, VDEV_NUM_PROPS); + + len = zprop_sysfs_show(attr->name, &prop_tbl[prop], buf, PAGE_SIZE); + + return (len); +} + static ssize_t pool_property_show(struct kobject *kobj, struct attribute *attr, char *buf) { @@ -577,6 +592,14 @@ zfs_sysfs_properties_init(zfs_mod_kobj_t *zfs_kobj, struct kobject *parent, context.p2k_show_func = pool_property_show; err = zfs_kobj_init(zfs_kobj, 0, ZPOOL_NUM_PROPS, pool_property_show); + } else if (type == ZFS_TYPE_VDEV) { + name = ZFS_SYSFS_VDEV_PROPERTIES; + context.p2k_table = vdev_prop_get_table(); + context.p2k_attr_count = ZPOOL_PROP_ATTR_COUNT; + context.p2k_parent = zfs_kobj; + context.p2k_show_func = vdev_property_show; + err = zfs_kobj_init(zfs_kobj, 0, VDEV_NUM_PROPS, + vdev_property_show); } else { name = ZFS_SYSFS_DATASET_PROPERTIES; context.p2k_table = zfs_prop_get_table(); @@ -639,12 +662,22 @@ zfs_sysfs_init(void) return; } + err = zfs_sysfs_properties_init(&vdev_props_kobj, parent, + ZFS_TYPE_VDEV); + if (err) { + zfs_kobj_fini(&kernel_features_kobj); + zfs_kobj_fini(&pool_features_kobj); + zfs_kobj_fini(&pool_props_kobj); + return; + } + err = zfs_sysfs_properties_init(&dataset_props_kobj, parent, ZFS_TYPE_FILESYSTEM); if (err) { zfs_kobj_fini(&kernel_features_kobj); zfs_kobj_fini(&pool_features_kobj); zfs_kobj_fini(&pool_props_kobj); + zfs_kobj_fini(&vdev_props_kobj); return; } } diff --git a/module/zcommon/zprop_common.c b/module/zcommon/zprop_common.c index bf87d37f02cc..17a48361f96e 100644 --- a/module/zcommon/zprop_common.c +++ b/module/zcommon/zprop_common.c @@ -85,7 +85,8 @@ zfs_mod_supported_prop(const char *name, zfs_type_t type) return (B_TRUE); #else return (zfs_mod_supported(type == ZFS_TYPE_POOL ? - ZFS_SYSFS_POOL_PROPERTIES : ZFS_SYSFS_DATASET_PROPERTIES, name)); + ZFS_SYSFS_POOL_PROPERTIES : (type == ZFS_TYPE_VDEV ? + ZFS_SYSFS_VDEV_PROPERTIES : ZFS_SYSFS_DATASET_PROPERTIES), name)); #endif } diff --git a/module/zfs/vdev.c b/module/zfs/vdev.c index 42b731bab2d1..e92426028e59 100644 --- a/module/zfs/vdev.c +++ b/module/zfs/vdev.c @@ -5349,26 +5349,19 @@ vdev_xlate_walk(vdev_t *vd, const range_seg64_t *logical_rs, } static char * -vdev_name(vdev_t *vd) +vdev_name(vdev_t *vd, char *buf, int buflen) { - char *ret; - - ret = vd->vdev_path; - if (ret == NULL) { + if (vd->vdev_path == NULL) { if (strcmp(vd->vdev_ops->vdev_op_type, "root") == 0) { - ret = vd->vdev_spa->spa_name; + return (vd->vdev_spa->spa_name); } else if (!vd->vdev_ops->vdev_op_leaf) { - char namestr[64] = { 0 }; - - snprintf((char *)&namestr, - sizeof (namestr), "%s-%llu", + snprintf(buf, buflen, "%s-%llu", vd->vdev_ops->vdev_op_type, (u_longlong_t)vd->vdev_id); - ret = (char *)&namestr; + return (buf); } } - - return (ret); + return (vd->vdev_path); } /* @@ -5636,6 +5629,8 @@ vdev_prop_get(vdev_t *vd, nvlist_t *innvl, nvlist_t *outnvl) mutex_enter(&spa->spa_props_lock); if (nvprops != NULL) { + char namebuf[64] = { 0 }; + while ((elem = nvlist_next_nvpair(nvprops, elem)) != NULL) { intval = 0; strval = NULL; @@ -5646,7 +5641,7 @@ vdev_prop_get(vdev_t *vd, nvlist_t *innvl, nvlist_t *outnvl) switch (prop) { /* Special Read-only Properties */ case VDEV_PROP_NAME: - strval = vdev_name(vd); + strval = vdev_name(vd, namebuf, 64); if (strval == NULL) continue; vdev_prop_add_list(outnvl, propname, strval, 0, @@ -5746,7 +5741,8 @@ vdev_prop_get(vdev_t *vd, nvlist_t *innvl, nvlist_t *outnvl) continue; case VDEV_PROP_PARENT: if (vd->vdev_parent != NULL) { - strval = vdev_name(vd->vdev_parent); + strval = vdev_name(vd->vdev_parent, + namebuf, 64); vdev_prop_add_list(outnvl, propname, strval, 0, ZPROP_SRC_NONE); } @@ -5759,7 +5755,8 @@ vdev_prop_get(vdev_t *vd, nvlist_t *innvl, nvlist_t *outnvl) i++) { char *vname; - vname = vdev_name(vd->vdev_child[i]); + vname = vdev_name(vd->vdev_child[i], + namebuf, 64); if (vname == NULL) vname = "(unknown)"; if (strlen(strval) > 0) From eac01956d546029c76959446b3cce39299d8f39c Mon Sep 17 00:00:00 2001 From: Allan Jude Date: Wed, 31 Mar 2021 14:26:04 +0000 Subject: [PATCH 4/7] Don't truncate property values at MAXNAMELEN use ZFS_MAXPROPLEN Signed-off-by: Allan Jude --- cmd/zpool/zpool_main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/zpool/zpool_main.c b/cmd/zpool/zpool_main.c index 94a24e55a370..d23040a93850 100644 --- a/cmd/zpool/zpool_main.c +++ b/cmd/zpool/zpool_main.c @@ -9885,7 +9885,7 @@ static int get_callback_vdev(zpool_handle_t *zhp, char *vdevname, void *data) { zprop_get_cbdata_t *cbp = (zprop_get_cbdata_t *)data; - char value[MAXNAMELEN]; + char value[ZFS_MAXPROPLEN]; zprop_source_t srctype; zprop_list_t *pl; From 09814187d8bc677690db0d873d8f450e04be799a Mon Sep 17 00:00:00 2001 From: Allan Jude Date: Wed, 31 Mar 2021 14:30:42 +0000 Subject: [PATCH 5/7] Case the RPM value to make compiler happy on ARM Signed-off-by: Allan Jude --- lib/libzfs/libzfs_pool.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/lib/libzfs/libzfs_pool.c b/lib/libzfs/libzfs_pool.c index 0714efc8bd2f..efe982cec261 100644 --- a/lib/libzfs/libzfs_pool.c +++ b/lib/libzfs/libzfs_pool.c @@ -5176,9 +5176,11 @@ zpool_get_vdev_prop_value(nvlist_t *nvprop, vdev_prop_t prop, char *prop_name, else if (intval == 1) /* VDEV_RATE_NON_ROTATING */ (void) snprintf(buf, len, "Non-Rotating"); else - (void) snprintf(buf, len, "%zu RPM", intval); + (void) snprintf(buf, len, "%llu RPM", + (u_longlong_t)intval); default: - (void) snprintf(buf, len, "%zu", intval); + (void) snprintf(buf, len, "%llu", + (u_longlong_t)intval); } break; From 403381d5c6d823d7d53144bdb932fd815843022d Mon Sep 17 00:00:00 2001 From: Mark Maybee Date: Mon, 26 Apr 2021 12:27:47 -0600 Subject: [PATCH 6/7] VDEV_PROP_NOALLOC plumbing --- cmd/zpool/zpool_main.c | 21 +++- include/sys/fs/zfs.h | 2 + include/sys/spa.h | 3 +- include/sys/vdev_impl.h | 1 + module/zfs/vdev.c | 64 ++++++---- module/zfs/vdev_label.c | 4 + module/zfs/vdev_removal.c | 252 ++++++++++++++++++++++++++++++++------ module/zfs/zio.c | 2 +- 8 files changed, 281 insertions(+), 68 deletions(-) diff --git a/cmd/zpool/zpool_main.c b/cmd/zpool/zpool_main.c index d23040a93850..02778abc52c5 100644 --- a/cmd/zpool/zpool_main.c +++ b/cmd/zpool/zpool_main.c @@ -2426,6 +2426,12 @@ print_status_config(zpool_handle_t *zhp, status_cbdata_t *cb, const char *name, 1 << vs->vs_configured_ashift, 1 << vs->vs_physical_ashift); } + if (vs->vs_scan_removing != 0) { + (void) printf(gettext(" (removing)")); + } else if (vs->vs_noalloc != 0) { + (void) printf(gettext(" (non-allocating)")); + } + /* The root vdev has the scrub/resilver stats */ root = fnvlist_lookup_nvlist(zpool_get_config(zhp, NULL), ZPOOL_CONFIG_VDEV_TREE); @@ -10212,7 +10218,14 @@ set_callback(zpool_handle_t *zhp, void *data) int error; set_cbdata_t *cb = (set_cbdata_t *)data; -<<<<<<< HEAD + if (cb->cb_type == ZFS_TYPE_VDEV) { + error = zpool_set_vdev_prop(zhp, *cb->cb_vdevs.cb_names, + cb->cb_propname, cb->cb_value); + if (!error) + cb->cb_any_successful = B_TRUE; + return (error); + } + /* Check if we have out-of-bounds features */ if (strcmp(cb->cb_propname, ZPOOL_CONFIG_COMPATIBILITY) == 0) { boolean_t features[SPA_FEATURES]; @@ -10270,11 +10283,7 @@ set_callback(zpool_handle_t *zhp, void *data) } } - if (cb->cb_type == ZFS_TYPE_VDEV) - error = zpool_set_vdev_prop(zhp, *cb->cb_vdevs.cb_names, - cb->cb_propname, cb->cb_value); - else - error = zpool_set_prop(zhp, cb->cb_propname, cb->cb_value); + error = zpool_set_prop(zhp, cb->cb_propname, cb->cb_value); if (!error) cb->cb_any_successful = B_TRUE; diff --git a/include/sys/fs/zfs.h b/include/sys/fs/zfs.h index cc727527241b..8bc69c1eb453 100644 --- a/include/sys/fs/zfs.h +++ b/include/sys/fs/zfs.h @@ -771,6 +771,7 @@ typedef struct zpool_load_policy { #define ZPOOL_CONFIG_ORIG_GUID "orig_guid" #define ZPOOL_CONFIG_SPLIT_GUID "split_guid" #define ZPOOL_CONFIG_SPLIT_LIST "guid_list" +#define ZPOOL_CONFIG_NONALLOCATING "non_allocating" #define ZPOOL_CONFIG_REMOVING "removing" #define ZPOOL_CONFIG_RESILVER_TXG "resilver_txg" #define ZPOOL_CONFIG_REBUILD_TXG "rebuild_txg" @@ -1148,6 +1149,7 @@ typedef struct vdev_stat { uint64_t vs_checksum_errors; /* checksum errors */ uint64_t vs_initialize_errors; /* initializing errors */ uint64_t vs_self_healed; /* self-healed bytes */ + uint64_t vs_noalloc; /* allocations halted? */ uint64_t vs_scan_removing; /* removing? */ uint64_t vs_scan_processed; /* scan processed bytes */ uint64_t vs_fragmentation; /* device fragmentation */ diff --git a/include/sys/spa.h b/include/sys/spa.h index c960478efe50..c5dfae2acaf0 100644 --- a/include/sys/spa.h +++ b/include/sys/spa.h @@ -813,7 +813,8 @@ extern int spa_vdev_attach(spa_t *spa, uint64_t guid, nvlist_t *nvroot, int replacing, int rebuild); extern int spa_vdev_detach(spa_t *spa, uint64_t guid, uint64_t pguid, int replace_done); -extern int spa_vdev_remove(spa_t *spa, uint64_t guid, boolean_t unspare); +extern int spa_vdev_alloc(spa_t *spa, uint64_t guid); +extern int spa_vdev_noalloc(spa_t *spa, uint64_t guid); extern boolean_t spa_vdev_remove_active(spa_t *spa); extern int spa_vdev_initialize(spa_t *spa, nvlist_t *nv, uint64_t cmd_type, nvlist_t *vdev_errlist); diff --git a/include/sys/vdev_impl.h b/include/sys/vdev_impl.h index 3cfde40a77fe..86959725a513 100644 --- a/include/sys/vdev_impl.h +++ b/include/sys/vdev_impl.h @@ -295,6 +295,7 @@ struct vdev { list_node_t vdev_state_dirty_node; /* state dirty list */ uint64_t vdev_deflate_ratio; /* deflation ratio (x512) */ uint64_t vdev_islog; /* is an intent log device */ + uint64_t vdev_noalloc; /* device is passivated? */ uint64_t vdev_removing; /* device is being removed? */ boolean_t vdev_ishole; /* is a hole in the namespace */ uint64_t vdev_top_zap; diff --git a/module/zfs/vdev.c b/module/zfs/vdev.c index e92426028e59..41cf3dd5075b 100644 --- a/module/zfs/vdev.c +++ b/module/zfs/vdev.c @@ -866,6 +866,8 @@ vdev_alloc(spa_t *spa, vdev_t **vdp, nvlist_t *nv, vdev_t *parent, uint_t id, &vd->vdev_ms_shift); (void) nvlist_lookup_uint64(nv, ZPOOL_CONFIG_ASIZE, &vd->vdev_asize); + (void) nvlist_lookup_uint64(nv, ZPOOL_CONFIG_NONALLOCATING, + &vd->vdev_noalloc); (void) nvlist_lookup_uint64(nv, ZPOOL_CONFIG_REMOVING, &vd->vdev_removing); (void) nvlist_lookup_uint64(nv, ZPOOL_CONFIG_VDEV_TOP_ZAP, @@ -1184,8 +1186,10 @@ vdev_top_transfer(vdev_t *svd, vdev_t *tvd) ASSERT3P(tvd->vdev_indirect_mapping, ==, NULL); ASSERT3P(tvd->vdev_indirect_births, ==, NULL); ASSERT3P(tvd->vdev_obsolete_sm, ==, NULL); + ASSERT0(tvd->vdev_noalloc); ASSERT0(tvd->vdev_removing); ASSERT0(tvd->vdev_rebuilding); + tvd->vdev_noalloc = svd->vdev_noalloc; tvd->vdev_removing = svd->vdev_removing; tvd->vdev_rebuilding = svd->vdev_rebuilding; tvd->vdev_rebuild_config = svd->vdev_rebuild_config; @@ -1201,6 +1205,7 @@ vdev_top_transfer(vdev_t *svd, vdev_t *tvd) svd->vdev_indirect_mapping = NULL; svd->vdev_indirect_births = NULL; svd->vdev_obsolete_sm = NULL; + svd->vdev_noalloc = 0; svd->vdev_removing = 0; svd->vdev_rebuilding = 0; @@ -1499,11 +1504,11 @@ vdev_metaslab_init(vdev_t *vd, uint64_t txg) spa_config_enter(spa, SCL_ALLOC, FTAG, RW_WRITER); /* - * If the vdev is being removed we don't activate - * the metaslabs since we want to ensure that no new - * allocations are performed on this device. + * If the vdev is marked as non-allocating then don't + * activate the metaslabs since we want to ensure that + * no allocations are performed on this device. */ - if (!expanding && !vd->vdev_removing) { + if (!expanding && !vd->vdev_noalloc) { metaslab_group_activate(vd->vdev_mg); if (vd->vdev_log_mg != NULL) metaslab_group_activate(vd->vdev_log_mg); @@ -4439,6 +4444,7 @@ vdev_get_stats_ex(vdev_t *vd, vdev_stat_t *vs, vdev_stat_ex_t *vsx) vs->vs_fragmentation = (vd->vdev_mg != NULL) ? vd->vdev_mg->mg_fragmentation : 0; } + vs->vs_noalloc = vd->vdev_noalloc; } vdev_get_stats_ex_impl(vd, vs, vsx); @@ -5515,6 +5521,7 @@ vdev_prop_set(vdev_t *vd, nvlist_t *innvl, nvlist_t *outnvl) nvpair_t *elem = NULL; uint64_t vdev_guid; nvlist_t *nvprops; + int error; ASSERT(vd != NULL); @@ -5527,7 +5534,7 @@ vdev_prop_set(vdev_t *vd, nvlist_t *innvl, nvlist_t *outnvl) #if 0 if ((error = vdev_prop_validate(spa, nvprops)) != 0) - return; + return (error); #endif while ((elem = nvlist_next_nvpair(nvprops, elem)) != NULL) { @@ -5537,15 +5544,13 @@ vdev_prop_set(vdev_t *vd, nvlist_t *innvl, nvlist_t *outnvl) char *strval = NULL; if (prop == VDEV_PROP_INVAL && !vdev_prop_user(propname)) { - intval = EINVAL; - vdev_prop_add_list(outnvl, propname, strval, intval, 0); - continue; + error = EINVAL; + goto end; } - if (vdev_prop_readonly(prop) == B_TRUE) { - intval = EROFS; - vdev_prop_add_list(outnvl, propname, strval, intval, 0); - continue; + if (vdev_prop_readonly(prop)) { + error = EROFS; + goto end; } /* Special Processing */ @@ -5553,28 +5558,41 @@ vdev_prop_set(vdev_t *vd, nvlist_t *innvl, nvlist_t *outnvl) case VDEV_PROP_PATH: strval = vd->vdev_path; if (strval == NULL) - intval = EROFS; - if (nvpair_type(elem) != DATA_TYPE_STRING) - intval = EINVAL; - if (intval == 0) - strval = fnvpair_value_string(elem); + error = EROFS; + else if (nvpair_type(elem) != DATA_TYPE_STRING) + error = EINVAL; + if (error != 0) + break; + strval = fnvpair_value_string(elem); if (strval == NULL) - intval = EINVAL; - if (intval != 0) { - vdev_prop_add_list(outnvl, propname, strval, - intval, 0); - continue; - } + error = EINVAL; + if (error != 0) + break; spa_strfree(vd->vdev_path); vd->vdev_path = spa_strdup(strval); spa_config_enter(spa, SCL_CONFIG, FTAG, RW_WRITER); vdev_config_dirty(vd->vdev_top); spa_config_exit(spa, SCL_CONFIG, FTAG); break; + case VDEV_PROP_NOALLOC: + intval = fnvpair_value_uint64(elem); + if (intval == vd->vdev_noalloc) + return (0); /* noop */ + if (intval == 1) + error = spa_vdev_noalloc(spa, vdev_guid); + else + error = spa_vdev_alloc(spa, vdev_guid); + break; default: /* Most processing is done in vdev_sync_props */ break; } +end: + if (error != 0) { + intval = error; + vdev_prop_add_list(outnvl, propname, strval, intval, 0); + return (error); + } } return (dsl_sync_task(spa->spa_name, NULL, vdev_sync_props, diff --git a/module/zfs/vdev_label.c b/module/zfs/vdev_label.c index 04202a9f8960..5a6ece817b47 100644 --- a/module/zfs/vdev_label.c +++ b/module/zfs/vdev_label.c @@ -478,6 +478,10 @@ vdev_config_generate(spa_t *spa, vdev_t *vd, boolean_t getstats, fnvlist_add_uint64(nv, ZPOOL_CONFIG_ASIZE, vd->vdev_asize); fnvlist_add_uint64(nv, ZPOOL_CONFIG_IS_LOG, vd->vdev_islog); + if (vd->vdev_noalloc) { + fnvlist_add_uint64(nv, ZPOOL_CONFIG_NONALLOCATING, + vd->vdev_noalloc); + } if (vd->vdev_removing) { fnvlist_add_uint64(nv, ZPOOL_CONFIG_REMOVING, vd->vdev_removing); diff --git a/module/zfs/vdev_removal.c b/module/zfs/vdev_removal.c index a758fe4fb343..0ca47b325970 100644 --- a/module/zfs/vdev_removal.c +++ b/module/zfs/vdev_removal.c @@ -167,6 +167,16 @@ spa_nvlist_lookup_by_guid(nvlist_t **nvpp, int count, uint64_t target_guid) return (NULL); } +static void +vdev_activate(vdev_t *vd) +{ + metaslab_group_t *mg = vd->vdev_mg; + metaslab_group_activate(mg); + ASSERT(!vd->vdev_islog); + metaslab_group_activate(vd->vdev_log_mg); + vd->vdev_noalloc = B_FALSE; +} + static void spa_vdev_remove_aux(nvlist_t *config, char *name, nvlist_t **dev, int count, nvlist_t *dev_to_remove) @@ -1620,6 +1630,44 @@ spa_vdev_remove_suspend(spa_t *spa) mutex_exit(&svr->svr_lock); } +static boolean_t +vdev_prop_noalloc(vdev_t *vd) +{ + spa_t *spa = vd->vdev_spa; + objset_t *mos = spa->spa_meta_objset; + uint64_t objid; + uint64_t noalloc = 0; + int err = 0; + + ASSERT(vd != NULL); + + if (vd->vdev_top_zap != 0) { + objid = vd->vdev_top_zap; + } else if (vd->vdev_leaf_zap != 0) { + objid = vd->vdev_leaf_zap; + } else { + objid = 0; + } + + /* no vdev property object => no props */ + if (mos == NULL || objid == 0) { + return (B_FALSE); + } + + mutex_enter(&spa->spa_props_lock); + + err = zap_lookup(mos, objid, vdev_prop_to_name(VDEV_PROP_NOALLOC), + sizeof (uint64_t), 1, &noalloc); + + mutex_exit(&spa->spa_props_lock); + + if (err && err == ENOENT) { + return (B_FALSE); + } + + return (noalloc > 0); +} + /* ARGSUSED */ static int spa_vdev_remove_cancel_check(void *arg, dmu_tx_t *tx) @@ -1762,6 +1810,14 @@ spa_vdev_remove_cancel_sync(void *arg, dmu_tx_t *tx) spa_finish_removal(spa, DSS_CANCELED, tx); vd->vdev_removing = B_FALSE; + + if (!vdev_prop_noalloc(vd)) { + /* XXX - not sure locking is correct/necessary... */ + spa_config_enter(spa, SCL_ALLOC | SCL_VDEV, FTAG, RW_WRITER); + vdev_activate(vd); + spa_config_exit(spa, SCL_ALLOC | SCL_VDEV, FTAG); + } + vdev_config_dirty(vd); zfs_dbgmsg("canceled device removal for vdev %llu in %llu", @@ -1775,21 +1831,9 @@ spa_vdev_remove_cancel_sync(void *arg, dmu_tx_t *tx) static int spa_vdev_remove_cancel_impl(spa_t *spa) { - uint64_t vdid = spa->spa_vdev_removal->svr_vdev_id; - int error = dsl_sync_task(spa->spa_name, spa_vdev_remove_cancel_check, spa_vdev_remove_cancel_sync, NULL, 0, ZFS_SPACE_CHECK_EXTRA_RESERVED); - - if (error == 0) { - spa_config_enter(spa, SCL_ALLOC | SCL_VDEV, FTAG, RW_WRITER); - vdev_t *vd = vdev_lookup_top(spa, vdid); - metaslab_group_activate(vd->vdev_mg); - ASSERT(!vd->vdev_islog); - metaslab_group_activate(vd->vdev_log_mg); - spa_config_exit(spa, SCL_ALLOC | SCL_VDEV, FTAG); - } - return (error); } @@ -2097,6 +2141,110 @@ spa_vdev_remove_top_check(vdev_t *vd) return (0); } +int +spa_vdev_alloc(spa_t *spa, uint64_t guid) +{ + vdev_t *vd; + uint64_t txg; + int error = 0; + + ASSERT(!MUTEX_HELD(&spa_namespace_lock)); + ASSERT(spa_writeable(spa)); + + /* XXX - is this necessary for activate? */ + txg = spa_vdev_enter(spa); + + ASSERT(MUTEX_HELD(&spa_namespace_lock)); + + vd = spa_lookup_by_guid(spa, guid, B_FALSE); + + if (vd == NULL) + error = SET_ERROR(ENOENT); + else if (vd->vdev_mg == NULL) + error = SET_ERROR(ENOTSUP); + else + vdev_activate(vd); + + if (error == 0) { + vdev_dirty_leaves(vd, VDD_DTL, txg); + vdev_config_dirty(vd); + } + + (void) spa_vdev_exit(spa, NULL, txg, error); + + return (error); +} + +static int +vdev_passivate(vdev_t *vd, uint64_t *txg) +{ + spa_t *spa = vd->vdev_spa; + int error; + + vdev_t *rvd = spa->spa_root_vdev; + metaslab_group_t *mg = vd->vdev_mg; + metaslab_class_t *normal = spa_normal_class(spa); + if (mg->mg_class == normal) { + /* + * We must check that this is not the only allocating device in + * the pool before passivating, otherwise we will not be able + * to make progress because we can't allocate from any vdevs. + */ + boolean_t last = B_TRUE; + for (uint64_t id = 0; id < rvd->vdev_children; id++) { + vdev_t *cvd = rvd->vdev_child[id]; + + if (cvd == vd || + cvd->vdev_ops == &vdev_indirect_ops) + continue; + + metaslab_class_t *mc = vd->vdev_mg->mg_class; + if (mc != normal) + continue; + + if (!cvd->vdev_noalloc) { + last = B_FALSE; + break; + } + } + if (last) + return (SET_ERROR(EINVAL)); + } + + metaslab_group_passivate(mg); + ASSERT(!vd->vdev_islog); + metaslab_group_passivate(vd->vdev_log_mg); + + /* + * Wait for the youngest allocations and frees to sync, + * and then wait for the deferral of those frees to finish. + */ + spa_vdev_config_exit(spa, NULL, + *txg + TXG_CONCURRENT_STATES + TXG_DEFER_SIZE, 0, FTAG); + + /* + * We must ensure that no "stubby" log blocks are allocated + * on the device to be removed. These blocks could be + * written at any time, including while we are in the middle + * of copying them. + */ + error = spa_reset_logs(spa); + + *txg = spa_vdev_config_enter(spa); + + if (error != 0) { + metaslab_group_activate(mg); + ASSERT(!vd->vdev_islog); + if (vd->vdev_log_mg != NULL) + metaslab_group_activate(vd->vdev_log_mg); + return (error); + } + + vd->vdev_noalloc = B_TRUE; + + return (0); +} + /* * Initiate removal of a top-level vdev, reducing the total space in the pool. * The config lock is held for the specified TXG. Once initiated, @@ -2109,6 +2257,7 @@ static int spa_vdev_remove_top(vdev_t *vd, uint64_t *txg) { spa_t *spa = vd->vdev_spa; + boolean_t set_noalloc = B_FALSE; int error; /* @@ -2117,8 +2266,6 @@ spa_vdev_remove_top(vdev_t *vd, uint64_t *txg) * are errors. */ error = spa_vdev_remove_top_check(vd); - if (error != 0) - return (error); /* * Stop allocating from this vdev. Note that we must check @@ -2128,31 +2275,22 @@ spa_vdev_remove_top(vdev_t *vd, uint64_t *txg) * The above check for sufficient free space serves this * purpose. */ - metaslab_group_t *mg = vd->vdev_mg; - metaslab_group_passivate(mg); - ASSERT(!vd->vdev_islog); - metaslab_group_passivate(vd->vdev_log_mg); - - /* - * Wait for the youngest allocations and frees to sync, - * and then wait for the deferral of those frees to finish. - */ - spa_vdev_config_exit(spa, NULL, - *txg + TXG_CONCURRENT_STATES + TXG_DEFER_SIZE, 0, FTAG); + if (error == 0 && !vd->vdev_noalloc) { + set_noalloc = B_TRUE; + error = vdev_passivate(vd, txg); + } - /* - * We must ensure that no "stubby" log blocks are allocated - * on the device to be removed. These blocks could be - * written at any time, including while we are in the middle - * of copying them. - */ - error = spa_reset_logs(spa); + if (error != 0) + return (error); /* * We stop any initializing and TRIM that is currently in progress * but leave the state as "active". This will allow the process to * resume if the removal is canceled sometime later. */ + + spa_vdev_config_exit(spa, NULL, *txg, 0, FTAG); + vdev_initialize_stop_all(vd, VDEV_INITIALIZE_ACTIVE); vdev_trim_stop_all(vd, VDEV_TRIM_ACTIVE); vdev_autotrim_stop_wait(vd); @@ -2163,13 +2301,11 @@ spa_vdev_remove_top(vdev_t *vd, uint64_t *txg) * Things might have changed while the config lock was dropped * (e.g. space usage). Check for errors again. */ - if (error == 0) - error = spa_vdev_remove_top_check(vd); + error = spa_vdev_remove_top_check(vd); if (error != 0) { - metaslab_group_activate(mg); - ASSERT(!vd->vdev_islog); - metaslab_group_activate(vd->vdev_log_mg); + if (set_noalloc) + vdev_activate(vd); spa_async_request(spa, SPA_ASYNC_INITIALIZE_RESTART); spa_async_request(spa, SPA_ASYNC_TRIM_RESTART); spa_async_request(spa, SPA_ASYNC_AUTOTRIM_RESTART); @@ -2188,6 +2324,48 @@ spa_vdev_remove_top(vdev_t *vd, uint64_t *txg) return (0); } +/* + * Turn off allocations for a top-level device from the pool. + * + * Turning off allocations for a top-level device can take a significant + * amount of time. As a result we use the spa_vdev_config_[enter/exit] + * functions which allow us to grab and release the spa_config_lock while + * still holding the namespace lock. During each step the configuration + * is synced out. + */ +int +spa_vdev_noalloc(spa_t *spa, uint64_t guid) +{ + vdev_t *vd; + uint64_t txg; + int error; + + ASSERT(!MUTEX_HELD(&spa_namespace_lock)); + ASSERT(spa_writeable(spa)); + + txg = spa_vdev_enter(spa); + + ASSERT(MUTEX_HELD(&spa_namespace_lock)); + + vd = spa_lookup_by_guid(spa, guid, B_FALSE); + + if (vd == NULL) + error = SET_ERROR(ENOENT); + else if (vd->vdev_mg == NULL) + error = SET_ERROR(ENOTSUP); + else + error = vdev_passivate(vd, &txg); + + if (error == 0) { + vdev_dirty_leaves(vd, VDD_DTL, txg); + vdev_config_dirty(vd); + } + + error = spa_vdev_exit(spa, NULL, txg, error); + + return (error); +} + /* * Remove a device from the pool. * diff --git a/module/zfs/zio.c b/module/zfs/zio.c index 87ccb6861850..1d5d5e2093be 100644 --- a/module/zfs/zio.c +++ b/module/zfs/zio.c @@ -3736,7 +3736,7 @@ zio_vdev_io_start(zio_t *zio) * Note: the code can handle other kinds of writes, * but we don't expect them. */ - if (zio->io_vd->vdev_removing) { + if (zio->io_vd->vdev_noalloc) { ASSERT(zio->io_flags & (ZIO_FLAG_PHYSICAL | ZIO_FLAG_SELF_HEAL | ZIO_FLAG_RESILVER | ZIO_FLAG_INDUCE_DAMAGE)); From e541bc3954104dd076606f8e722083e99ea3d565 Mon Sep 17 00:00:00 2001 From: Mark Maybee Date: Wed, 12 May 2021 09:28:08 -0600 Subject: [PATCH 7/7] Rename "noalloc" vdev property to "allocating" Add support for read-only "removing" vdev property Simplify get vdev prop interface to always require pool name Refactor space management for non-allocating devices Add ability to remove user vdev property by setting value to "" --- cmd/zhack/zhack.c | 4 +-- cmd/zpool/zpool_iter.c | 5 ++-- cmd/zpool/zpool_main.c | 57 +++++++++++++++-------------------- include/sys/fs/zfs.h | 13 ++++---- include/sys/spa_impl.h | 1 + lib/libzfs/libzfs_pool.c | 40 ++++++++++++++++--------- module/zcommon/zpool_prop.c | 13 ++++---- module/zfs/spa_misc.c | 22 +++++++------- module/zfs/vdev.c | 47 ++++++++++++++++++++--------- module/zfs/vdev_removal.c | 59 ++++++++++++++++++++++++++++--------- module/zfs/zfs_ioctl.c | 12 ++++---- 11 files changed, 162 insertions(+), 111 deletions(-) diff --git a/cmd/zhack/zhack.c b/cmd/zhack/zhack.c index b5ac5dcf9da2..a6d5c7677173 100644 --- a/cmd/zhack/zhack.c +++ b/cmd/zhack/zhack.c @@ -475,7 +475,6 @@ static int zhack_do_zap(int argc, char **argv) { spa_t *spa; - objset_t *os; uint64_t obj; char *target; @@ -494,9 +493,8 @@ zhack_do_zap(int argc, char **argv) obj = strtoull(argv[1], NULL, 0); zhack_spa_open(target, B_TRUE, FTAG, &spa); - os = spa->spa_meta_objset; - dump_obj(os, obj, argv[1]); + dump_obj(spa->spa_meta_objset, obj, argv[1]); spa_close(spa, FTAG); diff --git a/cmd/zpool/zpool_iter.c b/cmd/zpool/zpool_iter.c index 02767d24076b..3da9f080059b 100644 --- a/cmd/zpool/zpool_iter.c +++ b/cmd/zpool/zpool_iter.c @@ -304,8 +304,9 @@ for_each_vdev_cb(zpool_handle_t *zhp, nvlist_t *nv, pool_vdev_iter_f func, if (nvlist_lookup_string(nv, ZPOOL_CONFIG_TYPE, &type) != 0) return (ret); - /* Don't run our function on root vdevs */ - if (strcmp(type, VDEV_TYPE_ROOT) != 0) { + /* Don't run our function on root or indirect vdevs */ + if ((strcmp(type, VDEV_TYPE_ROOT) != 0) && + (strcmp(type, VDEV_TYPE_INDIRECT) != 0)) { ret |= func(zhp, nv, data); } diff --git a/cmd/zpool/zpool_main.c b/cmd/zpool/zpool_main.c index 02778abc52c5..16b2f27c058c 100644 --- a/cmd/zpool/zpool_main.c +++ b/cmd/zpool/zpool_main.c @@ -330,7 +330,7 @@ static zpool_command_t command_table[] = { #define VDEV_ALLOC_CLASS_LOGS "logs" static zpool_command_t *current_command; -static zfs_type_t current_prop_type = ZFS_TYPE_POOL; +static zfs_type_t current_prop_type = (ZFS_TYPE_POOL | ZFS_TYPE_VDEV); static char history_str[HIS_MAX_RECORD_LEN]; static boolean_t log_history = B_TRUE; static uint_t timestamp_fmt = NODATE; @@ -487,7 +487,7 @@ print_prop_cb(int prop, void *cb) } /* - * Callback routine that will print out a pool property value. + * Callback routine that will print out a vdev property value. */ static int print_vdev_prop_cb(int prop, void *cb) @@ -539,6 +539,7 @@ usage(boolean_t requested) } if (current_command != NULL && + current_prop_type != (ZFS_TYPE_POOL | ZFS_TYPE_VDEV) && ((strcmp(current_command->name, "set") == 0) || (strcmp(current_command->name, "get") == 0) || (strcmp(current_command->name, "list") == 0))) { @@ -825,7 +826,7 @@ add_prop_list(const char *propname, char *propval, nvlist_t **props, (!zpool_prop_feature(propname) || zpool_prop_vdev(propname))) { (void) fprintf(stderr, gettext("property '%s' is " - "not a valid pool property\n"), propname); + "not a valid pool or vdev property\n"), propname); return (2); } @@ -9896,6 +9897,7 @@ get_callback_vdev(zpool_handle_t *zhp, char *vdevname, void *data) zprop_list_t *pl; for (pl = cbp->cb_proplist; pl != NULL; pl = pl->pl_next) { + char *prop_name; /* * Skip the special fake placeholder. This will also skip * over the name property when 'all' is specified. @@ -9906,21 +9908,14 @@ get_callback_vdev(zpool_handle_t *zhp, char *vdevname, void *data) if (pl->pl_prop == ZPROP_INVAL) { /* User Properties */ - if (zpool_get_vdev_prop(zhp, vdevname, pl->pl_prop, - pl->pl_user_prop, value, sizeof (value), &srctype, - cbp->cb_literal) == 0) { - zprop_print_one_property( - vdevname, cbp, pl->pl_user_prop, value, - srctype, NULL, NULL); - } + prop_name = pl->pl_user_prop; } else { - if (zpool_get_vdev_prop(zhp, vdevname, pl->pl_prop, - NULL, value, sizeof (value), &srctype, - cbp->cb_literal) != 0) - continue; - - zprop_print_one_property( - vdevname, cbp, vdev_prop_to_name(pl->pl_prop), + prop_name = (char *)vdev_prop_to_name(pl->pl_prop); + } + if (zpool_get_vdev_prop(zhp, vdevname, pl->pl_prop, + prop_name, value, sizeof (value), &srctype, + cbp->cb_literal) == 0) { + zprop_print_one_property(vdevname, cbp, prop_name, value, srctype, NULL, NULL); } } @@ -9966,20 +9961,20 @@ get_callback(zpool_handle_t *zhp, void *data) int vid; if (cbp->cb_type == ZFS_TYPE_VDEV) { - if (strcmp(cbp->cb_vdevs.cb_names[0], "all") == 0) { + if (strcmp(cbp->cb_vdevs.cb_names[0], "all-vdevs") == 0) { for_each_vdev(zhp, get_callback_vdev_width_cb, data); for_each_vdev(zhp, get_callback_vdev_cb, data); } else { + /* Adjust column widths for vdev properties */ for (vid = 0; vid < cbp->cb_vdevs.cb_names_count; vid++) { - /* Adjust column widths for vdev properties */ vdev_expand_proplist(zhp, cbp->cb_vdevs.cb_names[vid], &cbp->cb_proplist); } + /* Display the properties */ for (vid = 0; vid < cbp->cb_vdevs.cb_names_count; vid++) { - /* Display the properties */ get_callback_vdev(zhp, cbp->cb_vdevs.cb_names[vid], data); } @@ -10143,24 +10138,18 @@ zpool_do_get(int argc, char **argv) /* No args, so just print the defaults. */ } else if (are_all_pools(argc, argv)) { /* All the args are pool names */ - } else if (are_vdevs_in_pool(argc, argv, NULL, &cb.cb_vdevs)) { - /* All the args are vdevs */ - cb.cb_vdevs.cb_names = argv; - cb.cb_vdevs.cb_names_count = argc; - cb.cb_type = ZFS_TYPE_VDEV; - argc = 0; /* No pools to process */ } else if (are_all_pools(1, argv)) { /* The first arg is a pool name */ - if (are_vdevs_in_pool(argc - 1, argv + 1, argv[0], - &cb.cb_vdevs) || (strcmp(argv[1], "all") == 0)) { - /* ...and the rest are vdev names */ + if ((argc == 2 && strcmp(argv[1], "all-vdevs") == 0) || + are_vdevs_in_pool(argc - 1, argv + 1, argv[0], + &cb.cb_vdevs)) { + /* ... and the rest are vdev names */ cb.cb_vdevs.cb_names = argv + 1; cb.cb_vdevs.cb_names_count = argc - 1; cb.cb_type = ZFS_TYPE_VDEV; argc = 1; /* One pool to process */ } else { - fprintf(stderr, gettext("Expected either a list of ")); - fprintf(stderr, gettext("pools, or list of vdevs in")); + fprintf(stderr, gettext("Expected a list of vdevs in")); fprintf(stderr, " \"%s\", ", argv[0]); fprintf(stderr, gettext("but got:\n")); error_list_unresolved_vdevs(argc - 1, argv + 1, @@ -10171,11 +10160,11 @@ zpool_do_get(int argc, char **argv) } } else { /* - * The args don't make sense. The first arg isn't a pool name, - * nor are all the args vdevs. + * The first arg isn't a pool name, */ - fprintf(stderr, gettext("Unable to parse pools/vdevs list.\n")); + fprintf(stderr, gettext("missing pool name.\n")); fprintf(stderr, "\n"); + usage(B_FALSE); return (1); } diff --git a/include/sys/fs/zfs.h b/include/sys/fs/zfs.h index 8bc69c1eb453..0db7434dab48 100644 --- a/include/sys/fs/zfs.h +++ b/include/sys/fs/zfs.h @@ -347,7 +347,8 @@ typedef enum { VDEV_PROP_BYTES_FREE, VDEV_PROP_BYTES_CLAIM, VDEV_PROP_BYTES_TRIM, - VDEV_PROP_NOALLOC, + VDEV_PROP_REMOVING, + VDEV_PROP_ALLOCATING, VDEV_NUM_PROPS } vdev_prop_t; @@ -1149,7 +1150,6 @@ typedef struct vdev_stat { uint64_t vs_checksum_errors; /* checksum errors */ uint64_t vs_initialize_errors; /* initializing errors */ uint64_t vs_self_healed; /* self-healed bytes */ - uint64_t vs_noalloc; /* allocations halted? */ uint64_t vs_scan_removing; /* removing? */ uint64_t vs_scan_processed; /* scan processed bytes */ uint64_t vs_fragmentation; /* device fragmentation */ @@ -1170,6 +1170,7 @@ typedef struct vdev_stat { uint64_t vs_configured_ashift; /* TLV vdev_ashift */ uint64_t vs_logical_ashift; /* vdev_logical_ashift */ uint64_t vs_physical_ashift; /* vdev_physical_ashift */ + uint64_t vs_noalloc; /* allocations halted? */ } vdev_stat_t; /* BEGIN CSTYLED */ @@ -1574,14 +1575,14 @@ typedef enum { /* * The following are names used when invoking ZFS_IOC_VDEV_GET_PROP. */ -#define ZPOOL_VDEV_GET_PROPS_VDEV "vdevprops_get_vdev" -#define ZPOOL_VDEV_GET_PROPS_PROPS "vdevprops_get_props" +#define ZPOOL_VDEV_PROPS_GET_VDEV "vdevprops_get_vdev" +#define ZPOOL_VDEV_PROPS_GET_PROPS "vdevprops_get_props" /* * The following are names used when invoking ZFS_IOC_VDEV_SET_PROP. */ -#define ZPOOL_VDEV_SET_PROPS_VDEV "vdevprops_set_vdev" -#define ZPOOL_VDEV_SET_PROPS_PROPS "vdevprops_set_props" +#define ZPOOL_VDEV_PROPS_SET_VDEV "vdevprops_set_vdev" +#define ZPOOL_VDEV_PROPS_SET_PROPS "vdevprops_set_props" /* * The following are names used when invoking ZFS_IOC_WAIT_FS. diff --git a/include/sys/spa_impl.h b/include/sys/spa_impl.h index bc88cfa15e8e..982dab1de253 100644 --- a/include/sys/spa_impl.h +++ b/include/sys/spa_impl.h @@ -305,6 +305,7 @@ struct spa { uint64_t spa_missing_tvds; /* unopenable tvds on load */ uint64_t spa_missing_tvds_allowed; /* allow loading spa? */ + uint64_t spa_nonallocating_dspace; spa_removing_phys_t spa_removing_phys; spa_vdev_removal_t *spa_vdev_removal; diff --git a/lib/libzfs/libzfs_pool.c b/lib/libzfs/libzfs_pool.c index efe982cec261..6fbe5b3a95d1 100644 --- a/lib/libzfs/libzfs_pool.c +++ b/lib/libzfs/libzfs_pool.c @@ -5064,8 +5064,8 @@ zpool_get_vdev_prop_value(nvlist_t *nvprop, vdev_prop_t prop, char *prop_name, verify(nvlist_lookup_string(nv, ZPROP_VALUE, &strval) == 0); } else { - src = ZPROP_SRC_NONE; - strval = "-"; + /* user prop not found */ + return (-1); } (void) strlcpy(buf, strval, len); if (srctype) @@ -5220,7 +5220,8 @@ zpool_get_vdev_prop(zpool_handle_t *zhp, const char *vdevname, vdev_prop_t prop, char *prop_name, char *buf, size_t len, zprop_source_t *srctype, boolean_t literal) { - nvlist_t *tgt, *reqnvl, *reqprops, *retprops; + nvlist_t *tgt, *reqnvl, *reqprops; + nvlist_t *retprops = NULL; uint64_t vdev_guid; boolean_t avail_spare, l2cache; char errbuf[1024]; @@ -5234,8 +5235,12 @@ zpool_get_vdev_prop(zpool_handle_t *zhp, const char *vdevname, vdev_prop_t prop, } if ((tgt = zpool_find_vdev(zhp, vdevname, &avail_spare, &l2cache, - NULL)) == NULL) - return (-1); + NULL)) == NULL) { + (void) snprintf(errbuf, sizeof (errbuf), + dgettext(TEXT_DOMAIN, "can not find %s in %s"), + vdevname, zhp->zpool_name); + return (zfs_error(zhp->zpool_hdl, EZFS_NODEVICE, errbuf)); + } verify(nvlist_lookup_uint64(tgt, ZPOOL_CONFIG_GUID, &vdev_guid) == 0); @@ -5244,11 +5249,18 @@ zpool_get_vdev_prop(zpool_handle_t *zhp, const char *vdevname, vdev_prop_t prop, if (nvlist_alloc(&reqprops, NV_UNIQUE_NAME, 0) != 0) return (no_memory(zhp->zpool_hdl)); - fnvlist_add_uint64(reqnvl, ZPOOL_VDEV_GET_PROPS_VDEV, vdev_guid); + fnvlist_add_uint64(reqnvl, ZPOOL_VDEV_PROPS_GET_VDEV, vdev_guid); - if (prop != VDEV_PROP_INVAL && prop_name == NULL) - prop_name = (char *)vdev_prop_to_name(prop); + if (prop != VDEV_PROP_INVAL) { + /* prop_name overrides prop value */ + if (prop_name != NULL) + prop = vdev_name_to_prop(prop_name); + else + prop_name = (char *)vdev_prop_to_name(prop); + } + if (prop != VDEV_PROP_INVAL) + assert(prop < VDEV_NUM_PROPS); assert(prop_name != NULL); if (nvlist_add_uint64(reqprops, prop_name, prop) != 0) { nvlist_free(reqnvl); @@ -5256,7 +5268,7 @@ zpool_get_vdev_prop(zpool_handle_t *zhp, const char *vdevname, vdev_prop_t prop, return (no_memory(zhp->zpool_hdl)); } - fnvlist_add_nvlist(reqnvl, ZPOOL_VDEV_GET_PROPS_PROPS, reqprops); + fnvlist_add_nvlist(reqnvl, ZPOOL_VDEV_PROPS_GET_PROPS, reqprops); (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN, "cannot get vdev property %s from %s in %s"), @@ -5291,7 +5303,7 @@ zpool_get_all_vdev_props(zpool_handle_t *zhp, const char *vdevname, uint64_t vdev_guid; boolean_t avail_spare, l2cache; char errbuf[1024]; - int ret = -1; + int ret; verify(zhp != NULL); if (zpool_get_state(zhp) == POOL_STATE_UNAVAIL) { @@ -5313,7 +5325,7 @@ zpool_get_all_vdev_props(zpool_handle_t *zhp, const char *vdevname, if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0) != 0) return (no_memory(zhp->zpool_hdl)); - fnvlist_add_uint64(nvl, ZPOOL_VDEV_GET_PROPS_VDEV, vdev_guid); + fnvlist_add_uint64(nvl, ZPOOL_VDEV_PROPS_GET_VDEV, vdev_guid); ret = lzc_get_vdev_prop(zhp->zpool_name, nvl, outnvl); @@ -5333,7 +5345,7 @@ int zpool_set_vdev_prop(zpool_handle_t *zhp, const char *vdevname, const char *propname, const char *propval) { - int ret = -1; + int ret; char errbuf[1024]; vdev_prop_t vprop; nvlist_t *nvl = NULL; @@ -5370,7 +5382,7 @@ zpool_set_vdev_prop(zpool_handle_t *zhp, const char *vdevname, if (nvlist_alloc(&props, NV_UNIQUE_NAME, 0) != 0) return (no_memory(zhp->zpool_hdl)); - fnvlist_add_uint64(nvl, ZPOOL_VDEV_SET_PROPS_VDEV, vdev_guid); + fnvlist_add_uint64(nvl, ZPOOL_VDEV_PROPS_SET_VDEV, vdev_guid); if (nvlist_add_string(props, propname, propval) != 0) { nvlist_free(props); @@ -5389,7 +5401,7 @@ zpool_set_vdev_prop(zpool_handle_t *zhp, const char *vdevname, nvlist_free(props); props = realprops; - fnvlist_add_nvlist(nvl, ZPOOL_VDEV_SET_PROPS_PROPS, props); + fnvlist_add_nvlist(nvl, ZPOOL_VDEV_PROPS_SET_PROPS, props); ret = lzc_set_vdev_prop(zhp->zpool_name, nvl, &outnvl); diff --git a/module/zcommon/zpool_prop.c b/module/zcommon/zpool_prop.c index ad924b15a061..2d9b85b7c70e 100644 --- a/module/zcommon/zpool_prop.c +++ b/module/zcommon/zpool_prop.c @@ -359,12 +359,16 @@ vdev_prop_init(void) PROP_READONLY, ZFS_TYPE_VDEV, "", "CLAIMBYTE"); zprop_register_number(VDEV_PROP_BYTES_TRIM, "trim_bytes", 0, PROP_READONLY, ZFS_TYPE_VDEV, "", "TRIMBYTE"); + /* XXX - register this as an index (yes | no) type? */ + zprop_register_number(VDEV_PROP_REMOVING, "removing", 0, + PROP_READONLY, ZFS_TYPE_VDEV, "", "REMOVING"); /* default numeric properties */ /* default index (boolean) properties */ - zprop_register_index(VDEV_PROP_NOALLOC, "noalloc", 0, - PROP_DEFAULT, ZFS_TYPE_VDEV, "on | off", "NOALLOC", boolean_table); + zprop_register_index(VDEV_PROP_ALLOCATING, "allocating", 1, + PROP_DEFAULT, ZFS_TYPE_VDEV, "on | off", "ALLOCATING", + boolean_table); /* default index properties */ @@ -400,10 +404,7 @@ vdev_prop_user(const char *name) foundsep = B_TRUE; } - if (!foundsep) - return (B_FALSE); - - return (B_TRUE); + return (foundsep); } /* diff --git a/module/zfs/spa_misc.c b/module/zfs/spa_misc.c index f86f755a46ec..e08a6c34d4d2 100644 --- a/module/zfs/spa_misc.c +++ b/module/zfs/spa_misc.c @@ -1836,13 +1836,15 @@ spa_update_dspace(spa_t *spa) { spa->spa_dspace = metaslab_class_get_dspace(spa_normal_class(spa)) + ddt_get_dedup_dspace(spa); - if (spa->spa_vdev_removal != NULL) { + if (spa->spa_nonallocating_dspace > 0) { /* - * We can't allocate from the removing device, so subtract - * its size if it was included in dspace (i.e. if this is a - * normal-class vdev, not special/dedup). This prevents the - * DMU/DSL from filling up the (now smaller) pool while we - * are in the middle of removing the device. + * Subtract the space from all the non-allocating vdevs that + * contribute to dspace. This provides a stable/consistent + * value for the available space in the pool (i.e., if + * the data on non-allocating drives is re-written the + * available free space does not fluctuate oddly). It also + * prevents the DMU/DSL from filling up the (now smaller) + * pool if we are in the middle of removing a device. * * Note that the DMU/DSL doesn't actually know or care * how much space is allocated (it does its own tracking @@ -1852,12 +1854,8 @@ spa_update_dspace(spa_t *spa) * device). */ spa_config_enter(spa, SCL_VDEV, FTAG, RW_READER); - vdev_t *vd = - vdev_lookup_top(spa, spa->spa_vdev_removal->svr_vdev_id); - if (vd->vdev_mg->mg_class == spa_normal_class(spa)) { - spa->spa_dspace -= spa_deflate(spa) ? - vd->vdev_stat.vs_dspace : vd->vdev_stat.vs_space; - } + ASSERT3U(spa->spa_dspace, >, spa->spa_nonallocating_dspace); + spa->spa_dspace -= spa->spa_nonallocating_dspace; spa_config_exit(spa, SCL_VDEV, FTAG); } } diff --git a/module/zfs/vdev.c b/module/zfs/vdev.c index 41cf3dd5075b..c5ae8d0d9ce6 100644 --- a/module/zfs/vdev.c +++ b/module/zfs/vdev.c @@ -1512,6 +1512,10 @@ vdev_metaslab_init(vdev_t *vd, uint64_t txg) metaslab_group_activate(vd->vdev_mg); if (vd->vdev_log_mg != NULL) metaslab_group_activate(vd->vdev_log_mg); + } else if (vd->vdev_noalloc) { + /* track non-allocating vdev space */ + spa->spa_nonallocating_dspace += spa_deflate(spa) ? + vd->vdev_stat.vs_dspace : vd->vdev_stat.vs_space; } if (txg == 0) @@ -5434,14 +5438,14 @@ vdev_sync_props(void *arg, dmu_tx_t *tx) uint64_t vdev_guid; nvlist_t *nvprops; - if (nvlist_lookup_uint64(nvp, ZPOOL_VDEV_SET_PROPS_VDEV, + if (nvlist_lookup_uint64(nvp, ZPOOL_VDEV_PROPS_SET_VDEV, &vdev_guid) != 0) return; if ((vd = spa_lookup_by_guid(spa, vdev_guid, B_TRUE)) == NULL) return; - (void) nvlist_lookup_nvlist(nvp, ZPOOL_VDEV_SET_PROPS_PROPS, &nvprops); + (void) nvlist_lookup_nvlist(nvp, ZPOOL_VDEV_PROPS_SET_PROPS, &nvprops); mutex_enter(&spa->spa_props_lock); @@ -5468,8 +5472,14 @@ vdev_sync_props(void *arg, dmu_tx_t *tx) case VDEV_PROP_INVAL: if (vdev_prop_user(propname)) { strval = fnvpair_value_string(elem); - VERIFY0(zap_update(mos, objid, propname, - 1, strlen(strval) + 1, strval, tx)); + if (strlen(strval) == 0) { + /* remove the property if value == "" */ + (void) zap_remove(mos, objid, propname, + tx); + } else { + VERIFY0(zap_update(mos, objid, propname, + 1, strlen(strval) + 1, strval, tx)); + } spa_history_log_internal(spa, "vdev set", tx, "vdev_guid=%llu: %s=%s", (u_longlong_t)vdev_guid, nvpair_name(elem), @@ -5525,11 +5535,11 @@ vdev_prop_set(vdev_t *vd, nvlist_t *innvl, nvlist_t *outnvl) ASSERT(vd != NULL); - if (nvlist_lookup_uint64(innvl, ZPOOL_VDEV_SET_PROPS_VDEV, + if (nvlist_lookup_uint64(innvl, ZPOOL_VDEV_PROPS_SET_VDEV, &vdev_guid) != 0) return (SET_ERROR(EINVAL)); - (void) nvlist_lookup_nvlist(innvl, ZPOOL_VDEV_SET_PROPS_PROPS, + (void) nvlist_lookup_nvlist(innvl, ZPOOL_VDEV_PROPS_SET_PROPS, &nvprops); #if 0 @@ -5574,14 +5584,14 @@ vdev_prop_set(vdev_t *vd, nvlist_t *innvl, nvlist_t *outnvl) vdev_config_dirty(vd->vdev_top); spa_config_exit(spa, SCL_CONFIG, FTAG); break; - case VDEV_PROP_NOALLOC: + case VDEV_PROP_ALLOCATING: intval = fnvpair_value_uint64(elem); - if (intval == vd->vdev_noalloc) - return (0); /* noop */ + if (intval != vd->vdev_noalloc) + break; if (intval == 1) - error = spa_vdev_noalloc(spa, vdev_guid); - else error = spa_vdev_alloc(spa, vdev_guid); + else + error = spa_vdev_noalloc(spa, vdev_guid); break; default: /* Most processing is done in vdev_sync_props */ @@ -5618,11 +5628,11 @@ vdev_prop_get(vdev_t *vd, nvlist_t *innvl, nvlist_t *outnvl) ASSERT(vd != NULL); - if (nvlist_lookup_uint64(innvl, ZPOOL_VDEV_GET_PROPS_VDEV, + if (nvlist_lookup_uint64(innvl, ZPOOL_VDEV_PROPS_GET_VDEV, &vdev_guid) != 0) return (SET_ERROR(EINVAL)); - (void) nvlist_lookup_nvlist(innvl, ZPOOL_VDEV_GET_PROPS_PROPS, + (void) nvlist_lookup_nvlist(innvl, ZPOOL_VDEV_PROPS_GET_PROPS, &nvprops); /* @@ -5884,13 +5894,22 @@ vdev_prop_get(vdev_t *vd, nvlist_t *innvl, nvlist_t *outnvl) vd->vdev_stat.vs_bytes[ZIO_TYPE_IOCTL], ZPROP_SRC_NONE); continue; + case VDEV_PROP_REMOVING: + vdev_prop_add_list(outnvl, propname, NULL, + vd->vdev_removing, ZPROP_SRC_NONE); + continue; /* Numeric Properites */ - case VDEV_PROP_NOALLOC: + case VDEV_PROP_ALLOCATING: src = ZPROP_SRC_LOCAL; strval = NULL; err = zap_lookup(mos, objid, nvpair_name(elem), sizeof (uint64_t), 1, &intval); + if (err == ENOENT) + intval = + vdev_prop_default_numeric(prop); + else if (err) + break; if (intval == vdev_prop_default_numeric(prop)) src = ZPROP_SRC_DEFAULT; vdev_prop_add_list(outnvl, propname, strval, diff --git a/module/zfs/vdev_removal.c b/module/zfs/vdev_removal.c index 0ca47b325970..2d96d24c7399 100644 --- a/module/zfs/vdev_removal.c +++ b/module/zfs/vdev_removal.c @@ -171,9 +171,20 @@ static void vdev_activate(vdev_t *vd) { metaslab_group_t *mg = vd->vdev_mg; - metaslab_group_activate(mg); + spa_t *spa = vd->vdev_spa; + ASSERT(!vd->vdev_islog); + ASSERT(vd->vdev_noalloc); + + metaslab_group_activate(mg); metaslab_group_activate(vd->vdev_log_mg); + + ASSERT3U(spa->spa_nonallocating_dspace, >=, spa_deflate(spa) ? + vd->vdev_stat.vs_dspace : vd->vdev_stat.vs_space); + + spa->spa_nonallocating_dspace -= spa_deflate(spa) ? + vd->vdev_stat.vs_dspace : vd->vdev_stat.vs_space; + vd->vdev_noalloc = B_FALSE; } @@ -1208,6 +1219,15 @@ vdev_remove_complete(spa_t *spa) zfs_dbgmsg("finishing device removal for vdev %llu in txg %llu", vd->vdev_id, txg); + ASSERT3U(0, <, spa_deflate(spa) ? + vd->vdev_stat.vs_dspace : vd->vdev_stat.vs_space); + ASSERT3U(spa->spa_nonallocating_dspace, >=, spa_deflate(spa) ? + vd->vdev_stat.vs_dspace : vd->vdev_stat.vs_space); + + /* the vdev is no longer part of the dspace */ + spa->spa_nonallocating_dspace -= spa_deflate(spa) ? + vd->vdev_stat.vs_dspace : vd->vdev_stat.vs_space; + /* * Discard allocation state. */ @@ -1636,7 +1656,7 @@ vdev_prop_noalloc(vdev_t *vd) spa_t *spa = vd->vdev_spa; objset_t *mos = spa->spa_meta_objset; uint64_t objid; - uint64_t noalloc = 0; + uint64_t allocating = 1; int err = 0; ASSERT(vd != NULL); @@ -1656,8 +1676,8 @@ vdev_prop_noalloc(vdev_t *vd) mutex_enter(&spa->spa_props_lock); - err = zap_lookup(mos, objid, vdev_prop_to_name(VDEV_PROP_NOALLOC), - sizeof (uint64_t), 1, &noalloc); + err = zap_lookup(mos, objid, vdev_prop_to_name(VDEV_PROP_ALLOCATING), + sizeof (uint64_t), 1, &allocating); mutex_exit(&spa->spa_props_lock); @@ -1665,7 +1685,7 @@ vdev_prop_noalloc(vdev_t *vd) return (B_FALSE); } - return (noalloc > 0); + return (allocating == 0); } /* ARGSUSED */ @@ -2029,6 +2049,11 @@ spa_vdev_remove_top_check(vdev_t *vd) if (!spa_feature_is_enabled(spa, SPA_FEATURE_DEVICE_REMOVAL)) return (SET_ERROR(ENOTSUP)); + /* + * This device is already being removed + */ + if (vd->vdev_removing) + return (SET_ERROR(EALREADY)); metaslab_class_t *mc = vd->vdev_mg->mg_class; metaslab_class_t *normal = spa_normal_class(spa); @@ -2051,13 +2076,15 @@ spa_vdev_remove_top_check(vdev_t *vd) /* available space in the pool's normal class */ uint64_t available = dsl_dir_space_available( spa->spa_dsl_pool->dp_root_dir, NULL, 0, B_TRUE); - if (available < - vd->vdev_stat.vs_dspace + spa_get_slop_space(spa)) { + /* non-allocating vdevs have already been taken out of space */ + if (!vd->vdev_noalloc) + available -= vd->vdev_stat.vs_dspace; + if (available < spa_get_slop_space(spa)) { /* - * This is a normal device. There has to be enough free - * space to remove the device and leave double the - * "slop" space (i.e. we must leave at least 3% of the - * pool free, in addition to the normal slop space). + * There has to be enough free space to remove the + * device and leave double the "slop" space (i.e. + * we must leave at least 3% of the pool free, in + * addition to the normal slop space). */ return (SET_ERROR(ENOSPC)); } @@ -2162,7 +2189,7 @@ spa_vdev_alloc(spa_t *spa, uint64_t guid) error = SET_ERROR(ENOENT); else if (vd->vdev_mg == NULL) error = SET_ERROR(ENOTSUP); - else + else if (!vd->vdev_removing) vdev_activate(vd); if (error == 0) { @@ -2181,6 +2208,8 @@ vdev_passivate(vdev_t *vd, uint64_t *txg) spa_t *spa = vd->vdev_spa; int error; + ASSERT(!vd->vdev_noalloc); + vdev_t *rvd = spa->spa_root_vdev; metaslab_group_t *mg = vd->vdev_mg; metaslab_class_t *normal = spa_normal_class(spa); @@ -2240,6 +2269,8 @@ vdev_passivate(vdev_t *vd, uint64_t *txg) return (error); } + spa->spa_nonallocating_dspace += spa_deflate(spa) ? + vd->vdev_stat.vs_dspace : vd->vdev_stat.vs_space; vd->vdev_noalloc = B_TRUE; return (0); @@ -2338,7 +2369,7 @@ spa_vdev_noalloc(spa_t *spa, uint64_t guid) { vdev_t *vd; uint64_t txg; - int error; + int error = 0; ASSERT(!MUTEX_HELD(&spa_namespace_lock)); ASSERT(spa_writeable(spa)); @@ -2353,7 +2384,7 @@ spa_vdev_noalloc(spa_t *spa, uint64_t guid) error = SET_ERROR(ENOENT); else if (vd->vdev_mg == NULL) error = SET_ERROR(ENOTSUP); - else + else if (!vd->vdev_noalloc) error = vdev_passivate(vd, &txg); if (error == 0) { diff --git a/module/zfs/zfs_ioctl.c b/module/zfs/zfs_ioctl.c index e7eddede1530..eb81b7e6be93 100644 --- a/module/zfs/zfs_ioctl.c +++ b/module/zfs/zfs_ioctl.c @@ -2963,8 +2963,8 @@ zfs_ioc_pool_get_props(zfs_cmd_t *zc) * outnvl: propname -> error code (int32) */ static const zfs_ioc_key_t zfs_keys_vdev_set_props[] = { - {ZPOOL_VDEV_SET_PROPS_VDEV, DATA_TYPE_UINT64, 0}, - {ZPOOL_VDEV_SET_PROPS_PROPS, DATA_TYPE_NVLIST, 0} + {ZPOOL_VDEV_PROPS_SET_VDEV, DATA_TYPE_UINT64, 0}, + {ZPOOL_VDEV_PROPS_SET_PROPS, DATA_TYPE_NVLIST, 0} }; static int @@ -2976,7 +2976,7 @@ zfs_ioc_vdev_set_props(const char *poolname, nvlist_t *innvl, nvlist_t *outnvl) uint64_t vdev_guid; /* Early validation */ - if (nvlist_lookup_uint64(innvl, ZPOOL_VDEV_SET_PROPS_VDEV, + if (nvlist_lookup_uint64(innvl, ZPOOL_VDEV_PROPS_SET_VDEV, &vdev_guid) != 0) return (SET_ERROR(EINVAL)); @@ -3006,8 +3006,8 @@ zfs_ioc_vdev_set_props(const char *poolname, nvlist_t *innvl, nvlist_t *outnvl) * outnvl: propname -> value */ static const zfs_ioc_key_t zfs_keys_vdev_get_props[] = { - {ZPOOL_VDEV_GET_PROPS_VDEV, DATA_TYPE_UINT64, 0}, - {ZPOOL_VDEV_GET_PROPS_PROPS, DATA_TYPE_NVLIST, ZK_OPTIONAL} + {ZPOOL_VDEV_PROPS_GET_VDEV, DATA_TYPE_UINT64, 0}, + {ZPOOL_VDEV_PROPS_GET_PROPS, DATA_TYPE_NVLIST, ZK_OPTIONAL} }; static int @@ -3019,7 +3019,7 @@ zfs_ioc_vdev_get_props(const char *poolname, nvlist_t *innvl, nvlist_t *outnvl) uint64_t vdev_guid; /* Early validation */ - if (nvlist_lookup_uint64(innvl, ZPOOL_VDEV_GET_PROPS_VDEV, + if (nvlist_lookup_uint64(innvl, ZPOOL_VDEV_PROPS_GET_VDEV, &vdev_guid) != 0) return (SET_ERROR(EINVAL));