xref: /freebsd/sys/contrib/openzfs/module/zcommon/zfeature_common.c (revision 6132212808e8dccedc9e5d85fea4390c2f38059a)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 
22 /*
23  * Copyright (c) 2011, 2018 by Delphix. All rights reserved.
24  * Copyright (c) 2013 by Saso Kiselkov. All rights reserved.
25  * Copyright (c) 2013, Joyent, Inc. All rights reserved.
26  * Copyright (c) 2014, Nexenta Systems, Inc. All rights reserved.
27  * Copyright (c) 2017, Intel Corporation.
28  * Copyright (c) 2019, Klara Inc.
29  * Copyright (c) 2019, Allan Jude
30  */
31 
32 #ifndef _KERNEL
33 #include <errno.h>
34 #include <string.h>
35 #include <sys/stat.h>
36 #endif
37 #include <sys/debug.h>
38 #include <sys/fs/zfs.h>
39 #include <sys/inttypes.h>
40 #include <sys/types.h>
41 #include <sys/param.h>
42 #include <sys/zfs_sysfs.h>
43 #include "zfeature_common.h"
44 
45 /*
46  * Set to disable all feature checks while opening pools, allowing pools with
47  * unsupported features to be opened. Set for testing only.
48  */
49 boolean_t zfeature_checks_disable = B_FALSE;
50 
51 zfeature_info_t spa_feature_table[SPA_FEATURES];
52 
53 /*
54  * Valid characters for feature guids. This list is mainly for aesthetic
55  * purposes and could be expanded in the future. There are different allowed
56  * characters in the guids reverse dns portion (before the colon) and its
57  * short name (after the colon).
58  */
59 static int
60 valid_char(char c, boolean_t after_colon)
61 {
62 	return ((c >= 'a' && c <= 'z') ||
63 	    (c >= '0' && c <= '9') ||
64 	    (after_colon && c == '_') ||
65 	    (!after_colon && (c == '.' || c == '-')));
66 }
67 
68 /*
69  * Every feature guid must contain exactly one colon which separates a reverse
70  * dns organization name from the feature's "short" name (e.g.
71  * "com.company:feature_name").
72  */
73 boolean_t
74 zfeature_is_valid_guid(const char *name)
75 {
76 	int i;
77 	boolean_t has_colon = B_FALSE;
78 
79 	i = 0;
80 	while (name[i] != '\0') {
81 		char c = name[i++];
82 		if (c == ':') {
83 			if (has_colon)
84 				return (B_FALSE);
85 			has_colon = B_TRUE;
86 			continue;
87 		}
88 		if (!valid_char(c, has_colon))
89 			return (B_FALSE);
90 	}
91 
92 	return (has_colon);
93 }
94 
95 boolean_t
96 zfeature_is_supported(const char *guid)
97 {
98 	if (zfeature_checks_disable)
99 		return (B_TRUE);
100 
101 	for (spa_feature_t i = 0; i < SPA_FEATURES; i++) {
102 		zfeature_info_t *feature = &spa_feature_table[i];
103 		if (strcmp(guid, feature->fi_guid) == 0)
104 			return (B_TRUE);
105 	}
106 	return (B_FALSE);
107 }
108 
109 int
110 zfeature_lookup_guid(const char *guid, spa_feature_t *res)
111 {
112 	for (spa_feature_t i = 0; i < SPA_FEATURES; i++) {
113 		zfeature_info_t *feature = &spa_feature_table[i];
114 		if (!feature->fi_zfs_mod_supported)
115 			continue;
116 		if (strcmp(guid, feature->fi_guid) == 0) {
117 			if (res != NULL)
118 				*res = i;
119 			return (0);
120 		}
121 	}
122 
123 	return (ENOENT);
124 }
125 
126 int
127 zfeature_lookup_name(const char *name, spa_feature_t *res)
128 {
129 	for (spa_feature_t i = 0; i < SPA_FEATURES; i++) {
130 		zfeature_info_t *feature = &spa_feature_table[i];
131 		if (!feature->fi_zfs_mod_supported)
132 			continue;
133 		if (strcmp(name, feature->fi_uname) == 0) {
134 			if (res != NULL)
135 				*res = i;
136 			return (0);
137 		}
138 	}
139 
140 	return (ENOENT);
141 }
142 
143 boolean_t
144 zfeature_depends_on(spa_feature_t fid, spa_feature_t check)
145 {
146 	zfeature_info_t *feature = &spa_feature_table[fid];
147 
148 	for (int i = 0; feature->fi_depends[i] != SPA_FEATURE_NONE; i++) {
149 		if (feature->fi_depends[i] == check)
150 			return (B_TRUE);
151 	}
152 	return (B_FALSE);
153 }
154 
155 static boolean_t
156 deps_contains_feature(const spa_feature_t *deps, const spa_feature_t feature)
157 {
158 	for (int i = 0; deps[i] != SPA_FEATURE_NONE; i++)
159 		if (deps[i] == feature)
160 			return (B_TRUE);
161 
162 	return (B_FALSE);
163 }
164 
165 #if !defined(_KERNEL) && !defined(LIB_ZPOOL_BUILD)
166 static boolean_t
167 zfs_mod_supported_impl(const char *scope, const char *name, const char *sysfs)
168 {
169 	boolean_t supported = B_FALSE;
170 	char *path;
171 
172 	int len = asprintf(&path, "%s%s%s%s%s", sysfs,
173 	    scope == NULL ? "" : "/", scope == NULL ? "" : scope,
174 	    name == NULL ? "" : "/", name == NULL ? "" : name);
175 	if (len > 0) {
176 		struct stat64 statbuf;
177 		supported = !!(stat64(path, &statbuf) == 0);
178 		free(path);
179 	}
180 
181 	return (supported);
182 }
183 
184 boolean_t
185 zfs_mod_supported(const char *scope, const char *name)
186 {
187 	boolean_t supported;
188 
189 	/*
190 	 * Check both the primary and alternate sysfs locations to determine
191 	 * if the required functionality is supported.
192 	 */
193 	supported = (zfs_mod_supported_impl(scope, name, ZFS_SYSFS_DIR) ||
194 	    zfs_mod_supported_impl(scope, name, ZFS_SYSFS_ALT_DIR));
195 
196 	/*
197 	 * For backwards compatibility with kernel modules that predate
198 	 * supported feature/property checking.  Report the feature/property
199 	 * as supported if the kernel module is loaded but the requested
200 	 * scope directory does not exist.
201 	 */
202 	if (supported == B_FALSE) {
203 		struct stat64 statbuf;
204 		if ((stat64(ZFS_SYSFS_DIR, &statbuf) == 0) &&
205 		    !zfs_mod_supported_impl(scope, NULL, ZFS_SYSFS_DIR) &&
206 		    !zfs_mod_supported_impl(scope, NULL, ZFS_SYSFS_ALT_DIR)) {
207 			supported = B_TRUE;
208 		}
209 	}
210 
211 	return (supported);
212 }
213 #endif
214 
215 static boolean_t
216 zfs_mod_supported_feature(const char *name)
217 {
218 	/*
219 	 * The zfs module spa_feature_table[], whether in-kernel or in
220 	 * libzpool, always supports all the features. libzfs needs to
221 	 * query the running module, via sysfs, to determine which
222 	 * features are supported.
223 	 *
224 	 * The equivalent _can_ be done on FreeBSD by way of the sysctl
225 	 * tree, but this has not been done yet.
226 	 */
227 #if defined(_KERNEL) || defined(LIB_ZPOOL_BUILD) || defined(__FreeBSD__)
228 	return (B_TRUE);
229 #else
230 	return (zfs_mod_supported(ZFS_SYSFS_POOL_FEATURES, name));
231 #endif
232 }
233 
234 static void
235 zfeature_register(spa_feature_t fid, const char *guid, const char *name,
236     const char *desc, zfeature_flags_t flags, zfeature_type_t type,
237     const spa_feature_t *deps)
238 {
239 	zfeature_info_t *feature = &spa_feature_table[fid];
240 	static spa_feature_t nodeps[] = { SPA_FEATURE_NONE };
241 
242 	ASSERT(name != NULL);
243 	ASSERT(desc != NULL);
244 	ASSERT((flags & ZFEATURE_FLAG_READONLY_COMPAT) == 0 ||
245 	    (flags & ZFEATURE_FLAG_MOS) == 0);
246 	ASSERT3U(fid, <, SPA_FEATURES);
247 	ASSERT(zfeature_is_valid_guid(guid));
248 
249 	if (deps == NULL)
250 		deps = nodeps;
251 
252 	VERIFY(((flags & ZFEATURE_FLAG_PER_DATASET) == 0) ||
253 	    (deps_contains_feature(deps, SPA_FEATURE_EXTENSIBLE_DATASET)));
254 
255 	feature->fi_feature = fid;
256 	feature->fi_guid = guid;
257 	feature->fi_uname = name;
258 	feature->fi_desc = desc;
259 	feature->fi_flags = flags;
260 	feature->fi_type = type;
261 	feature->fi_depends = deps;
262 	feature->fi_zfs_mod_supported = zfs_mod_supported_feature(guid);
263 }
264 
265 /*
266  * Every feature has a GUID of the form com.example:feature_name.  The
267  * reversed DNS name ensures that the feature's GUID is unique across all ZFS
268  * implementations.  This allows companies to independently develop and
269  * release features.  Examples include org.delphix and org.datto.  Previously,
270  * features developed on one implementation have used that implementation's
271  * domain name (e.g. org.illumos and org.zfsonlinux).  Use of the org.openzfs
272  * domain name is recommended for new features which are developed by the
273  * OpenZFS community and its platforms.  This domain may optionally be used by
274  * companies developing features for initial release through an OpenZFS
275  * implementation.  Use of the org.openzfs domain requires reserving the
276  * feature name in advance with the OpenZFS project.
277  */
278 void
279 zpool_feature_init(void)
280 {
281 	zfeature_register(SPA_FEATURE_ASYNC_DESTROY,
282 	    "com.delphix:async_destroy", "async_destroy",
283 	    "Destroy filesystems asynchronously.",
284 	    ZFEATURE_FLAG_READONLY_COMPAT, ZFEATURE_TYPE_BOOLEAN, NULL);
285 
286 	zfeature_register(SPA_FEATURE_EMPTY_BPOBJ,
287 	    "com.delphix:empty_bpobj", "empty_bpobj",
288 	    "Snapshots use less space.",
289 	    ZFEATURE_FLAG_READONLY_COMPAT, ZFEATURE_TYPE_BOOLEAN, NULL);
290 
291 	zfeature_register(SPA_FEATURE_LZ4_COMPRESS,
292 	    "org.illumos:lz4_compress", "lz4_compress",
293 	    "LZ4 compression algorithm support.",
294 	    ZFEATURE_FLAG_ACTIVATE_ON_ENABLE, ZFEATURE_TYPE_BOOLEAN, NULL);
295 
296 	zfeature_register(SPA_FEATURE_MULTI_VDEV_CRASH_DUMP,
297 	    "com.joyent:multi_vdev_crash_dump", "multi_vdev_crash_dump",
298 	    "Crash dumps to multiple vdev pools.",
299 	    0, ZFEATURE_TYPE_BOOLEAN, NULL);
300 
301 	zfeature_register(SPA_FEATURE_SPACEMAP_HISTOGRAM,
302 	    "com.delphix:spacemap_histogram", "spacemap_histogram",
303 	    "Spacemaps maintain space histograms.",
304 	    ZFEATURE_FLAG_READONLY_COMPAT, ZFEATURE_TYPE_BOOLEAN, NULL);
305 
306 	zfeature_register(SPA_FEATURE_ENABLED_TXG,
307 	    "com.delphix:enabled_txg", "enabled_txg",
308 	    "Record txg at which a feature is enabled",
309 	    ZFEATURE_FLAG_READONLY_COMPAT, ZFEATURE_TYPE_BOOLEAN, NULL);
310 
311 	{
312 	static const spa_feature_t hole_birth_deps[] = {
313 		SPA_FEATURE_ENABLED_TXG,
314 		SPA_FEATURE_NONE
315 	};
316 	zfeature_register(SPA_FEATURE_HOLE_BIRTH,
317 	    "com.delphix:hole_birth", "hole_birth",
318 	    "Retain hole birth txg for more precise zfs send",
319 	    ZFEATURE_FLAG_MOS | ZFEATURE_FLAG_ACTIVATE_ON_ENABLE,
320 	    ZFEATURE_TYPE_BOOLEAN, hole_birth_deps);
321 	}
322 
323 	zfeature_register(SPA_FEATURE_POOL_CHECKPOINT,
324 	    "com.delphix:zpool_checkpoint", "zpool_checkpoint",
325 	    "Pool state can be checkpointed, allowing rewind later.",
326 	    ZFEATURE_FLAG_READONLY_COMPAT, ZFEATURE_TYPE_BOOLEAN, NULL);
327 
328 	zfeature_register(SPA_FEATURE_SPACEMAP_V2,
329 	    "com.delphix:spacemap_v2", "spacemap_v2",
330 	    "Space maps representing large segments are more efficient.",
331 	    ZFEATURE_FLAG_READONLY_COMPAT | ZFEATURE_FLAG_ACTIVATE_ON_ENABLE,
332 	    ZFEATURE_TYPE_BOOLEAN, NULL);
333 
334 	zfeature_register(SPA_FEATURE_EXTENSIBLE_DATASET,
335 	    "com.delphix:extensible_dataset", "extensible_dataset",
336 	    "Enhanced dataset functionality, used by other features.",
337 	    0, ZFEATURE_TYPE_BOOLEAN, NULL);
338 
339 	{
340 	static const spa_feature_t bookmarks_deps[] = {
341 		SPA_FEATURE_EXTENSIBLE_DATASET,
342 		SPA_FEATURE_NONE
343 	};
344 
345 	zfeature_register(SPA_FEATURE_BOOKMARKS,
346 	    "com.delphix:bookmarks", "bookmarks",
347 	    "\"zfs bookmark\" command",
348 	    ZFEATURE_FLAG_READONLY_COMPAT, ZFEATURE_TYPE_BOOLEAN,
349 	    bookmarks_deps);
350 	}
351 
352 	{
353 	static const spa_feature_t filesystem_limits_deps[] = {
354 		SPA_FEATURE_EXTENSIBLE_DATASET,
355 		SPA_FEATURE_NONE
356 	};
357 	zfeature_register(SPA_FEATURE_FS_SS_LIMIT,
358 	    "com.joyent:filesystem_limits", "filesystem_limits",
359 	    "Filesystem and snapshot limits.",
360 	    ZFEATURE_FLAG_READONLY_COMPAT, ZFEATURE_TYPE_BOOLEAN,
361 	    filesystem_limits_deps);
362 	}
363 
364 	zfeature_register(SPA_FEATURE_EMBEDDED_DATA,
365 	    "com.delphix:embedded_data", "embedded_data",
366 	    "Blocks which compress very well use even less space.",
367 	    ZFEATURE_FLAG_MOS | ZFEATURE_FLAG_ACTIVATE_ON_ENABLE,
368 	    ZFEATURE_TYPE_BOOLEAN, NULL);
369 
370 	{
371 	static const spa_feature_t livelist_deps[] = {
372 		SPA_FEATURE_EXTENSIBLE_DATASET,
373 		SPA_FEATURE_NONE
374 	};
375 	zfeature_register(SPA_FEATURE_LIVELIST,
376 	    "com.delphix:livelist", "livelist",
377 	    "Improved clone deletion performance.",
378 	    ZFEATURE_FLAG_READONLY_COMPAT, ZFEATURE_TYPE_BOOLEAN,
379 	    livelist_deps);
380 	}
381 
382 	{
383 	static const spa_feature_t log_spacemap_deps[] = {
384 		SPA_FEATURE_SPACEMAP_V2,
385 		SPA_FEATURE_NONE
386 	};
387 	zfeature_register(SPA_FEATURE_LOG_SPACEMAP,
388 	    "com.delphix:log_spacemap", "log_spacemap",
389 	    "Log metaslab changes on a single spacemap and "
390 	    "flush them periodically.",
391 	    ZFEATURE_FLAG_READONLY_COMPAT, ZFEATURE_TYPE_BOOLEAN,
392 	    log_spacemap_deps);
393 	}
394 
395 	{
396 	static const spa_feature_t large_blocks_deps[] = {
397 		SPA_FEATURE_EXTENSIBLE_DATASET,
398 		SPA_FEATURE_NONE
399 	};
400 	zfeature_register(SPA_FEATURE_LARGE_BLOCKS,
401 	    "org.open-zfs:large_blocks", "large_blocks",
402 	    "Support for blocks larger than 128KB.",
403 	    ZFEATURE_FLAG_PER_DATASET, ZFEATURE_TYPE_BOOLEAN,
404 	    large_blocks_deps);
405 	}
406 
407 	{
408 	static const spa_feature_t large_dnode_deps[] = {
409 		SPA_FEATURE_EXTENSIBLE_DATASET,
410 		SPA_FEATURE_NONE
411 	};
412 	zfeature_register(SPA_FEATURE_LARGE_DNODE,
413 	    "org.zfsonlinux:large_dnode", "large_dnode",
414 	    "Variable on-disk size of dnodes.",
415 	    ZFEATURE_FLAG_PER_DATASET, ZFEATURE_TYPE_BOOLEAN,
416 	    large_dnode_deps);
417 	}
418 
419 	{
420 	static const spa_feature_t sha512_deps[] = {
421 		SPA_FEATURE_EXTENSIBLE_DATASET,
422 		SPA_FEATURE_NONE
423 	};
424 	zfeature_register(SPA_FEATURE_SHA512,
425 	    "org.illumos:sha512", "sha512",
426 	    "SHA-512/256 hash algorithm.",
427 	    ZFEATURE_FLAG_PER_DATASET, ZFEATURE_TYPE_BOOLEAN,
428 	    sha512_deps);
429 	}
430 
431 	{
432 	static const spa_feature_t skein_deps[] = {
433 		SPA_FEATURE_EXTENSIBLE_DATASET,
434 		SPA_FEATURE_NONE
435 	};
436 	zfeature_register(SPA_FEATURE_SKEIN,
437 	    "org.illumos:skein", "skein",
438 	    "Skein hash algorithm.",
439 	    ZFEATURE_FLAG_PER_DATASET, ZFEATURE_TYPE_BOOLEAN,
440 	    skein_deps);
441 	}
442 
443 #if !defined(__FreeBSD__)
444 
445 	{
446 	static const spa_feature_t edonr_deps[] = {
447 		SPA_FEATURE_EXTENSIBLE_DATASET,
448 		SPA_FEATURE_NONE
449 	};
450 	zfeature_register(SPA_FEATURE_EDONR,
451 	    "org.illumos:edonr", "edonr",
452 	    "Edon-R hash algorithm.",
453 	    ZFEATURE_FLAG_PER_DATASET, ZFEATURE_TYPE_BOOLEAN,
454 	    edonr_deps);
455 	}
456 #endif
457 
458 	{
459 	static const spa_feature_t redact_books_deps[] = {
460 		SPA_FEATURE_BOOKMARK_V2,
461 		SPA_FEATURE_EXTENSIBLE_DATASET,
462 		SPA_FEATURE_BOOKMARKS,
463 		SPA_FEATURE_NONE
464 	};
465 	zfeature_register(SPA_FEATURE_REDACTION_BOOKMARKS,
466 	    "com.delphix:redaction_bookmarks", "redaction_bookmarks",
467 	    "Support for bookmarks which store redaction lists for zfs "
468 	    "redacted send/recv.", 0, ZFEATURE_TYPE_BOOLEAN,
469 	    redact_books_deps);
470 	}
471 
472 	{
473 	static const spa_feature_t redact_datasets_deps[] = {
474 		SPA_FEATURE_EXTENSIBLE_DATASET,
475 		SPA_FEATURE_NONE
476 	};
477 	zfeature_register(SPA_FEATURE_REDACTED_DATASETS,
478 	    "com.delphix:redacted_datasets", "redacted_datasets", "Support for "
479 	    "redacted datasets, produced by receiving a redacted zfs send "
480 	    "stream.", ZFEATURE_FLAG_PER_DATASET, ZFEATURE_TYPE_UINT64_ARRAY,
481 	    redact_datasets_deps);
482 	}
483 
484 	{
485 	static const spa_feature_t bookmark_written_deps[] = {
486 		SPA_FEATURE_BOOKMARK_V2,
487 		SPA_FEATURE_EXTENSIBLE_DATASET,
488 		SPA_FEATURE_BOOKMARKS,
489 		SPA_FEATURE_NONE
490 	};
491 	zfeature_register(SPA_FEATURE_BOOKMARK_WRITTEN,
492 	    "com.delphix:bookmark_written", "bookmark_written",
493 	    "Additional accounting, enabling the written#<bookmark> property"
494 	    "(space written since a bookmark), and estimates of send stream "
495 	    "sizes for incrementals from bookmarks.",
496 	    0, ZFEATURE_TYPE_BOOLEAN, bookmark_written_deps);
497 	}
498 
499 	zfeature_register(SPA_FEATURE_DEVICE_REMOVAL,
500 	    "com.delphix:device_removal", "device_removal",
501 	    "Top-level vdevs can be removed, reducing logical pool size.",
502 	    ZFEATURE_FLAG_MOS, ZFEATURE_TYPE_BOOLEAN, NULL);
503 
504 	{
505 	static const spa_feature_t obsolete_counts_deps[] = {
506 		SPA_FEATURE_EXTENSIBLE_DATASET,
507 		SPA_FEATURE_DEVICE_REMOVAL,
508 		SPA_FEATURE_NONE
509 	};
510 	zfeature_register(SPA_FEATURE_OBSOLETE_COUNTS,
511 	    "com.delphix:obsolete_counts", "obsolete_counts",
512 	    "Reduce memory used by removed devices when their blocks are "
513 	    "freed or remapped.",
514 	    ZFEATURE_FLAG_READONLY_COMPAT, ZFEATURE_TYPE_BOOLEAN,
515 	    obsolete_counts_deps);
516 	}
517 
518 	{
519 	static const spa_feature_t userobj_accounting_deps[] = {
520 		SPA_FEATURE_EXTENSIBLE_DATASET,
521 		SPA_FEATURE_NONE
522 	};
523 	zfeature_register(SPA_FEATURE_USEROBJ_ACCOUNTING,
524 	    "org.zfsonlinux:userobj_accounting", "userobj_accounting",
525 	    "User/Group object accounting.",
526 	    ZFEATURE_FLAG_READONLY_COMPAT | ZFEATURE_FLAG_PER_DATASET,
527 	    ZFEATURE_TYPE_BOOLEAN, userobj_accounting_deps);
528 	}
529 
530 	{
531 	static const spa_feature_t bookmark_v2_deps[] = {
532 		SPA_FEATURE_EXTENSIBLE_DATASET,
533 		SPA_FEATURE_BOOKMARKS,
534 		SPA_FEATURE_NONE
535 	};
536 	zfeature_register(SPA_FEATURE_BOOKMARK_V2,
537 	    "com.datto:bookmark_v2", "bookmark_v2",
538 	    "Support for larger bookmarks",
539 	    0, ZFEATURE_TYPE_BOOLEAN, bookmark_v2_deps);
540 	}
541 
542 	{
543 	static const spa_feature_t encryption_deps[] = {
544 		SPA_FEATURE_EXTENSIBLE_DATASET,
545 		SPA_FEATURE_BOOKMARK_V2,
546 		SPA_FEATURE_NONE
547 	};
548 	zfeature_register(SPA_FEATURE_ENCRYPTION,
549 	    "com.datto:encryption", "encryption",
550 	    "Support for dataset level encryption",
551 	    ZFEATURE_FLAG_PER_DATASET, ZFEATURE_TYPE_BOOLEAN,
552 	    encryption_deps);
553 	}
554 
555 	{
556 	static const spa_feature_t project_quota_deps[] = {
557 		SPA_FEATURE_EXTENSIBLE_DATASET,
558 		SPA_FEATURE_NONE
559 	};
560 	zfeature_register(SPA_FEATURE_PROJECT_QUOTA,
561 	    "org.zfsonlinux:project_quota", "project_quota",
562 	    "space/object accounting based on project ID.",
563 	    ZFEATURE_FLAG_READONLY_COMPAT | ZFEATURE_FLAG_PER_DATASET,
564 	    ZFEATURE_TYPE_BOOLEAN, project_quota_deps);
565 	}
566 
567 	zfeature_register(SPA_FEATURE_ALLOCATION_CLASSES,
568 	    "org.zfsonlinux:allocation_classes", "allocation_classes",
569 	    "Support for separate allocation classes.",
570 	    ZFEATURE_FLAG_READONLY_COMPAT, ZFEATURE_TYPE_BOOLEAN, NULL);
571 
572 	zfeature_register(SPA_FEATURE_RESILVER_DEFER,
573 	    "com.datto:resilver_defer", "resilver_defer",
574 	    "Support for deferring new resilvers when one is already running.",
575 	    ZFEATURE_FLAG_READONLY_COMPAT, ZFEATURE_TYPE_BOOLEAN, NULL);
576 
577 	zfeature_register(SPA_FEATURE_DEVICE_REBUILD,
578 	    "org.openzfs:device_rebuild", "device_rebuild",
579 	    "Support for sequential device rebuilds",
580 	    ZFEATURE_FLAG_READONLY_COMPAT, ZFEATURE_TYPE_BOOLEAN, NULL);
581 
582 	{
583 	static const spa_feature_t zstd_deps[] = {
584 		SPA_FEATURE_EXTENSIBLE_DATASET,
585 		SPA_FEATURE_NONE
586 	};
587 	zfeature_register(SPA_FEATURE_ZSTD_COMPRESS,
588 	    "org.freebsd:zstd_compress", "zstd_compress",
589 	    "zstd compression algorithm support.",
590 	    ZFEATURE_FLAG_PER_DATASET, ZFEATURE_TYPE_BOOLEAN, zstd_deps);
591 	}
592 }
593 
594 #if defined(_KERNEL)
595 EXPORT_SYMBOL(zfeature_lookup_guid);
596 EXPORT_SYMBOL(zfeature_lookup_name);
597 EXPORT_SYMBOL(zfeature_is_supported);
598 EXPORT_SYMBOL(zfeature_is_valid_guid);
599 EXPORT_SYMBOL(zfeature_depends_on);
600 EXPORT_SYMBOL(zpool_feature_init);
601 EXPORT_SYMBOL(spa_feature_table);
602 #endif
603