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