1219820Sjeff/*-
2219820Sjeff * Copyright (c) 2010 Isilon Systems, Inc.
3219820Sjeff * Copyright (c) 2010 iX Systems, Inc.
4219820Sjeff * Copyright (c) 2010 Panasas, Inc.
5270710Shselasky * Copyright (c) 2013, 2014 Mellanox Technologies, Ltd.
6219820Sjeff * All rights reserved.
7219820Sjeff *
8219820Sjeff * Redistribution and use in source and binary forms, with or without
9219820Sjeff * modification, are permitted provided that the following conditions
10219820Sjeff * are met:
11219820Sjeff * 1. Redistributions of source code must retain the above copyright
12219820Sjeff *    notice unmodified, this list of conditions, and the following
13219820Sjeff *    disclaimer.
14219820Sjeff * 2. Redistributions in binary form must reproduce the above copyright
15219820Sjeff *    notice, this list of conditions and the following disclaimer in the
16219820Sjeff *    documentation and/or other materials provided with the distribution.
17219820Sjeff *
18219820Sjeff * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
19219820Sjeff * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
20219820Sjeff * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
21219820Sjeff * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
22219820Sjeff * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
23219820Sjeff * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24219820Sjeff * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25219820Sjeff * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26219820Sjeff * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
27219820Sjeff * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28289644Shselasky *
29289644Shselasky * $FreeBSD: stable/11/sys/compat/linuxkpi/common/include/linux/sysfs.h 354616 2019-11-11 15:28:27Z hselasky $
30219820Sjeff */
31219820Sjeff#ifndef	_LINUX_SYSFS_H_
32219820Sjeff#define	_LINUX_SYSFS_H_
33219820Sjeff
34290335Shselasky#include <sys/types.h>
35219820Sjeff#include <sys/sysctl.h>
36290335Shselasky#include <sys/errno.h>
37219820Sjeff
38290335Shselasky#include <linux/kobject.h>
39219820Sjeff
40219820Sjeffstruct sysfs_ops {
41219820Sjeff	ssize_t (*show)(struct kobject *, struct attribute *, char *);
42219820Sjeff	ssize_t (*store)(struct kobject *, struct attribute *, const char *,
43219820Sjeff	    size_t);
44219820Sjeff};
45219820Sjeff
46219820Sjeffstruct attribute_group {
47219820Sjeff	const char		*name;
48331756Semaste	mode_t			(*is_visible)(struct kobject *,
49219820Sjeff				    struct attribute *, int);
50219820Sjeff	struct attribute	**attrs;
51219820Sjeff};
52219820Sjeff
53219820Sjeff#define	__ATTR(_name, _mode, _show, _store) {				\
54219820Sjeff	.attr = { .name = __stringify(_name), .mode = _mode },		\
55331756Semaste	.show = _show, .store  = _store,				\
56219820Sjeff}
57328653Shselasky#define	__ATTR_RO(_name)	__ATTR(_name, 0444, _name##_show, NULL)
58328653Shselasky#define	__ATTR_WO(_name)	__ATTR(_name, 0200, NULL, _name##_store)
59328653Shselasky#define	__ATTR_RW(_name)	__ATTR(_name, 0644, _name##_show, _name##_store)
60219820Sjeff
61219820Sjeff#define	__ATTR_NULL	{ .attr = { .name = NULL } }
62219820Sjeff
63328653Shselasky#define	ATTRIBUTE_GROUPS(_name)						\
64328653Shselasky	static struct attribute_group _name##_group = {			\
65354616Shselasky		.name = __stringify(_name),				\
66328653Shselasky		.attrs = _name##_attrs,					\
67328653Shselasky	};								\
68354616Shselasky	static const struct attribute_group *_name##_groups[] = {	\
69328653Shselasky		&_name##_group,						\
70328653Shselasky		NULL,							\
71354616Shselasky	}
72328653Shselasky
73219820Sjeff/*
74219820Sjeff * Handle our generic '\0' terminated 'C' string.
75219820Sjeff * Two cases:
76219820Sjeff *      a variable string:  point arg1 at it, arg2 is max length.
77219820Sjeff *      a constant string:  point arg1 at it, arg2 is zero.
78219820Sjeff */
79219820Sjeff
80219820Sjeffstatic inline int
81219820Sjeffsysctl_handle_attr(SYSCTL_HANDLER_ARGS)
82219820Sjeff{
83219820Sjeff	struct kobject *kobj;
84219820Sjeff	struct attribute *attr;
85219820Sjeff	const struct sysfs_ops *ops;
86253449Sjhb	char *buf;
87219820Sjeff	int error;
88219820Sjeff	ssize_t len;
89219820Sjeff
90219820Sjeff	kobj = arg1;
91290613Scem	attr = (struct attribute *)(intptr_t)arg2;
92219820Sjeff	if (kobj->ktype == NULL || kobj->ktype->sysfs_ops == NULL)
93219820Sjeff		return (ENODEV);
94253449Sjhb	buf = (char *)get_zeroed_page(GFP_KERNEL);
95219820Sjeff	if (buf == NULL)
96219820Sjeff		return (ENOMEM);
97253449Sjhb	ops = kobj->ktype->sysfs_ops;
98219820Sjeff	if (ops->show) {
99219820Sjeff		len = ops->show(kobj, attr, buf);
100219820Sjeff		/*
101253449Sjhb		 * It's valid to not have a 'show' so just return an
102253449Sjhb		 * empty string.
103331756Semaste		 */
104219820Sjeff		if (len < 0) {
105219820Sjeff			error = -len;
106219820Sjeff			if (error != EIO)
107219820Sjeff				goto out;
108254121Sjeff			buf[0] = '\0';
109254121Sjeff		} else if (len) {
110254121Sjeff			len--;
111254121Sjeff			if (len >= PAGE_SIZE)
112254121Sjeff				len = PAGE_SIZE - 1;
113254121Sjeff			/* Trim trailing newline. */
114254121Sjeff			buf[len] = '\0';
115219820Sjeff		}
116219820Sjeff	}
117253449Sjhb
118253449Sjhb	/* Leave one trailing byte to append a newline. */
119253449Sjhb	error = sysctl_handle_string(oidp, buf, PAGE_SIZE - 1, req);
120253449Sjhb	if (error != 0 || req->newptr == NULL || ops->store == NULL)
121219820Sjeff		goto out;
122253449Sjhb	len = strlcat(buf, "\n", PAGE_SIZE);
123253449Sjhb	KASSERT(len < PAGE_SIZE, ("new attribute truncated"));
124253048Sjhb	len = ops->store(kobj, attr, buf, len);
125219820Sjeff	if (len < 0)
126219820Sjeff		error = -len;
127219820Sjeffout:
128219820Sjeff	free_page((unsigned long)buf);
129219820Sjeff
130219820Sjeff	return (error);
131219820Sjeff}
132219820Sjeff
133219820Sjeffstatic inline int
134219820Sjeffsysfs_create_file(struct kobject *kobj, const struct attribute *attr)
135219820Sjeff{
136345931Shselasky	struct sysctl_oid *oid;
137219820Sjeff
138345931Shselasky	oid = SYSCTL_ADD_OID(NULL, SYSCTL_CHILDREN(kobj->oidp), OID_AUTO,
139219820Sjeff	    attr->name, CTLTYPE_STRING|CTLFLAG_RW|CTLFLAG_MPSAFE, kobj,
140219820Sjeff	    (uintptr_t)attr, sysctl_handle_attr, "A", "");
141345931Shselasky	if (!oid) {
142345931Shselasky		return (-ENOMEM);
143345931Shselasky	}
144219820Sjeff
145219820Sjeff	return (0);
146219820Sjeff}
147219820Sjeff
148219820Sjeffstatic inline void
149219820Sjeffsysfs_remove_file(struct kobject *kobj, const struct attribute *attr)
150219820Sjeff{
151219820Sjeff
152219820Sjeff	if (kobj->oidp)
153219820Sjeff		sysctl_remove_name(kobj->oidp, attr->name, 1, 1);
154219820Sjeff}
155219820Sjeff
156354614Shselaskystatic inline int
157354614Shselaskysysfs_create_files(struct kobject *kobj, const struct attribute * const *attrs)
158354614Shselasky{
159354614Shselasky	int error = 0;
160354614Shselasky	int i;
161354614Shselasky
162354614Shselasky	for (i = 0; attrs[i] && !error; i++)
163354614Shselasky		error = sysfs_create_file(kobj, attrs[i]);
164354614Shselasky	while (error && --i >= 0)
165354614Shselasky		sysfs_remove_file(kobj, attrs[i]);
166354614Shselasky
167354614Shselasky	return (error);
168354614Shselasky}
169354614Shselasky
170219820Sjeffstatic inline void
171354614Shselaskysysfs_remove_files(struct kobject *kobj, const struct attribute * const *attrs)
172354614Shselasky{
173354614Shselasky	int i;
174354614Shselasky
175354614Shselasky	for (i = 0; attrs[i]; i++)
176354614Shselasky		sysfs_remove_file(kobj, attrs[i]);
177354614Shselasky}
178354614Shselasky
179354615Shselaskystatic inline int
180354615Shselaskysysfs_create_group(struct kobject *kobj, const struct attribute_group *grp)
181354615Shselasky{
182354615Shselasky	struct attribute **attr;
183354615Shselasky	struct sysctl_oid *oidp;
184354615Shselasky
185354615Shselasky	/* Don't create the group node if grp->name is undefined. */
186354615Shselasky	if (grp->name)
187354615Shselasky		oidp = SYSCTL_ADD_NODE(NULL, SYSCTL_CHILDREN(kobj->oidp),
188354615Shselasky		    OID_AUTO, grp->name, CTLFLAG_RD|CTLFLAG_MPSAFE, NULL, grp->name);
189354615Shselasky	else
190354615Shselasky		oidp = kobj->oidp;
191354615Shselasky	for (attr = grp->attrs; *attr != NULL; attr++) {
192354615Shselasky		SYSCTL_ADD_OID(NULL, SYSCTL_CHILDREN(oidp), OID_AUTO,
193354615Shselasky		    (*attr)->name, CTLTYPE_STRING|CTLFLAG_RW|CTLFLAG_MPSAFE,
194354615Shselasky		    kobj, (uintptr_t)*attr, sysctl_handle_attr, "A", "");
195354615Shselasky	}
196354615Shselasky
197354615Shselasky	return (0);
198354615Shselasky}
199354615Shselasky
200354614Shselaskystatic inline void
201219820Sjeffsysfs_remove_group(struct kobject *kobj, const struct attribute_group *grp)
202219820Sjeff{
203219820Sjeff
204219820Sjeff	if (kobj->oidp)
205219820Sjeff		sysctl_remove_name(kobj->oidp, grp->name, 1, 1);
206219820Sjeff}
207219820Sjeff
208219820Sjeffstatic inline int
209354615Shselaskysysfs_create_groups(struct kobject *kobj, const struct attribute_group **grps)
210219820Sjeff{
211354615Shselasky	int error = 0;
212354615Shselasky	int i;
213354615Shselasky
214354616Shselasky	if (grps == NULL)
215354616Shselasky		goto done;
216354615Shselasky	for (i = 0; grps[i] && !error; i++)
217354615Shselasky		error = sysfs_create_group(kobj, grps[i]);
218354615Shselasky	while (error && --i >= 0)
219354615Shselasky		sysfs_remove_group(kobj, grps[i]);
220354616Shselaskydone:
221354615Shselasky	return (error);
222354615Shselasky}
223354615Shselasky
224354616Shselaskystatic inline void
225354616Shselaskysysfs_remove_groups(struct kobject *kobj, const struct attribute_group **grps)
226354616Shselasky{
227354616Shselasky	int i;
228354616Shselasky
229354616Shselasky	if (grps == NULL)
230354616Shselasky		return;
231354616Shselasky	for (i = 0; grps[i]; i++)
232354616Shselasky		sysfs_remove_group(kobj, grps[i]);
233354616Shselasky}
234354616Shselasky
235354615Shselaskystatic inline int
236354615Shselaskysysfs_merge_group(struct kobject *kobj, const struct attribute_group *grp)
237354615Shselasky{
238354615Shselasky
239354615Shselasky	/* Really expected behavior is to return failure if group exists. */
240354615Shselasky	return (sysfs_create_group(kobj, grp));
241354615Shselasky}
242354615Shselasky
243354615Shselaskystatic inline void
244354615Shselaskysysfs_unmerge_group(struct kobject *kobj, const struct attribute_group *grp)
245354615Shselasky{
246219820Sjeff	struct attribute **attr;
247219820Sjeff	struct sysctl_oid *oidp;
248219820Sjeff
249354615Shselasky	SLIST_FOREACH(oidp, SYSCTL_CHILDREN(kobj->oidp), oid_link) {
250354615Shselasky		if (strcmp(oidp->oid_name, grp->name) != 0)
251354615Shselasky			continue;
252354615Shselasky		for (attr = grp->attrs; *attr != NULL; attr++) {
253354615Shselasky			sysctl_remove_name(oidp, (*attr)->name, 1, 1);
254354615Shselasky		}
255219820Sjeff	}
256219820Sjeff}
257219820Sjeff
258219820Sjeffstatic inline int
259219820Sjeffsysfs_create_dir(struct kobject *kobj)
260219820Sjeff{
261345931Shselasky	struct sysctl_oid *oid;
262219820Sjeff
263345931Shselasky	oid = SYSCTL_ADD_NODE(NULL, SYSCTL_CHILDREN(kobj->parent->oidp),
264219820Sjeff	    OID_AUTO, kobj->name, CTLFLAG_RD|CTLFLAG_MPSAFE, NULL, kobj->name);
265345931Shselasky	if (!oid) {
266345931Shselasky		return (-ENOMEM);
267345931Shselasky	}
268345931Shselasky	kobj->oidp = oid;
269219820Sjeff
270331756Semaste	return (0);
271219820Sjeff}
272219820Sjeff
273219820Sjeffstatic inline void
274219820Sjeffsysfs_remove_dir(struct kobject *kobj)
275219820Sjeff{
276219820Sjeff
277219820Sjeff	if (kobj->oidp == NULL)
278219820Sjeff		return;
279219820Sjeff	sysctl_remove_oid(kobj->oidp, 1, 1);
280219820Sjeff}
281219820Sjeff
282354615Shselaskystatic inline bool
283354615Shselaskysysfs_streq(const char *s1, const char *s2)
284354615Shselasky{
285354615Shselasky	int l1, l2;
286354615Shselasky
287354615Shselasky	l1 = strlen(s1);
288354615Shselasky	l2 = strlen(s2);
289354615Shselasky
290354615Shselasky	if (l1 != 0 && s1[l1-1] == '\n')
291354615Shselasky		l1--;
292354615Shselasky	if (l2 != 0 && s2[l2-1] == '\n')
293354615Shselasky		l2--;
294354615Shselasky
295354615Shselasky	return (l1 == l2 && strncmp(s1, s2, l1) == 0);
296354615Shselasky}
297354615Shselasky
298255932Salfred#define sysfs_attr_init(attr) do {} while(0)
299255932Salfred
300219820Sjeff#endif	/* _LINUX_SYSFS_H_ */
301