1 /*- 2 * Copyright (c) 2010 Isilon Systems, Inc. 3 * Copyright (c) 2010 iX Systems, Inc. 4 * Copyright (c) 2010 Panasas, Inc. 5 * Copyright (c) 2013, 2014 Mellanox Technologies, Ltd. 6 * All rights reserved. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice unmodified, this list of conditions, and the following 13 * disclaimer. 14 * 2. Redistributions in binary form must reproduce the above copyright 15 * notice, this list of conditions and the following disclaimer in the 16 * documentation and/or other materials provided with the distribution. 17 * 18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 19 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 20 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 21 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 22 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 23 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 27 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 */ 29 #ifndef _LINUXKPI_LINUX_SYSFS_H_ 30 #define _LINUXKPI_LINUX_SYSFS_H_ 31 32 #include <sys/types.h> 33 #include <sys/sysctl.h> 34 #include <sys/errno.h> 35 36 #include <linux/kobject.h> 37 #include <linux/stringify.h> 38 #include <linux/mm.h> 39 40 struct sysfs_ops { 41 ssize_t (*show)(struct kobject *, struct attribute *, char *); 42 ssize_t (*store)(struct kobject *, struct attribute *, const char *, 43 size_t); 44 }; 45 46 struct attribute_group { 47 const char *name; 48 mode_t (*is_visible)(struct kobject *, 49 struct attribute *, int); 50 struct attribute **attrs; 51 }; 52 53 struct bin_attribute { 54 struct attribute attr; 55 size_t size; 56 ssize_t (*read)(struct linux_file *, struct kobject *, 57 struct bin_attribute *, char *, loff_t, size_t); 58 ssize_t (*write)(struct linux_file *, struct kobject *, 59 struct bin_attribute *, char *, loff_t, size_t); 60 }; 61 62 #define __ATTR(_name, _mode, _show, _store) { \ 63 .attr = { .name = __stringify(_name), .mode = _mode }, \ 64 .show = _show, .store = _store, \ 65 } 66 #define __ATTR_RO(_name) { \ 67 .attr = { .name = __stringify(_name), .mode = 0444 }, \ 68 .show = _name##_show, \ 69 } 70 #define __ATTR_WO(_name) __ATTR(_name, 0200, NULL, _name##_store) 71 #define __ATTR_RW(_name) __ATTR(_name, 0644, _name##_show, _name##_store) 72 #define __ATTR_NULL { .attr = { .name = NULL } } 73 74 #define ATTRIBUTE_GROUPS(_name) \ 75 static struct attribute_group _name##_group = { \ 76 .name = __stringify(_name), \ 77 .attrs = _name##_attrs, \ 78 }; \ 79 static const struct attribute_group *_name##_groups[] = { \ 80 &_name##_group, \ 81 NULL, \ 82 } 83 84 #define __BIN_ATTR(_name, _mode, _read, _write, _size) { \ 85 .attr = { .name = __stringify(_name), .mode = _mode }, \ 86 .read = _read, .write = _write, .size = _size, \ 87 } 88 #define __BIN_ATTR_RO(_name, _size) { \ 89 .attr = { .name = __stringify(_name), .mode = 0444 }, \ 90 .read = _name##_read, .size = _size, \ 91 } 92 #define __BIN_ATTR_WO(_name, _size) { \ 93 .attr = { .name = __stringify(_name), .mode = 0200 }, \ 94 .write = _name##_write, .size = _size, \ 95 } 96 #define __BIN_ATTR_WR(_name, _size) { \ 97 .attr = { .name = __stringify(_name), .mode = 0644 }, \ 98 .read = _name##_read, .write = _name##_write, .size = _size, \ 99 } 100 101 #define BIN_ATTR(_name, _mode, _read, _write, _size) \ 102 struct bin_attribute bin_attr_##_name = \ 103 __BIN_ATTR(_name, _mode, _read, _write, _size); 104 105 #define BIN_ATTR_RO(_name, _size) \ 106 struct bin_attribute bin_attr_##_name = \ 107 __BIN_ATTR_RO(_name, _size); 108 109 #define BIN_ATTR_WO(_name, _size) \ 110 struct bin_attribute bin_attr_##_name = \ 111 __BIN_ATTR_WO(_name, _size); 112 113 #define BIN_ATTR_WR(_name, _size) \ 114 struct bin_attribute bin_attr_##_name = \ 115 __BIN_ATTR_WR(_name, _size); 116 117 /* 118 * Handle our generic '\0' terminated 'C' string. 119 * Two cases: 120 * a variable string: point arg1 at it, arg2 is max length. 121 * a constant string: point arg1 at it, arg2 is zero. 122 */ 123 124 static inline int 125 sysctl_handle_attr(SYSCTL_HANDLER_ARGS) 126 { 127 struct kobject *kobj; 128 struct attribute *attr; 129 const struct sysfs_ops *ops; 130 char *buf; 131 int error; 132 ssize_t len; 133 134 kobj = arg1; 135 attr = (struct attribute *)(intptr_t)arg2; 136 if (kobj->ktype == NULL || kobj->ktype->sysfs_ops == NULL) 137 return (ENODEV); 138 buf = (char *)get_zeroed_page(GFP_KERNEL); 139 if (buf == NULL) 140 return (ENOMEM); 141 ops = kobj->ktype->sysfs_ops; 142 if (ops->show) { 143 len = ops->show(kobj, attr, buf); 144 /* 145 * It's valid to not have a 'show' so just return an 146 * empty string. 147 */ 148 if (len < 0) { 149 error = -len; 150 if (error != EIO) 151 goto out; 152 buf[0] = '\0'; 153 } else if (len) { 154 len--; 155 if (len >= PAGE_SIZE) 156 len = PAGE_SIZE - 1; 157 /* Trim trailing newline. */ 158 buf[len] = '\0'; 159 } 160 } 161 162 /* Leave one trailing byte to append a newline. */ 163 error = sysctl_handle_string(oidp, buf, PAGE_SIZE - 1, req); 164 if (error != 0 || req->newptr == NULL || ops->store == NULL) 165 goto out; 166 len = strlcat(buf, "\n", PAGE_SIZE); 167 KASSERT(len < PAGE_SIZE, ("new attribute truncated")); 168 len = ops->store(kobj, attr, buf, len); 169 if (len < 0) 170 error = -len; 171 out: 172 free_page((unsigned long)buf); 173 174 return (error); 175 } 176 177 static inline int 178 sysfs_create_file(struct kobject *kobj, const struct attribute *attr) 179 { 180 struct sysctl_oid *oid; 181 182 oid = SYSCTL_ADD_OID(NULL, SYSCTL_CHILDREN(kobj->oidp), OID_AUTO, 183 attr->name, CTLTYPE_STRING|CTLFLAG_RW|CTLFLAG_MPSAFE, kobj, 184 (uintptr_t)attr, sysctl_handle_attr, "A", ""); 185 if (!oid) { 186 return (-ENOMEM); 187 } 188 189 return (0); 190 } 191 192 static inline void 193 sysfs_remove_file(struct kobject *kobj, const struct attribute *attr) 194 { 195 196 if (kobj->oidp) 197 sysctl_remove_name(kobj->oidp, attr->name, 1, 1); 198 } 199 200 static inline int 201 sysctl_handle_bin_attr(SYSCTL_HANDLER_ARGS) 202 { 203 struct kobject *kobj; 204 struct bin_attribute *attr; 205 char *buf; 206 int error; 207 ssize_t len; 208 209 kobj = arg1; 210 attr = (struct bin_attribute *)(intptr_t)arg2; 211 if (kobj->ktype == NULL || kobj->ktype->sysfs_ops == NULL) 212 return (ENODEV); 213 buf = (char *)get_zeroed_page(GFP_KERNEL); 214 if (buf == NULL) 215 return (ENOMEM); 216 217 if (attr->read) { 218 len = attr->read( 219 NULL, /* <-- struct file, unimplemented */ 220 kobj, attr, buf, req->oldidx, PAGE_SIZE); 221 if (len < 0) { 222 error = -len; 223 if (error != EIO) 224 goto out; 225 } 226 } 227 228 error = sysctl_handle_opaque(oidp, buf, PAGE_SIZE, req); 229 if (error != 0 || req->newptr == NULL || attr->write == NULL) 230 goto out; 231 232 len = attr->write( 233 NULL, /* <-- struct file, unimplemented */ 234 kobj, attr, buf, req->newidx, req->newlen); 235 if (len < 0) 236 error = -len; 237 out: 238 free_page((unsigned long)buf); 239 240 return (error); 241 } 242 243 static inline int 244 sysfs_create_bin_file(struct kobject *kobj, const struct bin_attribute *attr) 245 { 246 struct sysctl_oid *oid; 247 int ctlflags; 248 249 ctlflags = CTLTYPE_OPAQUE | CTLFLAG_MPSAFE; 250 if (attr->attr.mode & (S_IRUSR | S_IWUSR)) 251 ctlflags |= CTLFLAG_RW; 252 else if (attr->attr.mode & S_IRUSR) 253 ctlflags |= CTLFLAG_RD; 254 else if (attr->attr.mode & S_IWUSR) 255 ctlflags |= CTLFLAG_WR; 256 257 oid = SYSCTL_ADD_OID(NULL, SYSCTL_CHILDREN(kobj->oidp), OID_AUTO, 258 attr->attr.name, ctlflags, kobj, 259 (uintptr_t)attr, sysctl_handle_bin_attr, "", ""); 260 if (oid == NULL) 261 return (-ENOMEM); 262 263 return (0); 264 } 265 266 static inline void 267 sysfs_remove_bin_file(struct kobject *kobj, const struct bin_attribute *attr) 268 { 269 270 if (kobj->oidp) 271 sysctl_remove_name(kobj->oidp, attr->attr.name, 1, 1); 272 } 273 274 static inline int 275 sysfs_create_link(struct kobject *kobj __unused, 276 struct kobject *target __unused, const char *name __unused) 277 { 278 /* TODO */ 279 280 return (0); 281 } 282 283 static inline void 284 sysfs_remove_link(struct kobject *kobj, const char *name) 285 { 286 /* TODO (along with sysfs_create_link) */ 287 } 288 289 static inline int 290 sysfs_create_files(struct kobject *kobj, const struct attribute * const *attrs) 291 { 292 int error = 0; 293 int i; 294 295 for (i = 0; attrs[i] && !error; i++) 296 error = sysfs_create_file(kobj, attrs[i]); 297 while (error && --i >= 0) 298 sysfs_remove_file(kobj, attrs[i]); 299 300 return (error); 301 } 302 303 static inline void 304 sysfs_remove_files(struct kobject *kobj, const struct attribute * const *attrs) 305 { 306 int i; 307 308 for (i = 0; attrs[i]; i++) 309 sysfs_remove_file(kobj, attrs[i]); 310 } 311 312 static inline int 313 sysfs_create_group(struct kobject *kobj, const struct attribute_group *grp) 314 { 315 struct attribute **attr; 316 struct sysctl_oid *oidp; 317 318 /* Don't create the group node if grp->name is undefined. */ 319 if (grp->name) 320 oidp = SYSCTL_ADD_NODE(NULL, SYSCTL_CHILDREN(kobj->oidp), 321 OID_AUTO, grp->name, CTLFLAG_RD|CTLFLAG_MPSAFE, NULL, grp->name); 322 else 323 oidp = kobj->oidp; 324 for (attr = grp->attrs; *attr != NULL; attr++) { 325 SYSCTL_ADD_OID(NULL, SYSCTL_CHILDREN(oidp), OID_AUTO, 326 (*attr)->name, CTLTYPE_STRING|CTLFLAG_RW|CTLFLAG_MPSAFE, 327 kobj, (uintptr_t)*attr, sysctl_handle_attr, "A", ""); 328 } 329 330 return (0); 331 } 332 333 static inline void 334 sysfs_remove_group(struct kobject *kobj, const struct attribute_group *grp) 335 { 336 337 if (kobj->oidp) 338 sysctl_remove_name(kobj->oidp, grp->name, 1, 1); 339 } 340 341 static inline int 342 sysfs_create_groups(struct kobject *kobj, const struct attribute_group **grps) 343 { 344 int error = 0; 345 int i; 346 347 if (grps == NULL) 348 goto done; 349 for (i = 0; grps[i] && !error; i++) 350 error = sysfs_create_group(kobj, grps[i]); 351 while (error && --i >= 0) 352 sysfs_remove_group(kobj, grps[i]); 353 done: 354 return (error); 355 } 356 357 static inline void 358 sysfs_remove_groups(struct kobject *kobj, const struct attribute_group **grps) 359 { 360 int i; 361 362 if (grps == NULL) 363 return; 364 for (i = 0; grps[i]; i++) 365 sysfs_remove_group(kobj, grps[i]); 366 } 367 368 static inline int 369 sysfs_merge_group(struct kobject *kobj, const struct attribute_group *grp) 370 { 371 372 /* Really expected behavior is to return failure if group exists. */ 373 return (sysfs_create_group(kobj, grp)); 374 } 375 376 static inline void 377 sysfs_unmerge_group(struct kobject *kobj, const struct attribute_group *grp) 378 { 379 struct attribute **attr; 380 struct sysctl_oid *oidp; 381 382 SYSCTL_FOREACH(oidp, SYSCTL_CHILDREN(kobj->oidp)) { 383 if (strcmp(oidp->oid_name, grp->name) != 0) 384 continue; 385 for (attr = grp->attrs; *attr != NULL; attr++) { 386 sysctl_remove_name(oidp, (*attr)->name, 1, 1); 387 } 388 } 389 } 390 391 static inline int 392 sysfs_create_dir(struct kobject *kobj) 393 { 394 struct sysctl_oid *oid; 395 396 oid = SYSCTL_ADD_NODE(NULL, SYSCTL_CHILDREN(kobj->parent->oidp), 397 OID_AUTO, kobj->name, CTLFLAG_RD|CTLFLAG_MPSAFE, NULL, kobj->name); 398 if (!oid) { 399 return (-ENOMEM); 400 } 401 kobj->oidp = oid; 402 403 return (0); 404 } 405 406 static inline void 407 sysfs_remove_dir(struct kobject *kobj) 408 { 409 410 if (kobj->oidp == NULL) 411 return; 412 sysctl_remove_oid(kobj->oidp, 1, 1); 413 } 414 415 static inline bool 416 sysfs_streq(const char *s1, const char *s2) 417 { 418 int l1, l2; 419 420 l1 = strlen(s1); 421 l2 = strlen(s2); 422 423 if (l1 != 0 && s1[l1-1] == '\n') 424 l1--; 425 if (l2 != 0 && s2[l2-1] == '\n') 426 l2--; 427 428 return (l1 == l2 && strncmp(s1, s2, l1) == 0); 429 } 430 431 static inline int 432 sysfs_emit(char *buf, const char *fmt, ...) 433 { 434 va_list args; 435 int i; 436 437 if (!buf || offset_in_page(buf)) { 438 pr_warn("invalid sysfs_emit: buf:%p\n", buf); 439 return (0); 440 } 441 442 va_start(args, fmt); 443 i = vscnprintf(buf, PAGE_SIZE, fmt, args); 444 va_end(args); 445 446 return (i); 447 } 448 449 static inline int 450 sysfs_emit_at(char *buf, int at, const char *fmt, ...) 451 { 452 va_list args; 453 int i; 454 455 if (!buf || offset_in_page(buf) || at < 0 || at >= PAGE_SIZE) { 456 pr_warn("invalid sysfs_emit: buf:%p at:%d\n", buf, at); 457 return (0); 458 } 459 460 va_start(args, fmt); 461 i = vscnprintf(buf + at, PAGE_SIZE - at, fmt, args); 462 va_end(args); 463 464 return (i); 465 } 466 467 static inline int 468 _sysfs_match_string(const char * const *a, size_t l, const char *s) 469 { 470 const char *p; 471 int i; 472 473 for (i = 0; i < l; i++) { 474 p = a[i]; 475 if (p == NULL) 476 break; 477 if (sysfs_streq(p, s)) 478 return (i); 479 } 480 481 return (-ENOENT); 482 } 483 #define sysfs_match_string(a, s) _sysfs_match_string(a, ARRAY_SIZE(a), s) 484 485 #define sysfs_attr_init(attr) do {} while(0) 486 487 #endif /* _LINUXKPI_LINUX_SYSFS_H_ */ 488