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