Add "compatibility" property for zpool feature sets

Property to allow sets of features to be specified; for compatibility
with specific versions / releases / external systems. Influences
the behavior of 'zpool upgrade' and 'zpool create'. Initial man
page changes and test cases included.

Brief synopsis:

zpool create -o compatibility=off|legacy|file[,file...] pool vdev...

compatibility = off : disable compatibility mode (enable all features)
compatibility = legacy : request that no features be enabled
compatibility = file[,file...] : read features from specified files.
Only features present in *all* files will be enabled on the
resulting pool. Filenames may be absolute, or relative to
/etc/zfs/compatibility.d or /usr/share/zfs/compatibility.d (/etc
checked first).

Only affects zpool create, zpool upgrade and zpool status.

ABI changes in libzfs:

* New function "zpool_load_compat" to load and parse compat sets.
* Add "zpool_compat_status_t" typedef for compatibility parse status.
* Add ZPOOL_PROP_COMPATIBILITY to the pool properties enum
* Add ZPOOL_STATUS_COMPATIBILITY_ERR to the pool status enum

An initial set of base compatibility sets are included in
cmd/zpool/compatibility.d, and the Makefile for cmd/zpool is
modified to install these in $pkgdatadir/compatibility.d and to
create symbolic links to a reasonable set of aliases.

Reviewed-by: ericloewe
Reviewed-by: Matthew Ahrens <mahrens@delphix.com>
Reviewed-by: Richard Laager <rlaager@wiktel.com>
Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov>
Signed-off-by: Colm Buckley <colm@tuatha.org>
Closes #11468
This commit is contained in:
Colm 2021-02-18 05:30:45 +00:00 committed by GitHub
parent 35ec51796f
commit 658fb8020f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
48 changed files with 5621 additions and 2954 deletions

View File

@ -39,7 +39,7 @@ include $(top_srcdir)/config/CppCheck.am
zpoolconfdir = $(sysconfdir)/zfs/zpool.d
zpoolexecdir = $(zfsexecdir)/zpool.d
EXTRA_DIST = zpool.d/README
EXTRA_DIST = zpool.d/README compatibility.d
dist_zpoolexec_SCRIPTS = \
zpool.d/dm-deps \
@ -129,6 +129,48 @@ zpoolconfdefaults = \
test_progress \
test_ended
zpoolcompatdir = $(pkgdatadir)/compatibility.d
dist_zpoolcompat_DATA = \
compatibility.d/compat-2018 \
compatibility.d/compat-2019 \
compatibility.d/compat-2020 \
compatibility.d/compat-2021 \
compatibility.d/freebsd-11.0 \
compatibility.d/freebsd-11.2 \
compatibility.d/freebsd-11.3 \
compatibility.d/freenas-9.10.2 \
compatibility.d/grub2 \
compatibility.d/openzfsonosx-1.7.0 \
compatibility.d/openzfsonosx-1.8.1 \
compatibility.d/openzfsonosx-1.9.3 \
compatibility.d/openzfs-2.0-freebsd \
compatibility.d/openzfs-2.0-linux \
compatibility.d/zol-0.6.5 \
compatibility.d/zol-0.7 \
compatibility.d/zol-0.8
# canonical <- alias symbolic link pairs
# eg: "2018" is a link to "compat-2018"
zpoolcompatlinks = \
"compat-2018 2018" \
"compat-2019 2019" \
"compat-2020 2020" \
"compat-2021 2021" \
"freebsd-11.0 freebsd-11.1" \
"freebsd-11.0 freenas-11.0" \
"freebsd-11.2 freenas-11.2" \
"freebsd-11.3 freebsd-11.4" \
"freebsd-11.3 freebsd-12.0" \
"freebsd-11.3 freebsd-12.1" \
"freebsd-11.3 freebsd-12.2" \
"freebsd-11.3 freenas-11.3" \
"freenas-11.0 freenas-11.1" \
"openzfsonosx-1.9.3 openzfsonosx-1.9.4" \
"openzfs-2.0-freebsd truenas-12.0" \
"zol-0.7 ubuntu-18.04" \
"zol-0.8 ubuntu-20.04"
install-data-hook:
$(MKDIR_P) "$(DESTDIR)$(zpoolconfdir)"
for f in $(zpoolconfdefaults); do \
@ -136,3 +178,6 @@ install-data-hook:
-L "$(DESTDIR)$(zpoolconfdir)/$${f}" || \
ln -s "$(zpoolexecdir)/$${f}" "$(DESTDIR)$(zpoolconfdir)"; \
done
for l in $(zpoolcompatlinks); do \
(cd "$(DESTDIR)$(zpoolcompatdir)"; ln -s $${l} ); \
done

View File

@ -0,0 +1,12 @@
# Features supported by all Tier 1 platforms as of 2018
async_destroy
bookmarks
embedded_data
empty_bpobj
enabled_txg
extensible_dataset
filesystem_limits
hole_birth
large_blocks
lz4_compress
spacemap_histogram

View File

@ -0,0 +1,15 @@
# Features supported by all Tier 1 platforms as of 2019
async_destroy
bookmarks
embedded_data
empty_bpobj
enabled_txg
extensible_dataset
filesystem_limits
hole_birth
large_blocks
lz4_compress
multi_vdev_crash_dump
sha512
skein
spacemap_histogram

View File

@ -0,0 +1,15 @@
# Features supported by all Tier 1 platforms as of 2020
async_destroy
bookmarks
embedded_data
empty_bpobj
enabled_txg
extensible_dataset
filesystem_limits
hole_birth
large_blocks
lz4_compress
multi_vdev_crash_dump
sha512
skein
spacemap_histogram

View File

@ -0,0 +1,19 @@
# Features supported by all Tier 1 platforms as of 2021
async_destroy
bookmarks
device_removal
embedded_data
empty_bpobj
enabled_txg
extensible_dataset
filesystem_limits
hole_birth
large_blocks
lz4_compress
multi_vdev_crash_dump
obsolete_counts
sha512
skein
spacemap_histogram
spacemap_v2
zpool_checkpoint

View File

@ -0,0 +1,15 @@
# Features supported by FreeBSD 11.0
async_destroy
bookmarks
embedded_data
empty_bpobj
enabled_txg
extensible_dataset
filesystem_limits
hole_birth
large_blocks
lz4_compress
multi_vdev_crash_dump
sha512
skein
spacemap_histogram

View File

@ -0,0 +1,18 @@
# Features supported by FreeBSD 11.2
async_destroy
bookmarks
device_removal
embedded_data
empty_bpobj
enabled_txg
extensible_dataset
filesystem_limits
hole_birth
large_blocks
lz4_compress
multi_vdev_crash_dump
obsolete_counts
sha512
skein
spacemap_histogram
zpool_checkpoint

View File

@ -0,0 +1,19 @@
# Features supported by FreeBSD 11.3
async_destroy
bookmarks
device_removal
embedded_data
empty_bpobj
enabled_txg
extensible_dataset
filesystem_limits
hole_birth
large_blocks
lz4_compress
multi_vdev_crash_dump
obsolete_counts
sha512
skein
spacemap_histogram
spacemap_v2
zpool_checkpoint

View File

@ -0,0 +1,13 @@
# Features supported by FreeNAS 9.10.2
async_destroy
bookmarks
embedded_data
empty_bpobj
enabled_txg
extensible_dataset
filesystem_limits
hole_birth
large_blocks
lz4_compress
multi_vdev_crash_dump
spacemap_histogram

View File

@ -0,0 +1,12 @@
# Features which are supported by GRUB2
async_destroy
bookmarks
embedded_data
empty_bpobj
enabled_txg
extensible_dataset
filesystem_limits
hole_birth
large_blocks
lz4_compress
spacemap_histogram

View File

@ -0,0 +1,33 @@
# Features supported by OpenZFS 2.0 on FreeBSD
allocation_classes
async_destroy
bookmark_v2
bookmark_written
bookmarks
device_rebuild
device_removal
embedded_data
empty_bpobj
enabled_txg
encryption
extensible_dataset
filesystem_limits
hole_birth
large_blocks
large_dnode
livelist
log_spacemap
lz4_compress
multi_vdev_crash_dump
obsolete_counts
project_quota
redacted_datasets
redaction_bookmarks
resilver_defer
sha512
skein
spacemap_histogram
spacemap_v2
userobj_accounting
zpool_checkpoint
zstd_compress

View File

@ -0,0 +1,34 @@
# Features supported by OpenZFS 2.0 on Linux
allocation_classes
async_destroy
bookmark_v2
bookmark_written
bookmarks
device_rebuild
device_removal
edonr
embedded_data
empty_bpobj
enabled_txg
encryption
extensible_dataset
filesystem_limits
hole_birth
large_blocks
large_dnode
livelist
log_spacemap
lz4_compress
multi_vdev_crash_dump
obsolete_counts
project_quota
redacted_datasets
redaction_bookmarks
resilver_defer
sha512
skein
spacemap_histogram
spacemap_v2
userobj_accounting
zpool_checkpoint
zstd_compress

View File

@ -0,0 +1,16 @@
# Features supported by OpenZFSonOSX 1.7.0
async_destroy
bookmarks
edonr
embedded_data
empty_bpobj
enabled_txg
extensible_dataset
filesystem_limits
hole_birth
large_blocks
lz4_compress
multi_vdev_crash_dump
sha512
skein
spacemap_histogram

