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  * Copyright (c) 2018, 2019 by Delphix. All rights reserved.
23eda14cbcSMatt Macy  */
24eda14cbcSMatt Macy 
25eda14cbcSMatt Macy #include <sys/types.h>
26eda14cbcSMatt Macy #include <sys/param.h>
27eda14cbcSMatt Macy #include <sys/zfeature.h>
28eda14cbcSMatt Macy #include <sys/zfs_ioctl.h>
29eda14cbcSMatt Macy #include <sys/zfs_sysfs.h>
30eda14cbcSMatt Macy #include <sys/kmem.h>
31eda14cbcSMatt Macy #include <sys/fs/zfs.h>
32eda14cbcSMatt Macy #include <linux/kobject.h>
33eda14cbcSMatt Macy 
34eda14cbcSMatt Macy #include "zfs_prop.h"
35eda14cbcSMatt Macy 
36eda14cbcSMatt Macy #if !defined(_KERNEL)
37eda14cbcSMatt Macy #error kernel builds only
38eda14cbcSMatt Macy #endif
39eda14cbcSMatt Macy 
40eda14cbcSMatt Macy /*
41eda14cbcSMatt Macy  * ZFS Module sysfs support
42eda14cbcSMatt Macy  *
43eda14cbcSMatt Macy  * This extends our sysfs '/sys/module/zfs' entry to include feature
44eda14cbcSMatt Macy  * and property attributes. The primary consumer of this information
45eda14cbcSMatt Macy  * is user processes, like the zfs CLI, that need to know what the
46eda14cbcSMatt Macy  * current loaded ZFS module supports. The libzfs binary will consult
47eda14cbcSMatt Macy  * this information when instantiating the zfs|zpool property tables
48eda14cbcSMatt Macy  * and the pool features table.
49eda14cbcSMatt Macy  *
50eda14cbcSMatt Macy  * The added top-level directories are:
51eda14cbcSMatt Macy  * /sys/module/zfs
52eda14cbcSMatt Macy  *		├── features.kernel
53eda14cbcSMatt Macy  *		├── features.pool
54eda14cbcSMatt Macy  *		├── properties.dataset
55eda14cbcSMatt Macy  *		└── properties.pool
56eda14cbcSMatt Macy  *
57eda14cbcSMatt Macy  * The local interface for the zfs kobjects includes:
58eda14cbcSMatt Macy  *	zfs_kobj_init()
59eda14cbcSMatt Macy  *	zfs_kobj_add()
60eda14cbcSMatt Macy  *	zfs_kobj_release()
61eda14cbcSMatt Macy  *	zfs_kobj_add_attr()
62eda14cbcSMatt Macy  *	zfs_kobj_fini()
63eda14cbcSMatt Macy  */
64eda14cbcSMatt Macy 
65eda14cbcSMatt Macy /*
66eda14cbcSMatt Macy  * A zfs_mod_kobj_t represents a zfs kobject under '/sys/module/zfs'
67eda14cbcSMatt Macy  */
68eda14cbcSMatt Macy typedef struct zfs_mod_kobj zfs_mod_kobj_t;
69eda14cbcSMatt Macy struct zfs_mod_kobj {
70eda14cbcSMatt Macy 	struct kobject		zko_kobj;
71eda14cbcSMatt Macy 	struct kobj_type	zko_kobj_type;
72eda14cbcSMatt Macy 	struct sysfs_ops	zko_sysfs_ops;
73eda14cbcSMatt Macy 	size_t			zko_attr_count;
74eda14cbcSMatt Macy 	struct attribute	*zko_attr_list;		/* allocated */
75716fd348SMartin Matuska 	struct attribute_group	zko_default_group;	/* .attrs allocated */
76716fd348SMartin Matuska 	const struct attribute_group	*zko_default_groups[2];
77eda14cbcSMatt Macy 	size_t			zko_child_count;
78eda14cbcSMatt Macy 	zfs_mod_kobj_t		*zko_children;		/* allocated */
79eda14cbcSMatt Macy };
80eda14cbcSMatt Macy 
81eda14cbcSMatt Macy #define	ATTR_TABLE_SIZE(cnt)	(sizeof (struct attribute) * (cnt))
82eda14cbcSMatt Macy /* Note +1 for NULL terminator slot */
83eda14cbcSMatt Macy #define	DEFAULT_ATTR_SIZE(cnt)	(sizeof (struct attribute *) * (cnt + 1))
84eda14cbcSMatt Macy #define	CHILD_TABLE_SIZE(cnt)	(sizeof (zfs_mod_kobj_t) * (cnt))
85eda14cbcSMatt Macy 
86eda14cbcSMatt Macy /*
87eda14cbcSMatt Macy  * These are the top-level kobjects under '/sys/module/zfs/'
88eda14cbcSMatt Macy  */
89eda14cbcSMatt Macy static zfs_mod_kobj_t kernel_features_kobj;
90eda14cbcSMatt Macy static zfs_mod_kobj_t pool_features_kobj;
91eda14cbcSMatt Macy static zfs_mod_kobj_t dataset_props_kobj;
92681ce946SMartin Matuska static zfs_mod_kobj_t vdev_props_kobj;
93eda14cbcSMatt Macy static zfs_mod_kobj_t pool_props_kobj;
94eda14cbcSMatt Macy 
95eda14cbcSMatt Macy /*
96eda14cbcSMatt Macy  * The show function is used to provide the content
97eda14cbcSMatt Macy  * of an attribute into a PAGE_SIZE buffer.
98eda14cbcSMatt Macy  */
99eda14cbcSMatt Macy typedef ssize_t	(*sysfs_show_func)(struct kobject *, struct attribute *,
100eda14cbcSMatt Macy     char *);
101eda14cbcSMatt Macy 
102eda14cbcSMatt Macy static void
zfs_kobj_fini(zfs_mod_kobj_t * zkobj)103eda14cbcSMatt Macy zfs_kobj_fini(zfs_mod_kobj_t *zkobj)
104eda14cbcSMatt Macy {
105eda14cbcSMatt Macy 	/* finalize any child kobjects */
106eda14cbcSMatt Macy 	if (zkobj->zko_child_count != 0) {
107eda14cbcSMatt Macy 		ASSERT(zkobj->zko_children);
108eda14cbcSMatt Macy 		for (int i = 0; i < zkobj->zko_child_count; i++)
109eda14cbcSMatt Macy 			zfs_kobj_fini(&zkobj->zko_children[i]);
110eda14cbcSMatt Macy 	}
111eda14cbcSMatt Macy 
112eda14cbcSMatt Macy 	/* kobject_put() will call zfs_kobj_release() to release memory */
113eda14cbcSMatt Macy 	kobject_del(&zkobj->zko_kobj);
114eda14cbcSMatt Macy 	kobject_put(&zkobj->zko_kobj);
115eda14cbcSMatt Macy }
116eda14cbcSMatt Macy 
117eda14cbcSMatt Macy static void
zfs_kobj_release(struct kobject * kobj)118eda14cbcSMatt Macy zfs_kobj_release(struct kobject *kobj)
119eda14cbcSMatt Macy {
120eda14cbcSMatt Macy 	zfs_mod_kobj_t *zkobj = container_of(kobj, zfs_mod_kobj_t, zko_kobj);
121eda14cbcSMatt Macy 
122eda14cbcSMatt Macy 	if (zkobj->zko_attr_list != NULL) {
123eda14cbcSMatt Macy 		ASSERT3S(zkobj->zko_attr_count, !=, 0);
124eda14cbcSMatt Macy 		kmem_free(zkobj->zko_attr_list,
125eda14cbcSMatt Macy 		    ATTR_TABLE_SIZE(zkobj->zko_attr_count));
126eda14cbcSMatt Macy 		zkobj->zko_attr_list = NULL;
127eda14cbcSMatt Macy 	}
128eda14cbcSMatt Macy 
129716fd348SMartin Matuska 	if (zkobj->zko_default_group.attrs != NULL) {
130716fd348SMartin Matuska 		kmem_free(zkobj->zko_default_group.attrs,
131eda14cbcSMatt Macy 		    DEFAULT_ATTR_SIZE(zkobj->zko_attr_count));
132716fd348SMartin Matuska 		zkobj->zko_default_group.attrs = NULL;
133eda14cbcSMatt Macy 	}
134eda14cbcSMatt Macy 
135eda14cbcSMatt Macy 	if (zkobj->zko_child_count != 0) {
136eda14cbcSMatt Macy 		ASSERT(zkobj->zko_children);
137eda14cbcSMatt Macy 
138eda14cbcSMatt Macy 		kmem_free(zkobj->zko_children,
139eda14cbcSMatt Macy 		    CHILD_TABLE_SIZE(zkobj->zko_child_count));
140eda14cbcSMatt Macy 		zkobj->zko_child_count = 0;
141eda14cbcSMatt Macy 		zkobj->zko_children = NULL;
142eda14cbcSMatt Macy 	}
143eda14cbcSMatt Macy 
144eda14cbcSMatt Macy 	zkobj->zko_attr_count = 0;
145eda14cbcSMatt Macy }
146eda14cbcSMatt Macy 
147eda14cbcSMatt Macy #ifndef sysfs_attr_init
148eda14cbcSMatt Macy #define	sysfs_attr_init(attr) do {} while (0)
149eda14cbcSMatt Macy #endif
150eda14cbcSMatt Macy 
151eda14cbcSMatt Macy static void
zfs_kobj_add_attr(zfs_mod_kobj_t * zkobj,int attr_num,const char * attr_name)152eda14cbcSMatt Macy zfs_kobj_add_attr(zfs_mod_kobj_t *zkobj, int attr_num, const char *attr_name)
153eda14cbcSMatt Macy {
154eda14cbcSMatt Macy 	VERIFY3U(attr_num, <, zkobj->zko_attr_count);
155eda14cbcSMatt Macy 	ASSERT(zkobj->zko_attr_list);
156716fd348SMartin Matuska 	ASSERT(zkobj->zko_default_group.attrs);
157eda14cbcSMatt Macy 
158eda14cbcSMatt Macy 	zkobj->zko_attr_list[attr_num].name = attr_name;
159eda14cbcSMatt Macy 	zkobj->zko_attr_list[attr_num].mode = 0444;
160716fd348SMartin Matuska 	zkobj->zko_default_group.attrs[attr_num] =
161716fd348SMartin Matuska 	    &zkobj->zko_attr_list[attr_num];
162eda14cbcSMatt Macy 	sysfs_attr_init(&zkobj->zko_attr_list[attr_num]);
163eda14cbcSMatt Macy }
164eda14cbcSMatt Macy 
165eda14cbcSMatt Macy static int
zfs_kobj_init(zfs_mod_kobj_t * zkobj,int attr_cnt,int child_cnt,sysfs_show_func show_func)166eda14cbcSMatt Macy zfs_kobj_init(zfs_mod_kobj_t *zkobj, int attr_cnt, int child_cnt,
167eda14cbcSMatt Macy     sysfs_show_func show_func)
168eda14cbcSMatt Macy {
169eda14cbcSMatt Macy 	/*
170eda14cbcSMatt Macy 	 * Initialize object's attributes. Count can be zero.
171eda14cbcSMatt Macy 	 */
172eda14cbcSMatt Macy 	if (attr_cnt > 0) {
173eda14cbcSMatt Macy 		zkobj->zko_attr_list = kmem_zalloc(ATTR_TABLE_SIZE(attr_cnt),
174eda14cbcSMatt Macy 		    KM_SLEEP);
175eda14cbcSMatt Macy 		if (zkobj->zko_attr_list == NULL)
176eda14cbcSMatt Macy 			return (ENOMEM);
177eda14cbcSMatt Macy 	}
178eda14cbcSMatt Macy 	/* this will always have at least one slot for NULL termination */
179716fd348SMartin Matuska 	zkobj->zko_default_group.attrs =
180716fd348SMartin Matuska 	    kmem_zalloc(DEFAULT_ATTR_SIZE(attr_cnt), KM_SLEEP);
181716fd348SMartin Matuska 	if (zkobj->zko_default_group.attrs == NULL) {
182eda14cbcSMatt Macy 		if (zkobj->zko_attr_list != NULL) {
183eda14cbcSMatt Macy 			kmem_free(zkobj->zko_attr_list,
184eda14cbcSMatt Macy 			    ATTR_TABLE_SIZE(attr_cnt));
185eda14cbcSMatt Macy 		}
186eda14cbcSMatt Macy 		return (ENOMEM);
187eda14cbcSMatt Macy 	}
188eda14cbcSMatt Macy 	zkobj->zko_attr_count = attr_cnt;
189716fd348SMartin Matuska 	zkobj->zko_default_groups[0] = &zkobj->zko_default_group;
190716fd348SMartin Matuska #ifdef HAVE_SYSFS_DEFAULT_GROUPS
191716fd348SMartin Matuska 	zkobj->zko_kobj_type.default_groups = zkobj->zko_default_groups;
192716fd348SMartin Matuska #else
193716fd348SMartin Matuska 	zkobj->zko_kobj_type.default_attrs = zkobj->zko_default_group.attrs;
194716fd348SMartin Matuska #endif
195eda14cbcSMatt Macy 
196eda14cbcSMatt Macy 	if (child_cnt > 0) {
197eda14cbcSMatt Macy 		zkobj->zko_children = kmem_zalloc(CHILD_TABLE_SIZE(child_cnt),
198eda14cbcSMatt Macy 		    KM_SLEEP);
199eda14cbcSMatt Macy 		if (zkobj->zko_children == NULL) {
200716fd348SMartin Matuska 			if (zkobj->zko_default_group.attrs != NULL) {
201716fd348SMartin Matuska 				kmem_free(zkobj->zko_default_group.attrs,
202eda14cbcSMatt Macy 				    DEFAULT_ATTR_SIZE(attr_cnt));
203eda14cbcSMatt Macy 			}
204eda14cbcSMatt Macy 			if (zkobj->zko_attr_list != NULL) {
205eda14cbcSMatt Macy 				kmem_free(zkobj->zko_attr_list,
206eda14cbcSMatt Macy 				    ATTR_TABLE_SIZE(attr_cnt));
207eda14cbcSMatt Macy 			}
208eda14cbcSMatt Macy 			return (ENOMEM);
209eda14cbcSMatt Macy 		}
210eda14cbcSMatt Macy 		zkobj->zko_child_count = child_cnt;
211eda14cbcSMatt Macy 	}
212eda14cbcSMatt Macy 
213eda14cbcSMatt Macy 	zkobj->zko_sysfs_ops.show = show_func;
214eda14cbcSMatt Macy 	zkobj->zko_kobj_type.sysfs_ops = &zkobj->zko_sysfs_ops;
215eda14cbcSMatt Macy 	zkobj->zko_kobj_type.release = zfs_kobj_release;
216eda14cbcSMatt Macy 
217eda14cbcSMatt Macy 	return (0);
218eda14cbcSMatt Macy }
219eda14cbcSMatt Macy 
220eda14cbcSMatt Macy static int
zfs_kobj_add(zfs_mod_kobj_t * zkobj,struct kobject * parent,const char * name)221eda14cbcSMatt Macy zfs_kobj_add(zfs_mod_kobj_t *zkobj, struct kobject *parent, const char *name)
222eda14cbcSMatt Macy {
223716fd348SMartin Matuska 	/* zko_default_group.attrs must be NULL terminated */
224716fd348SMartin Matuska 	ASSERT(zkobj->zko_default_group.attrs != NULL);
225716fd348SMartin Matuska 	ASSERT(zkobj->zko_default_group.attrs[zkobj->zko_attr_count] == NULL);
226eda14cbcSMatt Macy 
227eda14cbcSMatt Macy 	kobject_init(&zkobj->zko_kobj, &zkobj->zko_kobj_type);
228eda14cbcSMatt Macy 	return (kobject_add(&zkobj->zko_kobj, parent, name));
229eda14cbcSMatt Macy }
230eda14cbcSMatt Macy 
231eda14cbcSMatt Macy /*
232eda14cbcSMatt Macy  * Each zfs property has these common attributes
233eda14cbcSMatt Macy  */
234716fd348SMartin Matuska static const char *const zprop_attrs[]  = {
235eda14cbcSMatt Macy 	"type",
236eda14cbcSMatt Macy 	"readonly",
237eda14cbcSMatt Macy 	"setonce",
238eda14cbcSMatt Macy 	"visible",
239eda14cbcSMatt Macy 	"values",
240eda14cbcSMatt Macy 	"default",
241eda14cbcSMatt Macy 	"datasets"	/* zfs properties only */
242eda14cbcSMatt Macy };
243eda14cbcSMatt Macy 
244eda14cbcSMatt Macy #define	ZFS_PROP_ATTR_COUNT	ARRAY_SIZE(zprop_attrs)
245eda14cbcSMatt Macy #define	ZPOOL_PROP_ATTR_COUNT	(ZFS_PROP_ATTR_COUNT - 1)
246eda14cbcSMatt Macy 
247716fd348SMartin Matuska static const char *const zprop_types[]  = {
248eda14cbcSMatt Macy 	"number",
249eda14cbcSMatt Macy 	"string",
250eda14cbcSMatt Macy 	"index",
251eda14cbcSMatt Macy };
252eda14cbcSMatt Macy 
253eda14cbcSMatt Macy typedef struct zfs_type_map {
254eda14cbcSMatt Macy 	zfs_type_t	ztm_type;
255eda14cbcSMatt Macy 	const char	*ztm_name;
256eda14cbcSMatt Macy } zfs_type_map_t;
257eda14cbcSMatt Macy 
258716fd348SMartin Matuska static const zfs_type_map_t type_map[] = {
259eda14cbcSMatt Macy 	{ZFS_TYPE_FILESYSTEM,	"filesystem"},
260eda14cbcSMatt Macy 	{ZFS_TYPE_SNAPSHOT,	"snapshot"},
261eda14cbcSMatt Macy 	{ZFS_TYPE_VOLUME,	"volume"},
262eda14cbcSMatt Macy 	{ZFS_TYPE_BOOKMARK,	"bookmark"}
263eda14cbcSMatt Macy };
264eda14cbcSMatt Macy 
265eda14cbcSMatt Macy /*
266eda14cbcSMatt Macy  * Show the content for a zfs property attribute
267eda14cbcSMatt Macy  */
268eda14cbcSMatt Macy static ssize_t
zprop_sysfs_show(const char * attr_name,const zprop_desc_t * property,char * buf,size_t buflen)269eda14cbcSMatt Macy zprop_sysfs_show(const char *attr_name, const zprop_desc_t *property,
270eda14cbcSMatt Macy     char *buf, size_t buflen)
271eda14cbcSMatt Macy {
272eda14cbcSMatt Macy 	const char *show_str;
273eda14cbcSMatt Macy 	char number[32];
274eda14cbcSMatt Macy 
275eda14cbcSMatt Macy 	/* For dataset properties list the dataset types that apply */
276eda14cbcSMatt Macy 	if (strcmp(attr_name, "datasets") == 0 &&
277eda14cbcSMatt Macy 	    property->pd_types != ZFS_TYPE_POOL) {
278eda14cbcSMatt Macy 		int len = 0;
279eda14cbcSMatt Macy 
280eda14cbcSMatt Macy 		for (int i = 0; i < ARRAY_SIZE(type_map); i++) {
281eda14cbcSMatt Macy 			if (type_map[i].ztm_type & property->pd_types)  {
282*dbd5678dSMartin Matuska 				len += kmem_scnprintf(buf + len, buflen - len,
283*dbd5678dSMartin Matuska 				    "%s ", type_map[i].ztm_name);
284eda14cbcSMatt Macy 			}
285eda14cbcSMatt Macy 		}
286*dbd5678dSMartin Matuska 		len += kmem_scnprintf(buf + len, buflen - len, "\n");
287eda14cbcSMatt Macy 		return (len);
288eda14cbcSMatt Macy 	}
289eda14cbcSMatt Macy 
290eda14cbcSMatt Macy 	if (strcmp(attr_name, "type") == 0) {
291eda14cbcSMatt Macy 		show_str = zprop_types[property->pd_proptype];
292eda14cbcSMatt Macy 	} else if (strcmp(attr_name, "readonly") == 0) {
293eda14cbcSMatt Macy 		show_str = property->pd_attr == PROP_READONLY ? "1" : "0";
294eda14cbcSMatt Macy 	} else if (strcmp(attr_name, "setonce") == 0) {
295eda14cbcSMatt Macy 		show_str = property->pd_attr == PROP_ONETIME ? "1" : "0";
296eda14cbcSMatt Macy 	} else if (strcmp(attr_name, "visible") == 0) {
297eda14cbcSMatt Macy 		show_str = property->pd_visible ? "1" : "0";
298eda14cbcSMatt Macy 	} else if (strcmp(attr_name, "values") == 0) {
299eda14cbcSMatt Macy 		show_str = property->pd_values ? property->pd_values : "";
300eda14cbcSMatt Macy 	} else if (strcmp(attr_name, "default") == 0) {
301eda14cbcSMatt Macy 		switch (property->pd_proptype) {
302eda14cbcSMatt Macy 		case PROP_TYPE_NUMBER:
303eda14cbcSMatt Macy 			(void) snprintf(number, sizeof (number), "%llu",
304eda14cbcSMatt Macy 			    (u_longlong_t)property->pd_numdefault);
305eda14cbcSMatt Macy 			show_str = number;
306eda14cbcSMatt Macy 			break;
307eda14cbcSMatt Macy 		case PROP_TYPE_STRING:
308eda14cbcSMatt Macy 			show_str = property->pd_strdefault ?
309eda14cbcSMatt Macy 			    property->pd_strdefault : "";
310eda14cbcSMatt Macy 			break;
311eda14cbcSMatt Macy 		case PROP_TYPE_INDEX:
312eda14cbcSMatt Macy 			if (zprop_index_to_string(property->pd_propnum,
313eda14cbcSMatt Macy 			    property->pd_numdefault, &show_str,
314eda14cbcSMatt Macy 			    property->pd_types) != 0) {
315eda14cbcSMatt Macy 				show_str = "";
316eda14cbcSMatt Macy 			}
317eda14cbcSMatt Macy 			break;
318eda14cbcSMatt Macy 		default:
319eda14cbcSMatt Macy 			return (0);
320eda14cbcSMatt Macy 		}
321eda14cbcSMatt Macy 	} else {
322eda14cbcSMatt Macy 		return (0);
323eda14cbcSMatt Macy 	}
324eda14cbcSMatt Macy 
325eda14cbcSMatt Macy 	return (snprintf(buf, buflen, "%s\n", show_str));
326eda14cbcSMatt Macy }
327eda14cbcSMatt Macy 
328eda14cbcSMatt Macy static ssize_t
dataset_property_show(struct kobject * kobj,struct attribute * attr,char * buf)329eda14cbcSMatt Macy dataset_property_show(struct kobject *kobj, struct attribute *attr, char *buf)
330eda14cbcSMatt Macy {
331eda14cbcSMatt Macy 	zfs_prop_t prop = zfs_name_to_prop(kobject_name(kobj));
332eda14cbcSMatt Macy 	zprop_desc_t *prop_tbl = zfs_prop_get_table();
333eda14cbcSMatt Macy 	ssize_t len;
334eda14cbcSMatt Macy 
335eda14cbcSMatt Macy 	ASSERT3U(prop, <, ZFS_NUM_PROPS);
336eda14cbcSMatt Macy 
337eda14cbcSMatt Macy 	len = zprop_sysfs_show(attr->name, &prop_tbl[prop], buf, PAGE_SIZE);
338eda14cbcSMatt Macy 
339eda14cbcSMatt Macy 	return (len);
340eda14cbcSMatt Macy }
341eda14cbcSMatt Macy 
342eda14cbcSMatt Macy static ssize_t
vdev_property_show(struct kobject * kobj,struct attribute * attr,char * buf)343681ce946SMartin Matuska vdev_property_show(struct kobject *kobj, struct attribute *attr, char *buf)
344681ce946SMartin Matuska {
345681ce946SMartin Matuska 	vdev_prop_t prop = vdev_name_to_prop(kobject_name(kobj));
346681ce946SMartin Matuska 	zprop_desc_t *prop_tbl = vdev_prop_get_table();
347681ce946SMartin Matuska 	ssize_t len;
348681ce946SMartin Matuska 
349681ce946SMartin Matuska 	ASSERT3U(prop, <, VDEV_NUM_PROPS);
350681ce946SMartin Matuska 
351681ce946SMartin Matuska 	len = zprop_sysfs_show(attr->name, &prop_tbl[prop], buf, PAGE_SIZE);
352681ce946SMartin Matuska 
353681ce946SMartin Matuska 	return (len);
354681ce946SMartin Matuska }
355681ce946SMartin Matuska 
356681ce946SMartin Matuska static ssize_t
pool_property_show(struct kobject * kobj,struct attribute * attr,char * buf)357eda14cbcSMatt Macy pool_property_show(struct kobject *kobj, struct attribute *attr, char *buf)
358eda14cbcSMatt Macy {
359eda14cbcSMatt Macy 	zpool_prop_t prop = zpool_name_to_prop(kobject_name(kobj));
360eda14cbcSMatt Macy 	zprop_desc_t *prop_tbl = zpool_prop_get_table();
361eda14cbcSMatt Macy 	ssize_t len;
362eda14cbcSMatt Macy 
363eda14cbcSMatt Macy 	ASSERT3U(prop, <, ZPOOL_NUM_PROPS);
364eda14cbcSMatt Macy 
365eda14cbcSMatt Macy 	len = zprop_sysfs_show(attr->name, &prop_tbl[prop], buf, PAGE_SIZE);
366eda14cbcSMatt Macy 
367eda14cbcSMatt Macy 	return (len);
368eda14cbcSMatt Macy }
369eda14cbcSMatt Macy 
370eda14cbcSMatt Macy /*
371eda14cbcSMatt Macy  * ZFS kernel feature attributes for '/sys/module/zfs/features.kernel'
372eda14cbcSMatt Macy  *
373eda14cbcSMatt Macy  * This list is intended for kernel features that don't have a pool feature
374eda14cbcSMatt Macy  * association or that extend existing user kernel interfaces.
375eda14cbcSMatt Macy  *
376eda14cbcSMatt Macy  * A user process can easily check if the running zfs kernel module
377eda14cbcSMatt Macy  * supports the new feature.
378eda14cbcSMatt Macy  */
379716fd348SMartin Matuska static const char *const zfs_kernel_features[] = {
380eda14cbcSMatt Macy 	/* --> Add new kernel features here */
381eda14cbcSMatt Macy 	"com.delphix:vdev_initialize",
382eda14cbcSMatt Macy 	"org.zfsonlinux:vdev_trim",
383eda14cbcSMatt Macy 	"org.openzfs:l2arc_persistent",
384eda14cbcSMatt Macy };
385eda14cbcSMatt Macy 
386eda14cbcSMatt Macy #define	KERNEL_FEATURE_COUNT	ARRAY_SIZE(zfs_kernel_features)
387eda14cbcSMatt Macy 
388eda14cbcSMatt Macy static ssize_t
kernel_feature_show(struct kobject * kobj,struct attribute * attr,char * buf)389eda14cbcSMatt Macy kernel_feature_show(struct kobject *kobj, struct attribute *attr, char *buf)
390eda14cbcSMatt Macy {
391eda14cbcSMatt Macy 	if (strcmp(attr->name, "supported") == 0)
392eda14cbcSMatt Macy 		return (snprintf(buf, PAGE_SIZE, "yes\n"));
393eda14cbcSMatt Macy 	return (0);
394eda14cbcSMatt Macy }
395eda14cbcSMatt Macy 
396eda14cbcSMatt Macy static void
kernel_feature_to_kobj(zfs_mod_kobj_t * parent,int slot,const char * name)397eda14cbcSMatt Macy kernel_feature_to_kobj(zfs_mod_kobj_t *parent, int slot, const char *name)
398eda14cbcSMatt Macy {
399eda14cbcSMatt Macy 	zfs_mod_kobj_t *zfs_kobj = &parent->zko_children[slot];
400eda14cbcSMatt Macy 
401eda14cbcSMatt Macy 	ASSERT3U(slot, <, KERNEL_FEATURE_COUNT);
402eda14cbcSMatt Macy 	ASSERT(name);
403eda14cbcSMatt Macy 
404eda14cbcSMatt Macy 	int err = zfs_kobj_init(zfs_kobj, 1, 0, kernel_feature_show);
405eda14cbcSMatt Macy 	if (err)
406eda14cbcSMatt Macy 		return;
407eda14cbcSMatt Macy 
408eda14cbcSMatt Macy 	zfs_kobj_add_attr(zfs_kobj, 0, "supported");
409eda14cbcSMatt Macy 
410eda14cbcSMatt Macy 	err = zfs_kobj_add(zfs_kobj, &parent->zko_kobj, name);
411eda14cbcSMatt Macy 	if (err)
412eda14cbcSMatt Macy 		zfs_kobj_release(&zfs_kobj->zko_kobj);
413eda14cbcSMatt Macy }
414eda14cbcSMatt Macy 
415eda14cbcSMatt Macy static int
zfs_kernel_features_init(zfs_mod_kobj_t * zfs_kobj,struct kobject * parent)416eda14cbcSMatt Macy zfs_kernel_features_init(zfs_mod_kobj_t *zfs_kobj, struct kobject *parent)
417eda14cbcSMatt Macy {
418eda14cbcSMatt Macy 	/*
419eda14cbcSMatt Macy 	 * Create a parent kobject to host kernel features.
420eda14cbcSMatt Macy 	 *
421eda14cbcSMatt Macy 	 * '/sys/module/zfs/features.kernel'
422eda14cbcSMatt Macy 	 */
423eda14cbcSMatt Macy 	int err = zfs_kobj_init(zfs_kobj, 0, KERNEL_FEATURE_COUNT,
424eda14cbcSMatt Macy 	    kernel_feature_show);
425eda14cbcSMatt Macy 	if (err)
426eda14cbcSMatt Macy 		return (err);
427eda14cbcSMatt Macy 	err = zfs_kobj_add(zfs_kobj, parent, ZFS_SYSFS_KERNEL_FEATURES);
428eda14cbcSMatt Macy 	if (err) {
429eda14cbcSMatt Macy 		zfs_kobj_release(&zfs_kobj->zko_kobj);
430eda14cbcSMatt Macy 		return (err);
431eda14cbcSMatt Macy 	}
432eda14cbcSMatt Macy 
433eda14cbcSMatt Macy 	/*
434eda14cbcSMatt Macy 	 * Now create a kobject for each feature.
435eda14cbcSMatt Macy 	 *
436eda14cbcSMatt Macy 	 * '/sys/module/zfs/features.kernel/<feature>'
437eda14cbcSMatt Macy 	 */
438eda14cbcSMatt Macy 	for (int f = 0; f < KERNEL_FEATURE_COUNT; f++)
439eda14cbcSMatt Macy 		kernel_feature_to_kobj(zfs_kobj, f, zfs_kernel_features[f]);
440eda14cbcSMatt Macy 
441eda14cbcSMatt Macy 	return (0);
442eda14cbcSMatt Macy }
443eda14cbcSMatt Macy 
444eda14cbcSMatt Macy /*
445eda14cbcSMatt Macy  * Each pool feature has these common attributes
446eda14cbcSMatt Macy  */
447716fd348SMartin Matuska static const char *const pool_feature_attrs[]  = {
448eda14cbcSMatt Macy 	"description",
449eda14cbcSMatt Macy 	"guid",
450eda14cbcSMatt Macy 	"uname",
451eda14cbcSMatt Macy 	"readonly_compatible",
452eda14cbcSMatt Macy 	"required_for_mos",
453eda14cbcSMatt Macy 	"activate_on_enable",
454eda14cbcSMatt Macy 	"per_dataset"
455eda14cbcSMatt Macy };
456eda14cbcSMatt Macy 
457eda14cbcSMatt Macy #define	ZPOOL_FEATURE_ATTR_COUNT	ARRAY_SIZE(pool_feature_attrs)
458eda14cbcSMatt Macy 
459eda14cbcSMatt Macy /*
460eda14cbcSMatt Macy  * Show the content for the given zfs pool feature attribute
461eda14cbcSMatt Macy  */
462eda14cbcSMatt Macy static ssize_t
pool_feature_show(struct kobject * kobj,struct attribute * attr,char * buf)463eda14cbcSMatt Macy pool_feature_show(struct kobject *kobj, struct attribute *attr, char *buf)
464eda14cbcSMatt Macy {
465eda14cbcSMatt Macy 	spa_feature_t fid;
466eda14cbcSMatt Macy 
467eda14cbcSMatt Macy 	if (zfeature_lookup_guid(kobject_name(kobj), &fid) != 0)
468eda14cbcSMatt Macy 		return (0);
469eda14cbcSMatt Macy 
470eda14cbcSMatt Macy 	ASSERT3U(fid, <, SPA_FEATURES);
471eda14cbcSMatt Macy 
472eda14cbcSMatt Macy 	zfeature_flags_t flags = spa_feature_table[fid].fi_flags;
473eda14cbcSMatt Macy 	const char *show_str = NULL;
474eda14cbcSMatt Macy 
475eda14cbcSMatt Macy 	if (strcmp(attr->name, "description") == 0) {
476eda14cbcSMatt Macy 		show_str = spa_feature_table[fid].fi_desc;
477eda14cbcSMatt Macy 	} else if (strcmp(attr->name, "guid") == 0) {
478eda14cbcSMatt Macy 		show_str = spa_feature_table[fid].fi_guid;
479eda14cbcSMatt Macy 	} else if (strcmp(attr->name, "uname") == 0) {
480eda14cbcSMatt Macy 		show_str = spa_feature_table[fid].fi_uname;
481eda14cbcSMatt Macy 	} else if (strcmp(attr->name, "readonly_compatible") == 0) {
482eda14cbcSMatt Macy 		show_str = flags & ZFEATURE_FLAG_READONLY_COMPAT ? "1" : "0";
483eda14cbcSMatt Macy 	} else if (strcmp(attr->name, "required_for_mos") == 0) {
484eda14cbcSMatt Macy 		show_str = flags & ZFEATURE_FLAG_MOS ? "1" : "0";
485eda14cbcSMatt Macy 	} else if (strcmp(attr->name, "activate_on_enable") == 0) {
486eda14cbcSMatt Macy 		show_str = flags & ZFEATURE_FLAG_ACTIVATE_ON_ENABLE ? "1" : "0";
487eda14cbcSMatt Macy 	} else if (strcmp(attr->name, "per_dataset") == 0) {
488eda14cbcSMatt Macy 		show_str = flags & ZFEATURE_FLAG_PER_DATASET ? "1" : "0";
489eda14cbcSMatt Macy 	}
490eda14cbcSMatt Macy 	if (show_str == NULL)
491eda14cbcSMatt Macy 		return (0);
492eda14cbcSMatt Macy 
493eda14cbcSMatt Macy 	return (snprintf(buf, PAGE_SIZE, "%s\n", show_str));
494eda14cbcSMatt Macy }
495eda14cbcSMatt Macy 
496eda14cbcSMatt Macy static void
pool_feature_to_kobj(zfs_mod_kobj_t * parent,spa_feature_t fid,const char * name)497eda14cbcSMatt Macy pool_feature_to_kobj(zfs_mod_kobj_t *parent, spa_feature_t fid,
498eda14cbcSMatt Macy     const char *name)
499eda14cbcSMatt Macy {
500eda14cbcSMatt Macy 	zfs_mod_kobj_t *zfs_kobj = &parent->zko_children[fid];
501eda14cbcSMatt Macy 
502eda14cbcSMatt Macy 	ASSERT3U(fid, <, SPA_FEATURES);
503eda14cbcSMatt Macy 	ASSERT(name);
504eda14cbcSMatt Macy 
505eda14cbcSMatt Macy 	int err = zfs_kobj_init(zfs_kobj, ZPOOL_FEATURE_ATTR_COUNT, 0,
506eda14cbcSMatt Macy 	    pool_feature_show);
507eda14cbcSMatt Macy 	if (err)
508eda14cbcSMatt Macy 		return;
509eda14cbcSMatt Macy 
510eda14cbcSMatt Macy 	for (int i = 0; i < ZPOOL_FEATURE_ATTR_COUNT; i++)
511eda14cbcSMatt Macy 		zfs_kobj_add_attr(zfs_kobj, i, pool_feature_attrs[i]);
512eda14cbcSMatt Macy 
513eda14cbcSMatt Macy 	err = zfs_kobj_add(zfs_kobj, &parent->zko_kobj, name);
514eda14cbcSMatt Macy 	if (err)
515eda14cbcSMatt Macy 		zfs_kobj_release(&zfs_kobj->zko_kobj);
516eda14cbcSMatt Macy }
517eda14cbcSMatt Macy 
518eda14cbcSMatt Macy static int
zfs_pool_features_init(zfs_mod_kobj_t * zfs_kobj,struct kobject * parent)519eda14cbcSMatt Macy zfs_pool_features_init(zfs_mod_kobj_t *zfs_kobj, struct kobject *parent)
520eda14cbcSMatt Macy {
521eda14cbcSMatt Macy 	/*
522eda14cbcSMatt Macy 	 * Create a parent kobject to host pool features.
523eda14cbcSMatt Macy 	 *
524eda14cbcSMatt Macy 	 * '/sys/module/zfs/features.pool'
525eda14cbcSMatt Macy 	 */
526eda14cbcSMatt Macy 	int err = zfs_kobj_init(zfs_kobj, 0, SPA_FEATURES, pool_feature_show);
527eda14cbcSMatt Macy 	if (err)
528eda14cbcSMatt Macy 		return (err);
529eda14cbcSMatt Macy 	err = zfs_kobj_add(zfs_kobj, parent, ZFS_SYSFS_POOL_FEATURES);
530eda14cbcSMatt Macy 	if (err) {
531eda14cbcSMatt Macy 		zfs_kobj_release(&zfs_kobj->zko_kobj);
532eda14cbcSMatt Macy 		return (err);
533eda14cbcSMatt Macy 	}
534eda14cbcSMatt Macy 
535eda14cbcSMatt Macy 	/*
536eda14cbcSMatt Macy 	 * Now create a kobject for each feature.
537eda14cbcSMatt Macy 	 *
538eda14cbcSMatt Macy 	 * '/sys/module/zfs/features.pool/<feature>'
539eda14cbcSMatt Macy 	 */
540eda14cbcSMatt Macy 	for (spa_feature_t i = 0; i < SPA_FEATURES; i++)
541eda14cbcSMatt Macy 		pool_feature_to_kobj(zfs_kobj, i, spa_feature_table[i].fi_guid);
542eda14cbcSMatt Macy 
543eda14cbcSMatt Macy 	return (0);
544eda14cbcSMatt Macy }
545eda14cbcSMatt Macy 
546eda14cbcSMatt Macy typedef struct prop_to_kobj_arg {
547eda14cbcSMatt Macy 	zprop_desc_t	*p2k_table;
548eda14cbcSMatt Macy 	zfs_mod_kobj_t	*p2k_parent;
549eda14cbcSMatt Macy 	sysfs_show_func	p2k_show_func;
550eda14cbcSMatt Macy 	int		p2k_attr_count;
551eda14cbcSMatt Macy } prop_to_kobj_arg_t;
552eda14cbcSMatt Macy 
553eda14cbcSMatt Macy static int
zprop_to_kobj(int prop,void * args)554eda14cbcSMatt Macy zprop_to_kobj(int prop, void *args)
555eda14cbcSMatt Macy {
556eda14cbcSMatt Macy 	prop_to_kobj_arg_t *data = args;
557eda14cbcSMatt Macy 	zfs_mod_kobj_t *parent = data->p2k_parent;
558eda14cbcSMatt Macy 	zfs_mod_kobj_t *zfs_kobj = &parent->zko_children[prop];
559eda14cbcSMatt Macy 	const char *name = data->p2k_table[prop].pd_name;
560eda14cbcSMatt Macy 	int err;
561eda14cbcSMatt Macy 
562eda14cbcSMatt Macy 	ASSERT(name);
563eda14cbcSMatt Macy 
564eda14cbcSMatt Macy 	err = zfs_kobj_init(zfs_kobj, data->p2k_attr_count, 0,
565eda14cbcSMatt Macy 	    data->p2k_show_func);
566eda14cbcSMatt Macy 	if (err)
567eda14cbcSMatt Macy 		return (ZPROP_CONT);
568eda14cbcSMatt Macy 
569eda14cbcSMatt Macy 	for (int i = 0; i < data->p2k_attr_count; i++)
570eda14cbcSMatt Macy 		zfs_kobj_add_attr(zfs_kobj, i, zprop_attrs[i]);
571eda14cbcSMatt Macy 
572eda14cbcSMatt Macy 	err = zfs_kobj_add(zfs_kobj, &parent->zko_kobj, name);
573eda14cbcSMatt Macy 	if (err)
574eda14cbcSMatt Macy 		zfs_kobj_release(&zfs_kobj->zko_kobj);
575eda14cbcSMatt Macy 
576eda14cbcSMatt Macy 	return (ZPROP_CONT);
577eda14cbcSMatt Macy }
578eda14cbcSMatt Macy 
579eda14cbcSMatt Macy static int
zfs_sysfs_properties_init(zfs_mod_kobj_t * zfs_kobj,struct kobject * parent,zfs_type_t type)580eda14cbcSMatt Macy zfs_sysfs_properties_init(zfs_mod_kobj_t *zfs_kobj, struct kobject *parent,
581eda14cbcSMatt Macy     zfs_type_t type)
582eda14cbcSMatt Macy {
583eda14cbcSMatt Macy 	prop_to_kobj_arg_t context;
584eda14cbcSMatt Macy 	const char *name;
585eda14cbcSMatt Macy 	int err;
586eda14cbcSMatt Macy 
587eda14cbcSMatt Macy 	/*
588eda14cbcSMatt Macy 	 * Create a parent kobject to host properties.
589eda14cbcSMatt Macy 	 *
590eda14cbcSMatt Macy 	 * '/sys/module/zfs/properties.<type>'
591eda14cbcSMatt Macy 	 */
592eda14cbcSMatt Macy 	if (type == ZFS_TYPE_POOL) {
593eda14cbcSMatt Macy 		name = ZFS_SYSFS_POOL_PROPERTIES;
594eda14cbcSMatt Macy 		context.p2k_table = zpool_prop_get_table();
595eda14cbcSMatt Macy 		context.p2k_attr_count = ZPOOL_PROP_ATTR_COUNT;
596eda14cbcSMatt Macy 		context.p2k_parent = zfs_kobj;
597eda14cbcSMatt Macy 		context.p2k_show_func = pool_property_show;
598eda14cbcSMatt Macy 		err = zfs_kobj_init(zfs_kobj, 0, ZPOOL_NUM_PROPS,
599eda14cbcSMatt Macy 		    pool_property_show);
600681ce946SMartin Matuska 	} else if (type == ZFS_TYPE_VDEV) {
601681ce946SMartin Matuska 		name = ZFS_SYSFS_VDEV_PROPERTIES;
602681ce946SMartin Matuska 		context.p2k_table = vdev_prop_get_table();
603681ce946SMartin Matuska 		context.p2k_attr_count = ZPOOL_PROP_ATTR_COUNT;
604681ce946SMartin Matuska 		context.p2k_parent = zfs_kobj;
605681ce946SMartin Matuska 		context.p2k_show_func = vdev_property_show;
606681ce946SMartin Matuska 		err = zfs_kobj_init(zfs_kobj, 0, VDEV_NUM_PROPS,
607681ce946SMartin Matuska 		    vdev_property_show);
608eda14cbcSMatt Macy 	} else {
609eda14cbcSMatt Macy 		name = ZFS_SYSFS_DATASET_PROPERTIES;
610eda14cbcSMatt Macy 		context.p2k_table = zfs_prop_get_table();
611eda14cbcSMatt Macy 		context.p2k_attr_count = ZFS_PROP_ATTR_COUNT;
612eda14cbcSMatt Macy 		context.p2k_parent = zfs_kobj;
613eda14cbcSMatt Macy 		context.p2k_show_func = dataset_property_show;
614eda14cbcSMatt Macy 		err = zfs_kobj_init(zfs_kobj, 0, ZFS_NUM_PROPS,
615eda14cbcSMatt Macy 		    dataset_property_show);
616eda14cbcSMatt Macy 	}
617eda14cbcSMatt Macy 
618eda14cbcSMatt Macy 	if (err)
619eda14cbcSMatt Macy 		return (err);
620eda14cbcSMatt Macy 
621eda14cbcSMatt Macy 	err = zfs_kobj_add(zfs_kobj, parent, name);
622eda14cbcSMatt Macy 	if (err) {
623eda14cbcSMatt Macy 		zfs_kobj_release(&zfs_kobj->zko_kobj);
624eda14cbcSMatt Macy 		return (err);
625eda14cbcSMatt Macy 	}
626eda14cbcSMatt Macy 
627eda14cbcSMatt Macy 	/*
628eda14cbcSMatt Macy 	 * Create a kobject for each property.
629eda14cbcSMatt Macy 	 *
630eda14cbcSMatt Macy 	 * '/sys/module/zfs/properties.<type>/<property>'
631eda14cbcSMatt Macy 	 */
632eda14cbcSMatt Macy 	(void) zprop_iter_common(zprop_to_kobj, &context, B_TRUE,
633eda14cbcSMatt Macy 	    B_FALSE, type);
634eda14cbcSMatt Macy 
635eda14cbcSMatt Macy 	return (err);
636eda14cbcSMatt Macy }
637eda14cbcSMatt Macy 
638eda14cbcSMatt Macy void
zfs_sysfs_init(void)639eda14cbcSMatt Macy zfs_sysfs_init(void)
640eda14cbcSMatt Macy {
641eda14cbcSMatt Macy 	struct kobject *parent;
642eda14cbcSMatt Macy #if defined(CONFIG_ZFS) && !defined(CONFIG_ZFS_MODULE)
643eda14cbcSMatt Macy 	parent = kobject_create_and_add("zfs", fs_kobj);
644eda14cbcSMatt Macy #else
645eda14cbcSMatt Macy 	parent = &(((struct module *)(THIS_MODULE))->mkobj).kobj;
646eda14cbcSMatt Macy #endif
647eda14cbcSMatt Macy 	int err;
648eda14cbcSMatt Macy 
649eda14cbcSMatt Macy 	if (parent == NULL)
650eda14cbcSMatt Macy 		return;
651eda14cbcSMatt Macy 
652eda14cbcSMatt Macy 	err = zfs_kernel_features_init(&kernel_features_kobj, parent);
653eda14cbcSMatt Macy 	if (err)
654eda14cbcSMatt Macy 		return;
655eda14cbcSMatt Macy 
656eda14cbcSMatt Macy 	err = zfs_pool_features_init(&pool_features_kobj, parent);
657eda14cbcSMatt Macy 	if (err) {
658eda14cbcSMatt Macy 		zfs_kobj_fini(&kernel_features_kobj);
659eda14cbcSMatt Macy 		return;
660eda14cbcSMatt Macy 	}
661eda14cbcSMatt Macy 
662eda14cbcSMatt Macy 	err = zfs_sysfs_properties_init(&pool_props_kobj, parent,
663eda14cbcSMatt Macy 	    ZFS_TYPE_POOL);
664eda14cbcSMatt Macy 	if (err) {
665eda14cbcSMatt Macy 		zfs_kobj_fini(&kernel_features_kobj);
666eda14cbcSMatt Macy 		zfs_kobj_fini(&pool_features_kobj);
667eda14cbcSMatt Macy 		return;
668eda14cbcSMatt Macy 	}
669eda14cbcSMatt Macy 
670681ce946SMartin Matuska 	err = zfs_sysfs_properties_init(&vdev_props_kobj, parent,
671681ce946SMartin Matuska 	    ZFS_TYPE_VDEV);
672681ce946SMartin Matuska 	if (err) {
673681ce946SMartin Matuska 		zfs_kobj_fini(&kernel_features_kobj);
674681ce946SMartin Matuska 		zfs_kobj_fini(&pool_features_kobj);
675681ce946SMartin Matuska 		zfs_kobj_fini(&pool_props_kobj);
676681ce946SMartin Matuska 		return;
677681ce946SMartin Matuska 	}
678681ce946SMartin Matuska 
679eda14cbcSMatt Macy 	err = zfs_sysfs_properties_init(&dataset_props_kobj, parent,
680eda14cbcSMatt Macy 	    ZFS_TYPE_FILESYSTEM);
681eda14cbcSMatt Macy 	if (err) {
682eda14cbcSMatt Macy 		zfs_kobj_fini(&kernel_features_kobj);
683eda14cbcSMatt Macy 		zfs_kobj_fini(&pool_features_kobj);
684eda14cbcSMatt Macy 		zfs_kobj_fini(&pool_props_kobj);
685681ce946SMartin Matuska 		zfs_kobj_fini(&vdev_props_kobj);
686eda14cbcSMatt Macy 		return;
687eda14cbcSMatt Macy 	}
688eda14cbcSMatt Macy }
689eda14cbcSMatt Macy 
690eda14cbcSMatt Macy void
zfs_sysfs_fini(void)691eda14cbcSMatt Macy zfs_sysfs_fini(void)
692eda14cbcSMatt Macy {
693eda14cbcSMatt Macy 	/*
694eda14cbcSMatt Macy 	 * Remove top-level kobjects; each will remove any children kobjects
695eda14cbcSMatt Macy 	 */
696eda14cbcSMatt Macy 	zfs_kobj_fini(&kernel_features_kobj);
697eda14cbcSMatt Macy 	zfs_kobj_fini(&pool_features_kobj);
698eda14cbcSMatt Macy 	zfs_kobj_fini(&pool_props_kobj);
699681ce946SMartin Matuska 	zfs_kobj_fini(&vdev_props_kobj);
700681ce946SMartin Matuska 	zfs_kobj_fini(&dataset_props_kobj);
701eda14cbcSMatt Macy }
702