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