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