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; 100254121Sjeff buf[0] = '\0'; 101254121Sjeff } else if (len) { 102254121Sjeff len--; 103254121Sjeff if (len >= PAGE_SIZE) 104254121Sjeff len = PAGE_SIZE - 1; 105254121Sjeff /* Trim trailing newline. */ 106254121Sjeff buf[len] = '\0'; 107219820Sjeff } 108219820Sjeff } 109253449Sjhb 110253449Sjhb /* Leave one trailing byte to append a newline. */ 111253449Sjhb error = sysctl_handle_string(oidp, buf, PAGE_SIZE - 1, req); 112253449Sjhb if (error != 0 || req->newptr == NULL || ops->store == NULL) 113219820Sjeff goto out; 114253449Sjhb len = strlcat(buf, "\n", PAGE_SIZE); 115253449Sjhb KASSERT(len < PAGE_SIZE, ("new attribute truncated")); 116253048Sjhb len = ops->store(kobj, attr, buf, len); 117219820Sjeff if (len < 0) 118219820Sjeff error = -len; 119219820Sjeffout: 120219820Sjeff free_page((unsigned long)buf); 121219820Sjeff 122219820Sjeff return (error); 123219820Sjeff} 124219820Sjeff 125219820Sjeffstatic inline int 126219820Sjeffsysfs_create_file(struct kobject *kobj, const struct attribute *attr) 127219820Sjeff{ 128219820Sjeff 129219820Sjeff sysctl_add_oid(NULL, SYSCTL_CHILDREN(kobj->oidp), OID_AUTO, 130219820Sjeff attr->name, CTLTYPE_STRING|CTLFLAG_RW|CTLFLAG_MPSAFE, kobj, 131219820Sjeff (uintptr_t)attr, sysctl_handle_attr, "A", ""); 132219820Sjeff 133219820Sjeff return (0); 134219820Sjeff} 135219820Sjeff 136219820Sjeffstatic inline void 137219820Sjeffsysfs_remove_file(struct kobject *kobj, const struct attribute *attr) 138219820Sjeff{ 139219820Sjeff 140219820Sjeff if (kobj->oidp) 141219820Sjeff sysctl_remove_name(kobj->oidp, attr->name, 1, 1); 142219820Sjeff} 143219820Sjeff 144219820Sjeffstatic inline void 145219820Sjeffsysfs_remove_group(struct kobject *kobj, const struct attribute_group *grp) 146219820Sjeff{ 147219820Sjeff 148219820Sjeff if (kobj->oidp) 149219820Sjeff sysctl_remove_name(kobj->oidp, grp->name, 1, 1); 150219820Sjeff} 151219820Sjeff 152219820Sjeffstatic inline int 153219820Sjeffsysfs_create_group(struct kobject *kobj, const struct attribute_group *grp) 154219820Sjeff{ 155219820Sjeff struct attribute **attr; 156219820Sjeff struct sysctl_oid *oidp; 157219820Sjeff 158219820Sjeff oidp = SYSCTL_ADD_NODE(NULL, SYSCTL_CHILDREN(kobj->oidp), 159219820Sjeff OID_AUTO, grp->name, CTLFLAG_RD|CTLFLAG_MPSAFE, NULL, grp->name); 160219820Sjeff for (attr = grp->attrs; *attr != NULL; attr++) { 161219820Sjeff sysctl_add_oid(NULL, SYSCTL_CHILDREN(oidp), OID_AUTO, 162219820Sjeff (*attr)->name, CTLTYPE_STRING|CTLFLAG_RW|CTLFLAG_MPSAFE, 163219820Sjeff kobj, (uintptr_t)*attr, sysctl_handle_attr, "A", ""); 164219820Sjeff } 165219820Sjeff 166219820Sjeff return (0); 167219820Sjeff} 168219820Sjeff 169219820Sjeffstatic inline int 170219820Sjeffsysfs_create_dir(struct kobject *kobj) 171219820Sjeff{ 172219820Sjeff 173219820Sjeff kobj->oidp = SYSCTL_ADD_NODE(NULL, SYSCTL_CHILDREN(kobj->parent->oidp), 174219820Sjeff OID_AUTO, kobj->name, CTLFLAG_RD|CTLFLAG_MPSAFE, NULL, kobj->name); 175219820Sjeff 176219820Sjeff return (0); 177219820Sjeff} 178219820Sjeff 179219820Sjeffstatic inline void 180219820Sjeffsysfs_remove_dir(struct kobject *kobj) 181219820Sjeff{ 182219820Sjeff 183219820Sjeff if (kobj->oidp == NULL) 184219820Sjeff return; 185219820Sjeff sysctl_remove_oid(kobj->oidp, 1, 1); 186219820Sjeff} 187219820Sjeff 188255932Salfred#define sysfs_attr_init(attr) do {} while(0) 189255932Salfred 190219820Sjeff#endif /* _LINUX_SYSFS_H_ */ 191