diff options
author | наб <[email protected]> | 2021-12-17 01:43:10 +0100 |
---|---|---|
committer | GitHub <[email protected]> | 2021-12-16 16:43:10 -0800 |
commit | eb51a9d74742bd2dc4e31c1806532149593101df (patch) | |
tree | d3ec2ed57a9a8e573f5304f6ca401160fe4ed3a9 /module/zcommon/zfeature_common.c | |
parent | 8fdc6f618cf19e1894f5a803e043e80e762a86b1 (diff) |
zcommon: pre-iterate over sysfs instead of statting every feature
If sufficient memory (<2K, realistically) is available, libzfs_init()
can be significantly shorted by iterating over the correct sysfs
directory before registrations, we can turn 168 stats into 15/18
syscalls (3 opens (6 if built in), 3 fstats, 6 getdentses, and 3
closes), a tenfoldish reduction; this is probably a bit faster, too.
The list is always optional, and registration functions (and one-off
users) can simply pass NULL, which will fall back to the previous
mechanism
Also, don't allocate in zfs_mod_supported_impl, and use use access()
instead of stat(), since existence is really what we care about
Also, fix pre-prop-checking compat in fallback for built-in ZFS
Reviewed-by: Brian Behlendorf <[email protected]>
Reviewed-by: Tony Nguyen <[email protected]>
Signed-off-by: Ahelenia Ziemiańska <[email protected]>
Closes #12089
Diffstat (limited to 'module/zcommon/zfeature_common.c')
-rw-r--r-- | module/zcommon/zfeature_common.c | 537 |
1 files changed, 319 insertions, 218 deletions
diff --git a/module/zcommon/zfeature_common.c b/module/zcommon/zfeature_common.c index 8d84c6d15..c7278fa00 100644 --- a/module/zcommon/zfeature_common.c +++ b/module/zcommon/zfeature_common.c @@ -32,6 +32,8 @@ #ifndef _KERNEL #include <errno.h> #include <string.h> +#include <dirent.h> +#include <search.h> #include <sys/stat.h> #endif #include <sys/debug.h> @@ -164,30 +166,109 @@ deps_contains_feature(const spa_feature_t *deps, const spa_feature_t feature) return (B_FALSE); } +#define STRCMP ((int(*)(const void *, const void *))&strcmp) +struct zfs_mod_supported_features { + void *tree; + boolean_t all_features; +}; + +struct zfs_mod_supported_features * +zfs_mod_list_supported(const char *scope) +{ +#if defined(__FreeBSD__) || defined(_KERNEL) || defined(LIB_ZPOOL_BUILD) + (void) scope; + return (NULL); +#else + struct zfs_mod_supported_features *ret = calloc(1, sizeof (*ret)); + if (ret == NULL) + return (NULL); + + DIR *sysfs_dir = NULL; + char path[128]; + + if (snprintf(path, sizeof (path), "%s/%s", + ZFS_SYSFS_DIR, scope) < sizeof (path)) + sysfs_dir = opendir(path); + if (sysfs_dir == NULL && errno == ENOENT) { + if (snprintf(path, sizeof (path), "%s/%s", + ZFS_SYSFS_ALT_DIR, scope) < sizeof (path)) + sysfs_dir = opendir(path); + } + if (sysfs_dir == NULL) { + ret->all_features = errno == ENOENT && + (access(ZFS_SYSFS_DIR, F_OK) == 0 || + access(ZFS_SYSFS_ALT_DIR, F_OK) == 0); + return (ret); + } + + struct dirent *node; + while ((node = readdir(sysfs_dir)) != NULL) { + if (strcmp(node->d_name, ".") == 0 || + strcmp(node->d_name, "..") == 0) + continue; + + char *name = strdup(node->d_name); + if (name == NULL) { + goto nomem; + } + + if (tsearch(name, &ret->tree, STRCMP) == NULL) { + /* + * Don't bother checking for duplicate entries: + * we're iterating a single directory. + */ + free(name); + goto nomem; + } + } + +end: + closedir(sysfs_dir); + return (ret); + +nomem: + zfs_mod_list_supported_free(ret); + ret = NULL; + goto end; +#endif +} + +void +zfs_mod_list_supported_free(struct zfs_mod_supported_features *list) +{ +#if !defined(__FreeBSD__) && !defined(_KERNEL) && !defined(LIB_ZPOOL_BUILD) + if (list) { + tdestroy(list->tree, free); + free(list); + } +#else + (void) list; +#endif +} + #if !defined(_KERNEL) && !defined(LIB_ZPOOL_BUILD) static boolean_t zfs_mod_supported_impl(const char *scope, const char *name, const char *sysfs) { - boolean_t supported = B_FALSE; - char *path; - - int len = asprintf(&path, "%s%s%s%s%s", sysfs, - scope == NULL ? "" : "/", scope == NULL ? "" : scope, - name == NULL ? "" : "/", name == NULL ? "" : name); - if (len > 0) { - struct stat64 statbuf; - supported = !!(stat64(path, &statbuf) == 0); - free(path); - } - - return (supported); + char path[128]; + if (snprintf(path, sizeof (path), "%s%s%s%s%s", sysfs, + scope == NULL ? "" : "/", scope ?: "", + name == NULL ? "" : "/", name ?: "") < sizeof (path)) + return (access(path, F_OK) == 0); + else + return (B_FALSE); } boolean_t -zfs_mod_supported(const char *scope, const char *name) +zfs_mod_supported(const char *scope, const char *name, + const struct zfs_mod_supported_features *sfeatures) { boolean_t supported; + if (sfeatures != NULL) + return (sfeatures->all_features || + tfind(name, &sfeatures->tree, STRCMP)); + /* * Check both the primary and alternate sysfs locations to determine * if the required functionality is supported. @@ -202,10 +283,10 @@ zfs_mod_supported(const char *scope, const char *name) * scope directory does not exist. */ if (supported == B_FALSE) { - struct stat64 statbuf; - if ((stat64(ZFS_SYSFS_DIR, &statbuf) == 0) && - !zfs_mod_supported_impl(scope, NULL, ZFS_SYSFS_DIR) && - !zfs_mod_supported_impl(scope, NULL, ZFS_SYSFS_ALT_DIR)) { + if ((access(ZFS_SYSFS_DIR, F_OK) == 0 && + !zfs_mod_supported_impl(scope, NULL, ZFS_SYSFS_DIR)) || + (access(ZFS_SYSFS_ALT_DIR, F_OK) == 0 && + !zfs_mod_supported_impl(scope, NULL, ZFS_SYSFS_ALT_DIR))) { supported = B_TRUE; } } @@ -215,7 +296,8 @@ zfs_mod_supported(const char *scope, const char *name) #endif static boolean_t -zfs_mod_supported_feature(const char *name) +zfs_mod_supported_feature(const char *name, + const struct zfs_mod_supported_features *sfeatures) { /* * The zfs module spa_feature_table[], whether in-kernel or in @@ -229,17 +311,18 @@ zfs_mod_supported_feature(const char *name) */ #if defined(_KERNEL) || defined(LIB_ZPOOL_BUILD) || defined(__FreeBSD__) - (void) name; + (void) name, (void) sfeatures; return (B_TRUE); #else - return (zfs_mod_supported(ZFS_SYSFS_POOL_FEATURES, name)); + return (zfs_mod_supported(ZFS_SYSFS_POOL_FEATURES, name, sfeatures)); #endif } static void zfeature_register(spa_feature_t fid, const char *guid, const char *name, const char *desc, zfeature_flags_t flags, zfeature_type_t type, - const spa_feature_t *deps) + const spa_feature_t *deps, + const struct zfs_mod_supported_features *sfeatures) { zfeature_info_t *feature = &spa_feature_table[fid]; static spa_feature_t nodeps[] = { SPA_FEATURE_NONE }; @@ -264,7 +347,8 @@ zfeature_register(spa_feature_t fid, const char *guid, const char *name, feature->fi_flags = flags; feature->fi_type = type; feature->fi_depends = deps; - feature->fi_zfs_mod_supported = zfs_mod_supported_feature(guid); + feature->fi_zfs_mod_supported = + zfs_mod_supported_feature(guid, sfeatures); } /* @@ -283,318 +367,335 @@ zfeature_register(spa_feature_t fid, const char *guid, const char *name, void zpool_feature_init(void) { + struct zfs_mod_supported_features *sfeatures = + zfs_mod_list_supported(ZFS_SYSFS_POOL_FEATURES); + zfeature_register(SPA_FEATURE_ASYNC_DESTROY, "com.delphix:async_destroy", "async_destroy", "Destroy filesystems asynchronously.", - ZFEATURE_FLAG_READONLY_COMPAT, ZFEATURE_TYPE_BOOLEAN, NULL); + ZFEATURE_FLAG_READONLY_COMPAT, ZFEATURE_TYPE_BOOLEAN, NULL, + sfeatures); zfeature_register(SPA_FEATURE_EMPTY_BPOBJ, "com.delphix:empty_bpobj", "empty_bpobj", "Snapshots use less space.", - ZFEATURE_FLAG_READONLY_COMPAT, ZFEATURE_TYPE_BOOLEAN, NULL); + ZFEATURE_FLAG_READONLY_COMPAT, ZFEATURE_TYPE_BOOLEAN, NULL, + sfeatures); zfeature_register(SPA_FEATURE_LZ4_COMPRESS, "org.illumos:lz4_compress", "lz4_compress", "LZ4 compression algorithm support.", - ZFEATURE_FLAG_ACTIVATE_ON_ENABLE, ZFEATURE_TYPE_BOOLEAN, NULL); + ZFEATURE_FLAG_ACTIVATE_ON_ENABLE, ZFEATURE_TYPE_BOOLEAN, NULL, + sfeatures); zfeature_register(SPA_FEATURE_MULTI_VDEV_CRASH_DUMP, "com.joyent:multi_vdev_crash_dump", "multi_vdev_crash_dump", "Crash dumps to multiple vdev pools.", - 0, ZFEATURE_TYPE_BOOLEAN, NULL); + 0, ZFEATURE_TYPE_BOOLEAN, NULL, sfeatures); zfeature_register(SPA_FEATURE_SPACEMAP_HISTOGRAM, "com.delphix:spacemap_histogram", "spacemap_histogram", "Spacemaps maintain space histograms.", - ZFEATURE_FLAG_READONLY_COMPAT, ZFEATURE_TYPE_BOOLEAN, NULL); + ZFEATURE_FLAG_READONLY_COMPAT, ZFEATURE_TYPE_BOOLEAN, NULL, + sfeatures); zfeature_register(SPA_FEATURE_ENABLED_TXG, "com.delphix:enabled_txg", "enabled_txg", "Record txg at which a feature is enabled", - ZFEATURE_FLAG_READONLY_COMPAT, ZFEATURE_TYPE_BOOLEAN, NULL); + ZFEATURE_FLAG_READONLY_COMPAT, ZFEATURE_TYPE_BOOLEAN, NULL, + sfeatures); { - static const spa_feature_t hole_birth_deps[] = { - SPA_FEATURE_ENABLED_TXG, - SPA_FEATURE_NONE - }; - zfeature_register(SPA_FEATURE_HOLE_BIRTH, - "com.delphix:hole_birth", "hole_birth", - "Retain hole birth txg for more precise zfs send", - ZFEATURE_FLAG_MOS | ZFEATURE_FLAG_ACTIVATE_ON_ENABLE, - ZFEATURE_TYPE_BOOLEAN, hole_birth_deps); + static const spa_feature_t hole_birth_deps[] = { + SPA_FEATURE_ENABLED_TXG, + SPA_FEATURE_NONE + }; + zfeature_register(SPA_FEATURE_HOLE_BIRTH, + "com.delphix:hole_birth", "hole_birth", + "Retain hole birth txg for more precise zfs send", + ZFEATURE_FLAG_MOS | ZFEATURE_FLAG_ACTIVATE_ON_ENABLE, + ZFEATURE_TYPE_BOOLEAN, hole_birth_deps, sfeatures); } zfeature_register(SPA_FEATURE_POOL_CHECKPOINT, "com.delphix:zpool_checkpoint", "zpool_checkpoint", "Pool state can be checkpointed, allowing rewind later.", - ZFEATURE_FLAG_READONLY_COMPAT, ZFEATURE_TYPE_BOOLEAN, NULL); + ZFEATURE_FLAG_READONLY_COMPAT, ZFEATURE_TYPE_BOOLEAN, NULL, + sfeatures); zfeature_register(SPA_FEATURE_SPACEMAP_V2, "com.delphix:spacemap_v2", "spacemap_v2", "Space maps representing large segments are more efficient.", ZFEATURE_FLAG_READONLY_COMPAT | ZFEATURE_FLAG_ACTIVATE_ON_ENABLE, - ZFEATURE_TYPE_BOOLEAN, NULL); + ZFEATURE_TYPE_BOOLEAN, NULL, sfeatures); zfeature_register(SPA_FEATURE_EXTENSIBLE_DATASET, "com.delphix:extensible_dataset", "extensible_dataset", "Enhanced dataset functionality, used by other features.", - 0, ZFEATURE_TYPE_BOOLEAN, NULL); + 0, ZFEATURE_TYPE_BOOLEAN, NULL, sfeatures); { - static const spa_feature_t bookmarks_deps[] = { - SPA_FEATURE_EXTENSIBLE_DATASET, - SPA_FEATURE_NONE - }; - - zfeature_register(SPA_FEATURE_BOOKMARKS, - "com.delphix:bookmarks", "bookmarks", - "\"zfs bookmark\" command", - ZFEATURE_FLAG_READONLY_COMPAT, ZFEATURE_TYPE_BOOLEAN, - bookmarks_deps); + static const spa_feature_t bookmarks_deps[] = { + SPA_FEATURE_EXTENSIBLE_DATASET, + SPA_FEATURE_NONE + }; + + zfeature_register(SPA_FEATURE_BOOKMARKS, + "com.delphix:bookmarks", "bookmarks", + "\"zfs bookmark\" command", + ZFEATURE_FLAG_READONLY_COMPAT, ZFEATURE_TYPE_BOOLEAN, + bookmarks_deps, sfeatures); } { - static const spa_feature_t filesystem_limits_deps[] = { - SPA_FEATURE_EXTENSIBLE_DATASET, - SPA_FEATURE_NONE - }; - zfeature_register(SPA_FEATURE_FS_SS_LIMIT, - "com.joyent:filesystem_limits", "filesystem_limits", - "Filesystem and snapshot limits.", - ZFEATURE_FLAG_READONLY_COMPAT, ZFEATURE_TYPE_BOOLEAN, - filesystem_limits_deps); + static const spa_feature_t filesystem_limits_deps[] = { + SPA_FEATURE_EXTENSIBLE_DATASET, + SPA_FEATURE_NONE + }; + zfeature_register(SPA_FEATURE_FS_SS_LIMIT, + "com.joyent:filesystem_limits", "filesystem_limits", + "Filesystem and snapshot limits.", + ZFEATURE_FLAG_READONLY_COMPAT, ZFEATURE_TYPE_BOOLEAN, + filesystem_limits_deps, sfeatures); } zfeature_register(SPA_FEATURE_EMBEDDED_DATA, "com.delphix:embedded_data", "embedded_data", "Blocks which compress very well use even less space.", ZFEATURE_FLAG_MOS | ZFEATURE_FLAG_ACTIVATE_ON_ENABLE, - ZFEATURE_TYPE_BOOLEAN, NULL); + ZFEATURE_TYPE_BOOLEAN, NULL, sfeatures); { - static const spa_feature_t livelist_deps[] = { - SPA_FEATURE_EXTENSIBLE_DATASET, - SPA_FEATURE_NONE - }; - zfeature_register(SPA_FEATURE_LIVELIST, - "com.delphix:livelist", "livelist", - "Improved clone deletion performance.", - ZFEATURE_FLAG_READONLY_COMPAT, ZFEATURE_TYPE_BOOLEAN, - livelist_deps); + static const spa_feature_t livelist_deps[] = { + SPA_FEATURE_EXTENSIBLE_DATASET, + SPA_FEATURE_NONE + }; + zfeature_register(SPA_FEATURE_LIVELIST, + "com.delphix:livelist", "livelist", + "Improved clone deletion performance.", + ZFEATURE_FLAG_READONLY_COMPAT, ZFEATURE_TYPE_BOOLEAN, + livelist_deps, sfeatures); } { - static const spa_feature_t log_spacemap_deps[] = { - SPA_FEATURE_SPACEMAP_V2, - SPA_FEATURE_NONE - }; - zfeature_register(SPA_FEATURE_LOG_SPACEMAP, - "com.delphix:log_spacemap", "log_spacemap", - "Log metaslab changes on a single spacemap and " - "flush them periodically.", - ZFEATURE_FLAG_READONLY_COMPAT, ZFEATURE_TYPE_BOOLEAN, - log_spacemap_deps); + static const spa_feature_t log_spacemap_deps[] = { + SPA_FEATURE_SPACEMAP_V2, + SPA_FEATURE_NONE + }; + zfeature_register(SPA_FEATURE_LOG_SPACEMAP, + "com.delphix:log_spacemap", "log_spacemap", + "Log metaslab changes on a single spacemap and " + "flush them periodically.", + ZFEATURE_FLAG_READONLY_COMPAT, ZFEATURE_TYPE_BOOLEAN, + log_spacemap_deps, sfeatures); } { - static const spa_feature_t large_blocks_deps[] = { - SPA_FEATURE_EXTENSIBLE_DATASET, - SPA_FEATURE_NONE - }; - zfeature_register(SPA_FEATURE_LARGE_BLOCKS, - "org.open-zfs:large_blocks", "large_blocks", - "Support for blocks larger than 128KB.", - ZFEATURE_FLAG_PER_DATASET, ZFEATURE_TYPE_BOOLEAN, - large_blocks_deps); + static const spa_feature_t large_blocks_deps[] = { + SPA_FEATURE_EXTENSIBLE_DATASET, + SPA_FEATURE_NONE + }; + zfeature_register(SPA_FEATURE_LARGE_BLOCKS, + "org.open-zfs:large_blocks", "large_blocks", + "Support for blocks larger than 128KB.", + ZFEATURE_FLAG_PER_DATASET, ZFEATURE_TYPE_BOOLEAN, + large_blocks_deps, sfeatures); } { - static const spa_feature_t large_dnode_deps[] = { - SPA_FEATURE_EXTENSIBLE_DATASET, - SPA_FEATURE_NONE - }; - zfeature_register(SPA_FEATURE_LARGE_DNODE, - "org.zfsonlinux:large_dnode", "large_dnode", - "Variable on-disk size of dnodes.", - ZFEATURE_FLAG_PER_DATASET, ZFEATURE_TYPE_BOOLEAN, - large_dnode_deps); + static const spa_feature_t large_dnode_deps[] = { + SPA_FEATURE_EXTENSIBLE_DATASET, + SPA_FEATURE_NONE + }; + zfeature_register(SPA_FEATURE_LARGE_DNODE, + "org.zfsonlinux:large_dnode", "large_dnode", + "Variable on-disk size of dnodes.", + ZFEATURE_FLAG_PER_DATASET, ZFEATURE_TYPE_BOOLEAN, + large_dnode_deps, sfeatures); } { - static const spa_feature_t sha512_deps[] = { - SPA_FEATURE_EXTENSIBLE_DATASET, - SPA_FEATURE_NONE - }; - zfeature_register(SPA_FEATURE_SHA512, - "org.illumos:sha512", "sha512", - "SHA-512/256 hash algorithm.", - ZFEATURE_FLAG_PER_DATASET, ZFEATURE_TYPE_BOOLEAN, - sha512_deps); + static const spa_feature_t sha512_deps[] = { + SPA_FEATURE_EXTENSIBLE_DATASET, + SPA_FEATURE_NONE + }; + zfeature_register(SPA_FEATURE_SHA512, + "org.illumos:sha512", "sha512", + "SHA-512/256 hash algorithm.", + ZFEATURE_FLAG_PER_DATASET, ZFEATURE_TYPE_BOOLEAN, + sha512_deps, sfeatures); } { - static const spa_feature_t skein_deps[] = { - SPA_FEATURE_EXTENSIBLE_DATASET, - SPA_FEATURE_NONE - }; - zfeature_register(SPA_FEATURE_SKEIN, - "org.illumos:skein", "skein", - "Skein hash algorithm.", - ZFEATURE_FLAG_PER_DATASET, ZFEATURE_TYPE_BOOLEAN, - skein_deps); + static const spa_feature_t skein_deps[] = { + SPA_FEATURE_EXTENSIBLE_DATASET, + SPA_FEATURE_NONE + }; + zfeature_register(SPA_FEATURE_SKEIN, + "org.illumos:skein", "skein", + "Skein hash algorithm.", + ZFEATURE_FLAG_PER_DATASET, ZFEATURE_TYPE_BOOLEAN, + skein_deps, sfeatures); } { - static const spa_feature_t edonr_deps[] = { - SPA_FEATURE_EXTENSIBLE_DATASET, - SPA_FEATURE_NONE - }; - zfeature_register(SPA_FEATURE_EDONR, - "org.illumos:edonr", "edonr", - "Edon-R hash algorithm.", - ZFEATURE_FLAG_PER_DATASET, ZFEATURE_TYPE_BOOLEAN, - edonr_deps); + static const spa_feature_t edonr_deps[] = { + SPA_FEATURE_EXTENSIBLE_DATASET, + SPA_FEATURE_NONE + }; + zfeature_register(SPA_FEATURE_EDONR, + "org.illumos:edonr", "edonr", + "Edon-R hash algorithm.", + ZFEATURE_FLAG_PER_DATASET, ZFEATURE_TYPE_BOOLEAN, + edonr_deps, sfeatures); } { - static const spa_feature_t redact_books_deps[] = { - SPA_FEATURE_BOOKMARK_V2, - SPA_FEATURE_EXTENSIBLE_DATASET, - SPA_FEATURE_BOOKMARKS, - SPA_FEATURE_NONE - }; - zfeature_register(SPA_FEATURE_REDACTION_BOOKMARKS, - "com.delphix:redaction_bookmarks", "redaction_bookmarks", - "Support for bookmarks which store redaction lists for zfs " - "redacted send/recv.", 0, ZFEATURE_TYPE_BOOLEAN, - redact_books_deps); + static const spa_feature_t redact_books_deps[] = { + SPA_FEATURE_BOOKMARK_V2, + SPA_FEATURE_EXTENSIBLE_DATASET, + SPA_FEATURE_BOOKMARKS, + SPA_FEATURE_NONE + }; + zfeature_register(SPA_FEATURE_REDACTION_BOOKMARKS, + "com.delphix:redaction_bookmarks", "redaction_bookmarks", + "Support for bookmarks which store redaction lists for zfs " + "redacted send/recv.", 0, ZFEATURE_TYPE_BOOLEAN, + redact_books_deps, sfeatures); } { - static const spa_feature_t redact_datasets_deps[] = { - SPA_FEATURE_EXTENSIBLE_DATASET, - SPA_FEATURE_NONE - }; - zfeature_register(SPA_FEATURE_REDACTED_DATASETS, - "com.delphix:redacted_datasets", "redacted_datasets", "Support for " - "redacted datasets, produced by receiving a redacted zfs send " - "stream.", ZFEATURE_FLAG_PER_DATASET, ZFEATURE_TYPE_UINT64_ARRAY, - redact_datasets_deps); + static const spa_feature_t redact_datasets_deps[] = { + SPA_FEATURE_EXTENSIBLE_DATASET, + SPA_FEATURE_NONE + }; + zfeature_register(SPA_FEATURE_REDACTED_DATASETS, + "com.delphix:redacted_datasets", "redacted_datasets", + "Support for redacted datasets, produced by receiving " + "a redacted zfs send stream.", + ZFEATURE_FLAG_PER_DATASET, ZFEATURE_TYPE_UINT64_ARRAY, + redact_datasets_deps, sfeatures); } { - static const spa_feature_t bookmark_written_deps[] = { - SPA_FEATURE_BOOKMARK_V2, - SPA_FEATURE_EXTENSIBLE_DATASET, - SPA_FEATURE_BOOKMARKS, - SPA_FEATURE_NONE - }; - zfeature_register(SPA_FEATURE_BOOKMARK_WRITTEN, - "com.delphix:bookmark_written", "bookmark_written", - "Additional accounting, enabling the written#<bookmark> property" - "(space written since a bookmark), and estimates of send stream " - "sizes for incrementals from bookmarks.", - 0, ZFEATURE_TYPE_BOOLEAN, bookmark_written_deps); + static const spa_feature_t bookmark_written_deps[] = { + SPA_FEATURE_BOOKMARK_V2, + SPA_FEATURE_EXTENSIBLE_DATASET, + SPA_FEATURE_BOOKMARKS, + SPA_FEATURE_NONE + }; + zfeature_register(SPA_FEATURE_BOOKMARK_WRITTEN, + "com.delphix:bookmark_written", "bookmark_written", + "Additional accounting, enabling the written#<bookmark> " + "property (space written since a bookmark), " + "and estimates of send stream sizes for incrementals from " + "bookmarks.", + 0, ZFEATURE_TYPE_BOOLEAN, bookmark_written_deps, sfeatures); } zfeature_register(SPA_FEATURE_DEVICE_REMOVAL, "com.delphix:device_removal", "device_removal", "Top-level vdevs can be removed, reducing logical pool size.", - ZFEATURE_FLAG_MOS, ZFEATURE_TYPE_BOOLEAN, NULL); + ZFEATURE_FLAG_MOS, ZFEATURE_TYPE_BOOLEAN, NULL, sfeatures); { - static const spa_feature_t obsolete_counts_deps[] = { - SPA_FEATURE_EXTENSIBLE_DATASET, - SPA_FEATURE_DEVICE_REMOVAL, - SPA_FEATURE_NONE - }; - zfeature_register(SPA_FEATURE_OBSOLETE_COUNTS, - "com.delphix:obsolete_counts", "obsolete_counts", - "Reduce memory used by removed devices when their blocks are " - "freed or remapped.", - ZFEATURE_FLAG_READONLY_COMPAT, ZFEATURE_TYPE_BOOLEAN, - obsolete_counts_deps); + static const spa_feature_t obsolete_counts_deps[] = { + SPA_FEATURE_EXTENSIBLE_DATASET, + SPA_FEATURE_DEVICE_REMOVAL, + SPA_FEATURE_NONE + }; + zfeature_register(SPA_FEATURE_OBSOLETE_COUNTS, + "com.delphix:obsolete_counts", "obsolete_counts", + "Reduce memory used by removed devices when their blocks " + "are freed or remapped.", + ZFEATURE_FLAG_READONLY_COMPAT, ZFEATURE_TYPE_BOOLEAN, + obsolete_counts_deps, sfeatures); } { - static const spa_feature_t userobj_accounting_deps[] = { - SPA_FEATURE_EXTENSIBLE_DATASET, - SPA_FEATURE_NONE - }; - zfeature_register(SPA_FEATURE_USEROBJ_ACCOUNTING, - "org.zfsonlinux:userobj_accounting", "userobj_accounting", - "User/Group object accounting.", - ZFEATURE_FLAG_READONLY_COMPAT | ZFEATURE_FLAG_PER_DATASET, - ZFEATURE_TYPE_BOOLEAN, userobj_accounting_deps); + static const spa_feature_t userobj_accounting_deps[] = { + SPA_FEATURE_EXTENSIBLE_DATASET, + SPA_FEATURE_NONE + }; + zfeature_register(SPA_FEATURE_USEROBJ_ACCOUNTING, + "org.zfsonlinux:userobj_accounting", "userobj_accounting", + "User/Group object accounting.", + ZFEATURE_FLAG_READONLY_COMPAT | ZFEATURE_FLAG_PER_DATASET, + ZFEATURE_TYPE_BOOLEAN, userobj_accounting_deps, sfeatures); } { - static const spa_feature_t bookmark_v2_deps[] = { - SPA_FEATURE_EXTENSIBLE_DATASET, - SPA_FEATURE_BOOKMARKS, - SPA_FEATURE_NONE - }; - zfeature_register(SPA_FEATURE_BOOKMARK_V2, - "com.datto:bookmark_v2", "bookmark_v2", - "Support for larger bookmarks", - 0, ZFEATURE_TYPE_BOOLEAN, bookmark_v2_deps); + static const spa_feature_t bookmark_v2_deps[] = { + SPA_FEATURE_EXTENSIBLE_DATASET, + SPA_FEATURE_BOOKMARKS, + SPA_FEATURE_NONE + }; + zfeature_register(SPA_FEATURE_BOOKMARK_V2, + "com.datto:bookmark_v2", "bookmark_v2", + "Support for larger bookmarks", + 0, ZFEATURE_TYPE_BOOLEAN, bookmark_v2_deps, sfeatures); } { - static const spa_feature_t encryption_deps[] = { - SPA_FEATURE_EXTENSIBLE_DATASET, - SPA_FEATURE_BOOKMARK_V2, - SPA_FEATURE_NONE - }; - zfeature_register(SPA_FEATURE_ENCRYPTION, - "com.datto:encryption", "encryption", - "Support for dataset level encryption", - ZFEATURE_FLAG_PER_DATASET, ZFEATURE_TYPE_BOOLEAN, - encryption_deps); + static const spa_feature_t encryption_deps[] = { + SPA_FEATURE_EXTENSIBLE_DATASET, + SPA_FEATURE_BOOKMARK_V2, + SPA_FEATURE_NONE + }; + zfeature_register(SPA_FEATURE_ENCRYPTION, + "com.datto:encryption", "encryption", + "Support for dataset level encryption", + ZFEATURE_FLAG_PER_DATASET, ZFEATURE_TYPE_BOOLEAN, + encryption_deps, sfeatures); } { - static const spa_feature_t project_quota_deps[] = { - SPA_FEATURE_EXTENSIBLE_DATASET, - SPA_FEATURE_NONE - }; - zfeature_register(SPA_FEATURE_PROJECT_QUOTA, - "org.zfsonlinux:project_quota", "project_quota", - "space/object accounting based on project ID.", - ZFEATURE_FLAG_READONLY_COMPAT | ZFEATURE_FLAG_PER_DATASET, - ZFEATURE_TYPE_BOOLEAN, project_quota_deps); + static const spa_feature_t project_quota_deps[] = { + SPA_FEATURE_EXTENSIBLE_DATASET, + SPA_FEATURE_NONE + }; + zfeature_register(SPA_FEATURE_PROJECT_QUOTA, + "org.zfsonlinux:project_quota", "project_quota", + "space/object accounting based on project ID.", + ZFEATURE_FLAG_READONLY_COMPAT | ZFEATURE_FLAG_PER_DATASET, + ZFEATURE_TYPE_BOOLEAN, project_quota_deps, sfeatures); } zfeature_register(SPA_FEATURE_ALLOCATION_CLASSES, "org.zfsonlinux:allocation_classes", "allocation_classes", "Support for separate allocation classes.", - ZFEATURE_FLAG_READONLY_COMPAT, ZFEATURE_TYPE_BOOLEAN, NULL); + ZFEATURE_FLAG_READONLY_COMPAT, ZFEATURE_TYPE_BOOLEAN, NULL, + sfeatures); zfeature_register(SPA_FEATURE_RESILVER_DEFER, "com.datto:resilver_defer", "resilver_defer", "Support for deferring new resilvers when one is already running.", - ZFEATURE_FLAG_READONLY_COMPAT, ZFEATURE_TYPE_BOOLEAN, NULL); + ZFEATURE_FLAG_READONLY_COMPAT, ZFEATURE_TYPE_BOOLEAN, NULL, + sfeatures); zfeature_register(SPA_FEATURE_DEVICE_REBUILD, "org.openzfs:device_rebuild", "device_rebuild", "Support for sequential mirror/dRAID device rebuilds", - ZFEATURE_FLAG_READONLY_COMPAT, ZFEATURE_TYPE_BOOLEAN, NULL); + ZFEATURE_FLAG_READONLY_COMPAT, ZFEATURE_TYPE_BOOLEAN, NULL, + sfeatures); { - static const spa_feature_t zstd_deps[] = { - SPA_FEATURE_EXTENSIBLE_DATASET, - SPA_FEATURE_NONE - }; - zfeature_register(SPA_FEATURE_ZSTD_COMPRESS, - "org.freebsd:zstd_compress", "zstd_compress", - "zstd compression algorithm support.", - ZFEATURE_FLAG_PER_DATASET, ZFEATURE_TYPE_BOOLEAN, zstd_deps); + static const spa_feature_t zstd_deps[] = { + SPA_FEATURE_EXTENSIBLE_DATASET, + SPA_FEATURE_NONE + }; + zfeature_register(SPA_FEATURE_ZSTD_COMPRESS, + "org.freebsd:zstd_compress", "zstd_compress", + "zstd compression algorithm support.", + ZFEATURE_FLAG_PER_DATASET, ZFEATURE_TYPE_BOOLEAN, zstd_deps, + sfeatures); } zfeature_register(SPA_FEATURE_DRAID, "org.openzfs:draid", "draid", "Support for distributed spare RAID", - ZFEATURE_FLAG_MOS, ZFEATURE_TYPE_BOOLEAN, NULL); + ZFEATURE_FLAG_MOS, ZFEATURE_TYPE_BOOLEAN, NULL, sfeatures); + + zfs_mod_list_supported_free(sfeatures); } #if defined(_KERNEL) |