--- old/usr/src/cmd/zdb/zdb.c	Tue Oct  1 14:04:06 2013
+++ new/usr/src/cmd/zdb/zdb.c	Tue Oct  1 14:04:06 2013
@@ -563,16 +563,20 @@
 static int
 verify_spacemap_refcounts(spa_t *spa)
 {
-	int expected_refcount, actual_refcount;
+	uint64_t expected_refcount = 0;
+	uint64_t actual_refcount;
 
-	expected_refcount = spa_feature_get_refcount(spa,
-	    &spa_feature_table[SPA_FEATURE_SPACEMAP_HISTOGRAM]);
+	(void) feature_get_refcount(spa,
+	    &spa_feature_table[SPA_FEATURE_SPACEMAP_HISTOGRAM],
+	    &expected_refcount);
 	actual_refcount = get_dtl_refcount(spa->spa_root_vdev);
 	actual_refcount += get_metaslab_refcount(spa->spa_root_vdev);
 
 	if (expected_refcount != actual_refcount) {
-		(void) printf("space map refcount mismatch: expected %d != "
-		    "actual %d\n", expected_refcount, actual_refcount);
+		(void) printf("space map refcount mismatch: expected %lld != "
+		    "actual %lld\n",
+		    (longlong_t)expected_refcount,
+		    (longlong_t)actual_refcount);
 		return (2);
 	}
 	return (0);
@@ -674,8 +678,7 @@
 	}
 
 	if (dump_opt['m'] > 1 && sm != NULL &&
-	    spa_feature_is_active(spa,
-	    &spa_feature_table[SPA_FEATURE_SPACEMAP_HISTOGRAM])) {
+	    spa_feature_is_active(spa, SPA_FEATURE_SPACEMAP_HISTOGRAM)) {
 		/*
 		 * The space map histogram represents free space in chunks
 		 * of sm_shift (i.e. bucket 0 refers to 2^sm_shift).
@@ -2466,8 +2469,7 @@
 		(void) bpobj_iterate_nofree(&spa->spa_dsl_pool->dp_free_bpobj,
 		    count_block_cb, &zcb, NULL);
 	}
-	if (spa_feature_is_active(spa,
-	    &spa_feature_table[SPA_FEATURE_ASYNC_DESTROY])) {
+	if (spa_feature_is_active(spa, SPA_FEATURE_ASYNC_DESTROY)) {
 		VERIFY3U(0, ==, bptree_iterate(spa->spa_meta_objset,
 		    spa->spa_dsl_pool->dp_bptree_obj, B_FALSE, count_block_cb,
 		    &zcb, NULL));
@@ -2793,7 +2795,7 @@
 			}
 
 			if (spa_feature_is_active(spa,
-			    &spa_feature_table[SPA_FEATURE_ASYNC_DESTROY])) {
+			    SPA_FEATURE_ASYNC_DESTROY)) {
 				dump_bptree(spa->spa_meta_objset,
 				    spa->spa_dsl_pool->dp_bptree_obj,
 				    "Pool dataset frees");
--- old/usr/src/cmd/zhack/zhack.c	Tue Oct  1 14:04:07 2013
+++ new/usr/src/cmd/zhack/zhack.c	Tue Oct  1 14:04:07 2013
@@ -283,12 +283,13 @@
 }
 
 static void
-feature_enable_sync(void *arg, dmu_tx_t *tx)
+zhack_feature_enable_sync(void *arg, dmu_tx_t *tx)
 {
 	spa_t *spa = dmu_tx_pool(tx)->dp_spa;
 	zfeature_info_t *feature = arg;
 
-	spa_feature_enable(spa, feature, tx);
+	feature_enable_sync(spa, feature, tx);
+
 	spa_history_log_internal(spa, "zhack enable feature", tx,
 	    "guid=%s can_readonly=%u",
 	    feature->fi_guid, feature->fi_can_readonly);
@@ -302,7 +303,7 @@
 	spa_t *spa;
 	objset_t *mos;
 	zfeature_info_t feature;
-	zfeature_info_t *nodeps[] = { NULL };
+	spa_feature_t nodeps[] = { SPA_FEATURE_NONE };
 
 	/*
 	 * Features are not added to the pool's label until their refcounts
@@ -349,7 +350,7 @@
 	zhack_spa_open(target, B_FALSE, FTAG, &spa);
 	mos = spa->spa_meta_objset;
 
-	if (0 == zfeature_lookup_guid(feature.fi_guid, NULL))
+	if (zfeature_is_supported(feature.fi_guid))
 		fatal(spa, FTAG, "'%s' is a real feature, will not enable");
 	if (0 == zap_contains(mos, spa->spa_feat_desc_obj, feature.fi_guid))
 		fatal(spa, FTAG, "feature already enabled: %s",
@@ -356,7 +357,7 @@
 		    feature.fi_guid);
 
 	VERIFY0(dsl_sync_task(spa_name(spa), NULL,
-	    feature_enable_sync, &feature, 5));
+	    zhack_feature_enable_sync, &feature, 5));
 
 	spa_close(spa, FTAG);
 
@@ -368,8 +369,10 @@
 {
 	spa_t *spa = dmu_tx_pool(tx)->dp_spa;
 	zfeature_info_t *feature = arg;
+	uint64_t refcount;
 
-	spa_feature_incr(spa, feature, tx);
+	VERIFY0(feature_get_refcount(spa, feature, &refcount));
+	feature_sync(spa, feature, refcount + 1, tx);
 	spa_history_log_internal(spa, "zhack feature incr", tx,
 	    "guid=%s", feature->fi_guid);
 }
@@ -379,8 +382,10 @@
 {
 	spa_t *spa = dmu_tx_pool(tx)->dp_spa;
 	zfeature_info_t *feature = arg;
+	uint64_t refcount;
 
-	spa_feature_decr(spa, feature, tx);
+	VERIFY0(feature_get_refcount(spa, feature, &refcount));
+	feature_sync(spa, feature, refcount - 1, tx);
 	spa_history_log_internal(spa, "zhack feature decr", tx,
 	    "guid=%s", feature->fi_guid);
 }
@@ -394,7 +399,7 @@
 	spa_t *spa;
 	objset_t *mos;
 	zfeature_info_t feature;
-	zfeature_info_t *nodeps[] = { NULL };
+	spa_feature_t nodeps[] = { SPA_FEATURE_NONE };
 
 	/*
 	 * fi_desc does not matter here because it was written to disk
@@ -437,9 +442,10 @@
 	zhack_spa_open(target, B_FALSE, FTAG, &spa);
 	mos = spa->spa_meta_objset;
 
-	if (0 == zfeature_lookup_guid(feature.fi_guid, NULL))
-		fatal(spa, FTAG, "'%s' is a real feature, will not change "
-		    "refcount");
+	if (zfeature_is_supported(feature.fi_guid)) {
+		fatal(spa, FTAG,
+		    "'%s' is a real feature, will not change refcount");
+	}
 
 	if (0 == zap_contains(mos, spa->spa_feat_for_read_obj,
 	    feature.fi_guid)) {
@@ -451,9 +457,14 @@
 		fatal(spa, FTAG, "feature is not enabled: %s", feature.fi_guid);
 	}
 
-	if (decr && !spa_feature_is_active(spa, &feature))
-		fatal(spa, FTAG, "feature refcount already 0: %s",
-		    feature.fi_guid);
+	if (decr) {
+		uint64_t count;
+		if (feature_get_refcount(spa, &feature, &count) == 0 &&
+		    count != 0) {
+			fatal(spa, FTAG, "feature refcount already 0: %s",
+			    feature.fi_guid);
+		}
+	}
 
 	VERIFY0(dsl_sync_task(spa_name(spa), NULL,
 	    decr ? feature_decr_sync : feature_incr_sync, &feature, 5));
--- old/usr/src/cmd/zpool/zpool_main.c	Tue Oct  1 14:04:08 2013
+++ new/usr/src/cmd/zpool/zpool_main.c	Tue Oct  1 14:04:08 2013
@@ -22,7 +22,7 @@
 /*
  * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
  * Copyright 2011 Nexenta Systems, Inc. All rights reserved.
- * Copyright (c) 2012 by Delphix. All rights reserved.
+ * Copyright (c) 2013 by Delphix. All rights reserved.
  * Copyright (c) 2012 by Frederik Wessels. All rights reserved.
  * Copyright (c) 2013 by Prasad Joshi (sTec). All rights reserved.
  */
@@ -876,7 +876,7 @@
 		 * Hand off to libzfs.
 		 */
 		if (enable_all_pool_feat) {
-			int i;
+			spa_feature_t i;
 			for (i = 0; i < SPA_FEATURES; i++) {
 				char propname[MAXPATHLEN];
 				zfeature_info_t *feat = &spa_feature_table[i];
--- old/usr/src/common/zfs/zfeature_common.c	Tue Oct  1 14:04:10 2013
+++ new/usr/src/common/zfs/zfeature_common.c	Tue Oct  1 14:04:10 2013
@@ -92,32 +92,22 @@
 	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)
-{
-	for (int i = 0; i < SPA_FEATURES; i++) {
+	for (spa_feature_t 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)
 {
-	for (int i = 0; i < SPA_FEATURES; i++) {
+	for (spa_feature_t 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);
 		}
 	}
@@ -126,11 +116,12 @@
 }
 
 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);
@@ -141,6 +132,7 @@
 	if (deps == NULL)
 		deps = nodeps;
 
+	feature->fi_feature = fid;
 	feature->fi_guid = guid;
 	feature->fi_uname = name;
 	feature->fi_desc = desc;
@@ -167,4 +159,8 @@
 	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);
 }
