Illumos 5959 - clean up per-dataset feature count code

5959 clean up per-dataset feature count code
Reviewed by: Toomas Soome <tsoome@me.com>
Reviewed by: George Wilson <george@delphix.com>
Reviewed by: Alex Reece <alex@delphix.com>
Approved by: Richard Lowe <richlowe@richlowe.net>

References:
  https://www.illumos.org/issues/5959
  https://github.com/illumos/illumos-gate/commit/ca0cc39

Porting notes:

illumos code doesn't check for feature_get_refcount() returning
ENOTSUP (which means feature is disabled) in zdb. zfsonlinux added
a check in https://github.com/zfsonlinux/zfs/commit/784652c
due to #3468. The check was reintroduced here.

Ported-by: Witaut Bajaryn <vitaut.bayaryn@gmail.com>
Signed-off-by: Brian Behlendorf <behlendorf1@llnl.gov>
Closes #3965
This commit is contained in:
Matthew Ahrens 2015-07-24 09:53:55 -07:00 committed by Brian Behlendorf
parent 072484504f
commit 241b541574
11 changed files with 237 additions and 178 deletions

View File

@ -21,7 +21,7 @@
/* /*
* Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2011, 2014 by Delphix. All rights reserved. * Copyright (c) 2011, 2015 by Delphix. All rights reserved.
* Copyright (c) 2015, Intel Corporation. * Copyright (c) 2015, Intel Corporation.
*/ */
@ -2274,7 +2274,7 @@ dump_label(const char *dev)
(void) close(fd); (void) close(fd);
} }
static uint64_t num_large_blocks; static uint64_t dataset_feature_count[SPA_FEATURES];
/*ARGSUSED*/ /*ARGSUSED*/
static int static int
@ -2282,14 +2282,22 @@ dump_one_dir(const char *dsname, void *arg)
{ {
int error; int error;
objset_t *os; objset_t *os;
spa_feature_t f;
error = dmu_objset_own(dsname, DMU_OST_ANY, B_TRUE, FTAG, &os); error = dmu_objset_own(dsname, DMU_OST_ANY, B_TRUE, FTAG, &os);
if (error) { if (error) {
(void) printf("Could not open %s, error %d\n", dsname, error); (void) printf("Could not open %s, error %d\n", dsname, error);
return (0); return (0);
} }
if (dmu_objset_ds(os)->ds_large_blocks)
num_large_blocks++; for (f = 0; f < SPA_FEATURES; f++) {
if (!dmu_objset_ds(os)->ds_feature_inuse[f])
continue;
ASSERT(spa_feature_table[f].fi_flags &
ZFEATURE_FLAG_PER_DATASET);
dataset_feature_count[f]++;
}
dump_dir(os); dump_dir(os);
dmu_objset_disown(os, FTAG); dmu_objset_disown(os, FTAG);
fuid_table_destroy(); fuid_table_destroy();
@ -3094,7 +3102,7 @@ dump_zpool(spa_t *spa)
dump_metaslab_groups(spa); dump_metaslab_groups(spa);
if (dump_opt['d'] || dump_opt['i']) { if (dump_opt['d'] || dump_opt['i']) {
uint64_t refcount; spa_feature_t f;
dump_dir(dp->dp_meta_objset); dump_dir(dp->dp_meta_objset);
if (dump_opt['d'] >= 3) { if (dump_opt['d'] >= 3) {
@ -3117,18 +3125,28 @@ dump_zpool(spa_t *spa)
(void) dmu_objset_find(spa_name(spa), dump_one_dir, (void) dmu_objset_find(spa_name(spa), dump_one_dir,
NULL, DS_FIND_SNAPSHOTS | DS_FIND_CHILDREN); NULL, DS_FIND_SNAPSHOTS | DS_FIND_CHILDREN);
if (feature_get_refcount(spa, for (f = 0; f < SPA_FEATURES; f++) {
&spa_feature_table[SPA_FEATURE_LARGE_BLOCKS], uint64_t refcount;
&refcount) != ENOTSUP) {
if (num_large_blocks != refcount) { if (!(spa_feature_table[f].fi_flags &
(void) printf("large_blocks feature refcount " ZFEATURE_FLAG_PER_DATASET)) {
"mismatch: expected %lld != actual %lld\n", ASSERT0(dataset_feature_count[f]);
(longlong_t)num_large_blocks, continue;
}
if (feature_get_refcount(spa, &spa_feature_table[f],
&refcount) == ENOTSUP)
continue;
if (dataset_feature_count[f] != refcount) {
(void) printf("%s feature refcount mismatch: "
"%lld datasets != %lld refcount\n",
spa_feature_table[f].fi_uname,
(longlong_t)dataset_feature_count[f],
(longlong_t)refcount); (longlong_t)refcount);
rc = 2; rc = 2;
} else { } else {
(void) printf("Verified large_blocks feature " (void) printf("Verified %s feature refcount "
"refcount is correct (%llu)\n", "of %llu is correct\n",
spa_feature_table[f].fi_uname,
(longlong_t)refcount); (longlong_t)refcount);
} }
} }

View File

@ -20,7 +20,7 @@
*/ */
/* /*
* Copyright (c) 2011, 2014 by Delphix. All rights reserved. * Copyright (c) 2011, 2015 by Delphix. All rights reserved.
* Copyright (c) 2013 Steven Hartland. All rights reserved. * Copyright (c) 2013 Steven Hartland. All rights reserved.
*/ */
@ -294,8 +294,8 @@ zhack_feature_enable_sync(void *arg, dmu_tx_t *tx)
feature_enable_sync(spa, feature, tx); feature_enable_sync(spa, feature, tx);
spa_history_log_internal(spa, "zhack enable feature", tx, spa_history_log_internal(spa, "zhack enable feature", tx,
"name=%s can_readonly=%u", "name=%s flags=%u",
feature->fi_guid, feature->fi_can_readonly); feature->fi_guid, feature->fi_flags);
} }
static void static void
@ -314,9 +314,7 @@ zhack_do_feature_enable(int argc, char **argv)
*/ */
desc = NULL; desc = NULL;
feature.fi_uname = "zhack"; feature.fi_uname = "zhack";
feature.fi_mos = B_FALSE; feature.fi_flags = 0;
feature.fi_can_readonly = B_FALSE;
feature.fi_activate_on_enable = B_FALSE;
feature.fi_depends = nodeps; feature.fi_depends = nodeps;
feature.fi_feature = SPA_FEATURE_NONE; feature.fi_feature = SPA_FEATURE_NONE;
@ -324,7 +322,7 @@ zhack_do_feature_enable(int argc, char **argv)
while ((c = getopt(argc, argv, "rmd:")) != -1) { while ((c = getopt(argc, argv, "rmd:")) != -1) {
switch (c) { switch (c) {
case 'r': case 'r':
feature.fi_can_readonly = B_TRUE; feature.fi_flags |= ZFEATURE_FLAG_READONLY_COMPAT;
break; break;
case 'd': case 'd':
desc = strdup(optarg); desc = strdup(optarg);
@ -413,7 +411,7 @@ zhack_do_feature_ref(int argc, char **argv)
* disk later. * disk later.
*/ */
feature.fi_uname = "zhack"; feature.fi_uname = "zhack";
feature.fi_mos = B_FALSE; feature.fi_flags = 0;
feature.fi_desc = NULL; feature.fi_desc = NULL;
feature.fi_depends = nodeps; feature.fi_depends = nodeps;
feature.fi_feature = SPA_FEATURE_NONE; feature.fi_feature = SPA_FEATURE_NONE;
@ -422,7 +420,7 @@ zhack_do_feature_ref(int argc, char **argv)
while ((c = getopt(argc, argv, "md")) != -1) { while ((c = getopt(argc, argv, "md")) != -1) {
switch (c) { switch (c) {
case 'm': case 'm':
feature.fi_mos = B_TRUE; feature.fi_flags |= ZFEATURE_FLAG_MOS;
break; break;
case 'd': case 'd':
decr = B_TRUE; decr = B_TRUE;
@ -455,10 +453,10 @@ zhack_do_feature_ref(int argc, char **argv)
if (0 == zap_contains(mos, spa->spa_feat_for_read_obj, if (0 == zap_contains(mos, spa->spa_feat_for_read_obj,
feature.fi_guid)) { feature.fi_guid)) {
feature.fi_can_readonly = B_FALSE; feature.fi_flags &= ~ZFEATURE_FLAG_READONLY_COMPAT;
} else if (0 == zap_contains(mos, spa->spa_feat_for_write_obj, } else if (0 == zap_contains(mos, spa->spa_feat_for_write_obj,
feature.fi_guid)) { feature.fi_guid)) {
feature.fi_can_readonly = B_TRUE; feature.fi_flags |= ZFEATURE_FLAG_READONLY_COMPAT;
} else { } else {
fatal(spa, FTAG, "feature is not enabled: %s", feature.fi_guid); fatal(spa, FTAG, "feature is not enabled: %s", feature.fi_guid);
} }

View File

@ -22,7 +22,7 @@
/* /*
* Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
* Copyright 2011 Nexenta Systems, Inc. All rights reserved. * Copyright 2011 Nexenta Systems, Inc. All rights reserved.
* Copyright (c) 2011, 2014 by Delphix. All rights reserved. * Copyright (c) 2011, 2015 by Delphix. All rights reserved.
* Copyright (c) 2012 by Frederik Wessels. All rights reserved. * Copyright (c) 2012 by Frederik Wessels. All rights reserved.
* Copyright (c) 2012 by Cyril Plisko. All rights reserved. * Copyright (c) 2012 by Cyril Plisko. All rights reserved.
* Copyright (c) 2013 by Prasad Joshi (sTec). All rights reserved. * Copyright (c) 2013 by Prasad Joshi (sTec). All rights reserved.
@ -5105,7 +5105,8 @@ zpool_do_upgrade(int argc, char **argv)
"---------------\n"); "---------------\n");
for (i = 0; i < SPA_FEATURES; i++) { for (i = 0; i < SPA_FEATURES; i++) {
zfeature_info_t *fi = &spa_feature_table[i]; zfeature_info_t *fi = &spa_feature_table[i];
const char *ro = fi->fi_can_readonly ? const char *ro =
(fi->fi_flags & ZFEATURE_FLAG_READONLY_COMPAT) ?
" (read-only compatible)" : ""; " (read-only compatible)" : "";
(void) printf("%-37s%s\n", fi->fi_uname, ro); (void) printf("%-37s%s\n", fi->fi_uname, ro);

View File

@ -20,7 +20,7 @@
*/ */
/* /*
* Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2013 by Delphix. All rights reserved. * Copyright (c) 2011, 2015 by Delphix. All rights reserved.
* Copyright (c) 2013, Joyent, Inc. All rights reserved. * Copyright (c) 2013, Joyent, Inc. All rights reserved.
* Copyright (c) 2013 Steven Hartland. All rights reserved. * Copyright (c) 2013 Steven Hartland. All rights reserved.
* Copyright (c) 2014 Spectra Logic Corporation, All rights reserved. * Copyright (c) 2014 Spectra Logic Corporation, All rights reserved.
@ -38,6 +38,7 @@
#include <sys/zfs_context.h> #include <sys/zfs_context.h>
#include <sys/dsl_deadlist.h> #include <sys/dsl_deadlist.h>
#include <sys/refcount.h> #include <sys/refcount.h>
#include <zfeature_common.h>
#ifdef __cplusplus #ifdef __cplusplus
extern "C" { extern "C" {
@ -145,8 +146,6 @@ typedef struct dsl_dataset {
/* only used in syncing context, only valid for non-snapshots: */ /* only used in syncing context, only valid for non-snapshots: */
struct dsl_dataset *ds_prev; struct dsl_dataset *ds_prev;
uint64_t ds_bookmarks; /* DMU_OTN_ZAP_METADATA */ uint64_t ds_bookmarks; /* DMU_OTN_ZAP_METADATA */
boolean_t ds_large_blocks;
boolean_t ds_need_large_blocks;
/* has internal locking: */ /* has internal locking: */
dsl_deadlist_t ds_deadlist; dsl_deadlist_t ds_deadlist;
@ -185,6 +184,18 @@ typedef struct dsl_dataset {
kmutex_t ds_sendstream_lock; kmutex_t ds_sendstream_lock;
list_t ds_sendstreams; list_t ds_sendstreams;
/*
* For ZFEATURE_FLAG_PER_DATASET features, set if this dataset
* uses this feature.
*/
uint8_t ds_feature_inuse[SPA_FEATURES];
/*
* Set if we need to activate the feature on this dataset this txg
* (used only in syncing context).
*/
uint8_t ds_feature_activation_needed[SPA_FEATURES];
/* Protected by ds_lock; keep at end of struct for better locality */ /* Protected by ds_lock; keep at end of struct for better locality */
char ds_snapname[MAXNAMELEN]; char ds_snapname[MAXNAMELEN];
} dsl_dataset_t; } dsl_dataset_t;
@ -264,8 +275,6 @@ int dsl_dataset_space_written(dsl_dataset_t *oldsnap, dsl_dataset_t *new,
int dsl_dataset_space_wouldfree(dsl_dataset_t *firstsnap, dsl_dataset_t *last, int dsl_dataset_space_wouldfree(dsl_dataset_t *firstsnap, dsl_dataset_t *last,
uint64_t *usedp, uint64_t *compp, uint64_t *uncompp); uint64_t *usedp, uint64_t *compp, uint64_t *uncompp);
boolean_t dsl_dataset_is_dirty(dsl_dataset_t *ds); boolean_t dsl_dataset_is_dirty(dsl_dataset_t *ds);
int dsl_dataset_activate_large_blocks(const char *dsname);
void dsl_dataset_activate_large_blocks_sync_impl(uint64_t dsobj, dmu_tx_t *tx);
int dsl_dsobj_to_dsname(char *pname, uint64_t obj, char *buf); int dsl_dsobj_to_dsname(char *pname, uint64_t obj, char *buf);
@ -305,6 +314,9 @@ void dsl_dataset_set_refreservation_sync_impl(dsl_dataset_t *ds,
void dsl_dataset_zapify(dsl_dataset_t *ds, dmu_tx_t *tx); void dsl_dataset_zapify(dsl_dataset_t *ds, dmu_tx_t *tx);
int dsl_dataset_rollback(const char *fsname, void *owner, nvlist_t *result); int dsl_dataset_rollback(const char *fsname, void *owner, nvlist_t *result);
void dsl_dataset_deactivate_feature(uint64_t dsobj,
spa_feature_t f, dmu_tx_t *tx);
#ifdef ZFS_DEBUG #ifdef ZFS_DEBUG
#define dprintf_ds(ds, fmt, ...) do { \ #define dprintf_ds(ds, fmt, ...) do { \
if (zfs_flags & ZFS_DEBUG_DPRINTF) { \ if (zfs_flags & ZFS_DEBUG_DPRINTF) { \

View File

@ -20,7 +20,7 @@
*/ */
/* /*
* Copyright (c) 2013 by Delphix. All rights reserved. * Copyright (c) 2011, 2015 by Delphix. All rights reserved.
* Copyright (c) 2013 by Saso Kiselkov. All rights reserved. * Copyright (c) 2013 by Saso Kiselkov. All rights reserved.
*/ */
@ -55,15 +55,23 @@ typedef enum spa_feature {
#define SPA_FEATURE_DISABLED (-1ULL) #define SPA_FEATURE_DISABLED (-1ULL)
typedef enum zfeature_flags {
/* Can open pool readonly even if this feature is not supported. */
ZFEATURE_FLAG_READONLY_COMPAT = (1 << 0),
/* Is this feature necessary to read the MOS? */
ZFEATURE_FLAG_MOS = (1 << 1),
/* Activate this feature at the same time it is enabled. */
ZFEATURE_FLAG_ACTIVATE_ON_ENABLE = (1 << 2),
/* Each dataset has a field set if it has ever used this feature. */
ZFEATURE_FLAG_PER_DATASET = (1 << 3)
} zfeature_flags_t;
typedef struct zfeature_info { typedef struct zfeature_info {
spa_feature_t fi_feature; spa_feature_t fi_feature;
const char *fi_uname; /* User-facing feature name */ const char *fi_uname; /* User-facing feature name */
const char *fi_guid; /* On-disk feature identifier */ const char *fi_guid; /* On-disk feature identifier */
const char *fi_desc; /* Feature description */ const char *fi_desc; /* Feature description */
boolean_t fi_can_readonly; /* Can open pool readonly w/o support? */ zfeature_flags_t fi_flags;
boolean_t fi_mos; /* Is the feature necessary to read the MOS? */
/* Activate this feature at the same time it is enabled */
boolean_t fi_activate_on_enable;
/* array of dependencies, terminated by SPA_FEATURE_NONE */ /* array of dependencies, terminated by SPA_FEATURE_NONE */
const spa_feature_t *fi_depends; const spa_feature_t *fi_depends;
} zfeature_info_t; } zfeature_info_t;

View File

@ -1615,6 +1615,11 @@ dmu_buf_write_embedded(dmu_buf_t *dbuf, void *data,
struct dirty_leaf *dl; struct dirty_leaf *dl;
dmu_object_type_t type; dmu_object_type_t type;
if (etype == BP_EMBEDDED_TYPE_DATA) {
ASSERT(spa_feature_is_active(dmu_objset_spa(db->db_objset),
SPA_FEATURE_EMBEDDED_DATA));
}
DB_DNODE_ENTER(db); DB_DNODE_ENTER(db);
type = DB_DNODE(db)->dn_type; type = DB_DNODE(db)->dn_type;
DB_DNODE_EXIT(db); DB_DNODE_EXIT(db);

View File

@ -20,7 +20,7 @@
*/ */
/* /*
* Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2011 by Delphix. All rights reserved. * Copyright (c) 2011, 2015 by Delphix. All rights reserved.
* Copyright 2011 Nexenta Systems, Inc. All rights reserved. * Copyright 2011 Nexenta Systems, Inc. All rights reserved.
* Copyright (c) 2014, Joyent, Inc. All rights reserved. * Copyright (c) 2014, Joyent, Inc. All rights reserved.
* Copyright (c) 2011, 2014 by Delphix. All rights reserved. * Copyright (c) 2011, 2014 by Delphix. All rights reserved.
@ -611,7 +611,7 @@ dmu_send_impl(void *tag, dsl_pool_t *dp, dsl_dataset_t *ds,
} }
#endif #endif
if (large_block_ok && ds->ds_large_blocks) if (large_block_ok && ds->ds_feature_inuse[SPA_FEATURE_LARGE_BLOCKS])
featureflags |= DMU_BACKUP_FEATURE_LARGE_BLOCKS; featureflags |= DMU_BACKUP_FEATURE_LARGE_BLOCKS;
if (embedok && if (embedok &&
spa_feature_is_active(dp->dp_spa, SPA_FEATURE_EMBEDDED_DATA)) { spa_feature_is_active(dp->dp_spa, SPA_FEATURE_EMBEDDED_DATA)) {
@ -1226,13 +1226,6 @@ dmu_recv_begin_sync(void *arg, dmu_tx_t *tx)
} }
VERIFY0(dsl_dataset_own_obj(dp, dsobj, dmu_recv_tag, &newds)); VERIFY0(dsl_dataset_own_obj(dp, dsobj, dmu_recv_tag, &newds));
if ((DMU_GET_FEATUREFLAGS(drrb->drr_versioninfo) &
DMU_BACKUP_FEATURE_LARGE_BLOCKS) &&
!newds->ds_large_blocks) {
dsl_dataset_activate_large_blocks_sync_impl(dsobj, tx);
newds->ds_large_blocks = B_TRUE;
}
dmu_buf_will_dirty(newds->ds_dbuf, tx); dmu_buf_will_dirty(newds->ds_dbuf, tx);
dsl_dataset_phys(newds)->ds_flags |= DS_FLAG_INCONSISTENT; dsl_dataset_phys(newds)->ds_flags |= DS_FLAG_INCONSISTENT;

View File

@ -20,7 +20,7 @@
*/ */
/* /*
* Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2013 by Delphix. All rights reserved. * Copyright (c) 2011, 2015 by Delphix. All rights reserved.
* Copyright (c) 2014, Joyent, Inc. All rights reserved. * Copyright (c) 2014, Joyent, Inc. All rights reserved.
* Copyright (c) 2014 RackTop Systems. * Copyright (c) 2014 RackTop Systems.
* Copyright (c) 2014 Spectra Logic Corporation, All rights reserved. * Copyright (c) 2014 Spectra Logic Corporation, All rights reserved.
@ -126,8 +126,10 @@ dsl_dataset_block_born(dsl_dataset_t *ds, const blkptr_t *bp, dmu_tx_t *tx)
dsl_dataset_phys(ds)->ds_compressed_bytes += compressed; dsl_dataset_phys(ds)->ds_compressed_bytes += compressed;
dsl_dataset_phys(ds)->ds_uncompressed_bytes += uncompressed; dsl_dataset_phys(ds)->ds_uncompressed_bytes += uncompressed;
dsl_dataset_phys(ds)->ds_unique_bytes += used; dsl_dataset_phys(ds)->ds_unique_bytes += used;
if (BP_GET_LSIZE(bp) > SPA_OLD_MAXBLOCKSIZE) if (BP_GET_LSIZE(bp) > SPA_OLD_MAXBLOCKSIZE) {
ds->ds_need_large_blocks = B_TRUE; ds->ds_feature_activation_needed[SPA_FEATURE_LARGE_BLOCKS] =
B_TRUE;
}
mutex_exit(&ds->ds_lock); mutex_exit(&ds->ds_lock);
dsl_dir_diduse_space(ds->ds_dir, DD_USED_HEAD, delta, dsl_dir_diduse_space(ds->ds_dir, DD_USED_HEAD, delta,
compressed, uncompressed, tx); compressed, uncompressed, tx);
@ -432,19 +434,25 @@ dsl_dataset_hold_obj(dsl_pool_t *dp, uint64_t dsobj, void *tag,
offsetof(dmu_sendarg_t, dsa_link)); offsetof(dmu_sendarg_t, dsa_link));
if (doi.doi_type == DMU_OTN_ZAP_METADATA) { if (doi.doi_type == DMU_OTN_ZAP_METADATA) {
int zaperr = zap_contains(mos, dsobj, spa_feature_t f;
DS_FIELD_LARGE_BLOCKS);
if (zaperr != ENOENT) { for (f = 0; f < SPA_FEATURES; f++) {
VERIFY0(zaperr); if (!(spa_feature_table[f].fi_flags &
ds->ds_large_blocks = B_TRUE; ZFEATURE_FLAG_PER_DATASET))
continue;
err = zap_contains(mos, dsobj,
spa_feature_table[f].fi_guid);
if (err == 0) {
ds->ds_feature_inuse[f] = B_TRUE;
} else {
ASSERT3U(err, ==, ENOENT);
err = 0;
}
} }
} }
if (err == 0) { err = dsl_dir_hold_obj(dp,
err = dsl_dir_hold_obj(dp, dsl_dataset_phys(ds)->ds_dir_obj, NULL, ds, &ds->ds_dir);
dsl_dataset_phys(ds)->ds_dir_obj, NULL, ds,
&ds->ds_dir);
}
if (err != 0) { if (err != 0) {
mutex_destroy(&ds->ds_lock); mutex_destroy(&ds->ds_lock);
mutex_destroy(&ds->ds_opening_lock); mutex_destroy(&ds->ds_opening_lock);
@ -698,6 +706,34 @@ dsl_dataset_tryown(dsl_dataset_t *ds, void *tag)
return (gotit); return (gotit);
} }
static void
dsl_dataset_activate_feature(uint64_t dsobj, spa_feature_t f, dmu_tx_t *tx)
{
spa_t *spa = dmu_tx_pool(tx)->dp_spa;
objset_t *mos = dmu_tx_pool(tx)->dp_meta_objset;
uint64_t zero = 0;
VERIFY(spa_feature_table[f].fi_flags & ZFEATURE_FLAG_PER_DATASET);
spa_feature_incr(spa, f, tx);
dmu_object_zapify(mos, dsobj, DMU_OT_DSL_DATASET, tx);
VERIFY0(zap_add(mos, dsobj, spa_feature_table[f].fi_guid,
sizeof (zero), 1, &zero, tx));
}
void
dsl_dataset_deactivate_feature(uint64_t dsobj, spa_feature_t f, dmu_tx_t *tx)
{
spa_t *spa = dmu_tx_pool(tx)->dp_spa;
objset_t *mos = dmu_tx_pool(tx)->dp_meta_objset;
VERIFY(spa_feature_table[f].fi_flags & ZFEATURE_FLAG_PER_DATASET);
VERIFY0(zap_remove(mos, dsobj, spa_feature_table[f].fi_guid, tx));
spa_feature_decr(spa, f, tx);
}
uint64_t uint64_t
dsl_dataset_create_sync_dd(dsl_dir_t *dd, dsl_dataset_t *origin, dsl_dataset_create_sync_dd(dsl_dir_t *dd, dsl_dataset_t *origin,
uint64_t flags, dmu_tx_t *tx) uint64_t flags, dmu_tx_t *tx)
@ -736,6 +772,7 @@ dsl_dataset_create_sync_dd(dsl_dir_t *dd, dsl_dataset_t *origin,
if (origin == NULL) { if (origin == NULL) {
dsphys->ds_deadlist_obj = dsl_deadlist_alloc(mos, tx); dsphys->ds_deadlist_obj = dsl_deadlist_alloc(mos, tx);
} else { } else {
spa_feature_t f;
dsl_dataset_t *ohds; /* head of the origin snapshot */ dsl_dataset_t *ohds; /* head of the origin snapshot */
dsphys->ds_prev_snap_obj = origin->ds_object; dsphys->ds_prev_snap_obj = origin->ds_object;
@ -756,8 +793,10 @@ dsl_dataset_create_sync_dd(dsl_dir_t *dd, dsl_dataset_t *origin,
dsphys->ds_flags |= dsl_dataset_phys(origin)->ds_flags & dsphys->ds_flags |= dsl_dataset_phys(origin)->ds_flags &
(DS_FLAG_INCONSISTENT | DS_FLAG_CI_DATASET); (DS_FLAG_INCONSISTENT | DS_FLAG_CI_DATASET);
if (origin->ds_large_blocks) for (f = 0; f < SPA_FEATURES; f++) {
dsl_dataset_activate_large_blocks_sync_impl(dsobj, tx); if (origin->ds_feature_inuse[f])
dsl_dataset_activate_feature(dsobj, f, tx);
}
dmu_buf_will_dirty(origin->ds_dbuf, tx); dmu_buf_will_dirty(origin->ds_dbuf, tx);
dsl_dataset_phys(origin)->ds_num_children++; dsl_dataset_phys(origin)->ds_num_children++;
@ -1233,6 +1272,7 @@ dsl_dataset_snapshot_sync_impl(dsl_dataset_t *ds, const char *snapname,
dsl_dataset_phys_t *dsphys; dsl_dataset_phys_t *dsphys;
uint64_t dsobj, crtxg; uint64_t dsobj, crtxg;
objset_t *mos = dp->dp_meta_objset; objset_t *mos = dp->dp_meta_objset;
spa_feature_t f;
ASSERTV(static zil_header_t zero_zil); ASSERTV(static zil_header_t zero_zil);
ASSERTV(objset_t *os); ASSERTV(objset_t *os);
@ -1282,8 +1322,10 @@ dsl_dataset_snapshot_sync_impl(dsl_dataset_t *ds, const char *snapname,
dsphys->ds_bp = dsl_dataset_phys(ds)->ds_bp; dsphys->ds_bp = dsl_dataset_phys(ds)->ds_bp;
dmu_buf_rele(dbuf, FTAG); dmu_buf_rele(dbuf, FTAG);
if (ds->ds_large_blocks) for (f = 0; f < SPA_FEATURES; f++) {
dsl_dataset_activate_large_blocks_sync_impl(dsobj, tx); if (ds->ds_feature_inuse[f])
dsl_dataset_activate_feature(dsobj, f, tx);
}
ASSERT3U(ds->ds_prev != 0, ==, ASSERT3U(ds->ds_prev != 0, ==,
dsl_dataset_phys(ds)->ds_prev_snap_obj != 0); dsl_dataset_phys(ds)->ds_prev_snap_obj != 0);
@ -1561,6 +1603,8 @@ dsl_dataset_snapshot_tmp(const char *fsname, const char *snapname,
void void
dsl_dataset_sync(dsl_dataset_t *ds, zio_t *zio, dmu_tx_t *tx) dsl_dataset_sync(dsl_dataset_t *ds, zio_t *zio, dmu_tx_t *tx)
{ {
spa_feature_t f;
ASSERT(dmu_tx_is_syncing(tx)); ASSERT(dmu_tx_is_syncing(tx));
ASSERT(ds->ds_objset != NULL); ASSERT(ds->ds_objset != NULL);
ASSERT(dsl_dataset_phys(ds)->ds_next_snap_obj == 0); ASSERT(dsl_dataset_phys(ds)->ds_next_snap_obj == 0);
@ -1574,9 +1618,13 @@ dsl_dataset_sync(dsl_dataset_t *ds, zio_t *zio, dmu_tx_t *tx)
dmu_objset_sync(ds->ds_objset, zio, tx); dmu_objset_sync(ds->ds_objset, zio, tx);
if (ds->ds_need_large_blocks && !ds->ds_large_blocks) { for (f = 0; f < SPA_FEATURES; f++) {
dsl_dataset_activate_large_blocks_sync_impl(ds->ds_object, tx); if (ds->ds_feature_activation_needed[f]) {
ds->ds_large_blocks = B_TRUE; if (ds->ds_feature_inuse[f])
continue;
dsl_dataset_activate_feature(ds->ds_object, f, tx);
ds->ds_feature_inuse[f] = B_TRUE;
}
} }
} }
@ -2703,6 +2751,7 @@ void
dsl_dataset_clone_swap_sync_impl(dsl_dataset_t *clone, dsl_dataset_clone_swap_sync_impl(dsl_dataset_t *clone,
dsl_dataset_t *origin_head, dmu_tx_t *tx) dsl_dataset_t *origin_head, dmu_tx_t *tx)
{ {
spa_feature_t f;
dsl_pool_t *dp = dmu_tx_pool(tx); dsl_pool_t *dp = dmu_tx_pool(tx);
int64_t unused_refres_delta; int64_t unused_refres_delta;
@ -2711,6 +2760,43 @@ dsl_dataset_clone_swap_sync_impl(dsl_dataset_t *clone,
dsl_dataset_phys(clone)->ds_unique_bytes <= origin_head->ds_quota); dsl_dataset_phys(clone)->ds_unique_bytes <= origin_head->ds_quota);
ASSERT3P(clone->ds_prev, ==, origin_head->ds_prev); ASSERT3P(clone->ds_prev, ==, origin_head->ds_prev);
/*
* Swap per-dataset feature flags.
*/
for (f = 0; f < SPA_FEATURES; f++) {
boolean_t clone_inuse;
boolean_t origin_head_inuse;
if (!(spa_feature_table[f].fi_flags &
ZFEATURE_FLAG_PER_DATASET)) {
ASSERT(!clone->ds_feature_inuse[f]);
ASSERT(!origin_head->ds_feature_inuse[f]);
continue;
}
clone_inuse = clone->ds_feature_inuse[f];
origin_head_inuse = origin_head->ds_feature_inuse[f];
if (clone_inuse) {
dsl_dataset_deactivate_feature(clone->ds_object, f, tx);
clone->ds_feature_inuse[f] = B_FALSE;
}
if (origin_head_inuse) {
dsl_dataset_deactivate_feature(origin_head->ds_object,
f, tx);
origin_head->ds_feature_inuse[f] = B_FALSE;
}
if (clone_inuse) {
dsl_dataset_activate_feature(origin_head->ds_object,
f, tx);
origin_head->ds_feature_inuse[f] = B_TRUE;
}
if (origin_head_inuse) {
dsl_dataset_activate_feature(clone->ds_object, f, tx);
clone->ds_feature_inuse[f] = B_TRUE;
}
}
dmu_buf_will_dirty(clone->ds_dbuf, tx); dmu_buf_will_dirty(clone->ds_dbuf, tx);
dmu_buf_will_dirty(origin_head->ds_dbuf, tx); dmu_buf_will_dirty(origin_head->ds_dbuf, tx);
@ -3265,77 +3351,6 @@ dsl_dataset_space_wouldfree(dsl_dataset_t *firstsnap,
return (err); return (err);
} }
static int
dsl_dataset_activate_large_blocks_check(void *arg, dmu_tx_t *tx)
{
const char *dsname = arg;
dsl_dataset_t *ds;
dsl_pool_t *dp = dmu_tx_pool(tx);
int error = 0;
if (!spa_feature_is_enabled(dp->dp_spa, SPA_FEATURE_LARGE_BLOCKS))
return (SET_ERROR(ENOTSUP));
ASSERT(spa_feature_is_enabled(dp->dp_spa,
SPA_FEATURE_EXTENSIBLE_DATASET));
error = dsl_dataset_hold(dp, dsname, FTAG, &ds);
if (error != 0)
return (error);
if (ds->ds_large_blocks)
error = EALREADY;
dsl_dataset_rele(ds, FTAG);
return (error);
}
void
dsl_dataset_activate_large_blocks_sync_impl(uint64_t dsobj, dmu_tx_t *tx)
{
spa_t *spa = dmu_tx_pool(tx)->dp_spa;
objset_t *mos = dmu_tx_pool(tx)->dp_meta_objset;
uint64_t zero = 0;
spa_feature_incr(spa, SPA_FEATURE_LARGE_BLOCKS, tx);
dmu_object_zapify(mos, dsobj, DMU_OT_DSL_DATASET, tx);
VERIFY0(zap_add(mos, dsobj, DS_FIELD_LARGE_BLOCKS,
sizeof (zero), 1, &zero, tx));
}
static void
dsl_dataset_activate_large_blocks_sync(void *arg, dmu_tx_t *tx)
{
const char *dsname = arg;
dsl_dataset_t *ds;
VERIFY0(dsl_dataset_hold(dmu_tx_pool(tx), dsname, FTAG, &ds));
dsl_dataset_activate_large_blocks_sync_impl(ds->ds_object, tx);
ASSERT(!ds->ds_large_blocks);
ds->ds_large_blocks = B_TRUE;
dsl_dataset_rele(ds, FTAG);
}
int
dsl_dataset_activate_large_blocks(const char *dsname)
{
int error;
error = dsl_sync_task(dsname,
dsl_dataset_activate_large_blocks_check,
dsl_dataset_activate_large_blocks_sync, (void *)dsname,
1, ZFS_SPACE_CHECK_RESERVED);
/*
* EALREADY indicates that this dataset already supports large blocks.
*/
if (error == EALREADY)
error = 0;
return (error);
}
/* /*
* Return TRUE if 'earlier' is an earlier snapshot in 'later's timeline. * Return TRUE if 'earlier' is an earlier snapshot in 'later's timeline.
* For example, they could both be snapshots of the same filesystem, and * For example, they could both be snapshots of the same filesystem, and
@ -3380,7 +3395,6 @@ dsl_dataset_is_before(dsl_dataset_t *later, dsl_dataset_t *earlier,
return (ret); return (ret);
} }
void void
dsl_dataset_zapify(dsl_dataset_t *ds, dmu_tx_t *tx) dsl_dataset_zapify(dsl_dataset_t *ds, dmu_tx_t *tx)
{ {

View File

@ -20,7 +20,7 @@
*/ */
/* /*
* Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2012, 2014 by Delphix. All rights reserved. * Copyright (c) 2012, 2015 by Delphix. All rights reserved.
* Copyright (c) 2013 Steven Hartland. All rights reserved. * Copyright (c) 2013 Steven Hartland. All rights reserved.
* Copyright (c) 2013 by Joyent, Inc. All rights reserved. * Copyright (c) 2013 by Joyent, Inc. All rights reserved.
*/ */
@ -246,6 +246,7 @@ dsl_destroy_snapshot_sync_impl(dsl_dataset_t *ds, boolean_t defer, dmu_tx_t *tx)
#ifdef ZFS_DEBUG #ifdef ZFS_DEBUG
int err; int err;
#endif #endif
spa_feature_t f;
int after_branch_point = FALSE; int after_branch_point = FALSE;
dsl_pool_t *dp = ds->ds_dir->dd_pool; dsl_pool_t *dp = ds->ds_dir->dd_pool;
objset_t *mos = dp->dp_meta_objset; objset_t *mos = dp->dp_meta_objset;
@ -277,9 +278,11 @@ dsl_destroy_snapshot_sync_impl(dsl_dataset_t *ds, boolean_t defer, dmu_tx_t *tx)
obj = ds->ds_object; obj = ds->ds_object;
if (ds->ds_large_blocks) { for (f = 0; f < SPA_FEATURES; f++) {
ASSERT0(zap_contains(mos, obj, DS_FIELD_LARGE_BLOCKS)); if (ds->ds_feature_inuse[f]) {
spa_feature_decr(dp->dp_spa, SPA_FEATURE_LARGE_BLOCKS, tx); dsl_dataset_deactivate_feature(obj, f, tx);
ds->ds_feature_inuse[f] = B_FALSE;
}
} }
if (dsl_dataset_phys(ds)->ds_prev_snap_obj != 0) { if (dsl_dataset_phys(ds)->ds_prev_snap_obj != 0) {
ASSERT3P(ds->ds_prev, ==, NULL); ASSERT3P(ds->ds_prev, ==, NULL);
@ -715,6 +718,7 @@ void
dsl_destroy_head_sync_impl(dsl_dataset_t *ds, dmu_tx_t *tx) dsl_destroy_head_sync_impl(dsl_dataset_t *ds, dmu_tx_t *tx)
{ {
dsl_pool_t *dp = dmu_tx_pool(tx); dsl_pool_t *dp = dmu_tx_pool(tx);
spa_feature_t f;
objset_t *mos = dp->dp_meta_objset; objset_t *mos = dp->dp_meta_objset;
uint64_t obj, ddobj, prevobj = 0; uint64_t obj, ddobj, prevobj = 0;
boolean_t rmorigin; boolean_t rmorigin;
@ -742,13 +746,17 @@ dsl_destroy_head_sync_impl(dsl_dataset_t *ds, dmu_tx_t *tx)
ASSERT0(ds->ds_reserved); ASSERT0(ds->ds_reserved);
} }
if (ds->ds_large_blocks) obj = ds->ds_object;
spa_feature_decr(dp->dp_spa, SPA_FEATURE_LARGE_BLOCKS, tx);
for (f = 0; f < SPA_FEATURES; f++) {
if (ds->ds_feature_inuse[f]) {
dsl_dataset_deactivate_feature(obj, f, tx);
ds->ds_feature_inuse[f] = B_FALSE;
}
}
dsl_scan_ds_destroyed(ds, tx); dsl_scan_ds_destroyed(ds, tx);
obj = ds->ds_object;
if (dsl_dataset_phys(ds)->ds_prev_snap_obj != 0) { if (dsl_dataset_phys(ds)->ds_prev_snap_obj != 0) {
/* This is a clone */ /* This is a clone */
ASSERT(ds->ds_prev != NULL); ASSERT(ds->ds_prev != NULL);

View File

@ -20,7 +20,7 @@
*/ */
/* /*
* Copyright (c) 2013 by Delphix. All rights reserved. * Copyright (c) 2011, 2015 by Delphix. All rights reserved.
*/ */
#include <sys/zfs_context.h> #include <sys/zfs_context.h>
@ -253,7 +253,7 @@ feature_get_refcount_from_disk(spa_t *spa, zfeature_info_t *feature,
{ {
int err; int err;
uint64_t refcount; uint64_t refcount;
uint64_t zapobj = feature->fi_can_readonly ? uint64_t zapobj = (feature->fi_flags & ZFEATURE_FLAG_READONLY_COMPAT) ?
spa->spa_feat_for_write_obj : spa->spa_feat_for_read_obj; spa->spa_feat_for_write_obj : spa->spa_feat_for_read_obj;
/* /*
@ -306,7 +306,7 @@ feature_sync(spa_t *spa, zfeature_info_t *feature, uint64_t refcount,
uint64_t zapobj; uint64_t zapobj;
ASSERT(VALID_FEATURE_OR_NONE(feature->fi_feature)); ASSERT(VALID_FEATURE_OR_NONE(feature->fi_feature));
zapobj = feature->fi_can_readonly ? zapobj = (feature->fi_flags & ZFEATURE_FLAG_READONLY_COMPAT) ?
spa->spa_feat_for_write_obj : spa->spa_feat_for_read_obj; spa->spa_feat_for_write_obj : spa->spa_feat_for_read_obj;
VERIFY0(zap_update(spa->spa_meta_objset, zapobj, feature->fi_guid, VERIFY0(zap_update(spa->spa_meta_objset, zapobj, feature->fi_guid,
sizeof (uint64_t), 1, &refcount, tx)); sizeof (uint64_t), 1, &refcount, tx));
@ -327,7 +327,7 @@ feature_sync(spa_t *spa, zfeature_info_t *feature, uint64_t refcount,
if (refcount == 0) if (refcount == 0)
spa_deactivate_mos_feature(spa, feature->fi_guid); spa_deactivate_mos_feature(spa, feature->fi_guid);
else if (feature->fi_mos) else if (feature->fi_flags & ZFEATURE_FLAG_MOS)
spa_activate_mos_feature(spa, feature->fi_guid, tx); spa_activate_mos_feature(spa, feature->fi_guid, tx);
} }
@ -338,8 +338,9 @@ feature_sync(spa_t *spa, zfeature_info_t *feature, uint64_t refcount,
void void
feature_enable_sync(spa_t *spa, zfeature_info_t *feature, dmu_tx_t *tx) feature_enable_sync(spa_t *spa, zfeature_info_t *feature, dmu_tx_t *tx)
{ {
uint64_t initial_refcount = feature->fi_activate_on_enable ? 1 : 0; uint64_t initial_refcount =
uint64_t zapobj = feature->fi_can_readonly ? (feature->fi_flags & ZFEATURE_FLAG_ACTIVATE_ON_ENABLE) ? 1 : 0;
uint64_t zapobj = (feature->fi_flags & ZFEATURE_FLAG_READONLY_COMPAT) ?
spa->spa_feat_for_write_obj : spa->spa_feat_for_read_obj; spa->spa_feat_for_write_obj : spa->spa_feat_for_read_obj;
int i; int i;
@ -385,7 +386,8 @@ feature_do_action(spa_t *spa, spa_feature_t fid, feature_action_t action,
{ {
uint64_t refcount = 0; uint64_t refcount = 0;
zfeature_info_t *feature = &spa_feature_table[fid]; zfeature_info_t *feature = &spa_feature_table[fid];
ASSERTV(uint64_t zapobj = feature->fi_can_readonly ? ASSERTV(uint64_t zapobj =
(feature->fi_flags & ZFEATURE_FLAG_READONLY_COMPAT) ?
spa->spa_feat_for_write_obj : spa->spa_feat_for_read_obj); spa->spa_feat_for_write_obj : spa->spa_feat_for_read_obj);
ASSERT(VALID_FEATURE_FID(fid)); ASSERT(VALID_FEATURE_FID(fid));

View File

@ -20,7 +20,7 @@
*/ */
/* /*
* Copyright (c) 2013 by Delphix. All rights reserved. * Copyright (c) 2011, 2015 by Delphix. All rights reserved.
* Copyright (c) 2013 by Saso Kiselkov. All rights reserved. * Copyright (c) 2013 by Saso Kiselkov. All rights reserved.
* Copyright (c) 2014, Nexenta Systems, Inc. All rights reserved. * Copyright (c) 2014, Nexenta Systems, Inc. All rights reserved.
*/ */
@ -135,15 +135,15 @@ zfeature_depends_on(spa_feature_t fid, spa_feature_t check) {
static void static void
zfeature_register(spa_feature_t fid, const char *guid, const char *name, zfeature_register(spa_feature_t fid, const char *guid, const char *name,
const char *desc, boolean_t readonly, boolean_t mos, const char *desc, zfeature_flags_t flags, const spa_feature_t *deps)
boolean_t activate_on_enable, const spa_feature_t *deps)
{ {
zfeature_info_t *feature = &spa_feature_table[fid]; zfeature_info_t *feature = &spa_feature_table[fid];
static spa_feature_t nodeps[] = { SPA_FEATURE_NONE }; static spa_feature_t nodeps[] = { SPA_FEATURE_NONE };
ASSERT(name != NULL); ASSERT(name != NULL);
ASSERT(desc != NULL); ASSERT(desc != NULL);
ASSERT(!readonly || !mos); ASSERT((flags & ZFEATURE_FLAG_READONLY_COMPAT) == 0 ||
(flags & ZFEATURE_FLAG_MOS) == 0);
ASSERT3U(fid, <, SPA_FEATURES); ASSERT3U(fid, <, SPA_FEATURES);
ASSERT(zfeature_is_valid_guid(guid)); ASSERT(zfeature_is_valid_guid(guid));
@ -154,9 +154,7 @@ zfeature_register(spa_feature_t fid, const char *guid, const char *name,
feature->fi_guid = guid; feature->fi_guid = guid;
feature->fi_uname = name; feature->fi_uname = name;
feature->fi_desc = desc; feature->fi_desc = desc;
feature->fi_can_readonly = readonly; feature->fi_flags = flags;
feature->fi_mos = mos;
feature->fi_activate_on_enable = activate_on_enable;
feature->fi_depends = deps; feature->fi_depends = deps;
} }
@ -165,28 +163,28 @@ zpool_feature_init(void)
{ {
zfeature_register(SPA_FEATURE_ASYNC_DESTROY, zfeature_register(SPA_FEATURE_ASYNC_DESTROY,
"com.delphix:async_destroy", "async_destroy", "com.delphix:async_destroy", "async_destroy",
"Destroy filesystems asynchronously.", B_TRUE, B_FALSE, "Destroy filesystems asynchronously.",
B_FALSE, NULL); ZFEATURE_FLAG_READONLY_COMPAT, NULL);
zfeature_register(SPA_FEATURE_EMPTY_BPOBJ, zfeature_register(SPA_FEATURE_EMPTY_BPOBJ,
"com.delphix:empty_bpobj", "empty_bpobj", "com.delphix:empty_bpobj", "empty_bpobj",
"Snapshots use less space.", B_TRUE, B_FALSE, "Snapshots use less space.",
B_FALSE, NULL); ZFEATURE_FLAG_READONLY_COMPAT, NULL);
zfeature_register(SPA_FEATURE_LZ4_COMPRESS, zfeature_register(SPA_FEATURE_LZ4_COMPRESS,
"org.illumos:lz4_compress", "lz4_compress", "org.illumos:lz4_compress", "lz4_compress",
"LZ4 compression algorithm support.", B_FALSE, B_FALSE, "LZ4 compression algorithm support.",
B_TRUE, NULL); ZFEATURE_FLAG_ACTIVATE_ON_ENABLE, NULL);
zfeature_register(SPA_FEATURE_SPACEMAP_HISTOGRAM, zfeature_register(SPA_FEATURE_SPACEMAP_HISTOGRAM,
"com.delphix:spacemap_histogram", "spacemap_histogram", "com.delphix:spacemap_histogram", "spacemap_histogram",
"Spacemaps maintain space histograms.", B_TRUE, B_FALSE, "Spacemaps maintain space histograms.",
B_FALSE, NULL); ZFEATURE_FLAG_READONLY_COMPAT, NULL);
zfeature_register(SPA_FEATURE_ENABLED_TXG, zfeature_register(SPA_FEATURE_ENABLED_TXG,
"com.delphix:enabled_txg", "enabled_txg", "com.delphix:enabled_txg", "enabled_txg",
"Record txg at which a feature is enabled", B_TRUE, B_FALSE, "Record txg at which a feature is enabled",
B_FALSE, NULL); ZFEATURE_FLAG_READONLY_COMPAT, NULL);
{ {
static const spa_feature_t hole_birth_deps[] = { static const spa_feature_t hole_birth_deps[] = {
@ -196,13 +194,14 @@ zpool_feature_init(void)
zfeature_register(SPA_FEATURE_HOLE_BIRTH, zfeature_register(SPA_FEATURE_HOLE_BIRTH,
"com.delphix:hole_birth", "hole_birth", "com.delphix:hole_birth", "hole_birth",
"Retain hole birth txg for more precise zfs send", "Retain hole birth txg for more precise zfs send",
B_FALSE, B_TRUE, B_TRUE, hole_birth_deps); ZFEATURE_FLAG_MOS | ZFEATURE_FLAG_ACTIVATE_ON_ENABLE,
hole_birth_deps);
} }
zfeature_register(SPA_FEATURE_EXTENSIBLE_DATASET, zfeature_register(SPA_FEATURE_EXTENSIBLE_DATASET,
"com.delphix:extensible_dataset", "extensible_dataset", "com.delphix:extensible_dataset", "extensible_dataset",
"Enhanced dataset functionality, used by other features.", "Enhanced dataset functionality, used by other features.",
B_FALSE, B_FALSE, B_FALSE, NULL); 0, NULL);
{ {
static const spa_feature_t bookmarks_deps[] = { static const spa_feature_t bookmarks_deps[] = {
@ -213,7 +212,7 @@ zpool_feature_init(void)
zfeature_register(SPA_FEATURE_BOOKMARKS, zfeature_register(SPA_FEATURE_BOOKMARKS,
"com.delphix:bookmarks", "bookmarks", "com.delphix:bookmarks", "bookmarks",
"\"zfs bookmark\" command", "\"zfs bookmark\" command",
B_TRUE, B_FALSE, B_FALSE, bookmarks_deps); ZFEATURE_FLAG_READONLY_COMPAT, bookmarks_deps);
} }
{ {
@ -223,14 +222,15 @@ zpool_feature_init(void)
}; };
zfeature_register(SPA_FEATURE_FS_SS_LIMIT, zfeature_register(SPA_FEATURE_FS_SS_LIMIT,
"com.joyent:filesystem_limits", "filesystem_limits", "com.joyent:filesystem_limits", "filesystem_limits",
"Filesystem and snapshot limits.", B_TRUE, B_FALSE, B_FALSE, "Filesystem and snapshot limits.",
filesystem_limits_deps); ZFEATURE_FLAG_READONLY_COMPAT, filesystem_limits_deps);
} }
zfeature_register(SPA_FEATURE_EMBEDDED_DATA, zfeature_register(SPA_FEATURE_EMBEDDED_DATA,
"com.delphix:embedded_data", "embedded_data", "com.delphix:embedded_data", "embedded_data",
"Blocks which compress very well use even less space.", "Blocks which compress very well use even less space.",
B_FALSE, B_TRUE, B_TRUE, NULL); ZFEATURE_FLAG_MOS | ZFEATURE_FLAG_ACTIVATE_ON_ENABLE,
NULL);
{ {
static const spa_feature_t large_blocks_deps[] = { static const spa_feature_t large_blocks_deps[] = {
@ -239,7 +239,7 @@ zpool_feature_init(void)
}; };
zfeature_register(SPA_FEATURE_LARGE_BLOCKS, zfeature_register(SPA_FEATURE_LARGE_BLOCKS,
"org.open-zfs:large_blocks", "large_blocks", "org.open-zfs:large_blocks", "large_blocks",
"Support for blocks larger than 128KB.", B_FALSE, B_FALSE, B_FALSE, "Support for blocks larger than 128KB.",
large_blocks_deps); ZFEATURE_FLAG_PER_DATASET, large_blocks_deps);
} }
} }