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
zfs_kobj_fini(zfs_mod_kobj_t * zkobj)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
zfs_kobj_release(struct kobject * kobj)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
zfs_kobj_add_attr(zfs_mod_kobj_t * zkobj,int attr_num,const char * attr_name)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
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_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
zfs_kobj_add(zfs_mod_kobj_t * zkobj,struct kobject * parent,const char * name)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
zprop_sysfs_show(const char * attr_name,const zprop_desc_t * property,char * buf,size_t buflen)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
dataset_property_show(struct kobject * kobj,struct attribute * attr,char * buf)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
pool_property_show(struct kobject * kobj,struct attribute * attr,char * buf)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
kernel_feature_show(struct kobject * kobj,struct attribute * attr,char * buf)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
kernel_feature_to_kobj(zfs_mod_kobj_t * parent,int slot,const char * name)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
zfs_kernel_features_init(zfs_mod_kobj_t * zfs_kobj,struct kobject * parent)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
pool_feature_show(struct kobject * kobj,struct attribute * attr,char * buf)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
pool_feature_to_kobj(zfs_mod_kobj_t * parent,spa_feature_t fid,const char * name)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
zfs_pool_features_init(zfs_mod_kobj_t * zfs_kobj,struct kobject * parent)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
zprop_to_kobj(int prop,void * args)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
zfs_sysfs_properties_init(zfs_mod_kobj_t * zfs_kobj,struct kobject * parent,zfs_type_t type)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
zfs_sysfs_init(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
zfs_sysfs_fini(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