summaryrefslogtreecommitdiffstats
path: root/module/zfs
diff options
context:
space:
mode:
Diffstat (limited to 'module/zfs')
-rw-r--r--module/zfs/bpobj.c16
-rw-r--r--module/zfs/dmu_object.c55
-rw-r--r--module/zfs/dmu_traverse.c2
-rw-r--r--module/zfs/dnode_sync.c15
-rw-r--r--module/zfs/dsl_dataset.c10
-rw-r--r--module/zfs/dsl_destroy.c23
-rw-r--r--module/zfs/dsl_dir.c10
-rw-r--r--module/zfs/dsl_pool.c6
-rw-r--r--module/zfs/dsl_scan.c13
-rw-r--r--module/zfs/spa.c12
-rw-r--r--module/zfs/spa_misc.c10
-rw-r--r--module/zfs/space_map.c20
-rw-r--r--module/zfs/zap_micro.c6
-rw-r--r--module/zfs/zfeature.c214
-rw-r--r--module/zfs/zfeature_common.c37
-rw-r--r--module/zfs/zfs_ioctl.c27
16 files changed, 254 insertions, 222 deletions
diff --git a/module/zfs/bpobj.c b/module/zfs/bpobj.c
index 5787a6fb4..eb3233b10 100644
--- a/module/zfs/bpobj.c
+++ b/module/zfs/bpobj.c
@@ -36,13 +36,11 @@
uint64_t
bpobj_alloc_empty(objset_t *os, int blocksize, dmu_tx_t *tx)
{
- zfeature_info_t *empty_bpobj_feat =
- &spa_feature_table[SPA_FEATURE_EMPTY_BPOBJ];
spa_t *spa = dmu_objset_spa(os);
dsl_pool_t *dp = dmu_objset_pool(os);
- if (spa_feature_is_enabled(spa, empty_bpobj_feat)) {
- if (!spa_feature_is_active(spa, empty_bpobj_feat)) {
+ if (spa_feature_is_enabled(spa, SPA_FEATURE_EMPTY_BPOBJ)) {
+ if (!spa_feature_is_active(spa, SPA_FEATURE_EMPTY_BPOBJ)) {
ASSERT0(dp->dp_empty_bpobj);
dp->dp_empty_bpobj =
bpobj_alloc(os, SPA_MAXBLOCKSIZE, tx);
@@ -51,7 +49,7 @@ bpobj_alloc_empty(objset_t *os, int blocksize, dmu_tx_t *tx)
DMU_POOL_EMPTY_BPOBJ, sizeof (uint64_t), 1,
&dp->dp_empty_bpobj, tx) == 0);
}
- spa_feature_incr(spa, empty_bpobj_feat, tx);
+ spa_feature_incr(spa, SPA_FEATURE_EMPTY_BPOBJ, tx);
ASSERT(dp->dp_empty_bpobj != 0);
return (dp->dp_empty_bpobj);
} else {
@@ -62,12 +60,11 @@ bpobj_alloc_empty(objset_t *os, int blocksize, dmu_tx_t *tx)
void
bpobj_decr_empty(objset_t *os, dmu_tx_t *tx)
{
- zfeature_info_t *empty_bpobj_feat =
- &spa_feature_table[SPA_FEATURE_EMPTY_BPOBJ];
dsl_pool_t *dp = dmu_objset_pool(os);
- spa_feature_decr(dmu_objset_spa(os), empty_bpobj_feat, tx);
- if (!spa_feature_is_active(dmu_objset_spa(os), empty_bpobj_feat)) {
+ spa_feature_decr(dmu_objset_spa(os), SPA_FEATURE_EMPTY_BPOBJ, tx);
+ if (!spa_feature_is_active(dmu_objset_spa(os),
+ SPA_FEATURE_EMPTY_BPOBJ)) {
VERIFY3U(0, ==, zap_remove(dp->dp_meta_objset,
DMU_POOL_DIRECTORY_OBJECT,
DMU_POOL_EMPTY_BPOBJ, tx));
@@ -265,6 +262,7 @@ bpobj_iterate_impl(bpobj_t *bpo, bpobj_itor_t func, void *arg, dmu_tx_t *tx,
mutex_exit(&bpo->bpo_lock);
return (err);
}
+ ASSERT3U(doi.doi_type, ==, DMU_OT_BPOBJ_SUBOBJ);
epb = doi.doi_data_block_size / sizeof (uint64_t);
for (i = bpo->bpo_phys->bpo_num_subobjs - 1; i >= 0; i--) {
diff --git a/module/zfs/dmu_object.c b/module/zfs/dmu_object.c
index b6b82a24b..375439ed7 100644
--- a/module/zfs/dmu_object.c
+++ b/module/zfs/dmu_object.c
@@ -27,6 +27,8 @@
#include <sys/dmu_objset.h>
#include <sys/dmu_tx.h>
#include <sys/dnode.h>
+#include <sys/zap.h>
+#include <sys/zfeature.h>
uint64_t
dmu_object_alloc(objset_t *os, dmu_object_type_t ot, int blocksize,
@@ -196,10 +198,63 @@ dmu_object_next(objset_t *os, uint64_t *objectp, boolean_t hole, uint64_t txg)
return (error);
}
+/*
+ * Turn this object from old_type into DMU_OTN_ZAP_METADATA, and bump the
+ * refcount on SPA_FEATURE_EXTENSIBLE_DATASET.
+ *
+ * Only for use from syncing context, on MOS objects.
+ */
+void
+dmu_object_zapify(objset_t *mos, uint64_t object, dmu_object_type_t old_type,
+ dmu_tx_t *tx)
+{
+ dnode_t *dn;
+
+ ASSERT(dmu_tx_is_syncing(tx));
+
+ VERIFY0(dnode_hold(mos, object, FTAG, &dn));
+ if (dn->dn_type == DMU_OTN_ZAP_METADATA) {
+ dnode_rele(dn, FTAG);
+ return;
+ }
+ ASSERT3U(dn->dn_type, ==, old_type);
+ ASSERT0(dn->dn_maxblkid);
+ dn->dn_next_type[tx->tx_txg & TXG_MASK] = dn->dn_type =
+ DMU_OTN_ZAP_METADATA;
+ dnode_setdirty(dn, tx);
+ dnode_rele(dn, FTAG);
+
+ mzap_create_impl(mos, object, 0, 0, tx);
+
+ spa_feature_incr(dmu_objset_spa(mos),
+ SPA_FEATURE_EXTENSIBLE_DATASET, tx);
+}
+
+void
+dmu_object_free_zapified(objset_t *mos, uint64_t object, dmu_tx_t *tx)
+{
+ dnode_t *dn;
+ dmu_object_type_t t;
+
+ ASSERT(dmu_tx_is_syncing(tx));
+
+ VERIFY0(dnode_hold(mos, object, FTAG, &dn));
+ t = dn->dn_type;
+ dnode_rele(dn, FTAG);
+
+ if (t == DMU_OTN_ZAP_METADATA) {
+ spa_feature_decr(dmu_objset_spa(mos),
+ SPA_FEATURE_EXTENSIBLE_DATASET, tx);
+ }
+ VERIFY0(dmu_object_free(mos, object, tx));
+}
+
#if defined(_KERNEL) && defined(HAVE_SPL)
EXPORT_SYMBOL(dmu_object_alloc);
EXPORT_SYMBOL(dmu_object_claim);
EXPORT_SYMBOL(dmu_object_reclaim);
EXPORT_SYMBOL(dmu_object_free);
EXPORT_SYMBOL(dmu_object_next);
+EXPORT_SYMBOL(dmu_object_zapify);
+EXPORT_SYMBOL(dmu_object_free_zapified);
#endif
diff --git a/module/zfs/dmu_traverse.c b/module/zfs/dmu_traverse.c
index bd291c6a8..27f9eb332 100644
--- a/module/zfs/dmu_traverse.c
+++ b/module/zfs/dmu_traverse.c
@@ -619,7 +619,7 @@ traverse_pool(spa_t *spa, uint64_t txg_start, int flags,
continue;
}
- if (doi.doi_type == DMU_OT_DSL_DATASET) {
+ if (doi.doi_bonus_type == DMU_OT_DSL_DATASET) {
dsl_dataset_t *ds;
uint64_t txg = txg_start;
diff --git a/module/zfs/dnode_sync.c b/module/zfs/dnode_sync.c
index 0ff25d2af..78bccdfc6 100644
--- a/module/zfs/dnode_sync.c
+++ b/module/zfs/dnode_sync.c
@@ -21,7 +21,7 @@
/*
* Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
- * Copyright (c) 2012 by Delphix. All rights reserved.
+ * Copyright (c) 2013 by Delphix. All rights reserved.
*/
#include <sys/zfs_context.h>
@@ -578,7 +578,12 @@ dnode_sync(dnode_t *dn, dmu_tx_t *tx)
BP_GET_LSIZE(&dnp->dn_blkptr[0]) ==
dnp->dn_datablkszsec << SPA_MINBLOCKSHIFT);
- if (dn->dn_next_blksz[txgoff]) {
+ if (dn->dn_next_type[txgoff] != 0) {
+ dnp->dn_type = dn->dn_type;
+ dn->dn_next_type[txgoff] = 0;
+ }
+
+ if (dn->dn_next_blksz[txgoff] != 0) {
ASSERT(P2PHASE(dn->dn_next_blksz[txgoff],
SPA_MINBLOCKSIZE) == 0);
ASSERT(BP_IS_HOLE(&dnp->dn_blkptr[0]) ||
@@ -591,7 +596,7 @@ dnode_sync(dnode_t *dn, dmu_tx_t *tx)
dn->dn_next_blksz[txgoff] = 0;
}
- if (dn->dn_next_bonuslen[txgoff]) {
+ if (dn->dn_next_bonuslen[txgoff] != 0) {
if (dn->dn_next_bonuslen[txgoff] == DN_ZERO_BONUSLEN)
dnp->dn_bonuslen = 0;
else
@@ -600,7 +605,7 @@ dnode_sync(dnode_t *dn, dmu_tx_t *tx)
dn->dn_next_bonuslen[txgoff] = 0;
}
- if (dn->dn_next_bonustype[txgoff]) {
+ if (dn->dn_next_bonustype[txgoff] != 0) {
ASSERT(DMU_OT_IS_VALID(dn->dn_next_bonustype[txgoff]));
dnp->dn_bonustype = dn->dn_next_bonustype[txgoff];
dn->dn_next_bonustype[txgoff] = 0;
@@ -618,7 +623,7 @@ dnode_sync(dnode_t *dn, dmu_tx_t *tx)
dn->dn_rm_spillblk[txgoff] = 0;
}
- if (dn->dn_next_indblkshift[txgoff]) {
+ if (dn->dn_next_indblkshift[txgoff] != 0) {
ASSERT(dnp->dn_nlevels == 1);
dnp->dn_indblkshift = dn->dn_next_indblkshift[txgoff];
dn->dn_next_indblkshift[txgoff] = 0;
diff --git a/module/zfs/dsl_dataset.c b/module/zfs/dsl_dataset.c
index 5eb8c0199..cbdf393c0 100644
--- a/module/zfs/dsl_dataset.c
+++ b/module/zfs/dsl_dataset.c
@@ -357,7 +357,7 @@ dsl_dataset_hold_obj(dsl_pool_t *dp, uint64_t dsobj, void *tag,
/* Make sure dsobj has the correct object type. */
dmu_object_info_from_db(dbuf, &doi);
- if (doi.doi_type != DMU_OT_DSL_DATASET) {
+ if (doi.doi_bonus_type != DMU_OT_DSL_DATASET) {
dmu_buf_rele(dbuf, tag);
return (SET_ERROR(EINVAL));
}
@@ -3005,6 +3005,14 @@ dsl_dataset_is_before(dsl_dataset_t *later, dsl_dataset_t *earlier)
return (ret);
}
+
+void
+dsl_dataset_zapify(dsl_dataset_t *ds, dmu_tx_t *tx)
+{
+ objset_t *mos = ds->ds_dir->dd_pool->dp_meta_objset;
+ dmu_object_zapify(mos, ds->ds_object, DMU_OT_DSL_DATASET, tx);
+}
+
#if defined(_KERNEL) && defined(HAVE_SPL)
EXPORT_SYMBOL(dsl_dataset_hold);
EXPORT_SYMBOL(dsl_dataset_hold_obj);
diff --git a/module/zfs/dsl_destroy.c b/module/zfs/dsl_destroy.c
index 351165dbf..11038b1f9 100644
--- a/module/zfs/dsl_destroy.c
+++ b/module/zfs/dsl_destroy.c
@@ -38,6 +38,7 @@
#include <sys/zfeature.h>
#include <sys/zfs_ioctl.h>
#include <sys/dsl_deleg.h>
+#include <sys/dmu_impl.h>
typedef struct dmu_snapshots_destroy_arg {
nvlist_t *dsda_snaps;
@@ -452,7 +453,7 @@ dsl_destroy_snapshot_sync_impl(dsl_dataset_t *ds, boolean_t defer, dmu_tx_t *tx)
VERIFY0(zap_destroy(mos, ds->ds_phys->ds_userrefs_obj, tx));
dsl_dir_rele(ds->ds_dir, ds);
ds->ds_dir = NULL;
- VERIFY0(dmu_object_free(mos, obj, tx));
+ dmu_object_free_zapified(mos, obj, tx);
}
static void
@@ -679,7 +680,7 @@ dsl_dir_destroy_sync(uint64_t ddobj, dmu_tx_t *tx)
dd->dd_parent->dd_phys->dd_child_dir_zapobj, dd->dd_myname, tx));
dsl_dir_rele(dd, FTAG);
- VERIFY0(dmu_object_free(mos, ddobj, tx));
+ dmu_object_free_zapified(mos, ddobj, tx);
}
void
@@ -689,7 +690,6 @@ dsl_destroy_head_sync_impl(dsl_dataset_t *ds, dmu_tx_t *tx)
objset_t *mos = dp->dp_meta_objset;
uint64_t obj, ddobj, prevobj = 0;
boolean_t rmorigin;
- zfeature_info_t *async_destroy;
objset_t *os;
ASSERT3U(ds->ds_phys->ds_num_children, <=, 1);
@@ -734,9 +734,6 @@ dsl_destroy_head_sync_impl(dsl_dataset_t *ds, dmu_tx_t *tx)
ds->ds_prev->ds_phys->ds_num_children--;
}
- async_destroy =
- &spa_feature_table[SPA_FEATURE_ASYNC_DESTROY];
-
/*
* Destroy the deadlist. Unless it's a clone, the
* deadlist should be empty. (If it's a clone, it's
@@ -749,7 +746,7 @@ dsl_destroy_head_sync_impl(dsl_dataset_t *ds, dmu_tx_t *tx)
VERIFY0(dmu_objset_from_ds(ds, &os));
- if (!spa_feature_is_enabled(dp->dp_spa, async_destroy)) {
+ if (!spa_feature_is_enabled(dp->dp_spa, SPA_FEATURE_ASYNC_DESTROY)) {
old_synchronous_dataset_destroy(ds, tx);
} else {
/*
@@ -760,10 +757,11 @@ dsl_destroy_head_sync_impl(dsl_dataset_t *ds, dmu_tx_t *tx)
zil_destroy_sync(dmu_objset_zil(os), tx);
- if (!spa_feature_is_active(dp->dp_spa, async_destroy)) {
+ if (!spa_feature_is_active(dp->dp_spa,
+ SPA_FEATURE_ASYNC_DESTROY)) {
dsl_scan_t *scn = dp->dp_scan;
-
- spa_feature_incr(dp->dp_spa, async_destroy, tx);
+ spa_feature_incr(dp->dp_spa, SPA_FEATURE_ASYNC_DESTROY,
+ tx);
dp->dp_bptree_obj = bptree_alloc(mos, tx);
VERIFY0(zap_add(mos,
DMU_POOL_DIRECTORY_OBJECT,
@@ -823,7 +821,7 @@ dsl_destroy_head_sync_impl(dsl_dataset_t *ds, dmu_tx_t *tx)
ASSERT0(ds->ds_phys->ds_userrefs_obj);
dsl_dir_rele(ds->ds_dir, ds);
ds->ds_dir = NULL;
- VERIFY0(dmu_object_free(mos, obj, tx));
+ dmu_object_free_zapified(mos, obj, tx);
dsl_dir_destroy_sync(ddobj, tx);
@@ -879,8 +877,7 @@ dsl_destroy_head(const char *name)
error = spa_open(name, &spa, FTAG);
if (error != 0)
return (error);
- isenabled = spa_feature_is_enabled(spa,
- &spa_feature_table[SPA_FEATURE_ASYNC_DESTROY]);
+ isenabled = spa_feature_is_enabled(spa, SPA_FEATURE_ASYNC_DESTROY);
spa_close(spa, FTAG);
ddha.ddha_name = name;
diff --git a/module/zfs/dsl_dir.c b/module/zfs/dsl_dir.c
index fb7cd2cd6..7eafdfd2e 100644
--- a/module/zfs/dsl_dir.c
+++ b/module/zfs/dsl_dir.c
@@ -32,6 +32,7 @@
#include <sys/dsl_prop.h>
#include <sys/dsl_synctask.h>
#include <sys/dsl_deleg.h>
+#include <sys/dmu_impl.h>
#include <sys/spa.h>
#include <sys/metaslab.h>
#include <sys/zap.h>
@@ -89,7 +90,7 @@ dsl_dir_hold_obj(dsl_pool_t *dp, uint64_t ddobj,
{
dmu_object_info_t doi;
dmu_object_info_from_db(dbuf, &doi);
- ASSERT3U(doi.doi_type, ==, DMU_OT_DSL_DIR);
+ ASSERT3U(doi.doi_bonus_type, ==, DMU_OT_DSL_DIR);
ASSERT3U(doi.doi_bonus_size, >=, sizeof (dsl_dir_phys_t));
}
#endif
@@ -1372,6 +1373,13 @@ dsl_dir_snap_cmtime_update(dsl_dir_t *dd)
mutex_exit(&dd->dd_lock);
}
+void
+dsl_dir_zapify(dsl_dir_t *dd, dmu_tx_t *tx)
+{
+ objset_t *mos = dd->dd_pool->dp_meta_objset;
+ dmu_object_zapify(mos, dd->dd_object, DMU_OT_DSL_DIR, tx);
+}
+
#if defined(_KERNEL) && defined(HAVE_SPL)
EXPORT_SYMBOL(dsl_dir_set_quota);
EXPORT_SYMBOL(dsl_dir_set_reservation);
diff --git a/module/zfs/dsl_pool.c b/module/zfs/dsl_pool.c
index 0ef50717c..91476cc0b 100644
--- a/module/zfs/dsl_pool.c
+++ b/module/zfs/dsl_pool.c
@@ -245,8 +245,7 @@ dsl_pool_open(dsl_pool_t *dp)
dp->dp_meta_objset, obj));
}
- if (spa_feature_is_active(dp->dp_spa,
- &spa_feature_table[SPA_FEATURE_ASYNC_DESTROY])) {
+ if (spa_feature_is_active(dp->dp_spa, SPA_FEATURE_ASYNC_DESTROY)) {
err = zap_lookup(dp->dp_meta_objset, DMU_POOL_DIRECTORY_OBJECT,
DMU_POOL_BPTREE_OBJ, sizeof (uint64_t), 1,
&dp->dp_bptree_obj);
@@ -254,8 +253,7 @@ dsl_pool_open(dsl_pool_t *dp)
goto out;
}
- if (spa_feature_is_active(dp->dp_spa,
- &spa_feature_table[SPA_FEATURE_EMPTY_BPOBJ])) {
+ if (spa_feature_is_active(dp->dp_spa, SPA_FEATURE_EMPTY_BPOBJ)) {
err = zap_lookup(dp->dp_meta_objset, DMU_POOL_DIRECTORY_OBJECT,
DMU_POOL_EMPTY_BPOBJ, sizeof (uint64_t), 1,
&dp->dp_empty_bpobj);
diff --git a/module/zfs/dsl_scan.c b/module/zfs/dsl_scan.c
index 7807f8485..22b68d685 100644
--- a/module/zfs/dsl_scan.c
+++ b/module/zfs/dsl_scan.c
@@ -98,7 +98,7 @@ dsl_scan_init(dsl_pool_t *dp, uint64_t txg)
*/
ASSERT(!scn->scn_async_destroying);
scn->scn_async_destroying = spa_feature_is_active(dp->dp_spa,
- &spa_feature_table[SPA_FEATURE_ASYNC_DESTROY]);
+ SPA_FEATURE_ASYNC_DESTROY);
err = zap_lookup(dp->dp_meta_objset, DMU_POOL_DIRECTORY_OBJECT,
"scrub_func", sizeof (uint64_t), 1, &f);
@@ -1415,7 +1415,6 @@ dsl_scan_active(dsl_scan_t *scn)
return (B_FALSE);
if (spa_shutting_down(spa))
return (B_FALSE);
-
if (scn->scn_phys.scn_state == DSS_SCANNING ||
scn->scn_async_destroying)
return (B_TRUE);
@@ -1474,7 +1473,7 @@ dsl_scan_sync(dsl_pool_t *dp, dmu_tx_t *tx)
VERIFY3U(0, ==, zio_wait(scn->scn_zio_root));
if (err == 0 && spa_feature_is_active(spa,
- &spa_feature_table[SPA_FEATURE_ASYNC_DESTROY])) {
+ SPA_FEATURE_ASYNC_DESTROY)) {
ASSERT(scn->scn_async_destroying);
scn->scn_is_bptree = B_TRUE;
scn->scn_zio_root = zio_root(dp->dp_spa, NULL,
@@ -1485,11 +1484,11 @@ dsl_scan_sync(dsl_pool_t *dp, dmu_tx_t *tx)
VERIFY0(zio_wait(scn->scn_zio_root));
if (err == 0) {
- zfeature_info_t *feat = &spa_feature_table
- [SPA_FEATURE_ASYNC_DESTROY];
/* finished; deactivate async destroy feature */
- spa_feature_decr(spa, feat, tx);
- ASSERT(!spa_feature_is_active(spa, feat));
+ spa_feature_decr(spa, SPA_FEATURE_ASYNC_DESTROY,
+ tx);
+ ASSERT(!spa_feature_is_active(spa,
+ SPA_FEATURE_ASYNC_DESTROY));
VERIFY0(zap_remove(dp->dp_meta_objset,
DMU_POOL_DIRECTORY_OBJECT,
DMU_POOL_BPTREE_OBJ, tx));
diff --git a/module/zfs/spa.c b/module/zfs/spa.c
index 397e9e627..fabf16169 100644
--- a/module/zfs/spa.c
+++ b/module/zfs/spa.c
@@ -2347,14 +2347,12 @@ spa_load_impl(spa_t *spa, uint64_t pool_guid, nvlist_t *config,
enabled_feat = fnvlist_alloc();
unsup_feat = fnvlist_alloc();
- if (!feature_is_supported(spa->spa_meta_objset,
- spa->spa_feat_for_read_obj, spa->spa_feat_desc_obj,
+ if (!spa_features_check(spa, B_FALSE,
unsup_feat, enabled_feat))
missing_feat_read = B_TRUE;
if (spa_writeable(spa) || state == SPA_LOAD_TRYIMPORT) {
- if (!feature_is_supported(spa->spa_meta_objset,
- spa->spa_feat_for_write_obj, spa->spa_feat_desc_obj,
+ if (!spa_features_check(spa, B_TRUE,
unsup_feat, enabled_feat)) {
missing_feat_write = B_TRUE;
}
@@ -5957,7 +5955,7 @@ spa_sync_props(void *arg, dmu_tx_t *tx)
zpool_prop_t prop;
const char *propname;
zprop_type_t proptype;
- zfeature_info_t *feature;
+ spa_feature_t fid;
prop = zpool_name_to_prop(nvpair_name(elem));
switch ((int)prop) {
@@ -5968,9 +5966,9 @@ spa_sync_props(void *arg, dmu_tx_t *tx)
ASSERT(zpool_prop_feature(nvpair_name(elem)));
fname = strchr(nvpair_name(elem), '@') + 1;
- VERIFY0(zfeature_lookup_name(fname, &feature));
+ VERIFY0(zfeature_lookup_name(fname, &fid));
- spa_feature_enable(spa, feature, tx);
+ spa_feature_enable(spa, fid, tx);
spa_history_log_internal(spa, "set", tx,
"%s=enabled", nvpair_name(elem));
break;
diff --git a/module/zfs/spa_misc.c b/module/zfs/spa_misc.c
index ef59d2255..50c3b9afd 100644
--- a/module/zfs/spa_misc.c
+++ b/module/zfs/spa_misc.c
@@ -1096,15 +1096,17 @@ spa_vdev_state_exit(spa_t *spa, vdev_t *vd, int error)
void
spa_activate_mos_feature(spa_t *spa, const char *feature)
{
- (void) nvlist_add_boolean(spa->spa_label_features, feature);
- vdev_config_dirty(spa->spa_root_vdev);
+ if (!nvlist_exists(spa->spa_label_features, feature)) {
+ fnvlist_add_boolean(spa->spa_label_features, feature);
+ vdev_config_dirty(spa->spa_root_vdev);
+ }
}
void
spa_deactivate_mos_feature(spa_t *spa, const char *feature)
{
- (void) nvlist_remove_all(spa->spa_label_features, feature);
- vdev_config_dirty(spa->spa_root_vdev);
+ if (nvlist_remove_all(spa->spa_label_features, feature) == 0)
+ vdev_config_dirty(spa->spa_root_vdev);
}
/*
diff --git a/module/zfs/space_map.c b/module/zfs/space_map.c
index bbc926d4d..645a81387 100644
--- a/module/zfs/space_map.c
+++ b/module/zfs/space_map.c
@@ -482,8 +482,6 @@ space_map_truncate(space_map_t *sm, dmu_tx_t *tx)
{
objset_t *os = sm->sm_os;
spa_t *spa = dmu_objset_spa(os);
- zfeature_info_t *space_map_histogram =
- &spa_feature_table[SPA_FEATURE_SPACEMAP_HISTOGRAM];
dmu_object_info_t doi;
int bonuslen;
@@ -493,7 +491,7 @@ space_map_truncate(space_map_t *sm, dmu_tx_t *tx)
VERIFY0(dmu_free_range(os, space_map_object(sm), 0, -1ULL, tx));
dmu_object_info_from_db(sm->sm_dbuf, &doi);
- if (spa_feature_is_enabled(spa, space_map_histogram)) {
+ if (spa_feature_is_enabled(spa, SPA_FEATURE_SPACEMAP_HISTOGRAM)) {
bonuslen = sizeof (space_map_phys_t);
ASSERT3U(bonuslen, <=, dmu_bonus_max());
} else {
@@ -533,13 +531,11 @@ uint64_t
space_map_alloc(objset_t *os, dmu_tx_t *tx)
{
spa_t *spa = dmu_objset_spa(os);
- zfeature_info_t *space_map_histogram =
- &spa_feature_table[SPA_FEATURE_SPACEMAP_HISTOGRAM];
uint64_t object;
int bonuslen;
- if (spa_feature_is_enabled(spa, space_map_histogram)) {
- spa_feature_incr(spa, space_map_histogram, tx);
+ if (spa_feature_is_enabled(spa, SPA_FEATURE_SPACEMAP_HISTOGRAM)) {
+ spa_feature_incr(spa, SPA_FEATURE_SPACEMAP_HISTOGRAM, tx);
bonuslen = sizeof (space_map_phys_t);
ASSERT3U(bonuslen, <=, dmu_bonus_max());
} else {
@@ -557,20 +553,20 @@ void
space_map_free(space_map_t *sm, dmu_tx_t *tx)
{
spa_t *spa;
- zfeature_info_t *space_map_histogram =
- &spa_feature_table[SPA_FEATURE_SPACEMAP_HISTOGRAM];
if (sm == NULL)
return;
spa = dmu_objset_spa(sm->sm_os);
- if (spa_feature_is_enabled(spa, space_map_histogram)) {
+ if (spa_feature_is_enabled(spa, SPA_FEATURE_SPACEMAP_HISTOGRAM)) {
dmu_object_info_t doi;
dmu_object_info_from_db(sm->sm_dbuf, &doi);
if (doi.doi_bonus_size != SPACE_MAP_SIZE_V0) {
- VERIFY(spa_feature_is_active(spa, space_map_histogram));
- spa_feature_decr(spa, space_map_histogram, tx);
+ VERIFY(spa_feature_is_active(spa,
+ SPA_FEATURE_SPACEMAP_HISTOGRAM));
+ spa_feature_decr(spa,
+ SPA_FEATURE_SPACEMAP_HISTOGRAM, tx);
}
}
diff --git a/module/zfs/zap_micro.c b/module/zfs/zap_micro.c
index 555d52f4c..73d84b554 100644
--- a/module/zfs/zap_micro.c
+++ b/module/zfs/zap_micro.c
@@ -572,7 +572,7 @@ mzap_upgrade(zap_t **zapp, dmu_tx_t *tx, zap_flags_t flags)
return (err);
}
-static void
+void
mzap_create_impl(objset_t *os, uint64_t obj, int normflags, zap_flags_t flags,
dmu_tx_t *tx)
{
@@ -862,8 +862,8 @@ zap_lookup_uint64(objset_t *os, uint64_t zapobj, const uint64_t *key,
int
zap_contains(objset_t *os, uint64_t zapobj, const char *name)
{
- int err = (zap_lookup_norm(os, zapobj, name, 0,
- 0, NULL, MT_EXACT, NULL, 0, NULL));
+ int err = zap_lookup_norm(os, zapobj, name, 0,
+ 0, NULL, MT_EXACT, NULL, 0, NULL);
if (err == EOVERFLOW || err == EINVAL)
err = 0; /* found, but skipped reading the value */
return (err);
diff --git a/module/zfs/zfeature.c b/module/zfs/zfeature.c
index cdb9d6dce..89d4b43f0 100644
--- a/module/zfs/zfeature.c
+++ b/module/zfs/zfeature.c
@@ -161,23 +161,25 @@
*/
typedef enum {
- FEATURE_ACTION_ENABLE,
FEATURE_ACTION_INCR,
FEATURE_ACTION_DECR,
} feature_action_t;
/*
- * Checks that the features active in the specified object are supported by
+ * Checks that the active features in the pool are supported by
* this software. Adds each unsupported feature (name -> description) to
* the supplied nvlist.
*/
boolean_t
-feature_is_supported(objset_t *os, uint64_t obj, uint64_t desc_obj,
+spa_features_check(spa_t *spa, boolean_t for_write,
nvlist_t *unsup_feat, nvlist_t *enabled_feat)
{
+ objset_t *os = spa->spa_meta_objset;
boolean_t supported;
zap_cursor_t *zc;
zap_attribute_t *za;
+ uint64_t obj = for_write ?
+ spa->spa_feat_for_write_obj : spa->spa_feat_for_read_obj;
char *buf;
zc = kmem_alloc(sizeof (zap_cursor_t), KM_SLEEP);
@@ -203,8 +205,8 @@ feature_is_supported(objset_t *os, uint64_t obj, uint64_t desc_obj,
if (NULL != unsup_feat) {
char *desc = "";
- if (zap_lookup(os, desc_obj, za->za_name,
- 1, MAXPATHLEN, buf) == 0)
+ if (zap_lookup(os, spa->spa_feat_desc_obj,
+ za->za_name, 1, MAXPATHLEN, buf) == 0)
desc = buf;
VERIFY(nvlist_add_string(unsup_feat,
@@ -221,13 +223,18 @@ feature_is_supported(objset_t *os, uint64_t obj, uint64_t desc_obj,
return (supported);
}
-static int
-feature_get_refcount(objset_t *os, uint64_t read_obj, uint64_t write_obj,
- zfeature_info_t *feature, uint64_t *res)
+/*
+ * Note: well-designed features will not need to use this; they should
+ * use spa_feature_is_enabled() and spa_feature_is_active() instead.
+ * However, this is non-static for zdb and zhack.
+ */
+int
+feature_get_refcount(spa_t *spa, zfeature_info_t *feature, uint64_t *res)
{
int err;
uint64_t refcount;
- uint64_t zapobj = feature->fi_can_readonly ? write_obj : read_obj;
+ uint64_t zapobj = feature->fi_can_readonly ?
+ spa->spa_feat_for_write_obj : spa->spa_feat_for_read_obj;
/*
* If the pool is currently being created, the feature objects may not
@@ -236,8 +243,8 @@ feature_get_refcount(objset_t *os, uint64_t read_obj, uint64_t write_obj,
if (zapobj == 0)
return (SET_ERROR(ENOTSUP));
- err = zap_lookup(os, zapobj, feature->fi_guid, sizeof (uint64_t), 1,
- &refcount);
+ err = zap_lookup(spa->spa_meta_objset, zapobj,
+ feature->fi_guid, sizeof (uint64_t), 1, &refcount);
if (err != 0) {
if (err == ENOENT)
return (SET_ERROR(ENOTSUP));
@@ -248,49 +255,82 @@ feature_get_refcount(objset_t *os, uint64_t read_obj, uint64_t write_obj,
return (0);
}
-static int
-feature_do_action(objset_t *os, uint64_t read_obj, uint64_t write_obj,
- uint64_t desc_obj, zfeature_info_t *feature, feature_action_t action,
+/*
+ * This function is non-static for zhack; it should otherwise not be used
+ * outside this file.
+ */
+void
+feature_sync(spa_t *spa, zfeature_info_t *feature, uint64_t refcount,
dmu_tx_t *tx)
{
- int error;
- uint64_t refcount;
- uint64_t zapobj = feature->fi_can_readonly ? write_obj : read_obj;
+ uint64_t zapobj = feature->fi_can_readonly ?
+ spa->spa_feat_for_write_obj : spa->spa_feat_for_read_obj;
+
+ VERIFY0(zap_update(spa->spa_meta_objset, zapobj, feature->fi_guid,
+ sizeof (uint64_t), 1, &refcount, tx));
+
+ if (refcount == 0)
+ spa_deactivate_mos_feature(spa, feature->fi_guid);
+ else if (feature->fi_mos)
+ spa_activate_mos_feature(spa, feature->fi_guid);
+}
+
+/*
+ * This function is non-static for zhack; it should otherwise not be used
+ * outside this file.
+ */
+void
+feature_enable_sync(spa_t *spa, zfeature_info_t *feature, dmu_tx_t *tx)
+{
+ uint64_t zapobj = feature->fi_can_readonly ?
+ spa->spa_feat_for_write_obj : spa->spa_feat_for_read_obj;
+ int i;
ASSERT(0 != zapobj);
ASSERT(zfeature_is_valid_guid(feature->fi_guid));
-
- error = zap_lookup(os, zapobj, feature->fi_guid,
- sizeof (uint64_t), 1, &refcount);
+ ASSERT3U(spa_version(spa), >=, SPA_VERSION_FEATURES);
/*
- * If we can't ascertain the status of the specified feature, an I/O
- * error occurred.
+ * If the feature is already enabled, ignore the request.
*/
- if (error != 0 && error != ENOENT)
- return (error);
+ if (zap_contains(spa->spa_meta_objset, zapobj, feature->fi_guid) == 0)
+ return;
+
+ for (i = 0; feature->fi_depends[i] != SPA_FEATURE_NONE; i++)
+ spa_feature_enable(spa, feature->fi_depends[i], tx);
+
+ VERIFY0(zap_update(spa->spa_meta_objset, spa->spa_feat_desc_obj,
+ feature->fi_guid, 1, strlen(feature->fi_desc) + 1,
+ feature->fi_desc, tx));
+ feature_sync(spa, feature, 0, tx);
+}
+
+static void
+feature_do_action(spa_t *spa, spa_feature_t fid, feature_action_t action,
+ dmu_tx_t *tx)
+{
+ uint64_t refcount;
+ zfeature_info_t *feature = &spa_feature_table[fid];
+ uint64_t zapobj = feature->fi_can_readonly ?
+ spa->spa_feat_for_write_obj : spa->spa_feat_for_read_obj;
+
+ ASSERT3U(fid, <, SPA_FEATURES);
+ ASSERT(0 != zapobj);
+ ASSERT(zfeature_is_valid_guid(feature->fi_guid));
+
+ ASSERT(dmu_tx_is_syncing(tx));
+ ASSERT3U(spa_version(spa), >=, SPA_VERSION_FEATURES);
+
+ VERIFY0(zap_lookup(spa->spa_meta_objset, zapobj, feature->fi_guid,
+ sizeof (uint64_t), 1, &refcount));
switch (action) {
- case FEATURE_ACTION_ENABLE:
- /*
- * If the feature is already enabled, ignore the request.
- */
- if (error == 0)
- return (0);
- refcount = 0;
- break;
case FEATURE_ACTION_INCR:
- if (error == ENOENT)
- return (SET_ERROR(ENOTSUP));
- if (refcount == UINT64_MAX)
- return (SET_ERROR(EOVERFLOW));
+ VERIFY3U(refcount, !=, UINT64_MAX);
refcount++;
break;
case FEATURE_ACTION_DECR:
- if (error == ENOENT)
- return (SET_ERROR(ENOTSUP));
- if (refcount == 0)
- return (SET_ERROR(EOVERFLOW));
+ VERIFY3U(refcount, !=, 0);
refcount--;
break;
default:
@@ -298,42 +338,7 @@ feature_do_action(objset_t *os, uint64_t read_obj, uint64_t write_obj,
break;
}
- if (action == FEATURE_ACTION_ENABLE) {
- int i;
-
- for (i = 0; feature->fi_depends[i] != NULL; i++) {
- zfeature_info_t *dep = feature->fi_depends[i];
-
- error = feature_do_action(os, read_obj, write_obj,
- desc_obj, dep, FEATURE_ACTION_ENABLE, tx);
- if (error != 0)
- return (error);
- }
- }
-
- error = zap_update(os, zapobj, feature->fi_guid,
- sizeof (uint64_t), 1, &refcount, tx);
- if (error != 0)
- return (error);
-
- if (action == FEATURE_ACTION_ENABLE) {
- error = zap_update(os, desc_obj,
- feature->fi_guid, 1, strlen(feature->fi_desc) + 1,
- feature->fi_desc, tx);
- if (error != 0)
- return (error);
- }
-
- if (action == FEATURE_ACTION_INCR && refcount == 1 && feature->fi_mos) {
- spa_activate_mos_feature(dmu_objset_spa(os), feature->fi_guid);
- }
-
- if (action == FEATURE_ACTION_DECR && refcount == 0) {
- spa_deactivate_mos_feature(dmu_objset_spa(os),
- feature->fi_guid);
- }
-
- return (0);
+ feature_sync(spa, feature, refcount, tx);
}
void
@@ -361,82 +366,51 @@ spa_feature_create_zap_objects(spa_t *spa, dmu_tx_t *tx)
* Enable any required dependencies, then enable the requested feature.
*/
void
-spa_feature_enable(spa_t *spa, zfeature_info_t *feature, dmu_tx_t *tx)
+spa_feature_enable(spa_t *spa, spa_feature_t fid, dmu_tx_t *tx)
{
ASSERT3U(spa_version(spa), >=, SPA_VERSION_FEATURES);
- VERIFY3U(0, ==, feature_do_action(spa->spa_meta_objset,
- spa->spa_feat_for_read_obj, spa->spa_feat_for_write_obj,
- spa->spa_feat_desc_obj, feature, FEATURE_ACTION_ENABLE, tx));
+ ASSERT3U(fid, <, SPA_FEATURES);
+ feature_enable_sync(spa, &spa_feature_table[fid], tx);
}
void
-spa_feature_incr(spa_t *spa, zfeature_info_t *feature, dmu_tx_t *tx)
+spa_feature_incr(spa_t *spa, spa_feature_t fid, dmu_tx_t *tx)
{
- ASSERT(dmu_tx_is_syncing(tx));
- ASSERT3U(spa_version(spa), >=, SPA_VERSION_FEATURES);
- VERIFY3U(0, ==, feature_do_action(spa->spa_meta_objset,
- spa->spa_feat_for_read_obj, spa->spa_feat_for_write_obj,
- spa->spa_feat_desc_obj, feature, FEATURE_ACTION_INCR, tx));
+ feature_do_action(spa, fid, FEATURE_ACTION_INCR, tx);
}
void
-spa_feature_decr(spa_t *spa, zfeature_info_t *feature, dmu_tx_t *tx)
+spa_feature_decr(spa_t *spa, spa_feature_t fid, dmu_tx_t *tx)
{
- ASSERT(dmu_tx_is_syncing(tx));
- ASSERT3U(spa_version(spa), >=, SPA_VERSION_FEATURES);
- VERIFY3U(0, ==, feature_do_action(spa->spa_meta_objset,
- spa->spa_feat_for_read_obj, spa->spa_feat_for_write_obj,
- spa->spa_feat_desc_obj, feature, FEATURE_ACTION_DECR, tx));
-}
-
-/*
- * This interface is for debugging only. Normal consumers should use
- * spa_feature_is_enabled/spa_feature_is_active.
- */
-int
-spa_feature_get_refcount(spa_t *spa, zfeature_info_t *feature)
-{
- int err;
- uint64_t refcount = 0;
-
- if (spa_version(spa) < SPA_VERSION_FEATURES)
- return (B_FALSE);
-
- err = feature_get_refcount(spa->spa_meta_objset,
- spa->spa_feat_for_read_obj, spa->spa_feat_for_write_obj,
- feature, &refcount);
- ASSERT(err == 0 || err == ENOTSUP);
- return (err == 0 ? refcount : 0);
+ feature_do_action(spa, fid, FEATURE_ACTION_DECR, tx);
}
boolean_t
-spa_feature_is_enabled(spa_t *spa, zfeature_info_t *feature)
+spa_feature_is_enabled(spa_t *spa, spa_feature_t fid)
{
int err;
uint64_t refcount = 0;
+ ASSERT3U(fid, <, SPA_FEATURES);
if (spa_version(spa) < SPA_VERSION_FEATURES)
return (B_FALSE);
- err = feature_get_refcount(spa->spa_meta_objset,
- spa->spa_feat_for_read_obj, spa->spa_feat_for_write_obj,
- feature, &refcount);
+ err = feature_get_refcount(spa, &spa_feature_table[fid], &refcount);
ASSERT(err == 0 || err == ENOTSUP);
return (err == 0);
}
boolean_t
-spa_feature_is_active(spa_t *spa, zfeature_info_t *feature)
+spa_feature_is_active(spa_t *spa, spa_feature_t fid)
{
int err;
uint64_t refcount = 0;
+ ASSERT3U(fid, <, SPA_FEATURES);
if (spa_version(spa) < SPA_VERSION_FEATURES)
return (B_FALSE);
- err = feature_get_refcount(spa->spa_meta_objset,
- spa->spa_feat_for_read_obj, spa->spa_feat_for_write_obj,
- feature, &refcount);
+ err = feature_get_refcount(spa, &spa_feature_table[fid], &refcount);
ASSERT(err == 0 || err == ENOTSUP);
return (err == 0 && refcount > 0);
}
diff --git a/module/zfs/zfeature_common.c b/module/zfs/zfeature_common.c
index cee544880..5bcc08ade 100644
--- a/module/zfs/zfeature_common.c
+++ b/module/zfs/zfeature_common.c
@@ -88,39 +88,30 @@ zfeature_is_valid_guid(const char *name)
boolean_t
zfeature_is_supported(const char *guid)
{
+ spa_feature_t i;
+
if (zfeature_checks_disable)
return (B_TRUE);
- return (0 == zfeature_lookup_guid(guid, NULL));
-}
-
-int
-zfeature_lookup_guid(const char *guid, zfeature_info_t **res)
-{
- int i;
-
for (i = 0; i < SPA_FEATURES; i++) {
zfeature_info_t *feature = &spa_feature_table[i];
- if (strcmp(guid, feature->fi_guid) == 0) {
- if (res != NULL)
- *res = feature;
- return (0);
- }
+ if (strcmp(guid, feature->fi_guid) == 0)
+ return (B_TRUE);
}
- return (ENOENT);
+ return (B_FALSE);
}
int
-zfeature_lookup_name(const char *name, zfeature_info_t **res)
+zfeature_lookup_name(const char *name, spa_feature_t *res)
{
- int i;
+ spa_feature_t i;
for (i = 0; i < SPA_FEATURES; i++) {
zfeature_info_t *feature = &spa_feature_table[i];
if (strcmp(name, feature->fi_uname) == 0) {
if (res != NULL)
- *res = feature;
+ *res = i;
return (0);
}
}
@@ -129,11 +120,12 @@ zfeature_lookup_name(const char *name, zfeature_info_t **res)
}
static void
-zfeature_register(int fid, const char *guid, const char *name, const char *desc,
- boolean_t readonly, boolean_t mos, zfeature_info_t **deps)
+zfeature_register(spa_feature_t fid, const char *guid, const char *name,
+ const char *desc, boolean_t readonly, boolean_t mos,
+ const spa_feature_t *deps)
{
zfeature_info_t *feature = &spa_feature_table[fid];
- static zfeature_info_t *nodeps[] = { NULL };
+ static spa_feature_t nodeps[] = { SPA_FEATURE_NONE };
ASSERT(name != NULL);
ASSERT(desc != NULL);
@@ -144,6 +136,7 @@ zfeature_register(int fid, const char *guid, const char *name, const char *desc,
if (deps == NULL)
deps = nodeps;
+ feature->fi_feature = fid;
feature->fi_guid = guid;
feature->fi_uname = name;
feature->fi_desc = desc;
@@ -167,4 +160,8 @@ zpool_feature_init(void)
zfeature_register(SPA_FEATURE_SPACEMAP_HISTOGRAM,
"com.delphix:spacemap_histogram", "spacemap_histogram",
"Spacemaps maintain space histograms.", B_TRUE, B_FALSE, NULL);
+ zfeature_register(SPA_FEATURE_EXTENSIBLE_DATASET,
+ "com.delphix:extensible_dataset", "extensible_dataset",
+ "Enhanced dataset functionality, used by other features.",
+ B_FALSE, B_FALSE, NULL);
}
diff --git a/module/zfs/zfs_ioctl.c b/module/zfs/zfs_ioctl.c
index db7683ad9..453571598 100644
--- a/module/zfs/zfs_ioctl.c
+++ b/module/zfs/zfs_ioctl.c
@@ -245,7 +245,7 @@ static int zfs_fill_zplprops_root(uint64_t, nvlist_t *, nvlist_t *,
int zfs_set_prop_nvlist(const char *, zprop_source_t, nvlist_t *, nvlist_t *);
static int get_nvlist(uint64_t nvl, uint64_t size, int iflag, nvlist_t **nvp);
-static int zfs_prop_activate_feature(spa_t *spa, zfeature_info_t *feature);
+static int zfs_prop_activate_feature(spa_t *spa, spa_feature_t feature);
static void
history_str_free(char *buf)
@@ -2347,8 +2347,6 @@ zfs_prop_set_special(const char *dsname, zprop_source_t source,
case ZFS_PROP_COMPRESSION:
{
if (intval == ZIO_COMPRESS_LZ4) {
- zfeature_info_t *feature =
- &spa_feature_table[SPA_FEATURE_LZ4_COMPRESS];
spa_t *spa;
if ((err = spa_open(dsname, &spa, FTAG)) != 0)
@@ -2358,9 +2356,10 @@ zfs_prop_set_special(const char *dsname, zprop_source_t source,
* Setting the LZ4 compression algorithm activates
* the feature.
*/
- if (!spa_feature_is_active(spa, feature)) {
+ if (!spa_feature_is_active(spa,
+ SPA_FEATURE_LZ4_COMPRESS)) {
if ((err = zfs_prop_activate_feature(spa,
- feature)) != 0) {
+ SPA_FEATURE_LZ4_COMPRESS)) != 0) {
spa_close(spa, FTAG);
return (err);
}
@@ -3631,15 +3630,13 @@ zfs_check_settable(const char *dsname, nvpair_t *pair, cred_t *cr)
return (SET_ERROR(ENOTSUP));
if (intval == ZIO_COMPRESS_LZ4) {
- zfeature_info_t *feature =
- &spa_feature_table[
- SPA_FEATURE_LZ4_COMPRESS];
spa_t *spa;
if ((err = spa_open(dsname, &spa, FTAG)) != 0)
return (err);
- if (!spa_feature_is_enabled(spa, feature)) {
+ if (!spa_feature_is_enabled(spa,
+ SPA_FEATURE_LZ4_COMPRESS)) {
spa_close(spa, FTAG);
return (SET_ERROR(ENOTSUP));
}
@@ -3699,9 +3696,9 @@ static int
zfs_prop_activate_feature_check(void *arg, dmu_tx_t *tx)
{
spa_t *spa = dmu_tx_pool(tx)->dp_spa;
- zfeature_info_t *feature = arg;
+ spa_feature_t *featurep = arg;
- if (!spa_feature_is_active(spa, feature))
+ if (!spa_feature_is_active(spa, *featurep))
return (0);
else
return (SET_ERROR(EBUSY));
@@ -3715,9 +3712,9 @@ static void
zfs_prop_activate_feature_sync(void *arg, dmu_tx_t *tx)
{
spa_t *spa = dmu_tx_pool(tx)->dp_spa;
- zfeature_info_t *feature = arg;
+ spa_feature_t *featurep = arg;
- spa_feature_incr(spa, feature, tx);
+ spa_feature_incr(spa, *featurep, tx);
}
/*
@@ -3726,14 +3723,14 @@ zfs_prop_activate_feature_sync(void *arg, dmu_tx_t *tx)
* as being active.
*/
static int
-zfs_prop_activate_feature(spa_t *spa, zfeature_info_t *feature)
+zfs_prop_activate_feature(spa_t *spa, spa_feature_t feature)
{
int err;
/* EBUSY here indicates that the feature is already active */
err = dsl_sync_task(spa_name(spa),
zfs_prop_activate_feature_check, zfs_prop_activate_feature_sync,
- feature, 2);
+ &feature, 2);
if (err != 0 && err != EBUSY)
return (err);