xref: /freebsd/sys/contrib/openzfs/module/zcommon/zfeature_common.c (revision 22649d4dba730d46244fd2dff4fd174903c8379f)
1 // SPDX-License-Identifier: CDDL-1.0
2 /*
3  * This file and its contents are supplied under the terms of the
4  * Common Development and Distribution License ("CDDL"), version 1.0.
5  * You may only use this file in accordance with the terms of version
6  * 1.0 of the CDDL.
7  *
8  * A full copy of the text of the CDDL should have accompanied this
9  * source.  A copy of the CDDL is also available via the Internet at
10  * https://opensource.org/license/CDDL-1.0.
11  */
12 
13 /*
14  * Copyright (c) 2011, 2018 by Delphix. All rights reserved.
15  * Copyright (c) 2013 by Saso Kiselkov. All rights reserved.
16  * Copyright (c) 2013, Joyent, Inc. All rights reserved.
17  * Copyright (c) 2014, Nexenta Systems, Inc. All rights reserved.
18  * Copyright (c) 2017, Intel Corporation.
19  * Copyright (c) 2019, 2024, Klara, Inc.
20  * Copyright (c) 2019, Allan Jude
21  */
22 
23 #ifndef _KERNEL
24 #include <errno.h>
25 #include <string.h>
26 #include <dirent.h>
27 #include <search.h>
28 #include <sys/stat.h>
29 #endif
30 #include <sys/debug.h>
31 #include <sys/fs/zfs.h>
32 #include <sys/inttypes.h>
33 #include <sys/types.h>
34 #include <sys/param.h>
35 #include <sys/zfs_sysfs.h>
36 #include "zfeature_common.h"
37 
38 /*
39  * Set to disable all feature checks while opening pools, allowing pools with
40  * unsupported features to be opened. Set for testing only.
41  */
42 boolean_t zfeature_checks_disable = B_FALSE;
43 
44 zfeature_info_t spa_feature_table[SPA_FEATURES];
45 
46 /*
47  * Valid characters for feature guids. This list is mainly for aesthetic
48  * purposes and could be expanded in the future. There are different allowed
49  * characters in the guids reverse dns portion (before the colon) and its
50  * short name (after the colon).
51  */
52 static int
valid_char(char c,boolean_t after_colon)53 valid_char(char c, boolean_t after_colon)
54 {
55 	return ((c >= 'a' && c <= 'z') ||
56 	    (c >= '0' && c <= '9') ||
57 	    (after_colon && c == '_') ||
58 	    (!after_colon && (c == '.' || c == '-')));
59 }
60 
61 /*
62  * Every feature guid must contain exactly one colon which separates a reverse
63  * dns organization name from the feature's "short" name (e.g.
64  * "com.company:feature_name").
65  */
66 boolean_t
zfeature_is_valid_guid(const char * name)67 zfeature_is_valid_guid(const char *name)
68 {
69 	int i;
70 	boolean_t has_colon = B_FALSE;
71 
72 	i = 0;
73 	while (name[i] != '\0') {
74 		char c = name[i++];
75 		if (c == ':') {
76 			if (has_colon)
77 				return (B_FALSE);
78 			has_colon = B_TRUE;
79 			continue;
80 		}
81 		if (!valid_char(c, has_colon))
82 			return (B_FALSE);
83 	}
84 
85 	return (has_colon);
86 }
87 
88 boolean_t
zfeature_is_supported(const char * guid)89 zfeature_is_supported(const char *guid)
90 {
91 	if (zfeature_checks_disable)
92 		return (B_TRUE);
93 
94 	for (spa_feature_t i = 0; i < SPA_FEATURES; i++) {
95 		zfeature_info_t *feature = &spa_feature_table[i];
96 		if (!feature->fi_zfs_mod_supported)
97 			continue;
98 		if (strcmp(guid, feature->fi_guid) == 0)
99 			return (B_TRUE);
100 	}
101 	return (B_FALSE);
102 }
103 
104 int
zfeature_lookup_guid(const char * guid,spa_feature_t * res)105 zfeature_lookup_guid(const char *guid, spa_feature_t *res)
106 {
107 	for (spa_feature_t i = 0; i < SPA_FEATURES; i++) {
108 		zfeature_info_t *feature = &spa_feature_table[i];
109 		if (!feature->fi_zfs_mod_supported)
110 			continue;
111 		if (strcmp(guid, feature->fi_guid) == 0) {
112 			if (res != NULL)
113 				*res = i;
114 			return (0);
115 		}
116 	}
117 
118 	return (ENOENT);
119 }
120 
121 int
zfeature_lookup_name(const char * name,spa_feature_t * res)122 zfeature_lookup_name(const char *name, spa_feature_t *res)
123 {
124 	for (spa_feature_t i = 0; i < SPA_FEATURES; i++) {
125 		zfeature_info_t *feature = &spa_feature_table[i];
126 		if (!feature->fi_zfs_mod_supported)
127 			continue;
128 		if (strcmp(name, feature->fi_uname) == 0) {
129 			if (res != NULL)
130 				*res = i;
131 			return (0);
132 		}
133 	}
134 
135 	return (ENOENT);
136 }
137 
138 boolean_t
zfeature_depends_on(spa_feature_t fid,spa_feature_t check)139 zfeature_depends_on(spa_feature_t fid, spa_feature_t check)
140 {
141 	zfeature_info_t *feature = &spa_feature_table[fid];
142 
143 	for (int i = 0; feature->fi_depends[i] != SPA_FEATURE_NONE; i++) {
144 		if (feature->fi_depends[i] == check)
145 			return (B_TRUE);
146 	}
147 	return (B_FALSE);
148 }
149 
150 static boolean_t
deps_contains_feature(const spa_feature_t * deps,const spa_feature_t feature)151 deps_contains_feature(const spa_feature_t *deps, const spa_feature_t feature)
152 {
153 	for (int i = 0; deps[i] != SPA_FEATURE_NONE; i++)
154 		if (deps[i] == feature)
155 			return (B_TRUE);
156 
157 	return (B_FALSE);
158 }
159 
160 #define	STRCMP ((int(*)(const void *, const void *))&strcmp)
161 struct zfs_mod_supported_features {
162 	void *tree;
163 	boolean_t all_features;
164 };
165 
166 struct zfs_mod_supported_features *
zfs_mod_list_supported(const char * scope)167 zfs_mod_list_supported(const char *scope)
168 {
169 #if defined(__FreeBSD__) || defined(_KERNEL) || defined(LIB_ZPOOL_BUILD)
170 	(void) scope;
171 	return (NULL);
172 #else
173 	struct zfs_mod_supported_features *ret = calloc(1, sizeof (*ret));
174 	if (ret == NULL)
175 		return (NULL);
176 
177 	DIR *sysfs_dir = NULL;
178 	char path[128];
179 
180 	if (snprintf(path, sizeof (path), "%s/%s",
181 	    ZFS_SYSFS_DIR, scope) < sizeof (path))
182 		sysfs_dir = opendir(path);
183 	if (sysfs_dir == NULL && errno == ENOENT) {
184 		if (snprintf(path, sizeof (path), "%s/%s",
185 		    ZFS_SYSFS_ALT_DIR, scope) < sizeof (path))
186 			sysfs_dir = opendir(path);
187 	}
188 	if (sysfs_dir == NULL) {
189 		ret->all_features = errno == ENOENT &&
190 		    (access(ZFS_SYSFS_DIR, F_OK) == 0 ||
191 		    access(ZFS_SYSFS_ALT_DIR, F_OK) == 0);
192 		return (ret);
193 	}
194 
195 	struct dirent *node;
196 	while ((node = readdir(sysfs_dir)) != NULL) {
197 		if (strcmp(node->d_name, ".") == 0 ||
198 		    strcmp(node->d_name, "..") == 0)
199 			continue;
200 
201 		char *name = strdup(node->d_name);
202 		if (name == NULL) {
203 			goto nomem;
204 		}
205 
206 		if (tsearch(name, &ret->tree, STRCMP) == NULL) {
207 			/*
208 			 * Don't bother checking for duplicate entries:
209 			 * we're iterating a single directory.
210 			 */
211 			free(name);
212 			goto nomem;
213 		}
214 	}
215 
216 end:
217 	closedir(sysfs_dir);
218 	return (ret);
219 
220 nomem:
221 	zfs_mod_list_supported_free(ret);
222 	ret = NULL;
223 	goto end;
224 #endif
225 }
226 
227 void
zfs_mod_list_supported_free(struct zfs_mod_supported_features * list)228 zfs_mod_list_supported_free(struct zfs_mod_supported_features *list)
229 {
230 #if !defined(__FreeBSD__) && !defined(_KERNEL) && !defined(LIB_ZPOOL_BUILD)
231 	if (list) {
232 		tdestroy(list->tree, free);
233 		free(list);
234 	}
235 #else
236 	(void) list;
237 #endif
238 }
239 
240 #if !defined(_KERNEL) && !defined(LIB_ZPOOL_BUILD)
241 static boolean_t
zfs_mod_supported_impl(const char * scope,const char * name,const char * sysfs)242 zfs_mod_supported_impl(const char *scope, const char *name, const char *sysfs)
243 {
244 	char path[128];
245 	if (snprintf(path, sizeof (path), "%s%s%s%s%s", sysfs,
246 	    scope == NULL ? "" : "/", scope ?: "",
247 	    name == NULL ? "" : "/", name ?: "") < sizeof (path))
248 		return (access(path, F_OK) == 0);
249 	else
250 		return (B_FALSE);
251 }
252 
253 boolean_t
zfs_mod_supported(const char * scope,const char * name,const struct zfs_mod_supported_features * sfeatures)254 zfs_mod_supported(const char *scope, const char *name,
255     const struct zfs_mod_supported_features *sfeatures)
256 {
257 	boolean_t supported;
258 
259 	if (sfeatures != NULL)
260 		return (sfeatures->all_features ||
261 		    tfind(name, &sfeatures->tree, STRCMP));
262 
263 	/*
264 	 * Check both the primary and alternate sysfs locations to determine
265 	 * if the required functionality is supported.
266 	 */
267 	supported = (zfs_mod_supported_impl(scope, name, ZFS_SYSFS_DIR) ||
268 	    zfs_mod_supported_impl(scope, name, ZFS_SYSFS_ALT_DIR));
269 
270 	/*
271 	 * For backwards compatibility with kernel modules that predate
272 	 * supported feature/property checking.  Report the feature/property
273 	 * as supported if the kernel module is loaded but the requested
274 	 * scope directory does not exist.
275 	 */
276 	if (supported == B_FALSE) {
277 		if ((access(ZFS_SYSFS_DIR, F_OK) == 0 &&
278 		    !zfs_mod_supported_impl(scope, NULL, ZFS_SYSFS_DIR)) ||
279 		    (access(ZFS_SYSFS_ALT_DIR, F_OK) == 0 &&
280 		    !zfs_mod_supported_impl(scope, NULL, ZFS_SYSFS_ALT_DIR))) {
281 			supported = B_TRUE;
282 		}
283 	}
284 
285 	return (supported);
286 }
287 #endif
288 
289 static boolean_t
zfs_mod_supported_feature(const char * name,const struct zfs_mod_supported_features * sfeatures)290 zfs_mod_supported_feature(const char *name,
291     const struct zfs_mod_supported_features *sfeatures)
292 {
293 	/*
294 	 * The zfs module spa_feature_table[], whether in-kernel or in
295 	 * libzpool, always supports all the features. libzfs needs to
296 	 * query the running module, via sysfs, to determine which
297 	 * features are supported.
298 	 *
299 	 * The equivalent _can_ be done on FreeBSD by way of the sysctl
300 	 * tree, but this has not been done yet.  Therefore, we return
301 	 * that all features are supported.
302 	 */
303 
304 #if defined(_KERNEL) || defined(LIB_ZPOOL_BUILD) || defined(__FreeBSD__)
305 	(void) name, (void) sfeatures;
306 	return (B_TRUE);
307 #else
308 	return (zfs_mod_supported(ZFS_SYSFS_POOL_FEATURES, name, sfeatures));
309 #endif
310 }
311 
312 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 struct zfs_mod_supported_features * sfeatures)313 zfeature_register(spa_feature_t fid, const char *guid, const char *name,
314     const char *desc, zfeature_flags_t flags, zfeature_type_t type,
315     const spa_feature_t *deps,
316     const struct zfs_mod_supported_features *sfeatures)
317 {
318 	zfeature_info_t *feature = &spa_feature_table[fid];
319 	static const spa_feature_t nodeps[] = { SPA_FEATURE_NONE };
320 
321 	ASSERT(name != NULL);
322 	ASSERT(desc != NULL);
323 	ASSERT((flags & ZFEATURE_FLAG_READONLY_COMPAT) == 0 ||
324 	    (flags & ZFEATURE_FLAG_MOS) == 0);
325 	ASSERT3U(fid, <, SPA_FEATURES);
326 	ASSERT(zfeature_is_valid_guid(guid));
327 
328 	if (deps == NULL)
329 		deps = nodeps;
330 
331 	VERIFY(((flags & ZFEATURE_FLAG_PER_DATASET) == 0) ||
332 	    (deps_contains_feature(deps, SPA_FEATURE_EXTENSIBLE_DATASET)));
333 
334 	feature->fi_feature = fid;
335 	feature->fi_guid = guid;
336 	feature->fi_uname = name;
337 	feature->fi_desc = desc;
338 	feature->fi_flags = flags;
339 	feature->fi_type = type;
340 	feature->fi_depends = deps;
341 	feature->fi_zfs_mod_supported =
342 	    zfs_mod_supported_feature(guid, sfeatures);
343 }
344 
345 /*
346  * Every feature has a GUID of the form com.example:feature_name.  The
347  * reversed DNS name ensures that the feature's GUID is unique across all ZFS
348  * implementations.  This allows companies to independently develop and
349  * release features.  Examples include org.delphix and org.datto.  Previously,
350  * features developed on one implementation have used that implementation's
351  * domain name (e.g. org.illumos and org.zfsonlinux).  Use of the org.openzfs
352  * domain name is recommended for new features which are developed by the
353  * OpenZFS community and its platforms.  This domain may optionally be used by
354  * companies developing features for initial release through an OpenZFS
355  * implementation.  Use of the org.openzfs domain requires reserving the
356  * feature name in advance with the OpenZFS project.
357  */
358 void
zpool_feature_init(void)359 zpool_feature_init(void)
360 {
361 	struct zfs_mod_supported_features *sfeatures =
362 	    zfs_mod_list_supported(ZFS_SYSFS_POOL_FEATURES);
363 
364 	zfeature_register(SPA_FEATURE_ASYNC_DESTROY,
365 	    "com.delphix:async_destroy", "async_destroy",
366 	    "Destroy filesystems asynchronously.",
367 	    ZFEATURE_FLAG_READONLY_COMPAT, ZFEATURE_TYPE_BOOLEAN, NULL,
368 	    sfeatures);
369 
370 	zfeature_register(SPA_FEATURE_EMPTY_BPOBJ,
371 	    "com.delphix:empty_bpobj", "empty_bpobj",
372 	    "Snapshots use less space.",
373 	    ZFEATURE_FLAG_READONLY_COMPAT, ZFEATURE_TYPE_BOOLEAN, NULL,
374 	    sfeatures);
375 
376 	zfeature_register(SPA_FEATURE_LZ4_COMPRESS,
377 	    "org.illumos:lz4_compress", "lz4_compress",
378 	    "LZ4 compression algorithm support.",
379 	    ZFEATURE_FLAG_ACTIVATE_ON_ENABLE, ZFEATURE_TYPE_BOOLEAN, NULL,
380 	    sfeatures);
381 
382 	zfeature_register(SPA_FEATURE_MULTI_VDEV_CRASH_DUMP,
383 	    "com.joyent:multi_vdev_crash_dump", "multi_vdev_crash_dump",
384 	    "Crash dumps to multiple vdev pools.",
385 	    0, ZFEATURE_TYPE_BOOLEAN, NULL, sfeatures);
386 
387 	zfeature_register(SPA_FEATURE_SPACEMAP_HISTOGRAM,
388 	    "com.delphix:spacemap_histogram", "spacemap_histogram",
389 	    "Spacemaps maintain space histograms.",
390 	    ZFEATURE_FLAG_READONLY_COMPAT, ZFEATURE_TYPE_BOOLEAN, NULL,
391 	    sfeatures);
392 
393 	zfeature_register(SPA_FEATURE_ENABLED_TXG,
394 	    "com.delphix:enabled_txg", "enabled_txg",
395 	    "Record txg at which a feature is enabled",
396 	    ZFEATURE_FLAG_READONLY_COMPAT, ZFEATURE_TYPE_BOOLEAN, NULL,
397 	    sfeatures);
398 
399 	{
400 		static const spa_feature_t hole_birth_deps[] = {
401 			SPA_FEATURE_ENABLED_TXG,
402 			SPA_FEATURE_NONE
403 		};
404 		zfeature_register(SPA_FEATURE_HOLE_BIRTH,
405 		    "com.delphix:hole_birth", "hole_birth",
406 		    "Retain hole birth txg for more precise zfs send",
407 		    ZFEATURE_FLAG_MOS | ZFEATURE_FLAG_ACTIVATE_ON_ENABLE,
408 		    ZFEATURE_TYPE_BOOLEAN, hole_birth_deps, sfeatures);
409 	}
410 
411 	zfeature_register(SPA_FEATURE_POOL_CHECKPOINT,
412 	    "com.delphix:zpool_checkpoint", "zpool_checkpoint",
413 	    "Pool state can be checkpointed, allowing rewind later.",
414 	    ZFEATURE_FLAG_READONLY_COMPAT, ZFEATURE_TYPE_BOOLEAN, NULL,
415 	    sfeatures);
416 
417 	zfeature_register(SPA_FEATURE_SPACEMAP_V2,
418 	    "com.delphix:spacemap_v2", "spacemap_v2",
419 	    "Space maps representing large segments are more efficient.",
420 	    ZFEATURE_FLAG_READONLY_COMPAT | ZFEATURE_FLAG_ACTIVATE_ON_ENABLE,
421 	    ZFEATURE_TYPE_BOOLEAN, NULL, sfeatures);
422 
423 	zfeature_register(SPA_FEATURE_EXTENSIBLE_DATASET,
424 	    "com.delphix:extensible_dataset", "extensible_dataset",
425 	    "Enhanced dataset functionality, used by other features.",
426 	    0, ZFEATURE_TYPE_BOOLEAN, NULL, sfeatures);
427 
428 	{
429 		static const spa_feature_t bookmarks_deps[] = {
430 			SPA_FEATURE_EXTENSIBLE_DATASET,
431 			SPA_FEATURE_NONE
432 		};
433 
434 		zfeature_register(SPA_FEATURE_BOOKMARKS,
435 		    "com.delphix:bookmarks", "bookmarks",
436 		    "\"zfs bookmark\" command",
437 		    ZFEATURE_FLAG_READONLY_COMPAT, ZFEATURE_TYPE_BOOLEAN,
438 		    bookmarks_deps, sfeatures);
439 	}
440 
441 	{
442 		static const spa_feature_t filesystem_limits_deps[] = {
443 			SPA_FEATURE_EXTENSIBLE_DATASET,
444 			SPA_FEATURE_NONE
445 		};
446 		zfeature_register(SPA_FEATURE_FS_SS_LIMIT,
447 		    "com.joyent:filesystem_limits", "filesystem_limits",
448 		    "Filesystem and snapshot limits.",
449 		    ZFEATURE_FLAG_READONLY_COMPAT, ZFEATURE_TYPE_BOOLEAN,
450 		    filesystem_limits_deps, sfeatures);
451 	}
452 
453 	zfeature_register(SPA_FEATURE_EMBEDDED_DATA,
454 	    "com.delphix:embedded_data", "embedded_data",
455 	    "Blocks which compress very well use even less space.",
456 	    ZFEATURE_FLAG_MOS | ZFEATURE_FLAG_ACTIVATE_ON_ENABLE,
457 	    ZFEATURE_TYPE_BOOLEAN, NULL, sfeatures);
458 
459 	{
460 		static const spa_feature_t livelist_deps[] = {
461 			SPA_FEATURE_EXTENSIBLE_DATASET,
462 			SPA_FEATURE_NONE
463 		};
464 		zfeature_register(SPA_FEATURE_LIVELIST,
465 		    "com.delphix:livelist", "livelist",
466 		    "Improved clone deletion performance.",
467 		    ZFEATURE_FLAG_READONLY_COMPAT, ZFEATURE_TYPE_BOOLEAN,
468 		    livelist_deps, sfeatures);
469 	}
470 
471 	{
472 		static const spa_feature_t log_spacemap_deps[] = {
473 			SPA_FEATURE_SPACEMAP_V2,
474 			SPA_FEATURE_NONE
475 		};
476 		zfeature_register(SPA_FEATURE_LOG_SPACEMAP,
477 		    "com.delphix:log_spacemap", "log_spacemap",
478 		    "Log metaslab changes on a single spacemap and "
479 		    "flush them periodically.",
480 		    ZFEATURE_FLAG_READONLY_COMPAT, ZFEATURE_TYPE_BOOLEAN,
481 		    log_spacemap_deps, sfeatures);
482 	}
483 
484 	{
485 		static const spa_feature_t large_blocks_deps[] = {
486 			SPA_FEATURE_EXTENSIBLE_DATASET,
487 			SPA_FEATURE_NONE
488 		};
489 		zfeature_register(SPA_FEATURE_LARGE_BLOCKS,
490 		    "org.open-zfs:large_blocks", "large_blocks",
491 		    "Support for blocks larger than 128KB.",
492 		    ZFEATURE_FLAG_PER_DATASET, ZFEATURE_TYPE_BOOLEAN,
493 		    large_blocks_deps, sfeatures);
494 	}
495 
496 	{
497 		static const spa_feature_t large_dnode_deps[] = {
498 			SPA_FEATURE_EXTENSIBLE_DATASET,
499 			SPA_FEATURE_NONE
500 		};
501 		zfeature_register(SPA_FEATURE_LARGE_DNODE,
502 		    "org.zfsonlinux:large_dnode", "large_dnode",
503 		    "Variable on-disk size of dnodes.",
504 		    ZFEATURE_FLAG_PER_DATASET, ZFEATURE_TYPE_BOOLEAN,
505 		    large_dnode_deps, sfeatures);
506 	}
507 
508 	{
509 		static const spa_feature_t sha512_deps[] = {
510 			SPA_FEATURE_EXTENSIBLE_DATASET,
511 			SPA_FEATURE_NONE
512 		};
513 		zfeature_register(SPA_FEATURE_SHA512,
514 		    "org.illumos:sha512", "sha512",
515 		    "SHA-512/256 hash algorithm.",
516 		    ZFEATURE_FLAG_PER_DATASET, ZFEATURE_TYPE_BOOLEAN,
517 		    sha512_deps, sfeatures);
518 	}
519 
520 	{
521 		static const spa_feature_t skein_deps[] = {
522 			SPA_FEATURE_EXTENSIBLE_DATASET,
523 			SPA_FEATURE_NONE
524 		};
525 		zfeature_register(SPA_FEATURE_SKEIN,
526 		    "org.illumos:skein", "skein",
527 		    "Skein hash algorithm.",
528 		    ZFEATURE_FLAG_PER_DATASET, ZFEATURE_TYPE_BOOLEAN,
529 		    skein_deps, sfeatures);
530 	}
531 
532 	{
533 		static const spa_feature_t edonr_deps[] = {
534 			SPA_FEATURE_EXTENSIBLE_DATASET,
535 			SPA_FEATURE_NONE
536 		};
537 		zfeature_register(SPA_FEATURE_EDONR,
538 		    "org.illumos:edonr", "edonr",
539 		    "Edon-R hash algorithm.",
540 		    ZFEATURE_FLAG_PER_DATASET, ZFEATURE_TYPE_BOOLEAN,
541 		    edonr_deps, sfeatures);
542 	}
543 
544 	{
545 		static const spa_feature_t redact_books_deps[] = {
546 			SPA_FEATURE_BOOKMARK_V2,
547 			SPA_FEATURE_EXTENSIBLE_DATASET,
548 			SPA_FEATURE_BOOKMARKS,
549 			SPA_FEATURE_NONE
550 		};
551 		zfeature_register(SPA_FEATURE_REDACTION_BOOKMARKS,
552 		    "com.delphix:redaction_bookmarks", "redaction_bookmarks",
553 		    "Support for bookmarks which store redaction lists for zfs "
554 		    "redacted send/recv.", 0, ZFEATURE_TYPE_BOOLEAN,
555 		    redact_books_deps, sfeatures);
556 	}
557 
558 	{
559 		static const spa_feature_t redact_datasets_deps[] = {
560 			SPA_FEATURE_EXTENSIBLE_DATASET,
561 			SPA_FEATURE_NONE
562 		};
563 		zfeature_register(SPA_FEATURE_REDACTED_DATASETS,
564 		    "com.delphix:redacted_datasets", "redacted_datasets",
565 		    "Support for redacted datasets, produced by receiving "
566 		    "a redacted zfs send stream.",
567 		    ZFEATURE_FLAG_PER_DATASET, ZFEATURE_TYPE_UINT64_ARRAY,
568 		    redact_datasets_deps, sfeatures);
569 	}
570 
571 	{
572 		static const spa_feature_t bookmark_written_deps[] = {
573 			SPA_FEATURE_BOOKMARK_V2,
574 			SPA_FEATURE_EXTENSIBLE_DATASET,
575 			SPA_FEATURE_BOOKMARKS,
576 			SPA_FEATURE_NONE
577 		};
578 		zfeature_register(SPA_FEATURE_BOOKMARK_WRITTEN,
579 		    "com.delphix:bookmark_written", "bookmark_written",
580 		    "Additional accounting, enabling the written#<bookmark> "
581 		    "property (space written since a bookmark), "
582 		    "and estimates of send stream sizes for incrementals from "
583 		    "bookmarks.",
584 		    0, ZFEATURE_TYPE_BOOLEAN, bookmark_written_deps, sfeatures);
585 	}
586 
587 	zfeature_register(SPA_FEATURE_DEVICE_REMOVAL,
588 	    "com.delphix:device_removal", "device_removal",
589 	    "Top-level vdevs can be removed, reducing logical pool size.",
590 	    ZFEATURE_FLAG_MOS, ZFEATURE_TYPE_BOOLEAN, NULL, sfeatures);
591 
592 	{
593 		static const spa_feature_t obsolete_counts_deps[] = {
594 			SPA_FEATURE_EXTENSIBLE_DATASET,
595 			SPA_FEATURE_DEVICE_REMOVAL,
596 			SPA_FEATURE_NONE
597 		};
598 		zfeature_register(SPA_FEATURE_OBSOLETE_COUNTS,
599 		    "com.delphix:obsolete_counts", "obsolete_counts",
600 		    "Reduce memory used by removed devices when their blocks "
601 		    "are freed or remapped.",
602 		    ZFEATURE_FLAG_READONLY_COMPAT, ZFEATURE_TYPE_BOOLEAN,
603 		    obsolete_counts_deps, sfeatures);
604 	}
605 
606 	{
607 		static const spa_feature_t userobj_accounting_deps[] = {
608 			SPA_FEATURE_EXTENSIBLE_DATASET,
609 			SPA_FEATURE_NONE
610 		};
611 		zfeature_register(SPA_FEATURE_USEROBJ_ACCOUNTING,
612 		    "org.zfsonlinux:userobj_accounting", "userobj_accounting",
613 		    "User/Group object accounting.",
614 		    ZFEATURE_FLAG_READONLY_COMPAT | ZFEATURE_FLAG_PER_DATASET,
615 		    ZFEATURE_TYPE_BOOLEAN, userobj_accounting_deps, sfeatures);
616 	}
617 
618 	{
619 		static const spa_feature_t bookmark_v2_deps[] = {
620 			SPA_FEATURE_EXTENSIBLE_DATASET,
621 			SPA_FEATURE_BOOKMARKS,
622 			SPA_FEATURE_NONE
623 		};
624 		zfeature_register(SPA_FEATURE_BOOKMARK_V2,
625 		    "com.datto:bookmark_v2", "bookmark_v2",
626 		    "Support for larger bookmarks",
627 		    0, ZFEATURE_TYPE_BOOLEAN, bookmark_v2_deps, sfeatures);
628 	}
629 
630 	{
631 		static const spa_feature_t encryption_deps[] = {
632 			SPA_FEATURE_EXTENSIBLE_DATASET,
633 			SPA_FEATURE_BOOKMARK_V2,
634 			SPA_FEATURE_NONE
635 		};
636 		zfeature_register(SPA_FEATURE_ENCRYPTION,
637 		    "com.datto:encryption", "encryption",
638 		    "Support for dataset level encryption",
639 		    ZFEATURE_FLAG_PER_DATASET, ZFEATURE_TYPE_BOOLEAN,
640 		    encryption_deps, sfeatures);
641 	}
642 
643 	{
644 		static const spa_feature_t project_quota_deps[] = {
645 			SPA_FEATURE_EXTENSIBLE_DATASET,
646 			SPA_FEATURE_NONE
647 		};
648 		zfeature_register(SPA_FEATURE_PROJECT_QUOTA,
649 		    "org.zfsonlinux:project_quota", "project_quota",
650 		    "space/object accounting based on project ID.",
651 		    ZFEATURE_FLAG_READONLY_COMPAT | ZFEATURE_FLAG_PER_DATASET,
652 		    ZFEATURE_TYPE_BOOLEAN, project_quota_deps, sfeatures);
653 	}
654 
655 	zfeature_register(SPA_FEATURE_ALLOCATION_CLASSES,
656 	    "org.zfsonlinux:allocation_classes", "allocation_classes",
657 	    "Support for separate allocation classes.",
658 	    ZFEATURE_FLAG_READONLY_COMPAT, ZFEATURE_TYPE_BOOLEAN, NULL,
659 	    sfeatures);
660 
661 	zfeature_register(SPA_FEATURE_RESILVER_DEFER,
662 	    "com.datto:resilver_defer", "resilver_defer",
663 	    "Support for deferring new resilvers when one is already running.",
664 	    ZFEATURE_FLAG_READONLY_COMPAT, ZFEATURE_TYPE_BOOLEAN, NULL,
665 	    sfeatures);
666 
667 	zfeature_register(SPA_FEATURE_DEVICE_REBUILD,
668 	    "org.openzfs:device_rebuild", "device_rebuild",
669 	    "Support for sequential mirror/dRAID device rebuilds",
670 	    ZFEATURE_FLAG_READONLY_COMPAT, ZFEATURE_TYPE_BOOLEAN, NULL,
671 	    sfeatures);
672 
673 	{
674 		static const spa_feature_t zstd_deps[] = {
675 			SPA_FEATURE_EXTENSIBLE_DATASET,
676 			SPA_FEATURE_NONE
677 		};
678 		zfeature_register(SPA_FEATURE_ZSTD_COMPRESS,
679 		    "org.freebsd:zstd_compress", "zstd_compress",
680 		    "zstd compression algorithm support.",
681 		    ZFEATURE_FLAG_PER_DATASET, ZFEATURE_TYPE_BOOLEAN, zstd_deps,
682 		    sfeatures);
683 	}
684 
685 	zfeature_register(SPA_FEATURE_DRAID,
686 	    "org.openzfs:draid", "draid", "Support for distributed spare RAID",
687 	    ZFEATURE_FLAG_MOS, ZFEATURE_TYPE_BOOLEAN, NULL, sfeatures);
688 
689 	{
690 		static const spa_feature_t draid_fdomain_deps[] = {
691 			SPA_FEATURE_DRAID,
692 			SPA_FEATURE_NONE
693 		};
694 		zfeature_register(SPA_FEATURE_DRAID_FAIL_DOMAINS,
695 		    "com.seagate:draid_failure_domains",
696 		    "draid_failure_domains",
697 		    "Support for failure domains in dRAID",
698 		    ZFEATURE_FLAG_MOS, ZFEATURE_TYPE_BOOLEAN,
699 		    draid_fdomain_deps, sfeatures);
700 	}
701 
702 	{
703 		static const spa_feature_t zilsaxattr_deps[] = {
704 			SPA_FEATURE_EXTENSIBLE_DATASET,
705 			SPA_FEATURE_NONE
706 		};
707 		zfeature_register(SPA_FEATURE_ZILSAXATTR,
708 		    "org.openzfs:zilsaxattr", "zilsaxattr",
709 		    "Support for xattr=sa extended attribute logging in ZIL.",
710 		    ZFEATURE_FLAG_PER_DATASET | ZFEATURE_FLAG_READONLY_COMPAT,
711 		    ZFEATURE_TYPE_BOOLEAN, zilsaxattr_deps, sfeatures);
712 	}
713 
714 	zfeature_register(SPA_FEATURE_HEAD_ERRLOG,
715 	    "com.delphix:head_errlog", "head_errlog",
716 	    "Support for per-dataset on-disk error logs.",
717 	    ZFEATURE_FLAG_ACTIVATE_ON_ENABLE, ZFEATURE_TYPE_BOOLEAN, NULL,
718 	    sfeatures);
719 
720 	{
721 		static const spa_feature_t blake3_deps[] = {
722 			SPA_FEATURE_EXTENSIBLE_DATASET,
723 			SPA_FEATURE_NONE
724 		};
725 		zfeature_register(SPA_FEATURE_BLAKE3,
726 		    "org.openzfs:blake3", "blake3",
727 		    "BLAKE3 hash algorithm.",
728 		    ZFEATURE_FLAG_PER_DATASET, ZFEATURE_TYPE_BOOLEAN,
729 		    blake3_deps, sfeatures);
730 	}
731 
732 	zfeature_register(SPA_FEATURE_BLOCK_CLONING,
733 	    "com.fudosecurity:block_cloning", "block_cloning",
734 	    "Support for block cloning via Block Reference Table.",
735 	    ZFEATURE_FLAG_READONLY_COMPAT, ZFEATURE_TYPE_BOOLEAN, NULL,
736 	    sfeatures);
737 
738 	zfeature_register(SPA_FEATURE_BLOCK_CLONING_ENDIAN,
739 	    "com.truenas:block_cloning_endian", "block_cloning_endian",
740 	    "Fixes BRT ZAP endianness on new pools.",
741 	    ZFEATURE_FLAG_READONLY_COMPAT, ZFEATURE_TYPE_BOOLEAN, NULL,
742 	    sfeatures);
743 
744 	zfeature_register(SPA_FEATURE_AVZ_V2,
745 	    "com.klarasystems:vdev_zaps_v2", "vdev_zaps_v2",
746 	    "Support for root vdev ZAP.",
747 	    ZFEATURE_FLAG_MOS, ZFEATURE_TYPE_BOOLEAN, NULL,
748 	    sfeatures);
749 
750 	{
751 		static const spa_feature_t redact_list_spill_deps[] = {
752 			SPA_FEATURE_REDACTION_BOOKMARKS,
753 			SPA_FEATURE_NONE
754 		};
755 		zfeature_register(SPA_FEATURE_REDACTION_LIST_SPILL,
756 		    "com.delphix:redaction_list_spill", "redaction_list_spill",
757 		    "Support for increased number of redaction_snapshot "
758 		    "arguments in zfs redact.", 0, ZFEATURE_TYPE_BOOLEAN,
759 		    redact_list_spill_deps, sfeatures);
760 	}
761 
762 	zfeature_register(SPA_FEATURE_RAIDZ_EXPANSION,
763 	    "org.openzfs:raidz_expansion", "raidz_expansion",
764 	    "Support for raidz expansion",
765 	    ZFEATURE_FLAG_MOS, ZFEATURE_TYPE_BOOLEAN, NULL, sfeatures);
766 
767 	zfeature_register(SPA_FEATURE_FAST_DEDUP,
768 	    "com.klarasystems:fast_dedup", "fast_dedup",
769 	    "Support for advanced deduplication",
770 	    ZFEATURE_FLAG_READONLY_COMPAT, ZFEATURE_TYPE_BOOLEAN, NULL,
771 	    sfeatures);
772 
773 	{
774 		static const spa_feature_t longname_deps[] = {
775 			SPA_FEATURE_EXTENSIBLE_DATASET,
776 			SPA_FEATURE_NONE
777 		};
778 		zfeature_register(SPA_FEATURE_LONGNAME,
779 		    "org.zfsonlinux:longname", "longname",
780 		    "support filename up to 1024 bytes",
781 		    ZFEATURE_FLAG_PER_DATASET, ZFEATURE_TYPE_BOOLEAN,
782 		    longname_deps, sfeatures);
783 	}
784 
785 	{
786 		static const spa_feature_t large_microzap_deps[] = {
787 			SPA_FEATURE_EXTENSIBLE_DATASET,
788 			SPA_FEATURE_LARGE_BLOCKS,
789 			SPA_FEATURE_NONE
790 		};
791 		zfeature_register(SPA_FEATURE_LARGE_MICROZAP,
792 		    "com.klarasystems:large_microzap", "large_microzap",
793 		    "Support for microzaps larger than 128KB.",
794 		    ZFEATURE_FLAG_PER_DATASET | ZFEATURE_FLAG_READONLY_COMPAT,
795 		    ZFEATURE_TYPE_BOOLEAN, large_microzap_deps, sfeatures);
796 	}
797 
798 	zfeature_register(SPA_FEATURE_DYNAMIC_GANG_HEADER,
799 	    "com.klarasystems:dynamic_gang_header", "dynamic_gang_header",
800 	    "Support for dynamically sized gang headers",
801 	    ZFEATURE_FLAG_MOS | ZFEATURE_FLAG_NO_UPGRADE,
802 	    ZFEATURE_TYPE_BOOLEAN, NULL, sfeatures);
803 
804 	{
805 		static const spa_feature_t physical_rewrite_deps[] = {
806 			SPA_FEATURE_EXTENSIBLE_DATASET,
807 			SPA_FEATURE_NONE
808 		};
809 		zfeature_register(SPA_FEATURE_PHYSICAL_REWRITE,
810 		    "com.truenas:physical_rewrite", "physical_rewrite",
811 		    "Support for preserving logical birth time during rewrite.",
812 		    ZFEATURE_FLAG_READONLY_COMPAT | ZFEATURE_FLAG_PER_DATASET,
813 		    ZFEATURE_TYPE_BOOLEAN, physical_rewrite_deps, sfeatures);
814 	}
815 
816 	zfs_mod_list_supported_free(sfeatures);
817 }
818 
819 #if defined(_KERNEL)
820 EXPORT_SYMBOL(zfeature_lookup_guid);
821 EXPORT_SYMBOL(zfeature_lookup_name);
822 EXPORT_SYMBOL(zfeature_is_supported);
823 EXPORT_SYMBOL(zfeature_is_valid_guid);
824 EXPORT_SYMBOL(zfeature_depends_on);
825 EXPORT_SYMBOL(zpool_feature_init);
826 EXPORT_SYMBOL(spa_feature_table);
827 #endif
828