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 https://opensource.org/licenses/CDDL-1.0.
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 vdev_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_group.attrs != NULL) {
130 kmem_free(zkobj->zko_default_group.attrs,
131 DEFAULT_ATTR_SIZE(zkobj->zko_attr_count));
132 zkobj->zko_default_group.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_group.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_group.attrs[attr_num] =
161 &zkobj->zko_attr_list[attr_num];
162 sysfs_attr_init(&zkobj->zko_attr_list[attr_num]);
163 }
164
165 static int
zfs_kobj_init(zfs_mod_kobj_t * zkobj,int attr_cnt,int child_cnt,sysfs_show_func show_func)166 zfs_kobj_init(zfs_mod_kobj_t *zkobj, int attr_cnt, int child_cnt,
167 sysfs_show_func show_func)
168 {
169 /*
170 * Initialize object's attributes. Count can be zero.
171 */
172 if (attr_cnt > 0) {
173 zkobj->zko_attr_list = kmem_zalloc(ATTR_TABLE_SIZE(attr_cnt),
174 KM_SLEEP);
175 if (zkobj->zko_attr_list == NULL)
176 return (ENOMEM);
177 }
178 /* this will always have at least one slot for NULL termination */
179 zkobj->zko_default_group.attrs =
180 kmem_zalloc(DEFAULT_ATTR_SIZE(attr_cnt), KM_SLEEP);
181 if (zkobj->zko_default_group.attrs == NULL) {
182 if (zkobj->zko_attr_list != NULL) {
183 kmem_free(zkobj->zko_attr_list,
184 ATTR_TABLE_SIZE(attr_cnt));
185 }
186 return (ENOMEM);
187 }
188 zkobj->zko_attr_count = attr_cnt;
189 zkobj->zko_default_groups[0] = &zkobj->zko_default_group;
190 #ifdef HAVE_SYSFS_DEFAULT_GROUPS
191 zkobj->zko_kobj_type.default_groups = zkobj->zko_default_groups;
192 #else
193 zkobj->zko_kobj_type.default_attrs = zkobj->zko_default_group.attrs;
194 #endif
195
196 if (child_cnt > 0) {
197 zkobj->zko_children = kmem_zalloc(CHILD_TABLE_SIZE(child_cnt),
198 KM_SLEEP);
199 if (zkobj->zko_children == NULL) {
200 if (zkobj->zko_default_group.attrs != NULL) {
201 kmem_free(zkobj->zko_default_group.attrs,
202 DEFAULT_ATTR_SIZE(attr_cnt));
203 }
204 if (zkobj->zko_attr_list != NULL) {
205 kmem_free(zkobj->zko_attr_list,
206 ATTR_TABLE_SIZE(attr_cnt));
207 }
208 return (ENOMEM);
209 }
210 zkobj->zko_child_count = child_cnt;
211 }
212
213 zkobj->zko_sysfs_ops.show = show_func;
214 zkobj->zko_kobj_type.sysfs_ops = &zkobj->zko_sysfs_ops;
215 zkobj->zko_kobj_type.release = zfs_kobj_release;
216
217 return (0);
218 }
219
220 static int
zfs_kobj_add(zfs_mod_kobj_t * zkobj,struct kobject * parent,const char * name)221 zfs_kobj_add(zfs_mod_kobj_t *zkobj, struct kobject *parent, const char *name)
222 {
223 /* zko_default_group.attrs must be NULL terminated */
224 ASSERT(zkobj->zko_default_group.attrs != NULL);
225 ASSERT(zkobj->zko_default_group.attrs[zkobj->zko_attr_count] == NULL);
226
227 kobject_init(&zkobj->zko_kobj, &zkobj->zko_kobj_type);
228 return (kobject_add(&zkobj->zko_kobj, parent, name));
229 }
230
231 /*
232 * Each zfs property has these common attributes
233 */
234 static const char *const zprop_attrs[] = {
235 "type",
236 "readonly",
237 "setonce",
238 "visible",
239 "values",
240 "default",
241 "datasets" /* zfs properties only */
242 };
243
244 #define ZFS_PROP_ATTR_COUNT ARRAY_SIZE(zprop_attrs)
245 #define ZPOOL_PROP_ATTR_COUNT (ZFS_PROP_ATTR_COUNT - 1)
246
247 static const char *const zprop_types[] = {
248 "number",
249 "string",
250 "index",
251 };
252
253 typedef struct zfs_type_map {
254 zfs_type_t ztm_type;
255 const char *ztm_name;
256 } zfs_type_map_t;
257
258 static const zfs_type_map_t type_map[] = {
259 {ZFS_TYPE_FILESYSTEM, "filesystem"},
260 {ZFS_TYPE_SNAPSHOT, "snapshot"},
261 {ZFS_TYPE_VOLUME, "volume"},
262 {ZFS_TYPE_BOOKMARK, "bookmark"}
263 };
264
265 /*
266 * Show the content for a zfs property attribute
267 */
268 static ssize_t
zprop_sysfs_show(const char * attr_name,const zprop_desc_t * property,char * buf,size_t buflen)269 zprop_sysfs_show(const char *attr_name, const zprop_desc_t *property,
270 char *buf, size_t buflen)
271 {
272 const char *show_str;
273 char number[32];
274
275 /* For dataset properties list the dataset types that apply */
276 if (strcmp(attr_name, "datasets") == 0 &&
277 property->pd_types != ZFS_TYPE_POOL) {
278 int len = 0;
279
280 for (int i = 0; i < ARRAY_SIZE(type_map); i++) {
281 if (type_map[i].ztm_type & property->pd_types) {
282 len += kmem_scnprintf(buf + len, buflen - len,
283 "%s ", type_map[i].ztm_name);
284 }
285 }
286 len += kmem_scnprintf(buf + len, buflen - len, "\n");
287 return (len);
288 }
289
290 if (strcmp(attr_name, "type") == 0) {
291 show_str = zprop_types[property->pd_proptype];
292 } else if (strcmp(attr_name, "readonly") == 0) {
293 show_str = property->pd_attr == PROP_READONLY ? "1" : "0";
294 } else if (strcmp(attr_name, "setonce") == 0) {
295 show_str = property->pd_attr == PROP_ONETIME ? "1" : "0";
296 } else if (strcmp(attr_name, "visible") == 0) {
297 show_str = property->pd_visible ? "1" : "0";
298 } else if (strcmp(attr_name, "values") == 0) {
299 show_str = property->pd_values ? property->pd_values : "";
300 } else if (strcmp(attr_name, "default") == 0) {
301 switch (property->pd_proptype) {
302 case PROP_TYPE_NUMBER:
303 (void) snprintf(number, sizeof (number), "%llu",
304 (u_longlong_t)property->pd_numdefault);
305 show_str = number;
306 break;
307 case PROP_TYPE_STRING:
308 show_str = property->pd_strdefault ?
309 property->pd_strdefault : "";
310 break;
311 case PROP_TYPE_INDEX:
312 if (zprop_index_to_string(property->pd_propnum,
313 property->pd_numdefault, &show_str,
314 property->pd_types) != 0) {
315 show_str = "";
316 }
317 break;
318 default:
319 return (0);
320 }
321 } else {
322 return (0);
323 }
324
325 return (snprintf(buf, buflen, "%s\n", show_str));
326 }
327
328 static ssize_t
dataset_property_show(struct kobject * kobj,struct attribute * attr,char * buf)329 dataset_property_show(struct kobject *kobj, struct attribute *attr, char *buf)
330 {
331 zfs_prop_t prop = zfs_name_to_prop(kobject_name(kobj));
332 zprop_desc_t *prop_tbl = zfs_prop_get_table();
333 ssize_t len;
334
335 ASSERT3U(prop, <, ZFS_NUM_PROPS);
336
337 len = zprop_sysfs_show(attr->name, &prop_tbl[prop], buf, PAGE_SIZE);
338
339 return (len);
340 }
341
342 static ssize_t
vdev_property_show(struct kobject * kobj,struct attribute * attr,char * buf)343 vdev_property_show(struct kobject *kobj, struct attribute *attr, char *buf)
344 {
345 vdev_prop_t prop = vdev_name_to_prop(kobject_name(kobj));
346 zprop_desc_t *prop_tbl = vdev_prop_get_table();
347 ssize_t len;
348
349 ASSERT3U(prop, <, VDEV_NUM_PROPS);
350
351 len = zprop_sysfs_show(attr->name, &prop_tbl[prop], buf, PAGE_SIZE);
352
353 return (len);
354 }
355
356 static ssize_t
pool_property_show(struct kobject * kobj,struct attribute * attr,char * buf)357 pool_property_show(struct kobject *kobj, struct attribute *attr, char *buf)
358 {
359 zpool_prop_t prop = zpool_name_to_prop(kobject_name(kobj));
360 zprop_desc_t *prop_tbl = zpool_prop_get_table();
361 ssize_t len;
362
363 ASSERT3U(prop, <, ZPOOL_NUM_PROPS);
364
365 len = zprop_sysfs_show(attr->name, &prop_tbl[prop], buf, PAGE_SIZE);
366
367 return (len);
368 }
369
370 /*
371 * ZFS kernel feature attributes for '/sys/module/zfs/features.kernel'
372 *
373 * This list is intended for kernel features that don't have a pool feature
374 * association or that extend existing user kernel interfaces.
375 *
376 * A user process can easily check if the running zfs kernel module
377 * supports the new feature.
378 */
379 static const char *const zfs_kernel_features[] = {
380 /* --> Add new kernel features here */
381 "com.delphix:vdev_initialize",
382 "org.zfsonlinux:vdev_trim",
383 "org.openzfs:l2arc_persistent",
384 };
385
386 #define KERNEL_FEATURE_COUNT ARRAY_SIZE(zfs_kernel_features)
387
388 static ssize_t
kernel_feature_show(struct kobject * kobj,struct attribute * attr,char * buf)389 kernel_feature_show(struct kobject *kobj, struct attribute *attr, char *buf)
390 {
391 if (strcmp(attr->name, "supported") == 0)
392 return (snprintf(buf, PAGE_SIZE, "yes\n"));
393 return (0);
394 }
395
396 static void
kernel_feature_to_kobj(zfs_mod_kobj_t * parent,int slot,const char * name)397 kernel_feature_to_kobj(zfs_mod_kobj_t *parent, int slot, const char *name)
398 {
399 zfs_mod_kobj_t *zfs_kobj = &parent->zko_children[slot];
400
401 ASSERT3U(slot, <, KERNEL_FEATURE_COUNT);
402 ASSERT(name);
403
404 int err = zfs_kobj_init(zfs_kobj, 1, 0, kernel_feature_show);
405 if (err)
406 return;
407
408 zfs_kobj_add_attr(zfs_kobj, 0, "supported");
409
410 err = zfs_kobj_add(zfs_kobj, &parent->zko_kobj, name);
411 if (err)
412 zfs_kobj_release(&zfs_kobj->zko_kobj);
413 }
414
415 static int
zfs_kernel_features_init(zfs_mod_kobj_t * zfs_kobj,struct kobject * parent)416 zfs_kernel_features_init(zfs_mod_kobj_t *zfs_kobj, struct kobject *parent)
417 {
418 /*
419 * Create a parent kobject to host kernel features.
420 *
421 * '/sys/module/zfs/features.kernel'
422 */
423 int err = zfs_kobj_init(zfs_kobj, 0, KERNEL_FEATURE_COUNT,
424 kernel_feature_show);
425 if (err)
426 return (err);
427 err = zfs_kobj_add(zfs_kobj, parent, ZFS_SYSFS_KERNEL_FEATURES);
428 if (err) {
429 zfs_kobj_release(&zfs_kobj->zko_kobj);
430 return (err);
431 }
432
433 /*
434 * Now create a kobject for each feature.
435 *
436 * '/sys/module/zfs/features.kernel/<feature>'
437 */
438 for (int f = 0; f < KERNEL_FEATURE_COUNT; f++)
439 kernel_feature_to_kobj(zfs_kobj, f, zfs_kernel_features[f]);
440
441 return (0);
442 }
443
444 /*
445 * Each pool feature has these common attributes
446 */
447 static const char *const pool_feature_attrs[] = {
448 "description",
449 "guid",
450 "uname",
451 "readonly_compatible",
452 "required_for_mos",
453 "activate_on_enable",
454 "per_dataset"
455 };
456
457 #define ZPOOL_FEATURE_ATTR_COUNT ARRAY_SIZE(pool_feature_attrs)
458
459 /*
460 * Show the content for the given zfs pool feature attribute
461 */
462 static ssize_t
pool_feature_show(struct kobject * kobj,struct attribute * attr,char * buf)463 pool_feature_show(struct kobject *kobj, struct attribute *attr, char *buf)
464 {
465 spa_feature_t fid;
466
467 if (zfeature_lookup_guid(kobject_name(kobj), &fid) != 0)
468 return (0);
469
470 ASSERT3U(fid, <, SPA_FEATURES);
471
472 zfeature_flags_t flags = spa_feature_table[fid].fi_flags;
473 const char *show_str = NULL;
474
475 if (strcmp(attr->name, "description") == 0) {
476 show_str = spa_feature_table[fid].fi_desc;
477 } else if (strcmp(attr->name, "guid") == 0) {
478 show_str = spa_feature_table[fid].fi_guid;
479 } else if (strcmp(attr->name, "uname") == 0) {
480 show_str = spa_feature_table[fid].fi_uname;
481 } else if (strcmp(attr->name, "readonly_compatible") == 0) {
482 show_str = flags & ZFEATURE_FLAG_READONLY_COMPAT ? "1" : "0";
483 } else if (strcmp(attr->name, "required_for_mos") == 0) {
484 show_str = flags & ZFEATURE_FLAG_MOS ? "1" : "0";
485 } else if (strcmp(attr->name, "activate_on_enable") == 0) {
486 show_str = flags & ZFEATURE_FLAG_ACTIVATE_ON_ENABLE ? "1" : "0";
487 } else if (strcmp(attr->name, "per_dataset") == 0) {
488 show_str = flags & ZFEATURE_FLAG_PER_DATASET ? "1" : "0";
489 }
490 if (show_str == NULL)
491 return (0);
492
493 return (snprintf(buf, PAGE_SIZE, "%s\n", show_str));
494 }
495
496 static void
pool_feature_to_kobj(zfs_mod_kobj_t * parent,spa_feature_t fid,const char * name)497 pool_feature_to_kobj(zfs_mod_kobj_t *parent, spa_feature_t fid,
498 const char *name)
499 {
500 zfs_mod_kobj_t *zfs_kobj = &parent->zko_children[fid];
501
502 ASSERT3U(fid, <, SPA_FEATURES);
503 ASSERT(name);
504
505 int err = zfs_kobj_init(zfs_kobj, ZPOOL_FEATURE_ATTR_COUNT, 0,
506 pool_feature_show);
507 if (err)
508 return;
509
510 for (int i = 0; i < ZPOOL_FEATURE_ATTR_COUNT; i++)
511 zfs_kobj_add_attr(zfs_kobj, i, pool_feature_attrs[i]);
512
513 err = zfs_kobj_add(zfs_kobj, &parent->zko_kobj, name);
514 if (err)
515 zfs_kobj_release(&zfs_kobj->zko_kobj);
516 }
517
518 static int
zfs_pool_features_init(zfs_mod_kobj_t * zfs_kobj,struct kobject * parent)519 zfs_pool_features_init(zfs_mod_kobj_t *zfs_kobj, struct kobject *parent)
520 {
521 /*
522 * Create a parent kobject to host pool features.
523 *
524 * '/sys/module/zfs/features.pool'
525 */
526 int err = zfs_kobj_init(zfs_kobj, 0, SPA_FEATURES, pool_feature_show);
527 if (err)
528 return (err);
529 err = zfs_kobj_add(zfs_kobj, parent, ZFS_SYSFS_POOL_FEATURES);
530 if (err) {
531 zfs_kobj_release(&zfs_kobj->zko_kobj);
532 return (err);
533 }
534
535 /*
536 * Now create a kobject for each feature.
537 *
538 * '/sys/module/zfs/features.pool/<feature>'
539 */
540 for (spa_feature_t i = 0; i < SPA_FEATURES; i++)
541 pool_feature_to_kobj(zfs_kobj, i, spa_feature_table[i].fi_guid);
542
543 return (0);
544 }
545
546 typedef struct prop_to_kobj_arg {
547 zprop_desc_t *p2k_table;
548 zfs_mod_kobj_t *p2k_parent;
549 sysfs_show_func p2k_show_func;
550 int p2k_attr_count;
551 } prop_to_kobj_arg_t;
552
553 static int
zprop_to_kobj(int prop,void * args)554 zprop_to_kobj(int prop, void *args)
555 {
556 prop_to_kobj_arg_t *data = args;
557 zfs_mod_kobj_t *parent = data->p2k_parent;
558 zfs_mod_kobj_t *zfs_kobj = &parent->zko_children[prop];
559 const char *name = data->p2k_table[prop].pd_name;
560 int err;
561
562 ASSERT(name);
563
564 err = zfs_kobj_init(zfs_kobj, data->p2k_attr_count, 0,
565 data->p2k_show_func);
566 if (err)
567 return (ZPROP_CONT);
568
569 for (int i = 0; i < data->p2k_attr_count; i++)
570 zfs_kobj_add_attr(zfs_kobj, i, zprop_attrs[i]);
571
572 err = zfs_kobj_add(zfs_kobj, &parent->zko_kobj, name);
573 if (err)
574 zfs_kobj_release(&zfs_kobj->zko_kobj);
575
576 return (ZPROP_CONT);
577 }
578
579 static int
zfs_sysfs_properties_init(zfs_mod_kobj_t * zfs_kobj,struct kobject * parent,zfs_type_t type)580 zfs_sysfs_properties_init(zfs_mod_kobj_t *zfs_kobj, struct kobject *parent,
581 zfs_type_t type)
582 {
583 prop_to_kobj_arg_t context;
584 const char *name;
585 int err;
586
587 /*
588 * Create a parent kobject to host properties.
589 *
590 * '/sys/module/zfs/properties.<type>'
591 */
592 if (type == ZFS_TYPE_POOL) {
593 name = ZFS_SYSFS_POOL_PROPERTIES;
594 context.p2k_table = zpool_prop_get_table();
595 context.p2k_attr_count = ZPOOL_PROP_ATTR_COUNT;
596 context.p2k_parent = zfs_kobj;
597 context.p2k_show_func = pool_property_show;
598 err = zfs_kobj_init(zfs_kobj, 0, ZPOOL_NUM_PROPS,
599 pool_property_show);
600 } else if (type == ZFS_TYPE_VDEV) {
601 name = ZFS_SYSFS_VDEV_PROPERTIES;
602 context.p2k_table = vdev_prop_get_table();
603 context.p2k_attr_count = ZPOOL_PROP_ATTR_COUNT;
604 context.p2k_parent = zfs_kobj;
605 context.p2k_show_func = vdev_property_show;
606 err = zfs_kobj_init(zfs_kobj, 0, VDEV_NUM_PROPS,
607 vdev_property_show);
608 } else {
609 name = ZFS_SYSFS_DATASET_PROPERTIES;
610 context.p2k_table = zfs_prop_get_table();
611 context.p2k_attr_count = ZFS_PROP_ATTR_COUNT;
612 context.p2k_parent = zfs_kobj;
613 context.p2k_show_func = dataset_property_show;
614 err = zfs_kobj_init(zfs_kobj, 0, ZFS_NUM_PROPS,
615 dataset_property_show);
616 }
617
618 if (err)
619 return (err);
620
621 err = zfs_kobj_add(zfs_kobj, parent, name);
622 if (err) {
623 zfs_kobj_release(&zfs_kobj->zko_kobj);
624 return (err);
625 }
626
627 /*
628 * Create a kobject for each property.
629 *
630 * '/sys/module/zfs/properties.<type>/<property>'
631 */
632 (void) zprop_iter_common(zprop_to_kobj, &context, B_TRUE,
633 B_FALSE, type);
634
635 return (err);
636 }
637
638 void
zfs_sysfs_init(void)639 zfs_sysfs_init(void)
640 {
641 struct kobject *parent;
642 #if defined(CONFIG_ZFS) && !defined(CONFIG_ZFS_MODULE)
643 parent = kobject_create_and_add("zfs", fs_kobj);
644 #else
645 parent = &(((struct module *)(THIS_MODULE))->mkobj).kobj;
646 #endif
647 int err;
648
649 if (parent == NULL)
650 return;
651
652 err = zfs_kernel_features_init(&kernel_features_kobj, parent);
653 if (err)
654 return;
655
656 err = zfs_pool_features_init(&pool_features_kobj, parent);
657 if (err) {
658 zfs_kobj_fini(&kernel_features_kobj);
659 return;
660 }
661
662 err = zfs_sysfs_properties_init(&pool_props_kobj, parent,
663 ZFS_TYPE_POOL);
664 if (err) {
665 zfs_kobj_fini(&kernel_features_kobj);
666 zfs_kobj_fini(&pool_features_kobj);
667 return;
668 }
669
670 err = zfs_sysfs_properties_init(&vdev_props_kobj, parent,
671 ZFS_TYPE_VDEV);
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 return;
677 }
678
679 err = zfs_sysfs_properties_init(&dataset_props_kobj, parent,
680 ZFS_TYPE_FILESYSTEM);
681 if (err) {
682 zfs_kobj_fini(&kernel_features_kobj);
683 zfs_kobj_fini(&pool_features_kobj);
684 zfs_kobj_fini(&pool_props_kobj);
685 zfs_kobj_fini(&vdev_props_kobj);
686 return;
687 }
688 }
689
690 void
zfs_sysfs_fini(void)691 zfs_sysfs_fini(void)
692 {
693 /*
694 * Remove top-level kobjects; each will remove any children kobjects
695 */
696 zfs_kobj_fini(&kernel_features_kobj);
697 zfs_kobj_fini(&pool_features_kobj);
698 zfs_kobj_fini(&pool_props_kobj);
699 zfs_kobj_fini(&vdev_props_kobj);
700 zfs_kobj_fini(&dataset_props_kobj);
701 }
702