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