From 9b68bedc42db1e1bf8029f6998b5ad79df889967 Mon Sep 17 00:00:00 2001 From: Brian Behlendorf Date: Thu, 1 Oct 2015 16:32:49 -0700 Subject: [PATCH] Add 'zhack feature disable' command --- cmd/zhack/zhack.c | 101 ++++++++++++++++++++++++++++++++++++++++- include/sys/zfeature.h | 2 + lib/libzpool/kernel.c | 2 - module/zfs/zfeature.c | 37 +++++++++++++++ 4 files changed, 138 insertions(+), 4 deletions(-) diff --git a/cmd/zhack/zhack.c b/cmd/zhack/zhack.c index 8406b70bcd4a..6c57be444036 100644 --- a/cmd/zhack/zhack.c +++ b/cmd/zhack/zhack.c @@ -58,12 +58,13 @@ libzfs_handle_t *g_zfs; static importargs_t g_importargs; static char *g_pool; static boolean_t g_readonly; +static boolean_t g_force = B_FALSE; static void usage(void) { (void) fprintf(stderr, - "Usage: %s [-c cachefile] [-d dir] ...\n" + "Usage: %s [-c cachefile] [-d dir] [-f] ...\n" "where is one of the following:\n" "\n", cmdname); @@ -73,6 +74,9 @@ usage(void) " feature enable [-d desc] \n" " add a new enabled feature to the pool\n" " -d sets the feature's description\n" + " feature disable \n" + " remove an enabled, but not active, feature\n" + " from the pool.\n" " feature ref [-md] \n" " change the refcount on the given feature\n" " -d decrease instead of increase the refcount\n" @@ -369,6 +373,94 @@ zhack_do_feature_enable(int argc, char **argv) free(desc); } +static void +zhack_feature_disable_sync(void *arg, dmu_tx_t *tx) +{ + spa_t *spa = dmu_tx_pool(tx)->dp_spa; + zfeature_info_t *feature = arg; + + feature_disable_sync(spa, feature, tx); + + spa_history_log_internal(spa, "zhack disable feature", tx, + "name=%s can_readonly=%u", + feature->fi_guid, feature->fi_can_readonly); +} + +static void +zhack_do_feature_disable(int argc, char **argv) +{ + char c; + char *target; + uint64_t count; + spa_t *spa; + objset_t *mos; + zfeature_info_t feature; + spa_feature_t nodeps[] = { SPA_FEATURE_NONE }; + + /* + * fi_desc does not matter here because it was written to disk + * when the feature was enabled, but we need to properly set the + * feature for read or write based on the information we read off + * disk later. + */ + feature.fi_uname = "zhack"; + feature.fi_mos = B_TRUE; + feature.fi_desc = NULL; + feature.fi_depends = nodeps; + feature.fi_feature = SPA_FEATURE_NONE; + + optind = 1; + while ((c = getopt(argc, argv, "")) != -1) { + switch (c) { + default: + usage(); + break; + } + } + argc -= optind; + argv += optind; + + if (argc < 2) { + (void) fprintf(stderr, "error: missing feature or pool name\n"); + usage(); + } + target = argv[0]; + feature.fi_guid = argv[1]; + + if (!zfeature_is_valid_guid(feature.fi_guid)) + fatal(NULL, FTAG, "invalid feature guid: %s", feature.fi_guid); + + zhack_spa_open(target, B_FALSE, FTAG, &spa); + mos = spa->spa_meta_objset; + + if (zfeature_is_supported(feature.fi_guid) && (g_force == B_FALSE)) { + fatal(spa, FTAG, + "'%s' is a real feature, will not disable\n" + "provide the -f option to force override", feature.fi_guid); + } + + if (0 == zap_contains(mos, spa->spa_feat_for_read_obj, + feature.fi_guid)) { + feature.fi_can_readonly = B_FALSE; + } else if (0 == zap_contains(mos, spa->spa_feat_for_write_obj, + feature.fi_guid)) { + feature.fi_can_readonly = B_TRUE; + } else { + fatal(spa, FTAG, "feature is not enabled: %s", feature.fi_guid); + } + + if (feature_get_refcount_from_disk(spa, &feature, &count) == 0 && + count > 0) { + fatal(spa, FTAG, "feature '%s' is active, can not disable", + feature.fi_guid); + } + + VERIFY0(dsl_sync_task(spa_name(spa), NULL, + zhack_feature_disable_sync, &feature, 5, ZFS_SPACE_CHECK_NORMAL)); + + spa_close(spa, FTAG); +} + static void feature_incr_sync(void *arg, dmu_tx_t *tx) { @@ -497,6 +589,8 @@ zhack_do_feature(int argc, char **argv) zhack_do_feature_stat(argc, argv); } else if (strcmp(subcommand, "enable") == 0) { zhack_do_feature_enable(argc, argv); + } else if (strcmp(subcommand, "disable") == 0) { + zhack_do_feature_disable(argc, argv); } else if (strcmp(subcommand, "ref") == 0) { zhack_do_feature_ref(argc, argv); } else { @@ -525,7 +619,7 @@ main(int argc, char **argv) dprintf_setup(&argc, argv); zfs_prop_init(); - while ((c = getopt(argc, argv, "c:d:")) != -1) { + while ((c = getopt(argc, argv, "c:d:f")) != -1) { switch (c) { case 'c': g_importargs.cachefile = optarg; @@ -534,6 +628,9 @@ main(int argc, char **argv) assert(g_importargs.paths < MAX_NUM_PATHS); g_importargs.path[g_importargs.paths++] = optarg; break; + case 'f': + g_force = B_TRUE; + break; default: usage(); break; diff --git a/include/sys/zfeature.h b/include/sys/zfeature.h index 5abde149a615..5ea77f847e10 100644 --- a/include/sys/zfeature.h +++ b/include/sys/zfeature.h @@ -63,6 +63,8 @@ extern int feature_get_refcount_from_disk(spa_t *spa, zfeature_info_t *feature, uint64_t *res); extern void feature_enable_sync(struct spa *, zfeature_info_t *, struct dmu_tx *); +extern void feature_disable_sync(struct spa *, zfeature_info_t *, + struct dmu_tx *); extern void feature_sync(struct spa *, zfeature_info_t *, uint64_t, struct dmu_tx *); diff --git a/lib/libzpool/kernel.c b/lib/libzpool/kernel.c index a451026999c5..c875bf0a133a 100644 --- a/lib/libzpool/kernel.c +++ b/lib/libzpool/kernel.c @@ -643,8 +643,6 @@ vn_open(char *path, int x1, int flags, int mode, vnode_t **vpp, int x2, int x3) #ifdef __linux__ flags |= O_DIRECT; #endif - /* We shouldn't be writing to block devices in userspace */ - VERIFY(!(flags & FWRITE)); } if (flags & FCREAT) diff --git a/module/zfs/zfeature.c b/module/zfs/zfeature.c index 352376f22b9e..f6382c3f9114 100644 --- a/module/zfs/zfeature.c +++ b/module/zfs/zfeature.c @@ -379,6 +379,43 @@ feature_enable_sync(spa_t *spa, zfeature_info_t *feature, dmu_tx_t *tx) } } +/* + * This function is non-static for zhack; it should otherwise not be used + * outside this file. + */ +void +feature_disable_sync(spa_t *spa, zfeature_info_t *feature, dmu_tx_t *tx) +{ + uint64_t descobj = spa->spa_feat_desc_obj; + uint64_t zapobj = feature->fi_can_readonly ? + spa->spa_feat_for_write_obj : spa->spa_feat_for_read_obj; + + ASSERT(0 != zapobj); + ASSERT(zfeature_is_valid_guid(feature->fi_guid)); + ASSERT3U(spa_version(spa), >=, SPA_VERSION_FEATURES); + + if (zap_contains(spa->spa_meta_objset, descobj, feature->fi_guid) == 0) + VERIFY0(zap_remove(spa->spa_meta_objset, descobj, + feature->fi_guid, tx)); + + if (zap_contains(spa->spa_meta_objset, zapobj, feature->fi_guid) == 0) + VERIFY0(zap_remove(spa->spa_meta_objset, zapobj, + feature->fi_guid, tx)); + + spa_deactivate_mos_feature(spa, feature->fi_guid); + + if (spa_feature_is_enabled(spa, SPA_FEATURE_ENABLED_TXG)) { + uint64_t txgobj = spa->spa_feat_enabled_txg_obj; + + if (txgobj && (zap_contains(spa->spa_meta_objset, + txgobj, feature->fi_guid) == 0)) { + spa_feature_decr(spa, SPA_FEATURE_ENABLED_TXG, tx); + VERIFY0(zap_remove(spa->spa_meta_objset, txgobj, + feature->fi_guid, tx)); + } + } +} + static void feature_do_action(spa_t *spa, spa_feature_t fid, feature_action_t action, dmu_tx_t *tx)