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