sysfs.h revision 253449
1171095Ssam/*-
2171095Ssam * Copyright (c) 2010 Isilon Systems, Inc.
3171095Ssam * Copyright (c) 2010 iX Systems, Inc.
4171095Ssam * Copyright (c) 2010 Panasas, Inc.
5171095Ssam * All rights reserved.
6171095Ssam *
7171095Ssam * Redistribution and use in source and binary forms, with or without
8171095Ssam * modification, are permitted provided that the following conditions
9171095Ssam * are met:
10171095Ssam * 1. Redistributions of source code must retain the above copyright
11171095Ssam *    notice unmodified, this list of conditions, and the following
12171095Ssam *    disclaimer.
13171095Ssam * 2. Redistributions in binary form must reproduce the above copyright
14171095Ssam *    notice, this list of conditions and the following disclaimer in the
15171095Ssam *    documentation and/or other materials provided with the distribution.
16171095Ssam *
17171095Ssam * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18171095Ssam * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19171095Ssam * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20171095Ssam * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21171095Ssam * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22171095Ssam * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23171095Ssam * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24171095Ssam * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25171095Ssam * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26171095Ssam * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27171095Ssam */
28171095Ssam
29171095Ssam#ifndef	_LINUX_SYSFS_H_
30171095Ssam#define	_LINUX_SYSFS_H_
31171095Ssam
32171095Ssam#include <sys/sysctl.h>
33171095Ssam
34171095Ssamstruct attribute {
35171095Ssam	const char 	*name;
36171095Ssam	struct module	*owner;
37171095Ssam	mode_t		mode;
38171095Ssam};
39171095Ssam
40171095Ssamstruct sysfs_ops {
41171095Ssam	ssize_t (*show)(struct kobject *, struct attribute *, char *);
42171095Ssam	ssize_t (*store)(struct kobject *, struct attribute *, const char *,
43171095Ssam	    size_t);
44171095Ssam};
45171095Ssam
46171095Ssamstruct attribute_group {
47171095Ssam	const char		*name;
48171095Ssam	mode_t                  (*is_visible)(struct kobject *,
49171095Ssam				    struct attribute *, int);
50171095Ssam	struct attribute	**attrs;
51171095Ssam};
52171095Ssam
53171095Ssam#define	__ATTR(_name, _mode, _show, _store) {				\
54171095Ssam	.attr = { .name = __stringify(_name), .mode = _mode },		\
55171095Ssam        .show = _show, .store  = _store,				\
56171095Ssam}
57171095Ssam
58171095Ssam#define	__ATTR_RO(_name) {						\
59171095Ssam	.attr = { .name = __stringify(_name), .mode = 0444 },		\
60171095Ssam	.show   = _name##_show,						\
61171095Ssam}
62171095Ssam
63171095Ssam#define	__ATTR_NULL	{ .attr = { .name = NULL } }
64171095Ssam
65171095Ssam/*
66171095Ssam * Handle our generic '\0' terminated 'C' string.
67171095Ssam * Two cases:
68171095Ssam *      a variable string:  point arg1 at it, arg2 is max length.
69171095Ssam *      a constant string:  point arg1 at it, arg2 is zero.
70171095Ssam */
71171095Ssam
72171095Ssamstatic inline int
73171095Ssamsysctl_handle_attr(SYSCTL_HANDLER_ARGS)
74171095Ssam{
75171095Ssam	struct kobject *kobj;
76171095Ssam	struct attribute *attr;
77171095Ssam	const struct sysfs_ops *ops;
78171095Ssam	char *buf;
79171095Ssam	int error;
80171095Ssam	ssize_t len;
81171095Ssam
82171095Ssam	kobj = arg1;
83171095Ssam	attr = (struct attribute *)arg2;
84171095Ssam	if (kobj->ktype == NULL || kobj->ktype->sysfs_ops == NULL)
85171095Ssam		return (ENODEV);
86171095Ssam	buf = (char *)get_zeroed_page(GFP_KERNEL);
87171095Ssam	if (buf == NULL)
88171095Ssam		return (ENOMEM);
89171095Ssam	ops = kobj->ktype->sysfs_ops;
90171095Ssam	if (ops->show) {
91171095Ssam		len = ops->show(kobj, attr, buf);
92171095Ssam		/*
93171095Ssam		 * It's valid to not have a 'show' so just return an
94171095Ssam		 * empty string.
95171095Ssam	 	 */
96171095Ssam		if (len < 0) {
97171095Ssam			error = -len;
98171095Ssam			if (error != EIO)
99171095Ssam				goto out;
100171095Ssam		}
101171095Ssam
102171095Ssam		/* Trim trailing newline. */
103171095Ssam		len--;
104171095Ssam		buf[len] = '\0';
105171095Ssam	}
106171095Ssam
107171095Ssam	/* Leave one trailing byte to append a newline. */
108171095Ssam	error = sysctl_handle_string(oidp, buf, PAGE_SIZE - 1, req);
109171095Ssam	if (error != 0 || req->newptr == NULL || ops->store == NULL)
110171095Ssam		goto out;
111171095Ssam	len = strlcat(buf, "\n", PAGE_SIZE);
112171095Ssam	KASSERT(len < PAGE_SIZE, ("new attribute truncated"));
113171095Ssam	len = ops->store(kobj, attr, buf, len);
114171095Ssam	if (len < 0)
115171095Ssam		error = -len;
116171095Ssamout:
117171095Ssam	free_page((unsigned long)buf);
118171095Ssam
119171095Ssam	return (error);
120171095Ssam}
121171095Ssam
122171095Ssamstatic inline int
123171095Ssamsysfs_create_file(struct kobject *kobj, const struct attribute *attr)
124171095Ssam{
125171095Ssam
126171095Ssam	sysctl_add_oid(NULL, SYSCTL_CHILDREN(kobj->oidp), OID_AUTO,
127171095Ssam	    attr->name, CTLTYPE_STRING|CTLFLAG_RW|CTLFLAG_MPSAFE, kobj,
128171095Ssam	    (uintptr_t)attr, sysctl_handle_attr, "A", "");
129171095Ssam
130171095Ssam	return (0);
131171095Ssam}
132171095Ssam
133171095Ssamstatic inline void
134171095Ssamsysfs_remove_file(struct kobject *kobj, const struct attribute *attr)
135171095Ssam{
136171095Ssam
137171095Ssam	if (kobj->oidp)
138171095Ssam		sysctl_remove_name(kobj->oidp, attr->name, 1, 1);
139171095Ssam}
140171095Ssam
141171095Ssamstatic inline void
142171095Ssamsysfs_remove_group(struct kobject *kobj, const struct attribute_group *grp)
143171095Ssam{
144171095Ssam
145171095Ssam	if (kobj->oidp)
146171095Ssam		sysctl_remove_name(kobj->oidp, grp->name, 1, 1);
147171095Ssam}
148171095Ssam
149171095Ssamstatic inline int
150171095Ssamsysfs_create_group(struct kobject *kobj, const struct attribute_group *grp)
151171095Ssam{
152171095Ssam	struct attribute **attr;
153171095Ssam	struct sysctl_oid *oidp;
154171095Ssam
155171095Ssam	oidp = SYSCTL_ADD_NODE(NULL, SYSCTL_CHILDREN(kobj->oidp),
156171095Ssam	    OID_AUTO, grp->name, CTLFLAG_RD|CTLFLAG_MPSAFE, NULL, grp->name);
157171095Ssam	for (attr = grp->attrs; *attr != NULL; attr++) {
158171095Ssam		sysctl_add_oid(NULL, SYSCTL_CHILDREN(oidp), OID_AUTO,
159171095Ssam		    (*attr)->name, CTLTYPE_STRING|CTLFLAG_RW|CTLFLAG_MPSAFE,
160171095Ssam		    kobj, (uintptr_t)*attr, sysctl_handle_attr, "A", "");
161171095Ssam	}
162171095Ssam
163171095Ssam	return (0);
164171095Ssam}
165171095Ssam
166171095Ssamstatic inline int
167171095Ssamsysfs_create_dir(struct kobject *kobj)
168171095Ssam{
169171095Ssam
170171095Ssam	kobj->oidp = SYSCTL_ADD_NODE(NULL, SYSCTL_CHILDREN(kobj->parent->oidp),
171171095Ssam	    OID_AUTO, kobj->name, CTLFLAG_RD|CTLFLAG_MPSAFE, NULL, kobj->name);
172171095Ssam
173171095Ssam        return (0);
174171095Ssam}
175171095Ssam
176171095Ssamstatic inline void
177171095Ssamsysfs_remove_dir(struct kobject *kobj)
178171095Ssam{
179171095Ssam
180171095Ssam	if (kobj->oidp == NULL)
181171095Ssam		return;
182171095Ssam	sysctl_remove_oid(kobj->oidp, 1, 1);
183171095Ssam}
184171095Ssam
185171095Ssam#endif	/* _LINUX_SYSFS_H_ */
186171095Ssam