--- old/usr/src/common/zfs/zfeature_common.h	Tue Oct  1 14:04:13 2013
+++ new/usr/src/common/zfs/zfeature_common.h	Tue Oct  1 14:04:14 2013
@@ -38,13 +38,26 @@
 
 struct zfeature_info;
 
+typedef enum spa_feature {
+	SPA_FEATURE_NONE = -1,
+	SPA_FEATURE_ASYNC_DESTROY,
+	SPA_FEATURE_EMPTY_BPOBJ,
+	SPA_FEATURE_LZ4_COMPRESS,
+	SPA_FEATURE_MULTI_VDEV_CRASH_DUMP,
+	SPA_FEATURE_SPACEMAP_HISTOGRAM,
+	SPA_FEATURE_EXTENSIBLE_DATASET,
+	SPA_FEATURES
+} spa_feature_t;
+
 typedef struct zfeature_info {
+	spa_feature_t fi_feature;
 	const char *fi_uname;	/* User-facing feature name */
 	const char *fi_guid;	/* On-disk feature identifier */
 	const char *fi_desc;	/* Feature description */
 	boolean_t fi_can_readonly; /* Can open pool readonly w/o support? */
 	boolean_t fi_mos;	/* Is the feature necessary to read the MOS? */
-	struct zfeature_info **fi_depends; /* array; null terminated */
+	/* array of dependencies, terminated by SPA_FEATURE_NONE */
+	const spa_feature_t *fi_depends;
 } zfeature_info_t;
 
 typedef int (zfeature_func_t)(zfeature_info_t *fi, void *arg);