View File

@ -0,0 +1,21 @@
# Features supported by OpenZFSonOSX 1.8.1
async_destroy
bookmarks
device_removal
edonr
embedded_data
empty_bpobj
enabled_txg
encryption
extensible_dataset
filesystem_limits
hole_birth
large_blocks
lz4_compress
multi_vdev_crash_dump
obsolete_counts
sha512
skein
spacemap_histogram
spacemap_v2
zpool_checkpoint

View File

@ -0,0 +1,27 @@
# Features supported by OpenZFSonOSX 1.9.3
allocation_classes
async_destroy
bookmark_v2
bookmarks
device_removal
edonr
embedded_data
empty_bpobj
enabled_txg
encryption
extensible_dataset
filesystem_limits
hole_birth
large_blocks
large_dnode
lz4_compress
multi_vdev_crash_dump
obsolete_counts
project_quota
resilver_defer
sha512
skein
spacemap_histogram
spacemap_v2
userobj_accounting
zpool_checkpoint

View File

@ -0,0 +1,12 @@
# Features supported by ZFSonLinux v0.6.5
async_destroy
bookmarks
embedded_data
empty_bpobj
enabled_txg
extensible_dataset
filesystem_limits
hole_birth
large_blocks
lz4_compress
spacemap_histogram

View File

@ -0,0 +1,18 @@
# Features supported by ZFSonLinux v0.7
async_destroy
bookmarks
edonr
embedded_data
empty_bpobj
enabled_txg
extensible_dataset
filesystem_limits
hole_birth
large_blocks
large_dnode
lz4_compress
multi_vdev_crash_dump
sha512
skein
spacemap_histogram
userobj_accounting

View File

@ -0,0 +1,27 @@
# Features supported by ZFSonLinux v0.8
allocation_classes
async_destroy
bookmark_v2
bookmarks
device_removal
edonr
embedded_data
empty_bpobj
enabled_txg
encryption
extensible_dataset
filesystem_limits
hole_birth
large_blocks
large_dnode
lz4_compress
multi_vdev_crash_dump
obsolete_counts
project_quota
resilver_defer
sha512
skein
spacemap_histogram
spacemap_v2
userobj_accounting
zpool_checkpoint

View File

