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