@@ -51,22 +64,12 @@
 
 #define	ZFS_FEATURE_DEBUG
 
-enum spa_feature {
-	SPA_FEATURE_ASYNC_DESTROY,
-	SPA_FEATURE_EMPTY_BPOBJ,
-	SPA_FEATURE_LZ4_COMPRESS,
-	SPA_FEATURE_MULTI_VDEV_CRASH_DUMP,
-	SPA_FEATURE_SPACEMAP_HISTOGRAM,
-	SPA_FEATURES
-} spa_feature_t;
-
 extern zfeature_info_t spa_feature_table[SPA_FEATURES];
 
 extern boolean_t zfeature_is_valid_guid(const char *);
 
 extern boolean_t zfeature_is_supported(const char *);
-extern int zfeature_lookup_guid(const char *, zfeature_info_t **res);
-extern int zfeature_lookup_name(const char *, zfeature_info_t **res);
+extern int zfeature_lookup_name(const char *name, spa_feature_t *res);
 
 extern void zpool_feature_init(void);
 
--- old/usr/src/grub/grub-0.97/stage2/fsys_zfs.c	Tue Oct  1 14:04:17 2013
+++ new/usr/src/grub/grub-0.97/stage2/fsys_zfs.c	Tue Oct  1 14:04:17 2013
@@ -23,7 +23,7 @@
  */
 
 /*
- * Copyright (c) 2012 by Delphix. All rights reserved.
+ * Copyright (c) 2013 by Delphix. All rights reserved.
  * Copyright (c) 2013 by Saso Kiselkov. All rights reserved.
  */
 
