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