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