@@ -966,6 +966,7 @@
  */
 static const char *spa_feature_names[] = {
 	"org.illumos:lz4_compress",
+	"com.delphix:extensible_dataset",
 	NULL
 };
 
@@ -1044,7 +1045,7 @@
 	    stack))
 		return (errnum);
 
-	if (errnum = dnode_get(mosmdn, objnum, DMU_OT_DSL_DIR, mdn, stack))
+	if (errnum = dnode_get(mosmdn, objnum, 0, mdn, stack))
 		return (errnum);
 
 	if (fsname == NULL) {
@@ -1086,7 +1087,7 @@
 		if (zap_lookup(mdn, cname, &objnum, stack))
 			return (ERR_FILESYSTEM_NOT_FOUND);
 
-		if (errnum = dnode_get(mosmdn, objnum, DMU_OT_DSL_DIR,
+		if (errnum = dnode_get(mosmdn, objnum, 0,
 		    mdn, stack))
 			return (errnum);
 
@@ -1099,7 +1100,7 @@
 		*obj = headobj;
 
 skip:
-	if (errnum = dnode_get(mosmdn, headobj, DMU_OT_DSL_DATASET, mdn, stack))
+	if (errnum = dnode_get(mosmdn, headobj, 0, mdn, stack))
 		return (errnum);
 	if (issnapshot) {
 		uint64_t snapobj;
@@ -1112,8 +1113,7 @@
 			return (errnum);
 		if (zap_lookup(mdn, snapname + 1, &headobj, stack))
 			return (ERR_FILESYSTEM_NOT_FOUND);
-		if (errnum = dnode_get(mosmdn, headobj,
-		    DMU_OT_DSL_DATASET, mdn, stack))
+		if (errnum = dnode_get(mosmdn, headobj, 0, mdn, stack))
 			return (errnum);
 		if (obj)
 			*obj = headobj;
--- old/usr/src/lib/libzfs/common/libzfs_pool.c	Tue Oct  1 14:04:19 2013
+++ new/usr/src/lib/libzfs/common/libzfs_pool.c	Tue Oct  1 14:04:19 2013
@@ -22,7 +22,7 @@
 /*
  * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
  * Copyright 2011 Nexenta Systems, Inc. All rights reserved.
- * Copyright (c) 2012 by Delphix. All rights reserved.
+ * Copyright (c) 2013 by Delphix. All rights reserved.
  * Copyright (c) 2013, Joyent, Inc. All rights reserved.
  */
 
@@ -413,10 +413,9 @@
 		prop = zpool_name_to_prop(propname);
 		if (prop == ZPROP_INVAL && zpool_prop_feature(propname)) {
 			int err;
-			zfeature_info_t *feature;
 			char *fname = strchr(propname, '@') + 1;
 
-			err = zfeature_lookup_name(fname, &feature);
+			err = zfeature_lookup_name(fname, NULL);
 			if (err != 0) {
 				ASSERT3U(err, ==, ENOENT);
 				zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
@@ -807,14 +806,14 @@
 	 */
 	if (supported) {
 		int ret;
-		zfeature_info_t *fi;
+		spa_feature_t fid;
 
-		ret = zfeature_lookup_name(feature, &fi);
+		ret = zfeature_lookup_name(feature, &fid);
 		if (ret != 0) {
 			(void) strlcpy(buf, "-", len);
 			return (ENOTSUP);
 		}
-		feature = fi->fi_guid;
+		feature = spa_feature_table[fid].fi_guid;
 	}
 
 	if (nvlist_lookup_uint64(features, feature, &refcount) == 0)
--- old/usr/src/man/man5/zpool-features.5	Tue Oct  1 14:04:22 2013
+++ new/usr/src/man/man5/zpool-features.5	Tue Oct  1 14:04:22 2013
@@ -271,6 +271,29 @@
 When the \fBmulti_vdev_crash_dump\fR feature is set to \fBenabled\fR,
 the administrator can use the \fBdumpadm\fR(1M) command to configure a
 dump device on a pool comprised of multiple vdevs.
+.RE
 
+.sp
+.ne 2
+.na
+\fB\fBextensible_dataset\fR\fR
+.ad
+.RS 4n
+.TS
+l l .
+GUID	com.delphix:extensible_dataset
+READ\-ONLY COMPATIBLE	no
+DEPENDENCIES	none
+.TE
+
+This feature allows more flexible use of internal ZFS data structures,
+and exists for other features to depend on.
+
+This feature will be \fBactive\fR when the first dependent feature uses it,
+and will be returned to the \fBenabled\fR state when all datasets that use
+this feature are destroyed.
+
+.RE
+
 .SH "SEE ALSO"
 \fBzpool\fR(1M)
--- old/usr/src/uts/common/fs/zfs/bpobj.c	Tue Oct  1 14:04:24 2013
+++ new/usr/src/uts/common/fs/zfs/bpobj.c	Tue Oct  1 14:04:24 2013
@@ -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 @@
 			    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 @@
 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 @@
 		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--) {
--- old/usr/src/uts/common/fs/zfs/dmu_object.c	Tue Oct  1 14:04:26 2013
+++ new/usr/src/uts/common/fs/zfs/dmu_object.c	Tue Oct  1 14:04:26 2013
@@ -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,
@@ -194,4 +196,55 @@
 	*objectp = offset >> DNODE_SHIFT;
 
 	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));
 }
--- old/usr/src/uts/common/fs/zfs/dmu_traverse.c	Tue Oct  1 14:04:28 2013
+++ new/usr/src/uts/common/fs/zfs/dmu_traverse.c	Tue Oct  1 14:04:28 2013
@@ -605,7 +605,7 @@
 			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;
 
--- old/usr/src/uts/common/fs/zfs/dnode_sync.c	Tue Oct  1 14:04:30 2013
+++ new/usr/src/uts/common/fs/zfs/dnode_sync.c	Tue Oct  1 14:04:30 2013
@@ -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>
@@ -574,7 +574,12 @@
 	    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]) ||
@@ -587,7 +592,7 @@
 		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
@@ -596,7 +601,7 @@
 		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;
@@ -614,7 +619,7 @@
 		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;
--- old/usr/src/uts/common/fs/zfs/dsl_dataset.c	Tue Oct  1 14:04:31 2013
+++ new/usr/src/uts/common/fs/zfs/dsl_dataset.c	Tue Oct  1 14:04:31 2013
@@ -352,7 +352,7 @@
 
 	/* 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));
 	}
@@ -2971,4 +2971,12 @@
 	ret = dsl_dataset_is_before(origin, earlier);
 	dsl_dataset_rele(origin, FTAG);
 	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);
 }
--- old/usr/src/uts/common/fs/zfs/dsl_destroy.c	Tue Oct  1 14:04:33 2013
+++ new/usr/src/uts/common/fs/zfs/dsl_destroy.c	Tue Oct  1 14:04:33 2013
@@ -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;
@@ -448,7 +449,7 @@
 		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
@@ -671,7 +672,7 @@
 	    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
@@ -724,10 +725,6 @@
 		ds->ds_prev->ds_phys->ds_num_children--;
 	}
 
-	zfeature_info_t *async_destroy =
-	    &spa_feature_table[SPA_FEATURE_ASYNC_DESTROY];
-	objset_t *os;
-
 	/*
 	 * Destroy the deadlist.  Unless it's a clone, the
 	 * deadlist should be empty.  (If it's a clone, it's
@@ -738,9 +735,10 @@
 	dmu_buf_will_dirty(ds->ds_dbuf, tx);
 	ds->ds_phys->ds_deadlist_obj = 0;
 
+	objset_t *os;
 	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 {
 		/*
@@ -751,10 +749,11 @@
 
 		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,
@@ -814,7 +813,7 @@
 	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);
 
@@ -870,8 +869,7 @@
 	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;
--- old/usr/src/uts/common/fs/zfs/dsl_dir.c	Tue Oct  1 14:04:35 2013
+++ new/usr/src/uts/common/fs/zfs/dsl_dir.c	Tue Oct  1 14:04:35 2013
@@ -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>
@@ -88,7 +89,7 @@
 	{
 		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
@@ -1351,4 +1352,11 @@
 	mutex_enter(&dd->dd_lock);
 	dd->dd_snap_cmtime = t;
 	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);
 }
--- old/usr/src/uts/common/fs/zfs/dsl_pool.c	Tue Oct  1 14:04:35 2013
+++ new/usr/src/uts/common/fs/zfs/dsl_pool.c	Tue Oct  1 14:04:35 2013
@@ -250,8 +250,7 @@
 		    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);
@@ -259,8 +258,7 @@
 			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);
--- old/usr/src/uts/common/fs/zfs/dsl_scan.c	Tue Oct  1 14:04:37 2013
+++ new/usr/src/uts/common/fs/zfs/dsl_scan.c	Tue Oct  1 14:04:37 2013
@@ -102,7 +102,7 @@
 	 */
 	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);
