From b68ea1d50ad3edeb9f11312c11db5d94b39645bb Mon Sep 17 00:00:00 2001 From: loli10K Date: Tue, 8 Oct 2019 22:21:53 +0200 Subject: [PATCH] Fix pool creation with feature@allocation_classes disabled When "feature@allocation_classes" is not enabled on the pool no vdev with "special" or "dedup" allocation type should be allowed to exist in the vdev tree. Signed-off-by: loli10K --- include/zfs_comutil.h | 1 + module/zcommon/zfs_comutil.c | 28 +++++++++++++++++++ module/zfs/spa.c | 10 +++++++ .../alloc_class/alloc_class_001_pos.ksh | 6 +++- 4 files changed, 44 insertions(+), 1 deletion(-) diff --git a/include/zfs_comutil.h b/include/zfs_comutil.h index 1360d6e1c171..7cdc6d6938ae 100644 --- a/include/zfs_comutil.h +++ b/include/zfs_comutil.h @@ -34,6 +34,7 @@ extern "C" { #endif extern boolean_t zfs_allocatable_devs(nvlist_t *); +extern boolean_t zfs_special_devs(nvlist_t *); extern void zpool_get_load_policy(nvlist_t *, zpool_load_policy_t *); extern int zfs_zpl_version_map(int spa_version); diff --git a/module/zcommon/zfs_comutil.c b/module/zcommon/zfs_comutil.c index 5daa6907c5d0..a3ff7d8e6991 100644 --- a/module/zcommon/zfs_comutil.c +++ b/module/zcommon/zfs_comutil.c @@ -64,6 +64,33 @@ zfs_allocatable_devs(nvlist_t *nv) return (B_FALSE); } +/* + * Are there special vdevs? + */ +boolean_t +zfs_special_devs(nvlist_t *nv) +{ + char *bias; + uint_t c; + nvlist_t **child; + uint_t children; + + if (nvlist_lookup_nvlist_array(nv, ZPOOL_CONFIG_CHILDREN, + &child, &children) != 0) { + return (B_FALSE); + } + for (c = 0; c < children; c++) { + if (nvlist_lookup_string(child[c], ZPOOL_CONFIG_ALLOCATION_BIAS, + &bias) == 0) { + if (strcmp(bias, VDEV_ALLOC_BIAS_SPECIAL) == 0 || + strcmp(bias, VDEV_ALLOC_BIAS_DEDUP) == 0) { + return (B_TRUE); + } + } + } + return (B_FALSE); +} + void zpool_get_load_policy(nvlist_t *nvl, zpool_load_policy_t *zlpp) { @@ -223,6 +250,7 @@ zfs_dataset_name_hidden(const char *name) #if defined(_KERNEL) EXPORT_SYMBOL(zfs_allocatable_devs); +EXPORT_SYMBOL(zfs_special_devs); EXPORT_SYMBOL(zpool_get_load_policy); EXPORT_SYMBOL(zfs_zpl_version_map); EXPORT_SYMBOL(zfs_spa_version_map); diff --git a/module/zfs/spa.c b/module/zfs/spa.c index 7e5c474eb2e2..33bd991e98c4 100644 --- a/module/zfs/spa.c +++ b/module/zfs/spa.c @@ -5620,6 +5620,7 @@ spa_create(const char *pool, nvlist_t *nvroot, nvlist_t *props, uint64_t version, obj; boolean_t has_features; boolean_t has_encryption; + boolean_t has_allocclass; spa_feature_t feat; char *feat_name; char *poolname; @@ -5664,6 +5665,7 @@ spa_create(const char *pool, nvlist_t *nvroot, nvlist_t *props, has_features = B_FALSE; has_encryption = B_FALSE; + has_allocclass = B_FALSE; for (nvpair_t *elem = nvlist_next_nvpair(props, NULL); elem != NULL; elem = nvlist_next_nvpair(props, elem)) { if (zpool_prop_feature(nvpair_name(elem))) { @@ -5673,6 +5675,8 @@ spa_create(const char *pool, nvlist_t *nvroot, nvlist_t *props, VERIFY0(zfeature_lookup_name(feat_name, &feat)); if (feat == SPA_FEATURE_ENCRYPTION) has_encryption = B_TRUE; + if (feat == SPA_FEATURE_ALLOCATION_CLASSES) + has_allocclass = B_TRUE; } } @@ -5686,6 +5690,12 @@ spa_create(const char *pool, nvlist_t *nvroot, nvlist_t *props, return (error); } } + if (!has_allocclass && zfs_special_devs(nvroot)) { + spa_deactivate(spa); + spa_remove(spa); + mutex_exit(&spa_namespace_lock); + return (ENOTSUP); + } if (has_features || nvlist_lookup_uint64(props, zpool_prop_to_name(ZPOOL_PROP_VERSION), &version) != 0) { diff --git a/tests/zfs-tests/tests/functional/alloc_class/alloc_class_001_pos.ksh b/tests/zfs-tests/tests/functional/alloc_class/alloc_class_001_pos.ksh index 441df8296718..3237d7cb784f 100755 --- a/tests/zfs-tests/tests/functional/alloc_class/alloc_class_001_pos.ksh +++ b/tests/zfs-tests/tests/functional/alloc_class/alloc_class_001_pos.ksh @@ -20,7 +20,8 @@ # # DESCRIPTION: -# Creating a pool with a special device succeeds. +# Creating a pool with a special device succeeds, but only if +# "feature@allocation_classes" is enabled. # verify_runnable "global" @@ -31,6 +32,9 @@ log_assert $claim log_onexit cleanup log_must disk_setup +for type in special dedup; do + log_mustnot zpool create -d $TESTPOOL $CLASS_DISK0 $type $CLASS_DISK1 +done log_must zpool create $TESTPOOL raidz $ZPOOL_DISKS special mirror \ $CLASS_DISK0 $CLASS_DISK1 log_must display_status "$TESTPOOL"