@ -31,6 +31,7 @@
* Copyright (c) 2017 Open-E, Inc. All Rights Reserved.
* Copyright (c) 2017, Intel Corporation.
* Copyright (c) 2019, loli10K <ezomori.nozomu@gmail.com>
* Copyright (c) 2021, Colm Buckley <colm@tuatha.org>
*/
#include <assert.h>
@ -124,6 +125,9 @@ static int zpool_do_version(int, char **);
static int zpool_do_wait(int, char **);
static zpool_compat_status_t zpool_do_load_compat(
const char *, boolean_t *);
/*
* These libumem hooks provide a reasonable set of defaults for the allocator's
* debugging facilities.
@ -782,6 +786,8 @@ add_prop_list(const char *propname, char *propval, nvlist_t **props,
if (poolprop) {
const char *vname = zpool_prop_to_name(ZPOOL_PROP_VERSION);
const char *fname =
zpool_prop_to_name(ZPOOL_PROP_COMPATIBILITY);
if ((prop = zpool_name_to_prop(propname)) == ZPOOL_PROP_INVAL &&
!zpool_prop_feature(propname)) {
@ -804,6 +810,19 @@ add_prop_list(const char *propname, char *propval, nvlist_t **props,
return (2);
}
/*
* compatibility property and version should not be specified
* at the same time.
*/
if ((prop == ZPOOL_PROP_COMPATIBILITY &&
nvlist_exists(proplist, vname)) ||
(prop == ZPOOL_PROP_VERSION &&
nvlist_exists(proplist, fname))) {
(void) fprintf(stderr, gettext("'compatibility' and "
"'version' properties cannot be specified "
"together\n"));
return (2);
}
if (zpool_prop_feature(propname))
normnm = propname;
@ -1374,13 +1393,15 @@ zpool_do_create(int argc, char **argv)
{
boolean_t force = B_FALSE;
boolean_t dryrun = B_FALSE;
boolean_t enable_all_pool_feat = B_TRUE;
boolean_t enable_pool_features = B_TRUE;
int c;
nvlist_t *nvroot = NULL;
char *poolname;
char *tname = NULL;
int ret = 1;
char *altroot = NULL;
char *compat = NULL;
char *mountpoint = NULL;
nvlist_t *fsprops = NULL;
nvlist_t *props = NULL;
@ -1396,7 +1417,7 @@ zpool_do_create(int argc, char **argv)
dryrun = B_TRUE;
break;
case 'd':
enable_all_pool_feat = B_FALSE;
enable_pool_features = B_FALSE;
break;
case 'R':
altroot = optarg;
@ -1434,11 +1455,14 @@ zpool_do_create(int argc, char **argv)
ver = strtoull(propval, &end, 10);
if (*end == '\0' &&
ver < SPA_VERSION_FEATURES) {
enable_all_pool_feat = B_FALSE;
enable_pool_features = B_FALSE;
}
}
if (zpool_name_to_prop(optarg) == ZPOOL_PROP_ALTROOT)
altroot = propval;
if (zpool_name_to_prop(optarg) ==
ZPOOL_PROP_COMPATIBILITY)
compat = propval;
break;
case 'O':
if ((propval = strchr(optarg, '=')) == NULL) {
@ -1632,10 +1656,26 @@ zpool_do_create(int argc, char **argv)
ret = 0;
} else {
/*
* Hand off to libzfs.
* Load in feature set.
* Note: if compatibility property not given, we'll have
* NULL, which means 'all features'.
*/
spa_feature_t i;
for (i = 0; i < SPA_FEATURES; i++) {
boolean_t requested_features[SPA_FEATURES];
if (zpool_do_load_compat(compat, requested_features) !=
ZPOOL_COMPATIBILITY_OK)
goto errout;
/*
* props contains list of features to enable.
* For each feature:
* - remove it if feature@name=disabled
* - leave it there if feature@name=enabled
* - add it if:
* - enable_pool_features (ie: no '-d' or '-o version')
* - it's supported by the kernel module
* - it's in the requested feature set
*/
for (spa_feature_t i = 0; i < SPA_FEATURES; i++) {
char propname[MAXPATHLEN];
char *propval;
zfeature_info_t *feat = &spa_feature_table[i];
@ -1643,18 +1683,14 @@ zpool_do_create(int argc, char **argv)
(void) snprintf(propname, sizeof (propname),
"feature@%s", feat->fi_uname);
/*
* Only features contained in props will be enabled:
* remove from the nvlist every ZFS_FEATURE_DISABLED
* value and add every missing ZFS_FEATURE_ENABLED if
* enable_all_pool_feat is set.
*/
if (!nvlist_lookup_string(props, propname, &propval)) {
if (strcmp(propval, ZFS_FEATURE_DISABLED) == 0)
(void) nvlist_remove_all(props,
propname);
} else if (enable_all_pool_feat &&
feat->fi_zfs_mod_supported) {
} else if (
enable_pool_features &&
feat->fi_zfs_mod_supported &&
requested_features[i]) {
ret = add_prop_list(propname,
ZFS_FEATURE_ENABLED, &props, B_TRUE);
if (ret != 0)
@ -2674,8 +2710,15 @@ show_import(nvlist_t *config)
case ZPOOL_STATUS_FEAT_DISABLED:
printf_color(ANSI_BOLD, gettext("status: "));
printf_color(ANSI_YELLOW, gettext("Some supported features are "
"not enabled on the pool.\n"));
printf_color(ANSI_YELLOW, gettext("Some supported and "
"requested features are not enabled on the pool.\n"));
break;
case ZPOOL_STATUS_COMPATIBILITY_ERR:
printf_color(ANSI_BOLD, gettext("status: "));
printf_color(ANSI_YELLOW, gettext("Error reading or parsing "
"the file(s) indicated by the 'compatibility'\n"
"property.\n"));
break;
case ZPOOL_STATUS_UNSUP_FEAT_READ:
@ -2767,6 +2810,12 @@ show_import(nvlist_t *config)
"imported using its name or numeric identifier, "
"though\n\tsome features will not be available "
"without an explicit 'zpool upgrade'.\n"));
} else if (reason == ZPOOL_STATUS_COMPATIBILITY_ERR) {
(void) printf(gettext(" action: The pool can be "
"imported using its name or numeric\n\tidentifier, "
"though the file(s) indicated by its "
"'compatibility'\n\tproperty cannot be parsed at "
"this time.\n"));
} else if (reason == ZPOOL_STATUS_HOSTID_MISMATCH) {
(void) printf(gettext(" action: The pool can be "
"imported using its name or numeric "
@ -7942,7 +7991,8 @@ status_callback(zpool_handle_t *zhp, void *data)
if (cbp->cb_explain &&
(reason == ZPOOL_STATUS_OK ||
reason == ZPOOL_STATUS_VERSION_OLDER ||
reason == ZPOOL_STATUS_FEAT_DISABLED)) {
reason == ZPOOL_STATUS_FEAT_DISABLED ||
reason == ZPOOL_STATUS_COMPATIBILITY_ERR)) {
if (!cbp->cb_allpools) {
(void) printf(gettext("pool '%s' is healthy\n"),
zpool_get_name(zhp));
@ -8117,9 +8167,10 @@ status_callback(zpool_handle_t *zhp, void *data)
case ZPOOL_STATUS_FEAT_DISABLED:
printf_color(ANSI_BOLD, gettext("status: "));
printf_color(ANSI_YELLOW, gettext("Some supported features are "
"not enabled on the pool. The pool can\n\tstill be used, "
"but some features are unavailable.\n"));
printf_color(ANSI_YELLOW, gettext("Some supported and "
"requested features are not enabled on the pool.\n\t"
"The pool can still be used, but some features are "
"unavailable.\n"));
printf_color(ANSI_BOLD, gettext("action: "));
printf_color(ANSI_YELLOW, gettext("Enable all features using "
"'zpool upgrade'. Once this is done,\n\tthe pool may no "
@ -8127,6 +8178,19 @@ status_callback(zpool_handle_t *zhp, void *data)
"the features. See zpool-features(5) for details.\n"));
break;
case ZPOOL_STATUS_COMPATIBILITY_ERR:
printf_color(ANSI_BOLD, gettext("status: "));
printf_color(ANSI_YELLOW, gettext("This pool has a "
"compatibility list specified, but it could not be\n\t"
"read/parsed at this time. The pool can still be used, "
"but this\n\tshould be investigated.\n"));
printf_color(ANSI_BOLD, gettext("action: "));
printf_color(ANSI_YELLOW, gettext("Check the value of the "
"'compatibility' property against the\n\t"
"appropriate file in " ZPOOL_SYSCONF_COMPAT_D " or "
ZPOOL_DATA_COMPAT_D ".\n"));
break;
case ZPOOL_STATUS_UNSUP_FEAT_READ:
printf_color(ANSI_BOLD, gettext("status: "));
printf_color(ANSI_YELLOW, gettext("The pool cannot be accessed "
@ -8625,6 +8689,16 @@ upgrade_enable_all(zpool_handle_t *zhp, int *countp)
boolean_t firstff = B_TRUE;
nvlist_t *enabled = zpool_get_features(zhp);
char compat[ZFS_MAXPROPLEN];
if (zpool_get_prop(zhp, ZPOOL_PROP_COMPATIBILITY, compat,
ZFS_MAXPROPLEN, NULL, B_FALSE) != 0)
compat[0] = '\0';
boolean_t requested_features[SPA_FEATURES];
if (zpool_do_load_compat(compat, requested_features) !=
ZPOOL_COMPATIBILITY_OK)
return (-1);
count = 0;
for (i = 0; i < SPA_FEATURES; i++) {
const char *fname = spa_feature_table[i].fi_uname;
@ -8633,7 +8707,7 @@ upgrade_enable_all(zpool_handle_t *zhp, int *countp)
if (!spa_feature_table[i].fi_zfs_mod_supported)
continue;
if (!nvlist_exists(enabled, fguid)) {
if (!nvlist_exists(enabled, fguid) && requested_features[i]) {
char *propname;
verify(-1 != asprintf(&propname, "feature@%s", fname));
ret = zpool_set_prop(zhp, propname,
@ -8855,7 +8929,7 @@ upgrade_one(zpool_handle_t *zhp, void *data)
printnl = B_TRUE;
} else if (cur_version == SPA_VERSION) {
(void) printf(gettext("Pool '%s' already has all "
"supported features enabled.\n"),
"supported and requested features enabled.\n"),
zpool_get_name(zhp));
}
}
@ -9016,8 +9090,8 @@ zpool_do_upgrade(int argc, char **argv)
(void) printf(gettext("All pools are already "
"formatted using feature flags.\n\n"));
(void) printf(gettext("Every feature flags "
"pool already has all supported features "
"enabled.\n"));
"pool already has all supported and "
"requested features enabled.\n"));
} else {
(void) printf(gettext("All pools are already "
"formatted with version %llu or higher.\n"),
@ -9043,7 +9117,7 @@ zpool_do_upgrade(int argc, char **argv)
if (cb.cb_first) {
(void) printf(gettext("Every feature flags pool has "
"all supported features enabled.\n"));
"all supported and requested features enabled.\n"));
} else {
(void) printf(gettext("\n"));
}
@ -10347,6 +10421,39 @@ zpool_do_version(int argc, char **argv)
return (0);
}
/*
* Do zpool_load_compat() and print error message on failure
*/
static zpool_compat_status_t
zpool_do_load_compat(const char *compat, boolean_t *list)
{
char badword[ZFS_MAXPROPLEN];
char badfile[MAXPATHLEN];
zpool_compat_status_t ret;
switch (ret = zpool_load_compat(compat, list, badword, badfile)) {
case ZPOOL_COMPATIBILITY_OK:
break;
case ZPOOL_COMPATIBILITY_READERR:
(void) fprintf(stderr, gettext("error reading compatibility "
"file '%s'\n"), badfile);
break;
case ZPOOL_COMPATIBILITY_BADFILE:
(void) fprintf(stderr, gettext("compatibility file '%s' "
"too large or not newline-terminated\n"), badfile);
break;
case ZPOOL_COMPATIBILITY_BADWORD:
(void) fprintf(stderr, gettext("unknown feature '%s' in "
"compatibility file '%s'\n"), badword, badfile);
break;
case ZPOOL_COMPATIBILITY_NOFILES:
(void) fprintf(stderr, gettext("no compatibility files "
"specified\n"));
break;
}
return (ret);
}
int
main(int argc, char **argv)
{

View File

@ -44,6 +44,7 @@ AM_CPPFLAGS += -DLIBEXECDIR=\"$(libexecdir)\"
AM_CPPFLAGS += -DRUNSTATEDIR=\"$(runstatedir)\"
AM_CPPFLAGS += -DSBINDIR=\"$(sbindir)\"
AM_CPPFLAGS += -DSYSCONFDIR=\"$(sysconfdir)\"
AM_CPPFLAGS += -DPKGDATADIR=\"$(pkgdatadir)\"
AM_CPPFLAGS += $(DEBUG_CPPFLAGS)
AM_CPPFLAGS += $(CODE_COVERAGE_CPPFLAGS)
if BUILD_LINUX

View File

@ -28,6 +28,7 @@
* Copyright 2016 Nexenta Systems, Inc.
* Copyright (c) 2017 Open-E, Inc. All Rights Reserved.
* Copyright (c) 2019 Datto Inc.
* Copyright (c) 2021, Colm Buckley <colm@tuatha.org>
*/
#ifndef _LIBZFS_H
@ -391,6 +392,7 @@ typedef enum {
ZPOOL_STATUS_REBUILDING, /* device being rebuilt */
ZPOOL_STATUS_REBUILD_SCRUB, /* recommend scrubbing the pool */
ZPOOL_STATUS_NON_NATIVE_ASHIFT, /* (e.g. 512e dev with ashift of 9) */
ZPOOL_STATUS_COMPATIBILITY_ERR, /* bad 'compatibility' property */
/*
* Finally, the following indicates a healthy pool.
@ -912,6 +914,20 @@ int zfs_smb_acl_rename(libzfs_handle_t *, char *, char *, char *, char *);
extern int zpool_enable_datasets(zpool_handle_t *, const char *, int);
extern int zpool_disable_datasets(zpool_handle_t *, boolean_t);
/*
* Parse a features file for -o compatibility
*/
typedef enum {
ZPOOL_COMPATIBILITY_OK,
ZPOOL_COMPATIBILITY_READERR,
ZPOOL_COMPATIBILITY_BADFILE,
ZPOOL_COMPATIBILITY_BADWORD,
ZPOOL_COMPATIBILITY_NOFILES
} zpool_compat_status_t;
extern zpool_compat_status_t zpool_load_compat(const char *,
boolean_t *, char *, char *);
#ifdef __FreeBSD__
/*

View File

@ -27,10 +27,10 @@
* Copyright (c) 2014 Integros [integros.com]
* Copyright (c) 2017, Intel Corporation.
* Copyright (c) 2019 Datto Inc.
* Portions Copyright 2010 Robert Milkowski
* Copyright (c) 2021, Colm Buckley <colm@tuatha.org>
*/
/* Portions Copyright 2010 Robert Milkowski */
#ifndef _SYS_FS_ZFS_H
#define _SYS_FS_ZFS_H
@ -246,6 +246,7 @@ typedef enum {
ZPOOL_PROP_CHECKPOINT,
ZPOOL_PROP_LOAD_GUID,
ZPOOL_PROP_AUTOTRIM,
ZPOOL_PROP_COMPATIBILITY,
ZPOOL_NUM_PROPS
} zpool_prop_t;
@ -733,6 +734,7 @@ typedef struct zpool_load_policy {
#define ZPOOL_CONFIG_ALLOCATION_BIAS "alloc_bias" /* not stored on disk */
#define ZPOOL_CONFIG_EXPANSION_TIME "expansion_time" /* not stored */
#define ZPOOL_CONFIG_REBUILD_STATS "org.openzfs:rebuild_stats"
#define ZPOOL_CONFIG_COMPATIBILITY "compatibility"
/*
* The persistent vdev state is stored as separate values rather than a single
@ -845,6 +847,19 @@ typedef struct zpool_load_policy {
*/
#define ZPOOL_CACHE_BOOT "/boot/zfs/zpool.cache"
#define ZPOOL_CACHE "/etc/zfs/zpool.cache"
/*
* Settings for zpool compatibility features files
*/
#define ZPOOL_SYSCONF_COMPAT_D SYSCONFDIR "/zfs/compatibility.d"
#define ZPOOL_DATA_COMPAT_D PKGDATADIR "/compatibility.d"
#define ZPOOL_COMPAT_MAXSIZE 16384
/*
* Hard-wired compatibility settings
*/
#define ZPOOL_COMPAT_LEGACY "legacy"
#define ZPOOL_COMPAT_OFF "off"
/*
* vdev states are ordered from least to most healthy.
* A vdev that's CANT_OPEN or below is considered unusable.

View File

@ -424,6 +424,8 @@ struct spa {
int spa_waiters; /* number of waiting threads */
boolean_t spa_waiters_cancel; /* waiters should return */
char *spa_compatibility; /* compatibility file(s) */
/*
* spa_refcount & spa_config_lock must be the last elements
* because zfs_refcount_t changes size based on compilation options.

File diff suppressed because it is too large Load Diff

View File

@ -28,6 +28,7 @@
* Copyright (c) 2017 Open-E, Inc. All Rights Reserved.
* Copyright (c) 2017, Intel Corporation.
* Copyright (c) 2018, loli10K <ezomori.nozomu@gmail.com>
* Copyright (c) 2021, Colm Buckley <colm@tuatha.org>
*/
#include <errno.h>
@ -44,8 +45,12 @@
#include <sys/zfs_ioctl.h>
#include <sys/zfs_sysfs.h>
#include <sys/vdev_disk.h>
#include <sys/types.h>
#include <dlfcn.h>
#include <libzutil.h>
#include <fcntl.h>
#include <unistd.h>
#include "zfs_namecheck.h"
#include "zfs_prop.h"
#include "libzfs_impl.h"
@ -302,6 +307,7 @@ zpool_get_prop(zpool_handle_t *zhp, zpool_prop_t prop, char *buf,
case ZPOOL_PROP_ALTROOT:
case ZPOOL_PROP_CACHEFILE:
case ZPOOL_PROP_COMMENT:
case ZPOOL_PROP_COMPATIBILITY:
if (zhp->zpool_props != NULL ||
zpool_get_all_props(zhp) == 0) {
(void) strlcpy(buf,
@ -462,6 +468,8 @@ zpool_valid_proplist(libzfs_handle_t *hdl, const char *poolname,
char *slash, *check;
struct stat64 statbuf;
zpool_handle_t *zhp;
char badword[ZFS_MAXPROPLEN];
char badfile[MAXPATHLEN];
if (nvlist_alloc(&retprops, NV_UNIQUE_NAME, 0) != 0) {
(void) no_memory(hdl);
@ -671,6 +679,39 @@ zpool_valid_proplist(libzfs_handle_t *hdl, const char *poolname,
*slash = '/';
break;
case ZPOOL_PROP_COMPATIBILITY:
switch (zpool_load_compat(strval, NULL,
badword, badfile)) {
case ZPOOL_COMPATIBILITY_OK:
break;
case ZPOOL_COMPATIBILITY_READERR:
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
"error reading feature file '%s'"),
badfile);
(void) zfs_error(hdl, EZFS_BADPROP, errbuf);
goto error;
case ZPOOL_COMPATIBILITY_BADFILE:
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
"feature file '%s' too large or not "
"newline-terminated"),
badfile);
(void) zfs_error(hdl, EZFS_BADPROP, errbuf);
goto error;
case ZPOOL_COMPATIBILITY_BADWORD:
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
"unknown feature '%s' in feature "
"file '%s'"),
badword, badfile);
(void) zfs_error(hdl, EZFS_BADPROP, errbuf);
goto error;
case ZPOOL_COMPATIBILITY_NOFILES:
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
"no feature files specified"));
(void) zfs_error(hdl, EZFS_BADPROP, errbuf);
goto error;
}
break;
case ZPOOL_PROP_COMMENT:
for (check = strval; *check != '\0'; check++) {
if (!isprint(*check)) {
@ -4663,3 +4704,190 @@ zpool_get_bootenv(zpool_handle_t *zhp, nvlist_t **nvlp)
return (error);
}
/*
* Attempt to read and parse feature file(s) (from "compatibility" property).
* Files contain zpool feature names, comma or whitespace-separated.
* Comments (# character to next newline) are discarded.
*
* Arguments:
* compatibility : string containing feature filenames
* features : either NULL or pointer to array of boolean
* badtoken : either NULL or pointer to char[ZFS_MAXPROPLEN]
* badfile : either NULL or pointer to char[MAXPATHLEN]
*
* compatibility is NULL (unset), "", "off", "legacy", or list of
* comma-separated filenames. filenames should either be absolute,
* or relative to:
* 1) ZPOOL_SYSCONF_COMPAT_D (eg: /etc/zfs/compatibility.d) or
* 2) ZPOOL_DATA_COMPAT_D (eg: /usr/share/zfs/compatibility.d).
* (Unset), "" or "off" => enable all features
* "legacy" => disable all features
* Any feature names read from files which match unames in spa_feature_table
* will have the corresponding boolean set in the features array (if non-NULL).
* If more than one feature set specified, only features present in *all* of
* them will be set.
*
* An unreadable filename will be strlcpy'd to badfile (if non-NULL).
* An unrecognized feature will be strlcpy'd to badtoken (if non-NULL).
*
* Return values:
* ZPOOL_COMPATIBILITY_OK : files read and parsed ok
* ZPOOL_COMPATIBILITY_READERR : file could not be opened / mmap'd
* ZPOOL_COMPATIBILITY_BADFILE : file too big or not a text file
* ZPOOL_COMPATIBILITY_BADWORD : file contains invalid feature name
* ZPOOL_COMPATIBILITY_NOFILES : no file names found
*/
zpool_compat_status_t
zpool_load_compat(const char *compatibility,
boolean_t *features, char *badtoken, char *badfile)
{
int sdirfd, ddirfd, featfd;
int i;
struct stat fs;
char *fc; /* mmap of file */
char *ps, *ls, *ws; /* strtok state */
char *file, *line, *word;
char filenames[ZFS_MAXPROPLEN];
int filecount = 0;
/* special cases (unset), "" and "off" => enable all features */
if (compatibility == NULL || compatibility[0] == '\0' ||
strcmp(compatibility, ZPOOL_COMPAT_OFF) == 0) {
if (features != NULL)
for (i = 0; i < SPA_FEATURES; i++)
features[i] = B_TRUE;
return (ZPOOL_COMPATIBILITY_OK);
}
/* Final special case "legacy" => disable all features */
if (strcmp(compatibility, ZPOOL_COMPAT_LEGACY) == 0) {
if (features != NULL)
for (i = 0; i < SPA_FEATURES; i++)
features[i] = B_FALSE;
return (ZPOOL_COMPATIBILITY_OK);
}
/*
* Start with all true; will be ANDed with results from each file
*/
if (features != NULL)
for (i = 0; i < SPA_FEATURES; i++)
features[i] = B_TRUE;
/*
* We ignore errors from the directory open()
* as they're only needed if the filename is relative
* which will be checked during the openat().
*/
#ifdef O_PATH
sdirfd = open(ZPOOL_SYSCONF_COMPAT_D, O_DIRECTORY | O_PATH);
ddirfd = open(ZPOOL_DATA_COMPAT_D, O_DIRECTORY | O_PATH);
#else
sdirfd = open(ZPOOL_SYSCONF_COMPAT_D, O_DIRECTORY | O_RDONLY);
ddirfd = open(ZPOOL_DATA_COMPAT_D, O_DIRECTORY | O_RDONLY);
#endif
(void) strlcpy(filenames, compatibility, ZFS_MAXPROPLEN);
file = strtok_r(filenames, ",", &ps);
while (file != NULL) {
boolean_t features_local[SPA_FEATURES];
/* try sysconfdir first, then datadir */
if ((featfd = openat(sdirfd, file, 0, O_RDONLY)) < 0)
featfd = openat(ddirfd, file, 0, O_RDONLY);
if (featfd < 0 || fstat(featfd, &fs) < 0) {
(void) close(featfd);
(void) close(sdirfd);
(void) close(ddirfd);
if (badfile != NULL)
(void) strlcpy(badfile, file, MAXPATHLEN);
return (ZPOOL_COMPATIBILITY_READERR);
}
/* Too big or too small */
if (fs.st_size < 1 || fs.st_size > ZPOOL_COMPAT_MAXSIZE) {
(void) close(featfd);
(void) close(sdirfd);
(void) close(ddirfd);
if (badfile != NULL)
(void) strlcpy(badfile, file, MAXPATHLEN);
return (ZPOOL_COMPATIBILITY_BADFILE);
}
/* private mmap() so we can strtok safely */
fc = (char *)mmap(NULL, fs.st_size,
PROT_READ|PROT_WRITE, MAP_PRIVATE, featfd, 0);
(void) close(featfd);
if (fc < 0) {
(void) close(sdirfd);
(void) close(ddirfd);
if (badfile != NULL)
(void) strlcpy(badfile, file, MAXPATHLEN);
return (ZPOOL_COMPATIBILITY_READERR);
}
/* Text file sanity check - last char should be newline */
if (fc[fs.st_size - 1] != '\n') {
(void) munmap((void *) fc, fs.st_size);
(void) close(sdirfd);
(void) close(ddirfd);
if (badfile != NULL)
(void) strlcpy(badfile, file, MAXPATHLEN);
return (ZPOOL_COMPATIBILITY_BADFILE);
}
/* replace with NUL to ensure we have a delimiter */
fc[fs.st_size - 1] = '\0';
for (i = 0; i < SPA_FEATURES; i++)
features_local[i] = B_FALSE;
line = strtok_r(fc, "\n", &ls);
while (line != NULL) {
/* discard comments */
*(strchrnul(line, '#')) = '\0';
word = strtok_r(line, ", \t", &ws);
while (word != NULL) {
/* Find matching feature name */
for (i = 0; i < SPA_FEATURES; i++) {
zfeature_info_t *fi =
&spa_feature_table[i];
if (strcmp(word, fi->fi_uname) == 0) {
features_local[i] = B_TRUE;
break;
}
}
if (i == SPA_FEATURES) {
if (badtoken != NULL)
(void) strlcpy(badtoken, word,
ZFS_MAXPROPLEN);
if (badfile != NULL)
(void) strlcpy(badfile, file,
MAXPATHLEN);
(void) munmap((void *) fc, fs.st_size);
(void) close(sdirfd);
(void) close(ddirfd);
return (ZPOOL_COMPATIBILITY_BADWORD);
}
word = strtok_r(NULL, ", \t", &ws);
}
line = strtok_r(NULL, "\n", &ls);
}
(void) munmap((void *) fc, fs.st_size);
if (features != NULL) {
for (i = 0; i < SPA_FEATURES; i++)
features[i] &= features_local[i];
}
filecount++;
file = strtok_r(NULL, ",", &ps);
}
(void) close(sdirfd);
(void) close(ddirfd);
if (filecount == 0)
return (ZPOOL_COMPATIBILITY_NOFILES);
return (ZPOOL_COMPATIBILITY_OK);
}

View File

@ -23,6 +23,7 @@
* Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2012 by Delphix. All rights reserved.
* Copyright (c) 2013 Steven Hartland. All rights reserved.
* Copyright (c) 2021, Colm Buckley <colm@tuatha.org>
*/
/*
@ -87,6 +88,7 @@ static char *zfs_msgid_table[] = {
* ZPOOL_STATUS_REMOVED_DEV
* ZPOOL_STATUS_REBUILDING
* ZPOOL_STATUS_REBUILD_SCRUB
* ZPOOL_STATUS_COMPATIBILITY_ERR
* ZPOOL_STATUS_OK
*/
};
@ -218,7 +220,8 @@ find_vdev_problem(nvlist_t *vdev, int (*func)(vdev_stat_t *, uint_t),
* only picks the most damaging of all the current errors to report.
*/
static zpool_status_t
check_status(nvlist_t *config, boolean_t isimport, zpool_errata_t *erratap)
check_status(nvlist_t *config, boolean_t isimport,
zpool_errata_t *erratap, const char *compat)
{
nvlist_t *nvroot;
vdev_stat_t *vs;
@ -471,9 +474,16 @@ check_status(nvlist_t *config, boolean_t isimport, zpool_errata_t *erratap)
ZPOOL_CONFIG_FEATURE_STATS);
}
/* check against all features, or limited set? */
boolean_t pool_features[SPA_FEATURES];
if (zpool_load_compat(compat, pool_features, NULL, NULL) !=
ZPOOL_COMPATIBILITY_OK)
return (ZPOOL_STATUS_COMPATIBILITY_ERR);
for (i = 0; i < SPA_FEATURES; i++) {
zfeature_info_t *fi = &spa_feature_table[i];
if (!nvlist_exists(feat, fi->fi_guid))
if (pool_features[i] &&
!nvlist_exists(feat, fi->fi_guid))
return (ZPOOL_STATUS_FEAT_DISABLED);
}
}
@ -484,7 +494,18 @@ check_status(nvlist_t *config, boolean_t isimport, zpool_errata_t *erratap)
zpool_status_t
zpool_get_status(zpool_handle_t *zhp, char **msgid, zpool_errata_t *errata)
{
zpool_status_t ret = check_status(zhp->zpool_config, B_FALSE, errata);
/*
* pass in the desired feature set, as
* it affects check for disabled features
*/
char compatibility[ZFS_MAXPROPLEN];
if (zpool_get_prop(zhp, ZPOOL_PROP_COMPATIBILITY, compatibility,
ZFS_MAXPROPLEN, NULL, B_FALSE) != 0)
compatibility[0] = '\0';
zpool_status_t ret = check_status(zhp->zpool_config, B_FALSE, errata,
compatibility);
if (msgid != NULL) {
if (ret >= NMSGID)
*msgid = NULL;
@ -497,7 +518,7 @@ zpool_get_status(zpool_handle_t *zhp, char **msgid, zpool_errata_t *errata)
zpool_status_t
zpool_import_status(nvlist_t *config, char **msgid, zpool_errata_t *errata)
{
zpool_status_t ret = check_status(config, B_TRUE, errata);
zpool_status_t ret = check_status(config, B_TRUE, errata, NULL);
if (ret >= NMSGID)
*msgid = NULL;

View File

@ -24,6 +24,7 @@
* Copyright (c) 2012, 2018 by Delphix. All rights reserved.
* Copyright 2015 RackTop Systems.
* Copyright (c) 2016, Intel Corporation.
* Copyright (c) 2021, Colm Buckley <colm@tuatha.org>
*/
/*
@ -552,12 +553,14 @@ get_configs(libpc_handle_t *hdl, pool_list_t *pl, boolean_t active_ok,
* pool guid
* name
* comment (if available)
* compatibility features (if available)
* pool state
* hostid (if available)
* hostname (if available)
*/
uint64_t state, version;
char *comment = NULL;
char *compatibility = NULL;
version = fnvlist_lookup_uint64(tmp,
ZPOOL_CONFIG_VERSION);
@ -577,6 +580,13 @@ get_configs(libpc_handle_t *hdl, pool_list_t *pl, boolean_t active_ok,
fnvlist_add_string(config,
ZPOOL_CONFIG_COMMENT, comment);
if (nvlist_lookup_string(tmp,
ZPOOL_CONFIG_COMPATIBILITY,
&compatibility) == 0)
fnvlist_add_string(config,
ZPOOL_CONFIG_COMPATIBILITY,
compatibility);
state = fnvlist_lookup_uint64(tmp,
ZPOOL_CONFIG_POOL_STATE);
fnvlist_add_uint64(config,

View File

@ -16,6 +16,7 @@
.\" Portions Copyright [yyyy] [name of copyright owner]
.\" Copyright (c) 2019, Klara Inc.
.\" Copyright (c) 2019, Allan Jude
.\" Copyright (c) 2021, Colm Buckley <colm@tuatha.org>
.TH ZPOOL-FEATURES 5 "Aug 24, 2020" OpenZFS
.SH NAME
zpool\-features \- ZFS pool feature descriptions
@ -26,7 +27,8 @@ ZFS pool on\-disk format versions are specified via "features" which replace
the old on\-disk format numbers (the last supported on\-disk format number is
28). To enable a feature on a pool use the \fBupgrade\fR subcommand of the
zpool(8) command, or set the \fBfeature@\fR\fIfeature_name\fR property
to \fBenabled\fR.
to \fBenabled\fR. Please also see the \fB"Compatibility feature sets"\fR
section for information on how sets of features may be enabled together.
.sp
.LP
The pool format does not affect file system version compatibility or the ability
@ -140,6 +142,61 @@ read\-only mode.
Some features depend on other features being enabled in order to function
properly. Enabling a feature will automatically enable any features it
depends on.
.SS "Compatibility feature sets"
.sp
.LP
It is sometimes necessary for a pool to maintain compatibility with a
specific on\-disk format, by enabling and disabling particular features. The
\fBcompatibility\fR feature facilitates this by allowing feature sets to
be read from text files. When set to \fBoff\fR (the default); compatibility
feature sets are disabled (ie: all features are enabled); when set to
\fBlegacy\fR; no features are enabled. When set to a comma\-separated list
of filenames (each filename may either be an absolute path, or relative to
\fB/etc/zfs/compatibility.d\fR or \fB/usr/share/zfs/compatibility.d\fR)
the lists of requested features are read from those files, separated by
whitespace and/or commas. Only features present in all files are enabled.
.LP
Simple sanity checks are applied to the files; they must be between 1 and
16,384 bytes in size, and must end with a newline character.
.LP
The requested features are applied when a pool is created using
\fBzpool create \-o compatibility=...\fR and controls which features are
enabled when using \fBzpool upgrade\fR. \fBzpool status\fR
will not show a warning about disabled features which are not part
of the requested feature set.
.LP
By convention, compatibility files in \fB/usr/share/zfs/compatibility.d\fR
are provided by the distribution package, and include feature sets
supported by important versions of popular distribtions, and feature
sets commonly supported at the start of each year. Compatibility files
in \fB/etc/zfs/compatibility.d\fR, if present, will take precedence over
files with the same name in \fB/usr/share/zfs/compatibility.d\fR.
.LP
Compatibility files may include comments; any text from \fB#\fR to the end
of the line is ignored.
.LP
\fBExample:\fR
.EX
# \fBcat /usr/share/zfs/compatibility.d/grub2\fR
# Features which are supported by GRUB2
async_destroy
bookmarks
embedded_data
empty_bpobj
enabled_txg
extensible_dataset
filesystem_limits
hole_birth
large_blocks
lz4_compress
spacemap_histogram
# \fBzpool create \-o compatibility=grub2 bootpool vdev\fR
.EE
.LP
See \fBzpool\-create(8)\fR and \fBzpool\-upgrade(8)\fR for more information
on how these commands are affected by feature sets.
.SH FEATURES
.sp
.LP

View File

@ -26,6 +26,7 @@
.\" Copyright (c) 2018 George Melikov. All Rights Reserved.
.\" Copyright 2017 Nexenta Systems, Inc.
.\" Copyright (c) 2017 Open-E, Inc. All Rights Reserved.
.\" Copyright (c) 2021, Colm Buckley <colm@tuatha.org>
.\"
.Dd August 9, 2019
.Dt ZPOOL-CREATE 8
@ -40,6 +41,7 @@
.Op Fl m Ar mountpoint
.Oo Fl o Ar property Ns = Ns Ar value Oc Ns ...
.Oo Fl o Ar feature@feature Ns = Ns Ar value Oc
.Op Fl o Ar compatibility Ns = Ns Ar off | legacy | file Bq , Ns Ar file Ns ...
.Oo Fl O Ar file-system-property Ns = Ns Ar value Oc Ns ...
.Op Fl R Ar root
.Ar pool vdev Ns ...
@ -52,6 +54,7 @@
.Op Fl m Ar mountpoint
.Oo Fl o Ar property Ns = Ns Ar value Oc Ns ...
.Oo Fl o Ar feature@feature Ns = Ns Ar value Oc Ns ...
.Op Fl o Ar compatibility Ns = Ns Ar off | legacy | file Bq , Ns Ar file Ns ...
.Oo Fl O Ar file-system-property Ns = Ns Ar value Oc Ns ...
.Op Fl R Ar root
.Op Fl t Ar tname
@ -135,9 +138,14 @@ This can be overridden with the
.Fl m
option.
.Pp
By default all supported features are enabled on the new pool unless the
By default all supported features are enabled on the new pool. The
.Fl d
option is specified.
option or the
.Fl o Ar compatibility
property (eg:
.Fl o Ar compatibility=2020
) can be used to restrict the features that are enabled, so that the
pool can be imported on other releases of the ZFS software.
.Bl -tag -width Ds
.It Fl d
Do not enable any features on the new pool.
@ -179,6 +187,10 @@ Sets the given pool properties.
See the
.Xr zpoolprops
manual page for a list of valid properties that can be set.
.It Fl o Ar compatibility Ns = Ns Ar off | legacy | file Bq , Ns Ar file Ns ...
Specifies compatibility feature sets. See
.Xr zpool-features 5
for more information about compatibility feature sets.
.It Fl o Ar feature@feature Ns = Ns Ar value
Sets the given pool feature. See the
.Xr zpool-features 5

View File

@ -26,6 +26,7 @@
.\" Copyright (c) 2018 George Melikov. All Rights Reserved.
.\" Copyright 2017 Nexenta Systems, Inc.
.\" Copyright (c) 2017 Open-E, Inc. All Rights Reserved.
.\" Copyright (c) 2021, Colm Buckley <colm@tuatha.org>
.\"
.Dd August 9, 2019
.Dt ZPOOL-UPGRADE 8
@ -54,7 +55,11 @@ formatted using a legacy ZFS version number.
These pools can continue to be used, but some features may not be available.
Use
.Nm zpool Cm upgrade Fl a
to enable all features on all pools.
to enable all features on all pools. (If a pool has specified compatibility
feature sets using the
.Fl o Ar compatibility
property, only the features present in all requested compatibility sets will
be enabled on that pool.)
.It Xo
.Nm zpool
.Cm upgrade
@ -70,7 +75,11 @@ for a description of feature flags features supported by the current software.
.Op Fl V Ar version
.Fl a Ns | Ns Ar pool Ns ...
.Xc
Enables all supported features on the given pool.
Enables all supported features on the given pool. (If the pool has specified
compatibility feature sets using the
.Fl o Ar compatibility
property, only the features present in all requested compatibility sets will be
enabled.)
Once this is done, the pool will no longer be accessible on systems that do not
support feature flags.
See
@ -79,7 +88,8 @@ for details on compatibility with systems that support feature flags, but do not
support all features enabled on the pool.
.Bl -tag -width Ds
.It Fl a
Enables all supported features on all pools.
Enables all supported features (from specified compatibility sets, if any) on all
pools.
.It Fl V Ar version
Upgrade to the specified legacy version.
If the

View File

@ -26,6 +26,7 @@
.\" Copyright (c) 2018 George Melikov. All Rights Reserved.
.\" Copyright 2017 Nexenta Systems, Inc.
.\" Copyright (c) 2017 Open-E, Inc. All Rights Reserved.
.\" Copyright (c) 2021, Colm Buckley <colm@tuatha.org>
.\"
.Dd August 9, 2019
.Dt ZPOOLPROPS 8
@ -285,6 +286,24 @@ A text string consisting of printable ASCII characters that will be stored
such that it is available even if the pool becomes faulted.
An administrator can provide additional information about a pool using this
property.
.It Sy compatibility Ns = Ns Ar off | legacy | file Bq , Ns Ar file Ns ...
Specifies that the pool maintain compatibility with specific feature sets.
When set to
.Sy off
(or unset); compatibility is disabled (all features are enabled); when set to
.Sy legacy Ns ;
no features are enabled. When set to a comma-separated list of
filenames (each filename may either be an absolute path, or relative to
.Pa /etc/zfs/compatibility.d or Pa /usr/share/zfs/compatibility.d Ns )
the lists of requested features are read from those files, separated by
whitespace and/or commas. Only features present in all files are enabled.
See
.Xr zpool-features 5 Ns ,
.Xr zpool-create 8
and
.Xr zpool-upgrade 8
for more information on the operation of compatibility feature sets.
.It Sy dedupditto Ns = Ns Ar number
This property is deprecated and no longer has any effect.
.It Sy delegation Ns = Ns Sy on Ns | Ns Sy off

View File

@ -22,6 +22,7 @@
* Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
* Copyright 2011 Nexenta Systems, Inc. All rights reserved.
* Copyright (c) 2012, 2018 by Delphix. All rights reserved.
* Copyright (c) 2021, Colm Buckley <colm@tuatha.org>
*/
#include <sys/zio.h>
@ -71,6 +72,9 @@ zpool_prop_init(void)
PROP_DEFAULT, ZFS_TYPE_POOL, "<file> | none", "CACHEFILE");
zprop_register_string(ZPOOL_PROP_COMMENT, "comment", NULL,
PROP_DEFAULT, ZFS_TYPE_POOL, "<comment-string>", "COMMENT");
zprop_register_string(ZPOOL_PROP_COMPATIBILITY, "compatibility",
"off", PROP_DEFAULT, ZFS_TYPE_POOL,
"<file[,file...]> | off | legacy", "COMPATIBILITY");
/* readonly number properties */
zprop_register_number(ZPOOL_PROP_SIZE, "size", 0, PROP_READONLY,

View File

@ -32,6 +32,7 @@
* Copyright (c) 2017, 2019, Datto Inc. All rights reserved.
* Copyright 2017 Joyent, Inc.
* Copyright (c) 2017, Intel Corporation.
* Copyright (c) 2021, Colm Buckley <colm@tuatha.org>
*/
/*
@ -377,6 +378,11 @@ spa_prop_get_config(spa_t *spa, nvlist_t **nvp)
0, ZPROP_SRC_LOCAL);
}
if (spa->spa_compatibility != NULL) {
spa_prop_add_list(*nvp, ZPOOL_PROP_COMPATIBILITY,
spa->spa_compatibility, 0, ZPROP_SRC_LOCAL);
}
if (spa->spa_root != NULL)
spa_prop_add_list(*nvp, ZPOOL_PROP_ALTROOT, spa->spa_root,
0, ZPROP_SRC_LOCAL);
@ -1669,6 +1675,10 @@ spa_unload(spa_t *spa)
spa_strfree(spa->spa_comment);
spa->spa_comment = NULL;
}
if (spa->spa_compatibility != NULL) {
spa_strfree(spa->spa_compatibility);
spa->spa_compatibility = NULL;
}
spa_config_exit(spa, SCL_ALL, spa);
}
@ -3249,6 +3259,7 @@ spa_ld_parse_config(spa_t *spa, spa_import_type_t type)
vdev_t *rvd;
uint64_t pool_guid;
char *comment;
char *compatibility;
/*
* Versioning wasn't explicitly added to the label until later, so if
@ -3297,6 +3308,11 @@ spa_ld_parse_config(spa_t *spa, spa_import_type_t type)
if (nvlist_lookup_string(config, ZPOOL_CONFIG_COMMENT, &comment) == 0)
spa->spa_comment = spa_strdup(comment);
ASSERT(spa->spa_compatibility == NULL);
if (nvlist_lookup_string(config, ZPOOL_CONFIG_COMPATIBILITY,
&compatibility) == 0)
spa->spa_compatibility = spa_strdup(compatibility);
(void) nvlist_lookup_uint64(config, ZPOOL_CONFIG_POOL_TXG,
&spa->spa_config_txg);
@ -8668,6 +8684,20 @@ spa_sync_props(void *arg, dmu_tx_t *tx)
spa_history_log_internal(spa, "set", tx,
"%s=%s", nvpair_name(elem), strval);
break;
case ZPOOL_PROP_COMPATIBILITY:
strval = fnvpair_value_string(elem);
if (spa->spa_compatibility != NULL)
spa_strfree(spa->spa_compatibility);
spa->spa_compatibility = spa_strdup(strval);
/*
* Dirty the configuration on vdevs as above.
*/
if (tx->tx_txg != TXG_INITIAL)
vdev_config_dirty(spa->spa_root_vdev);
spa_history_log_internal(spa, "set", tx,
"%s=%s", nvpair_name(elem), strval);
break;
default:
/*
* Set pool property values in the poolprops mos object.

View File

@ -24,6 +24,7 @@
* Copyright 2011 Nexenta Systems, Inc. All rights reserved.
* Copyright (c) 2011, 2020 by Delphix. All rights reserved.
* Copyright 2017 Joyent, Inc.
* Copyright (c) 2021, Colm Buckley <colm@tuatha.org>
*/
#include <sys/spa.h>
@ -446,6 +447,9 @@ spa_config_generate(spa_t *spa, vdev_t *vd, uint64_t txg, int getstats)
if (spa->spa_comment != NULL)
fnvlist_add_string(config, ZPOOL_CONFIG_COMMENT,
spa->spa_comment);
if (spa->spa_compatibility != NULL)
fnvlist_add_string(config, ZPOOL_CONFIG_COMPATIBILITY,
spa->spa_compatibility);
hostid = spa_get_hostid(spa);
if (hostid != 0)

View File

@ -457,6 +457,7 @@ systemctl --system daemon-reload >/dev/null || true
%{_udevdir}/vdev_id
%{_udevdir}/zvol_id
%{_udevdir}/rules.d/*
%{_datadir}/%{name}/compatibility.d
%if ! 0%{?_systemd} || 0%{?_initramfs}
# Files needed for sysvinit and initramfs-tools
%{_sysconfdir}/%{name}/zfs-functions
@ -503,7 +504,10 @@ systemctl --system daemon-reload >/dev/null || true
%doc AUTHORS COPYRIGHT LICENSE NOTICE README.md
%files test
%{_datadir}/%{name}
%{_datadir}/%{name}/zfs-tests
%{_datadir}/%{name}/test-runner
%{_datadir}/%{name}/runfiles
%{_datadir}/%{name}/*.sh
%files dracut
%doc contrib/dracut/README.dracut.markdown

View File

@ -34,6 +34,7 @@ export ZEDLET_ETC_DIR=$$CMD_DIR/zed/zed.d
export ZEDLET_LIBEXEC_DIR=$$CMD_DIR/zed/zed.d
export ZPOOL_SCRIPT_DIR=$$CMD_DIR/zpool/zpool.d
export ZPOOL_SCRIPTS_PATH=$$CMD_DIR/zpool/zpool.d
export ZPOOL_COMPAT_DIR=$$CMD_DIR/zpool/compatibility.d
export CONTRIB_DIR=@abs_top_builddir@/contrib
export LIB_DIR=@abs_top_builddir@/lib
export SYSCONF_DIR=@abs_top_builddir@/etc

View File

@ -344,7 +344,8 @@ tests = ['zpool_create_001_pos', 'zpool_create_002_pos',
'zpool_create_draid_003_pos', 'zpool_create_draid_004_pos',
'zpool_create_features_001_pos', 'zpool_create_features_002_pos',
'zpool_create_features_003_pos', 'zpool_create_features_004_neg',
'zpool_create_features_005_pos',
'zpool_create_features_005_pos', 'zpool_create_features_006_pos',
'zpool_create_features_007_pos', 'zpool_create_features_008_pos',
'create-o_ashift', 'zpool_create_tempname', 'zpool_create_dryrun_output']
tags = ['functional', 'cli_root', 'zpool_create']
@ -468,7 +469,8 @@ tests = ['zpool_split_cliargs', 'zpool_split_devices',
tags = ['functional', 'cli_root', 'zpool_split']
[tests/functional/cli_root/zpool_status]
tests = ['zpool_status_001_pos', 'zpool_status_002_pos']
tests = ['zpool_status_001_pos', 'zpool_status_002_pos',
'zpool_status_features_001_pos']
tags = ['functional', 'cli_root', 'zpool_status']
[tests/functional/cli_root/zpool_sync]
@ -491,7 +493,7 @@ tests = ['zpool_upgrade_001_pos', 'zpool_upgrade_002_pos',
'zpool_upgrade_003_pos', 'zpool_upgrade_004_pos',
'zpool_upgrade_005_neg', 'zpool_upgrade_006_neg',
'zpool_upgrade_007_pos', 'zpool_upgrade_008_pos',
'zpool_upgrade_009_neg']
'zpool_upgrade_009_neg', 'zpool_upgrade_features_001_pos']
tags = ['functional', 'cli_root', 'zpool_upgrade']
[tests/functional/cli_root/zpool_wait]

View File

@ -37,6 +37,7 @@
export ZEDLET_ETC_DIR=${ZEDLET_ETC_DIR:-@sysconfdir@/zfs/zed.d}
export ZEDLET_LIBEXEC_DIR=${ZEDLET_LIBEXEC_DIR:-@zfsexecdir@/zed.d}
export ZPOOL_SCRIPT_DIR=${ZPOOL_SCRIPT_DIR:-@sysconfdir@/zfs/zpool.d}
export ZPOOL_COMPAT_DIR=${ZPOOL_COMPAT_DIR:-@datadir@/zfs/compatibility.d}
# Define run length constants
export RT_LONG="3"

View File

@ -36,6 +36,9 @@ dist_pkgdata_SCRIPTS = \
zpool_create_features_003_pos.ksh \
zpool_create_features_004_neg.ksh \
zpool_create_features_005_pos.ksh \
zpool_create_features_006_pos.ksh \
zpool_create_features_007_pos.ksh \
zpool_create_features_008_pos.ksh \
create-o_ashift.ksh \
zpool_create_tempname.ksh \
zpool_create_dryrun_output.ksh

View File

@ -107,3 +107,84 @@ function save_dump_dev
fi
echo $dumpdev
}
#
# Verify a pools enabled features match the provided feature set.
# $1, pool name
# $2, feature set(s)
#
# check_feature_set $TESTPOOL set1 set2 set3 ...
#
function check_feature_set
{
typeset pool=$1
typeset feature_set=$2
shift
for set in "$@"; do
if test -e "$ZPOOL_COMPAT_DIR/$set"; then
file="$ZPOOL_COMPAT_DIR/$set"
else
log_fail "Missing feature file: $ZPOOL_COMPAT_DIR/$set"
fi
done
#
# Create a temporary file which contains all features which are
# common to the listed feature sets. This is used for comparison
# below to determine which features should be enabled.
#
typeset tmpfile=$(mktemp)
while read line; do
typeset flag=1
if [[ "$line" == "#*" ]]; then
continue
fi
for set in "$@"; do
if ! grep -q "$line" $ZPOOL_COMPAT_DIR/$set; then
flag=0
break;
fi
done
if [[ $flag -eq 1 ]]; then
echo "$line" >>$tmpfile
fi
done <"$file"
#
# Verify every enabled feature appears in the merged feature set.
# Verify every disabled feature does not.
#
for feature in $(zpool get all $pool | \
awk '$2 ~ /feature@/ { print $2 }'); do
state=$(get_pool_prop $feature $pool)
name=$(cut -d'@' -f2 <<<"$feature")
if [[ "$state" = "enabled" || "$state" = "active" ]]; then
if ! grep -q $name $tmpfile; then
cat $tmpfile
rm -f $tmpfile
log_fail "Enabled feature $name not " \
"in feature set file"
fi
elif [[ "$state" = "disabled" ]]; then
if grep -q $name $tmpfile; then
cat $tmpfile
rm -f $tmpfile
log_fail "Disabled feature $name is " \
"in feature set file"
fi
else
rm -f $tmpfile
log_fail "Feature $name in unknown state $state"
fi
done
log_note "Checked all features"
rm -f $tmpfile
}

View File

@ -0,0 +1,58 @@
#!/bin/ksh -p
#
# CDDL HEADER START
#
# The contents of this file are subject to the terms of the
# Common Development and Distribution License (the "License").
# You may not use this file except in compliance with the License.
#
# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
# or http://www.opensolaris.org/os/licensing.
# See the License for the specific language governing permissions
# and limitations under the License.
#
# When distributing Covered Code, include this CDDL HEADER in each
# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
# If applicable, add the following below this CDDL HEADER, with the
# fields enclosed by brackets "[]" replaced with your own identifying
# information: Portions Copyright [yyyy] [name of copyright owner]
#
# CDDL HEADER END
#
#
# Copyright (c) 2021 Lawrence Livermore National Security, LLC.
#
. $STF_SUITE/include/libtest.shlib
#
# DESCRIPTION:
# Verify '-o compatibility' reserved values 'off, legacy'
#
# STRATEGY:
# 1. Create a pool with '-o compatibility=off'
# 2. Create a pool with '-o compatibility=legacy'
# 3. Cannot create a pool with '-o compatibility=unknown'
#
verify_runnable "global"
function cleanup
{
datasetexists $TESTPOOL && log_must zpool destroy $TESTPOOL
}
log_onexit cleanup
log_assert "verify '-o compatibility' reserved values 'off, legacy'"
log_must zpool create -f -o compatibility=off $TESTPOOL $DISKS
log_must zpool destroy -f $TESTPOOL
log_must zpool create -f -o compatibility=legacy $TESTPOOL $DISKS
log_must zpool destroy -f $TESTPOOL
log_mustnot zpool create -f -o compatibility=unknown $TESTPOOL $DISKS
log_pass "verify '-o compatibility' reserved values 'off, legacy'"

View File

@ -0,0 +1,54 @@
#!/bin/ksh -p
#
# CDDL HEADER START
#
# The contents of this file are subject to the terms of the
# Common Development and Distribution License (the "License").
# You may not use this file except in compliance with the License.
#
# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
# or http://www.opensolaris.org/os/licensing.
# See the License for the specific language governing permissions
# and limitations under the License.
#
# When distributing Covered Code, include this CDDL HEADER in each
# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
# If applicable, add the following below this CDDL HEADER, with the
# fields enclosed by brackets "[]" replaced with your own identifying
# information: Portions Copyright [yyyy] [name of copyright owner]
#
# CDDL HEADER END
#
#
# Copyright (c) 2021 Lawrence Livermore National Security, LLC.
#
. $STF_SUITE/include/libtest.shlib
. $STF_SUITE/tests/functional/cli_root/zpool_create/zpool_create.shlib
#
# DESCRIPTION:
# Verify pools can be created with the expected feature set enabled.
#
# STRATEGY:
# 1. Create a pool with a known feature set.
# 2. Verify only those features are active/enabled.
#
verify_runnable "global"
function cleanup
{
datasetexists $TESTPOOL && log_must zpool destroy $TESTPOOL
}
log_onexit cleanup
log_assert "creates a pool with a specified feature set enabled"
log_must zpool create -f -o compatibility=compat-2020 $TESTPOOL $DISKS
check_feature_set $TESTPOOL compat-2020
log_must zpool destroy -f $TESTPOOL
log_pass "creates a pool with a specified feature set enabled"

View File

@ -0,0 +1,54 @@
#!/bin/ksh -p
#
# CDDL HEADER START
#
# The contents of this file are subject to the terms of the
# Common Development and Distribution License (the "License").
# You may not use this file except in compliance with the License.
#
# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
# or http://www.opensolaris.org/os/licensing.
# See the License for the specific language governing permissions
# and limitations under the License.
#
# When distributing Covered Code, include this CDDL HEADER in each
# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
# If applicable, add the following below this CDDL HEADER, with the
# fields enclosed by brackets "[]" replaced with your own identifying
# information: Portions Copyright [yyyy] [name of copyright owner]
#
# CDDL HEADER END
#
#
# Copyright (c) 2021 Lawrence Livermore National Security, LLC.
#
. $STF_SUITE/include/libtest.shlib
. $STF_SUITE/tests/functional/cli_root/zpool_create/zpool_create.shlib
#
# DESCRIPTION:
# Verify pools can be created with multiple feature sets.
#
# STRATEGY:
# 1. Create a pool with multiple feature sets.
# 2. Verify only the features common to both sets are enabled.
#
verify_runnable "global"
function cleanup
{
datasetexists $TESTPOOL && log_must zpool destroy $TESTPOOL
}
log_onexit cleanup
log_assert "creates a pool with multiple feature sets enabled"
log_must zpool create -f -o compatibility=freebsd-11.0,zol-0.8 $TESTPOOL $DISKS
check_feature_set $TESTPOOL freebsd-11.0 zol-0.8
log_must zpool destroy -f $TESTPOOL
log_pass "creates a pool with multiple feature sets enabled"

View File

@ -57,6 +57,7 @@ typeset -a properties=(
"leaked"
"multihost"
"autotrim"
"compatibility"
"feature@async_destroy"
"feature@empty_bpobj"
"feature@lz4_compress"

View File

@ -3,4 +3,5 @@ dist_pkgdata_SCRIPTS = \
setup.ksh \
cleanup.ksh \
zpool_status_001_pos.ksh \
zpool_status_002_pos.ksh
zpool_status_002_pos.ksh \
zpool_status_features_001_pos.ksh

View File

@ -0,0 +1,63 @@
#!/bin/ksh -p
#
# CDDL HEADER START
#
# The contents of this file are subject to the terms of the
# Common Development and Distribution License (the "License").
# You may not use this file except in compliance with the License.
#
# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
# or http://www.opensolaris.org/os/licensing.
# See the License for the specific language governing permissions
# and limitations under the License.
#
# When distributing Covered Code, include this CDDL HEADER in each
# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
# If applicable, add the following below this CDDL HEADER, with the
# fields enclosed by brackets "[]" replaced with your own identifying
# information: Portions Copyright [yyyy] [name of copyright owner]
#
# CDDL HEADER END
#
#
# Copyright (c) 2021 Lawrence Livermore National Security, LLC.
#
. $STF_SUITE/include/libtest.shlib
. $STF_SUITE/tests/functional/cli_root/zpool_create/zpool_create.shlib
#
# DESCRIPTION:
# Verify zpool status only recommends upgrading the pool when
# the enabled features don't match those in the feature set.
#
# STRATEGY:
# 1. Create a pool with a known feature set.
# 2. Verify there is no `zpool status` notice to upgrade the pool.
# 3. Set the pool compatibility to a newer feature set.
# 4. Verify there is a `zpool status` notice to upgrade the pool.
#
verify_runnable "global"
function cleanup
{
datasetexists $TESTPOOL1 && log_must zpool destroy $TESTPOOL1
rm -f $FILEDEV
}
FILEDEV="$TEST_BASE_DIR/filedev.$$"
log_onexit cleanup
log_assert "check 'zpool status' upgrade notice"
log_must truncate -s $MINVDEVSIZE $FILEDEV
log_must zpool create -f -o compatibility=compat-2018 $TESTPOOL1 $FILEDEV
log_mustnot check_pool_status $TESTPOOL1 "status" "features are not enabled"
log_must zpool set compatibility=compat-2020 $TESTPOOL1
log_must check_pool_status $TESTPOOL1 "status" "features are not enabled"
log_pass "check 'zpool status' upgrade notice"

View File

@ -12,7 +12,8 @@ dist_pkgdata_SCRIPTS = \
zpool_upgrade_006_neg.ksh \
zpool_upgrade_007_pos.ksh \
zpool_upgrade_008_pos.ksh \
zpool_upgrade_009_neg.ksh
zpool_upgrade_009_neg.ksh \
zpool_upgrade_features_001_pos.ksh
dist_pkgdata_DATA = \
zpool_upgrade.cfg \

View File

@ -0,0 +1,67 @@
#!/bin/ksh -p
#
# CDDL HEADER START
#
# The contents of this file are subject to the terms of the
# Common Development and Distribution License (the "License").
# You may not use this file except in compliance with the License.
#
# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
# or http://www.opensolaris.org/os/licensing.
# See the License for the specific language governing permissions
# and limitations under the License.
#
# When distributing Covered Code, include this CDDL HEADER in each
# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
# If applicable, add the following below this CDDL HEADER, with the
# fields enclosed by brackets "[]" replaced with your own identifying
# information: Portions Copyright [yyyy] [name of copyright owner]
#
# CDDL HEADER END
#
#
# Copyright (c) 2021 Lawrence Livermore National Security, LLC.
#
. $STF_SUITE/include/libtest.shlib
. $STF_SUITE/tests/functional/cli_root/zpool_create/zpool_create.shlib
#
# DESCRIPTION:
# Verify pools can be upgraded to known feature sets.
#
# STRATEGY:
# 1. Create a pool with a known feature set.
# 2. Verify only those features are active/enabled.
# 3. Upgrade the pool to a newer feature set.
# 4. Verify only those features are active/enabled.
#
verify_runnable "global"
function cleanup
{
datasetexists $TESTPOOL1 && log_must zpool destroy $TESTPOOL1
rm -f $FILEDEV
}
FILEDEV="$TEST_BASE_DIR/filedev.$$"
log_onexit cleanup
log_assert "verify pools can be upgraded to known feature sets."
log_must truncate -s $MINVDEVSIZE $FILEDEV
log_must zpool create -f -o compatibility=compat-2018 $TESTPOOL1 $FILEDEV
check_feature_set $TESTPOOL1 compat-2018
log_mustnot check_pool_status $TESTPOOL1 "status" "features are not enabled"
log_must zpool set compatibility=compat-2020 $TESTPOOL1
log_must check_pool_status $TESTPOOL1 "status" "features are not enabled"
log_must zpool upgrade $TESTPOOL1
check_feature_set $TESTPOOL1 compat-2020
log_mustnot check_pool_status $TESTPOOL1 "status" "features are not enabled"
log_pass "verify pools can be upgraded to known feature sets."