@@ -1353,7 +1353,6 @@
 		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);
@@ -1412,7 +1411,7 @@
 		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,
@@ -1423,11 +1422,11 @@
 			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));
--- old/usr/src/uts/common/fs/zfs/spa.c	Tue Oct  1 14:04:39 2013
+++ new/usr/src/uts/common/fs/zfs/spa.c	Tue Oct  1 14:04:39 2013
@@ -2300,14 +2300,12 @@
 		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;
 			}
@@ -5914,7 +5912,7 @@
 		zpool_prop_t prop;
 		const char *propname;
 		zprop_type_t proptype;
-		zfeature_info_t *feature;
+		spa_feature_t fid;
 
 		switch (prop = zpool_name_to_prop(nvpair_name(elem))) {
 		case ZPROP_INVAL:
@@ -5924,9 +5922,9 @@
 			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;
--- old/usr/src/uts/common/fs/zfs/spa_misc.c	Tue Oct  1 14:04:41 2013
+++ new/usr/src/uts/common/fs/zfs/spa_misc.c	Tue Oct  1 14:04:41 2013
@@ -1143,15 +1143,17 @@
 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);
 }
 
 /*
--- old/usr/src/uts/common/fs/zfs/space_map.c	Tue Oct  1 14:04:43 2013
+++ new/usr/src/uts/common/fs/zfs/space_map.c	Tue Oct  1 14:04:43 2013
@@ -474,8 +474,6 @@
 {
 	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;
 
@@ -485,7 +483,7 @@
 	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 {
@@ -525,13 +523,11 @@
 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 {
@@ -549,20 +545,20 @@
 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);
 		}
 	}
 
--- old/usr/src/uts/common/fs/zfs/sys/dmu_impl.h	Tue Oct  1 14:04:44 2013
+++ new/usr/src/uts/common/fs/zfs/sys/dmu_impl.h	Tue Oct  1 14:04:44 2013
@@ -298,6 +298,8 @@
 	uint64_t dsa_last_data_offset;
 } dmu_sendarg_t;
 
+void dmu_object_zapify(objset_t *, uint64_t, dmu_object_type_t, dmu_tx_t *);
+void dmu_object_free_zapified(objset_t *, uint64_t, dmu_tx_t *);
 
 #ifdef	__cplusplus
 }
--- old/usr/src/uts/common/fs/zfs/sys/dnode.h	Tue Oct  1 14:04:45 2013
+++ new/usr/src/uts/common/fs/zfs/sys/dnode.h	Tue Oct  1 14:04:45 2013
@@ -178,6 +178,7 @@
 	uint16_t dn_datablkszsec;	/* in 512b sectors */
 	uint32_t dn_datablksz;		/* in bytes */
 	uint64_t dn_maxblkid;
