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