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 struct kobject * 193 __sysfs_lookup_group(struct kobject *kobj, const char *group) 194 { 195 int found; 196 struct sysctl_oid *group_oidp; 197 struct kobject *group_kobj; 198 199 found = 0; 200 if (group != NULL) { 201 SYSCTL_FOREACH(group_oidp, SYSCTL_CHILDREN(kobj->oidp)) { 202 if (strcmp(group_oidp->oid_name, group) != 0) 203 continue; 204 found = 1; 205 break; 206 } 207 } else { 208 found = 1; 209 group_oidp = kobj->oidp; 210 } 211 212 if (!found) 213 return (NULL); 214 215 group_kobj = group_oidp->oid_arg1; 216 217 return (group_kobj); 218 } 219 220 static inline int 221 sysfs_add_file_to_group(struct kobject *kobj, 222 const struct attribute *attr, const char *group) 223 { 224 int ret; 225 struct kobject *group_kobj; 226 227 group_kobj = __sysfs_lookup_group(kobj, group); 228 if (group_kobj == NULL) 229 return (-ENOENT); 230 231 ret = sysfs_create_file(group_kobj, attr); 232 233 return (ret); 234 } 235 236 static inline void 237 sysfs_remove_file(struct kobject *kobj, const struct attribute *attr) 238 { 239 240 if (kobj->oidp) 241 sysctl_remove_name(kobj->oidp, attr->name, 1, 1); 242 } 243 244 static inline void 245 sysfs_remove_file_from_group(struct kobject *kobj, 246 const struct attribute *attr, const char *group) 247 { 248 struct kobject *group_kobj; 249 250 group_kobj = __sysfs_lookup_group(kobj, group); 251 if (group_kobj == NULL) 252 return; 253 254 sysfs_remove_file(group_kobj, attr); 255 } 256 257 static inline int 258 sysctl_handle_bin_attr(SYSCTL_HANDLER_ARGS) 259 { 260 struct kobject *kobj; 261 struct bin_attribute *attr; 262 char *buf; 263 int error; 264 ssize_t len; 265 266 kobj = arg1; 267 attr = (struct bin_attribute *)(intptr_t)arg2; 268 if (kobj->ktype == NULL || kobj->ktype->sysfs_ops == NULL) 269 return (ENODEV); 270 buf = (char *)get_zeroed_page(GFP_KERNEL); 271 if (buf == NULL) 272 return (ENOMEM); 273 274 if (attr->read) { 275 len = attr->read( 276 NULL, /* <-- struct file, unimplemented */ 277 kobj, attr, buf, req->oldidx, PAGE_SIZE); 278 if (len < 0) { 279 error = -len; 280 if (error != EIO) 281 goto out; 282 } 283 } 284 285 error = sysctl_handle_opaque(oidp, buf, PAGE_SIZE, req); 286 if (error != 0 || req->newptr == NULL || attr->write == NULL) 287 goto out; 288 289 len = attr->write( 290 NULL, /* <-- struct file, unimplemented */ 291 kobj, attr, buf, req->newidx, req->newlen); 292 if (len < 0) 293 error = -len; 294 out: 295 free_page((unsigned long)buf); 296 297 return (error); 298 } 299 300 static inline int 301 sysfs_create_bin_file(struct kobject *kobj, const struct bin_attribute *attr) 302 { 303 struct sysctl_oid *oid; 304 int ctlflags; 305 306 ctlflags = CTLTYPE_OPAQUE | CTLFLAG_MPSAFE; 307 if (attr->attr.mode & (S_IRUSR | S_IWUSR)) 308 ctlflags |= CTLFLAG_RW; 309 else if (attr->attr.mode & S_IRUSR) 310 ctlflags |= CTLFLAG_RD; 311 else if (attr->attr.mode & S_IWUSR) 312 ctlflags |= CTLFLAG_WR; 313 314 oid = SYSCTL_ADD_OID(NULL, SYSCTL_CHILDREN(kobj->oidp), OID_AUTO, 315 attr->attr.name, ctlflags, kobj, 316 (uintptr_t)attr, sysctl_handle_bin_attr, "", ""); 317 if (oid == NULL) 318 return (-ENOMEM); 319 320 return (0); 321 } 322 323 static inline void 324 sysfs_remove_bin_file(struct kobject *kobj, const struct bin_attribute *attr) 325 { 326 327 if (kobj->oidp) 328 sysctl_remove_name(kobj->oidp, attr->attr.name, 1, 1); 329 } 330 331 static inline int 332 sysfs_create_link(struct kobject *kobj __unused, 333 struct kobject *target __unused, const char *name __unused) 334 { 335 /* TODO */ 336 337 return (0); 338 } 339 340 static inline void 341 sysfs_remove_link(struct kobject *kobj, const char *name) 342 { 343 /* TODO (along with sysfs_create_link) */ 344 } 345 346 static inline int 347 sysfs_create_files(struct kobject *kobj, const struct attribute * const *attrs) 348 { 349 int error = 0; 350 int i; 351 352 for (i = 0; attrs[i] && !error; i++) 353 error = sysfs_create_file(kobj, attrs[i]); 354 while (error && --i >= 0) 355 sysfs_remove_file(kobj, attrs[i]); 356 357 return (error); 358 } 359 360 static inline void 361 sysfs_remove_files(struct kobject *kobj, const struct attribute * const *attrs) 362 { 363 int i; 364 365 for (i = 0; attrs[i]; i++) 366 sysfs_remove_file(kobj, attrs[i]); 367 } 368 369 static inline int 370 sysfs_create_group(struct kobject *kobj, const struct attribute_group *grp) 371 { 372 struct attribute **attr; 373 struct sysctl_oid *oidp; 374 375 /* Don't create the group node if grp->name is undefined. */ 376 if (grp->name) 377 oidp = SYSCTL_ADD_NODE(NULL, SYSCTL_CHILDREN(kobj->oidp), 378 OID_AUTO, grp->name, CTLFLAG_RD|CTLFLAG_MPSAFE, NULL, grp->name); 379 else 380 oidp = kobj->oidp; 381 for (attr = grp->attrs; *attr != NULL; attr++) { 382 SYSCTL_ADD_OID(NULL, SYSCTL_CHILDREN(oidp), OID_AUTO, 383 (*attr)->name, CTLTYPE_STRING|CTLFLAG_RW|CTLFLAG_MPSAFE, 384 kobj, (uintptr_t)*attr, sysctl_handle_attr, "A", ""); 385 } 386 387 return (0); 388 } 389 390 static inline void 391 sysfs_remove_group(struct kobject *kobj, const struct attribute_group *grp) 392 { 393 394 if (kobj->oidp) 395 sysctl_remove_name(kobj->oidp, grp->name, 1, 1); 396 } 397 398 static inline int 399 sysfs_create_groups(struct kobject *kobj, const struct attribute_group **grps) 400 { 401 int error = 0; 402 int i; 403 404 if (grps == NULL) 405 goto done; 406 for (i = 0; grps[i] && !error; i++) 407 error = sysfs_create_group(kobj, grps[i]); 408 while (error && --i >= 0) 409 sysfs_remove_group(kobj, grps[i]); 410 done: 411 return (error); 412 } 413 414 static inline void 415 sysfs_remove_groups(struct kobject *kobj, const struct attribute_group **grps) 416 { 417 int i; 418 419 if (grps == NULL) 420 return; 421 for (i = 0; grps[i]; i++) 422 sysfs_remove_group(kobj, grps[i]); 423 } 424 425 static inline int 426 sysfs_merge_group(struct kobject *kobj, const struct attribute_group *grp) 427 { 428 429 /* Really expected behavior is to return failure if group exists. */ 430 return (sysfs_create_group(kobj, grp)); 431 } 432 433 static inline void 434 sysfs_unmerge_group(struct kobject *kobj, const struct attribute_group *grp) 435 { 436 struct attribute **attr; 437 struct sysctl_oid *oidp; 438 439 SYSCTL_FOREACH(oidp, SYSCTL_CHILDREN(kobj->oidp)) { 440 if (strcmp(oidp->oid_name, grp->name) != 0) 441 continue; 442 for (attr = grp->attrs; *attr != NULL; attr++) { 443 sysctl_remove_name(oidp, (*attr)->name, 1, 1); 444 } 445 } 446 } 447 448 static inline int 449 sysfs_create_dir(struct kobject *kobj) 450 { 451 struct sysctl_oid *oid; 452 453 oid = SYSCTL_ADD_NODE(NULL, SYSCTL_CHILDREN(kobj->parent->oidp), 454 OID_AUTO, kobj->name, CTLFLAG_RD|CTLFLAG_MPSAFE, NULL, kobj->name); 455 if (!oid) { 456 return (-ENOMEM); 457 } 458 kobj->oidp = oid; 459 460 return (0); 461 } 462 463 static inline void 464 sysfs_remove_dir(struct kobject *kobj) 465 { 466 467 if (kobj->oidp == NULL) 468 return; 469 sysctl_remove_oid(kobj->oidp, 1, 1); 470 } 471 472 static inline bool 473 sysfs_streq(const char *s1, const char *s2) 474 { 475 int l1, l2; 476 477 l1 = strlen(s1); 478 l2 = strlen(s2); 479 480 if (l1 != 0 && s1[l1-1] == '\n') 481 l1--; 482 if (l2 != 0 && s2[l2-1] == '\n') 483 l2--; 484 485 return (l1 == l2 && strncmp(s1, s2, l1) == 0); 486 } 487 488 static inline int 489 sysfs_emit(char *buf, const char *fmt, ...) 490 { 491 va_list args; 492 int i; 493 494 if (!buf || offset_in_page(buf)) { 495 pr_warn("invalid sysfs_emit: buf:%p\n", buf); 496 return (0); 497 } 498 499 va_start(args, fmt); 500 i = vscnprintf(buf, PAGE_SIZE, fmt, args); 501 va_end(args); 502 503 return (i); 504 } 505 506 static inline int 507 sysfs_emit_at(char *buf, int at, const char *fmt, ...) 508 { 509 va_list args; 510 int i; 511 512 if (!buf || offset_in_page(buf) || at < 0 || at >= PAGE_SIZE) { 513 pr_warn("invalid sysfs_emit: buf:%p at:%d\n", buf, at); 514 return (0); 515 } 516 517 va_start(args, fmt); 518 i = vscnprintf(buf + at, PAGE_SIZE - at, fmt, args); 519 va_end(args); 520 521 return (i); 522 } 523 524 static inline int 525 _sysfs_match_string(const char * const *a, size_t l, const char *s) 526 { 527 const char *p; 528 int i; 529 530 for (i = 0; i < l; i++) { 531 p = a[i]; 532 if (p == NULL) 533 break; 534 if (sysfs_streq(p, s)) 535 return (i); 536 } 537 538 return (-ENOENT); 539 } 540 #define sysfs_match_string(a, s) _sysfs_match_string(a, ARRAY_SIZE(a), s) 541 542 #define sysfs_attr_init(attr) do {} while(0) 543 544 #endif /* _LINUXKPI_LINUX_SYSFS_H_ */ 545