+	uint8_t dn_next_type[TXG_SIZE];
 	uint8_t dn_next_nblkptr[TXG_SIZE];
 	uint8_t dn_next_nlevels[TXG_SIZE];
 	uint8_t dn_next_indblkshift[TXG_SIZE];
--- old/usr/src/uts/common/fs/zfs/sys/dsl_dataset.h	Tue Oct  1 14:04:46 2013
+++ new/usr/src/uts/common/fs/zfs/sys/dsl_dataset.h	Tue Oct  1 14:04:46 2013
@@ -49,9 +49,9 @@
 #define	DS_FLAG_INCONSISTENT	(1ULL<<0)
 #define	DS_IS_INCONSISTENT(ds)	\
 	((ds)->ds_phys->ds_flags & DS_FLAG_INCONSISTENT)
+
 /*
- * Note: nopromote can not yet be set, but we want support for it in this
- * on-disk version, so that we don't need to upgrade for it later.
+ * Do not allow this dataset to be promoted.
  */
 #define	DS_FLAG_NOPROMOTE	(1ULL<<1)
 
@@ -71,6 +71,11 @@
 	((ds)->ds_phys->ds_flags & DS_FLAG_DEFER_DESTROY)
 
 /*
+ * DS_FIELD_* are strings that are used in the "extensified" dataset zap object.
+ * They should be of the format <reverse-dns>:<field>.
+ */
+
+/*
  * DS_FLAG_CI_DATASET is set if the dataset contains a file system whose
  * name lookups should be performed case-insensitively.
  */
