aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--cmd/zhack/zhack.c101
-rw-r--r--include/sys/zfeature.h2
-rw-r--r--lib/libzpool/kernel.c2
-rw-r--r--module/zfs/zfeature.c37
4 files changed, 138 insertions, 4 deletions
diff --git a/cmd/zhack/zhack.c b/cmd/zhack/zhack.c
index eedd17c30..10dd8642e 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] <subcommand> <args> ...\n"
+ "Usage: %s [-c cachefile] [-d dir] [-f] <subcommand> <args> ...\n"
"where <subcommand> <args> is one of the following:\n"
"\n", cmdname);
@@ -73,6 +74,9 @@ usage(void)
" feature enable [-d desc] <pool> <feature>\n"
" add a new enabled feature to the pool\n"
" -d <desc> sets the feature's description\n"
+ " feature disable <pool> <feature>\n"
+ " remove an enabled, but not active, feature\n"
+ " from the pool.\n"
" feature ref [-md] <pool> <feature>\n"
" change the refcount on the given feature\n"
" -d decrease instead of increase the refcount\n"
@@ -368,6 +372,94 @@ zhack_do_feature_enable(int argc, char **argv)
}
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)
{
spa_t *spa = dmu_tx_pool(tx)->dp_spa;
@@ -495,6 +587,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 {
@@ -523,7 +617,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;
@@ -532,6 +626,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 5abde149a..5ea77f847 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 89e474c65..f14552374 100644
--- a/lib/libzpool/kernel.c
+++ b/lib/libzpool/kernel.c
@@ -647,8 +647,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 bda954829..c83b14568 100644
--- a/module/zfs/zfeature.c
+++ b/module/zfs/zfeature.c
@@ -380,6 +380,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)