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