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