--- old/usr/src/uts/common/fs/zfs/sys/zap.h	Tue Oct  1 14:04:47 2013
+++ new/usr/src/uts/common/fs/zfs/sys/zap.h	Tue Oct  1 14:04:47 2013
@@ -141,6 +141,12 @@
     uint64_t parent_obj, const char *name, dmu_tx_t *tx);
 
 /*
+ * Initialize an already-allocated object.
+ */
+void mzap_create_impl(objset_t *os, uint64_t obj, int normflags,
+    zap_flags_t flags, dmu_tx_t *tx);
+
+/*
  * Create a new zapobj with no attributes from the given (unallocated)
  * object number.
  */
--- old/usr/src/uts/common/fs/zfs/sys/zfeature.h	Tue Oct  1 14:04:47 2013
+++ new/usr/src/uts/common/fs/zfs/sys/zfeature.h	Tue Oct  1 14:04:47 2013
@@ -27,6 +27,7 @@
 #define	_SYS_ZFEATURE_H
 
 #include <sys/nvpair.h>
+#include <sys/txg.h>
 #include "zfeature_common.h"
 
 #ifdef	__cplusplus
@@ -37,18 +38,26 @@
 struct dmu_tx;
 struct objset;
 
-extern boolean_t feature_is_supported(struct objset *os, uint64_t obj,
-    uint64_t desc_obj, nvlist_t *unsup_feat, nvlist_t *enabled_feat);
-
 extern void spa_feature_create_zap_objects(struct spa *, struct dmu_tx *);
-extern void spa_feature_enable(struct spa *, zfeature_info_t *,
+extern void spa_feature_enable(struct spa *, spa_feature_t,
     struct dmu_tx *);
-extern void spa_feature_incr(struct spa *, zfeature_info_t *, struct dmu_tx *);
-extern void spa_feature_decr(struct spa *, zfeature_info_t *, struct dmu_tx *);
-extern boolean_t spa_feature_is_enabled(struct spa *, zfeature_info_t *);
-extern boolean_t spa_feature_is_active(struct spa *, zfeature_info_t *);
-extern int spa_feature_get_refcount(struct spa *, zfeature_info_t *);
+extern void spa_feature_incr(struct spa *, spa_feature_t, struct dmu_tx *);
+extern void spa_feature_decr(struct spa *, spa_feature_t, struct dmu_tx *);
+extern boolean_t spa_feature_is_enabled(struct spa *, spa_feature_t);
+extern boolean_t spa_feature_is_active(struct spa *, spa_feature_t);
+extern uint64_t spa_feature_refcount(spa_t *, spa_feature_t, uint64_t);
+extern boolean_t spa_features_check(spa_t *, boolean_t, nvlist_t *, nvlist_t *);
 
+/*
+ * These functions are only exported for zhack and zdb; normal callers should
+ * use the above interfaces.
+ */
+extern int feature_get_refcount(struct spa *, zfeature_info_t *, uint64_t *);
+extern void feature_enable_sync(struct spa *, zfeature_info_t *,
+    struct dmu_tx *);
+extern void feature_sync(struct spa *, zfeature_info_t *, uint64_t,
+    struct dmu_tx *);
+
 #ifdef	__cplusplus
 }
 #endif
--- old/usr/src/uts/common/fs/zfs/zap_micro.c	Tue Oct  1 14:04:48 2013
+++ new/usr/src/uts/common/fs/zfs/zap_micro.c	Tue Oct  1 14:04:48 2013
@@ -572,7 +572,7 @@
 	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 @@
 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);
