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