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
48 changed files with 5621 additions and 2954 deletions
+46 -1
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
+12
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
+15
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
+15
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
+19
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
+15
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
+18
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
+19
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
+13
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
+12
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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
+12
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
+18
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
+27
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
+132 -25
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)
{