--- old/usr/src/uts/common/fs/zfs/zfeature.c	Tue Oct  1 14:04:49 2013
+++ new/usr/src/uts/common/fs/zfs/zfeature.c	Tue Oct  1 14:04:49 2013
@@ -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;
 
 	supported = B_TRUE;
 	for (zap_cursor_init(&zc, os, obj);
@@ -199,8 +201,8 @@
 				char *desc = "";
 				char buf[MAXPATHLEN];
 
-				if (zap_lookup(os, desc_obj, za.za_name,
-				    1, sizeof (buf), buf) == 0)
+				if (zap_lookup(os, spa->spa_feat_desc_obj,
+				    za.za_name, 1, sizeof (buf), buf) == 0)
 					desc = buf;
 
 				VERIFY(nvlist_add_string(unsup_feat, za.za_name,
@@ -213,13 +215,18 @@
 	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
@@ -228,8 +235,8 @@
 	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));
@@ -240,49 +247,81 @@
 	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;
+
 	ASSERT(0 != zapobj);
 	ASSERT(zfeature_is_valid_guid(feature->fi_guid));
+	ASSERT3U(spa_version(spa), >=, SPA_VERSION_FEATURES);
 
-	error = zap_lookup(os, zapobj, feature->fi_guid,
-	    sizeof (uint64_t), 1, &refcount);
-
 	/*
-	 * 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 (int 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:
@@ -290,42 +329,7 @@
 		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
@@ -353,82 +357,51 @@
  * 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));
+	feature_do_action(spa, fid, 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;
-
-	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);
-}
-
 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;
 
+	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;
 
+	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);
 }
--- old/usr/src/uts/common/fs/zfs/zfs_ioctl.c	Tue Oct  1 14:04:49 2013
+++ new/usr/src/uts/common/fs/zfs/zfs_ioctl.c	Tue Oct  1 14:04:49 2013
@@ -242,7 +242,7 @@
 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);
 
 /* _NOTE(PRINTFLIKE(4)) - this is printf-like, but lint is too whiney */
 void
@@ -2382,8 +2382,6 @@
 	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)
@@ -2393,9 +2391,10 @@
 			 * 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);
 				}
@@ -3663,15 +3662,13 @@
 				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));
 				}
@@ -3729,9 +3726,9 @@
 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));
@@ -3745,9 +3742,9 @@
 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);
 }
 
 /*
@@ -3756,7 +3753,7 @@
  * 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;
 
@@ -3763,7 +3760,7 @@
 	/* 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);
--- old/usr/src/uts/common/fs/zfs/zvol.c	Tue Oct  1 14:04:50 2013
+++ new/usr/src/uts/common/fs/zfs/zvol.c	Tue Oct  1 14:04:50 2013
@@ -1834,8 +1834,7 @@
 {
 	spa_t *spa = dmu_tx_pool(tx)->dp_spa;
 
-	if (spa_feature_is_active(spa,
-	    &spa_feature_table[SPA_FEATURE_MULTI_VDEV_CRASH_DUMP]))
+	if (spa_feature_is_active(spa, SPA_FEATURE_MULTI_VDEV_CRASH_DUMP))
 		return (1);
 	return (0);
 }
@@ -1846,8 +1845,7 @@
 {
 	spa_t *spa = dmu_tx_pool(tx)->dp_spa;
 
-	spa_feature_incr(spa,
-	    &spa_feature_table[SPA_FEATURE_MULTI_VDEV_CRASH_DUMP], tx);
+	spa_feature_incr(spa, SPA_FEATURE_MULTI_VDEV_CRASH_DUMP, tx);
 }
 
 static int
@@ -1882,7 +1880,7 @@
 	 */
 	if (vd->vdev_children > 1 || vd->vdev_ops == &vdev_raidz_ops) {
 		if (!spa_feature_is_enabled(spa,
-		    &spa_feature_table[SPA_FEATURE_MULTI_VDEV_CRASH_DUMP]))
+		    SPA_FEATURE_MULTI_VDEV_CRASH_DUMP))
 			return (SET_ERROR(ENOTSUP));
 		(void) dsl_sync_task(spa_name(spa),
 		    zfs_mvdev_dump_feature_check,
@@ -1903,8 +1901,8 @@
 	 * function.  Otherwise, use the old default -- OFF.
 	 */
 	checksum = spa_feature_is_active(spa,
-	    &spa_feature_table[SPA_FEATURE_MULTI_VDEV_CRASH_DUMP]) ?
-	    ZIO_CHECKSUM_NOPARITY : ZIO_CHECKSUM_OFF;
+	    SPA_FEATURE_MULTI_VDEV_CRASH_DUMP) ? ZIO_CHECKSUM_NOPARITY :
+	    ZIO_CHECKSUM_OFF;
 
 	/*
 	 * If we are resizing the dump device then we only need to
