sysfs.h revision 253449
1219820Sjeff/*- 2219820Sjeff * Copyright (c) 2010 Isilon Systems, Inc. 3219820Sjeff * Copyright (c) 2010 iX Systems, Inc. 4219820Sjeff * Copyright (c) 2010 Panasas, Inc. 5219820Sjeff * All rights reserved. 6219820Sjeff * 7219820Sjeff * Redistribution and use in source and binary forms, with or without 8219820Sjeff * modification, are permitted provided that the following conditions 9219820Sjeff * are met: 10219820Sjeff * 1. Redistributions of source code must retain the above copyright 11219820Sjeff * notice unmodified, this list of conditions, and the following 12219820Sjeff * disclaimer. 13219820Sjeff * 2. Redistributions in binary form must reproduce the above copyright 14219820Sjeff * notice, this list of conditions and the following disclaimer in the 15219820Sjeff * documentation and/or other materials provided with the distribution. 16219820Sjeff * 17219820Sjeff * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 18219820Sjeff * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 19219820Sjeff * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 20219820Sjeff * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 21219820Sjeff * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 22219820Sjeff * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 23219820Sjeff * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 24219820Sjeff * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25219820Sjeff * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 26219820Sjeff * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27219820Sjeff */ 28219820Sjeff 29219820Sjeff#ifndef _LINUX_SYSFS_H_ 30219820Sjeff#define _LINUX_SYSFS_H_ 31219820Sjeff 32219820Sjeff#include <sys/sysctl.h> 33219820Sjeff 34219820Sjeffstruct attribute { 35219820Sjeff const char *name; 36219820Sjeff struct module *owner; 37219820Sjeff mode_t mode; 38219820Sjeff}; 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; 48219820Sjeff 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 }, \ 55219820Sjeff .show = _show, .store = _store, \ 56219820Sjeff} 57219820Sjeff 58219820Sjeff#define __ATTR_RO(_name) { \ 59219820Sjeff .attr = { .name = __stringify(_name), .mode = 0444 }, \ 60219820Sjeff .show = _name##_show, \ 61219820Sjeff} 62219820Sjeff 63219820Sjeff#define __ATTR_NULL { .attr = { .name = NULL } } 64219820Sjeff 65219820Sjeff/* 66219820Sjeff * Handle our generic '\0' terminated 'C' string. 67219820Sjeff * Two cases: 68219820Sjeff * a variable string: point arg1 at it, arg2 is max length. 69219820Sjeff * a constant string: point arg1 at it, arg2 is zero. 70219820Sjeff */ 71219820Sjeff 72219820Sjeffstatic inline int 73219820Sjeffsysctl_handle_attr(SYSCTL_HANDLER_ARGS) 74219820Sjeff{ 75219820Sjeff struct kobject *kobj; 76219820Sjeff struct attribute *attr; 77219820Sjeff const struct sysfs_ops *ops; 78253449Sjhb char *buf; 79219820Sjeff int error; 80219820Sjeff ssize_t len; 81219820Sjeff 82219820Sjeff kobj = arg1; 83219820Sjeff attr = (struct attribute *)arg2; 84219820Sjeff if (kobj->ktype == NULL || kobj->ktype->sysfs_ops == NULL) 85219820Sjeff return (ENODEV); 86253449Sjhb buf = (char *)get_zeroed_page(GFP_KERNEL); 87219820Sjeff if (buf == NULL) 88219820Sjeff return (ENOMEM); 89253449Sjhb ops = kobj->ktype->sysfs_ops; 90219820Sjeff if (ops->show) { 91219820Sjeff len = ops->show(kobj, attr, buf); 92219820Sjeff /* 93253449Sjhb * It's valid to not have a 'show' so just return an 94253449Sjhb * empty string. 95219820Sjeff */ 96219820Sjeff if (len < 0) { 97219820Sjeff error = -len; 98219820Sjeff if (error != EIO) 99219820Sjeff goto out; 100219820Sjeff } 101253449Sjhb 102253449Sjhb /* Trim trailing newline. */ 103253449Sjhb len--; 104253449Sjhb buf[len] = '\0'; 105219820Sjeff } 106253449Sjhb 107253449Sjhb /* Leave one trailing byte to append a newline. */ 108253449Sjhb error = sysctl_handle_string(oidp, buf, PAGE_SIZE - 1, req); 109253449Sjhb if (error != 0 || req->newptr == NULL || ops->store == NULL) 110219820Sjeff goto out; 111253449Sjhb len = strlcat(buf, "\n", PAGE_SIZE); 112253449Sjhb KASSERT(len < PAGE_SIZE, ("new attribute truncated")); 113253048Sjhb len = ops->store(kobj, attr, buf, len); 114219820Sjeff if (len < 0) 115219820Sjeff error = -len; 116219820Sjeffout: 117219820Sjeff free_page((unsigned long)buf); 118219820Sjeff 119219820Sjeff return (error); 120219820Sjeff} 121219820Sjeff 122219820Sjeffstatic inline int 123219820Sjeffsysfs_create_file(struct kobject *kobj, const struct attribute *attr) 124219820Sjeff{ 125219820Sjeff 126219820Sjeff sysctl_add_oid(NULL, SYSCTL_CHILDREN(kobj->oidp), OID_AUTO, 127219820Sjeff attr->name, CTLTYPE_STRING|CTLFLAG_RW|CTLFLAG_MPSAFE, kobj, 128219820Sjeff (uintptr_t)attr, sysctl_handle_attr, "A", ""); 129219820Sjeff 130219820Sjeff return (0); 131219820Sjeff} 132219820Sjeff 133219820Sjeffstatic inline void 134219820Sjeffsysfs_remove_file(struct kobject *kobj, const struct attribute *attr) 135219820Sjeff{ 136219820Sjeff 137219820Sjeff if (kobj->oidp) 138219820Sjeff sysctl_remove_name(kobj->oidp, attr->name, 1, 1); 139219820Sjeff} 140219820Sjeff 141219820Sjeffstatic inline void 142219820Sjeffsysfs_remove_group(struct kobject *kobj, const struct attribute_group *grp) 143219820Sjeff{ 144219820Sjeff 145219820Sjeff if (kobj->oidp) 146219820Sjeff sysctl_remove_name(kobj->oidp, grp->name, 1, 1); 147219820Sjeff} 148219820Sjeff 149219820Sjeffstatic inline int 150219820Sjeffsysfs_create_group(struct kobject *kobj, const struct attribute_group *grp) 151219820Sjeff{ 152219820Sjeff struct attribute **attr; 153219820Sjeff struct sysctl_oid *oidp; 154219820Sjeff 155219820Sjeff oidp = SYSCTL_ADD_NODE(NULL, SYSCTL_CHILDREN(kobj->oidp), 156219820Sjeff OID_AUTO, grp->name, CTLFLAG_RD|CTLFLAG_MPSAFE, NULL, grp->name); 157219820Sjeff for (attr = grp->attrs; *attr != NULL; attr++) { 158219820Sjeff sysctl_add_oid(NULL, SYSCTL_CHILDREN(oidp), OID_AUTO, 159219820Sjeff (*attr)->name, CTLTYPE_STRING|CTLFLAG_RW|CTLFLAG_MPSAFE, 160219820Sjeff kobj, (uintptr_t)*attr, sysctl_handle_attr, "A", ""); 161219820Sjeff } 162219820Sjeff 163219820Sjeff return (0); 164219820Sjeff} 165219820Sjeff 166219820Sjeffstatic inline int 167219820Sjeffsysfs_create_dir(struct kobject *kobj) 168219820Sjeff{ 169219820Sjeff 170219820Sjeff kobj->oidp = SYSCTL_ADD_NODE(NULL, SYSCTL_CHILDREN(kobj->parent->oidp), 171219820Sjeff OID_AUTO, kobj->name, CTLFLAG_RD|CTLFLAG_MPSAFE, NULL, kobj->name); 172219820Sjeff 173219820Sjeff return (0); 174219820Sjeff} 175219820Sjeff 176219820Sjeffstatic inline void 177219820Sjeffsysfs_remove_dir(struct kobject *kobj) 178219820Sjeff{ 179219820Sjeff 180219820Sjeff if (kobj->oidp == NULL) 181219820Sjeff return; 182219820Sjeff sysctl_remove_oid(kobj->oidp, 1, 1); 183219820Sjeff} 184219820Sjeff 185219820Sjeff#endif /* _LINUX_SYSFS_H_ */ 186