1 // SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) 2 #include "counts.h" 3 #include "debug.h" 4 #include "evsel.h" 5 #include "hashmap.h" 6 #include "hwmon_pmu.h" 7 #include "pmu.h" 8 #include <internal/xyarray.h> 9 #include <internal/threadmap.h> 10 #include <perf/threadmap.h> 11 #include <sys/types.h> 12 #include <assert.h> 13 #include <ctype.h> 14 #include <fcntl.h> 15 #include <stddef.h> 16 #include <stdlib.h> 17 #include <string.h> 18 #include <api/fs/fs.h> 19 #include <api/io.h> 20 #include <api/io_dir.h> 21 #include <linux/kernel.h> 22 #include <linux/string.h> 23 #include <linux/zalloc.h> 24 25 /** Strings that correspond to enum hwmon_type. */ 26 static const char * const hwmon_type_strs[HWMON_TYPE_MAX] = { 27 NULL, 28 "cpu", 29 "curr", 30 "energy", 31 "fan", 32 "humidity", 33 "in", 34 "intrusion", 35 "power", 36 "pwm", 37 "temp", 38 }; 39 #define LONGEST_HWMON_TYPE_STR "intrusion" 40 41 /** Strings that correspond to enum hwmon_item. */ 42 static const char * const hwmon_item_strs[HWMON_ITEM__MAX] = { 43 NULL, 44 "accuracy", 45 "alarm", 46 "auto_channels_temp", 47 "average", 48 "average_highest", 49 "average_interval", 50 "average_interval_max", 51 "average_interval_min", 52 "average_lowest", 53 "average_max", 54 "average_min", 55 "beep", 56 "cap", 57 "cap_hyst", 58 "cap_max", 59 "cap_min", 60 "crit", 61 "crit_hyst", 62 "div", 63 "emergency", 64 "emergency_hist", 65 "enable", 66 "fault", 67 "freq", 68 "highest", 69 "input", 70 "label", 71 "lcrit", 72 "lcrit_hyst", 73 "lowest", 74 "max", 75 "max_hyst", 76 "min", 77 "min_hyst", 78 "mod", 79 "offset", 80 "pulses", 81 "rated_max", 82 "rated_min", 83 "reset_history", 84 "target", 85 "type", 86 "vid", 87 }; 88 #define LONGEST_HWMON_ITEM_STR "average_interval_max" 89 90 static const char *const hwmon_units[HWMON_TYPE_MAX] = { 91 NULL, 92 "V", /* cpu */ 93 "A", /* curr */ 94 "J", /* energy */ 95 "rpm", /* fan */ 96 "%", /* humidity */ 97 "V", /* in */ 98 "", /* intrusion */ 99 "W", /* power */ 100 "Hz", /* pwm */ 101 "'C", /* temp */ 102 }; 103 104 struct hwmon_pmu { 105 struct perf_pmu pmu; 106 struct hashmap events; 107 char *hwmon_dir; 108 }; 109 110 /** 111 * struct hwmon_pmu_event_value: Value in hwmon_pmu->events. 112 * 113 * Hwmon files are of the form <type><number>_<item> and may have a suffix 114 * _alarm. 115 */ 116 struct hwmon_pmu_event_value { 117 /** @items: which item files are present. */ 118 DECLARE_BITMAP(items, HWMON_ITEM__MAX); 119 /** @alarm_items: which item files are present. */ 120 DECLARE_BITMAP(alarm_items, HWMON_ITEM__MAX); 121 /** @label: contents of <type><number>_label if present. */ 122 char *label; 123 /** @name: name computed from label of the form <type>_<label>. */ 124 char *name; 125 }; 126 127 bool perf_pmu__is_hwmon(const struct perf_pmu *pmu) 128 { 129 return pmu && pmu->type >= PERF_PMU_TYPE_HWMON_START && 130 pmu->type <= PERF_PMU_TYPE_HWMON_END; 131 } 132 133 bool evsel__is_hwmon(const struct evsel *evsel) 134 { 135 return perf_pmu__is_hwmon(evsel->pmu); 136 } 137 138 static size_t hwmon_pmu__event_hashmap_hash(long key, void *ctx __maybe_unused) 139 { 140 return ((union hwmon_pmu_event_key)key).type_and_num; 141 } 142 143 static bool hwmon_pmu__event_hashmap_equal(long key1, long key2, void *ctx __maybe_unused) 144 { 145 return ((union hwmon_pmu_event_key)key1).type_and_num == 146 ((union hwmon_pmu_event_key)key2).type_and_num; 147 } 148 149 static int hwmon_strcmp(const void *a, const void *b) 150 { 151 const char *sa = a; 152 const char * const *sb = b; 153 154 return strcmp(sa, *sb); 155 } 156 157 bool parse_hwmon_filename(const char *filename, 158 enum hwmon_type *type, 159 int *number, 160 enum hwmon_item *item, 161 bool *alarm) 162 { 163 char fn_type[24]; 164 const char * const *elem; 165 const char *fn_item = NULL; 166 size_t fn_item_len; 167 168 assert(strlen(LONGEST_HWMON_TYPE_STR) < sizeof(fn_type)); 169 strlcpy(fn_type, filename, sizeof(fn_type)); 170 for (size_t i = 0; fn_type[i] != '\0'; i++) { 171 if (fn_type[i] >= '0' && fn_type[i] <= '9') { 172 fn_type[i] = '\0'; 173 *number = strtoul(&filename[i], (char **)&fn_item, 10); 174 if (*fn_item == '_') 175 fn_item++; 176 break; 177 } 178 if (fn_type[i] == '_') { 179 fn_type[i] = '\0'; 180 *number = -1; 181 fn_item = &filename[i + 1]; 182 break; 183 } 184 } 185 if (fn_item == NULL || fn_type[0] == '\0' || (item != NULL && fn_item[0] == '\0')) { 186 pr_debug3("hwmon_pmu: not a hwmon file '%s'\n", filename); 187 return false; 188 } 189 elem = bsearch(&fn_type, hwmon_type_strs + 1, ARRAY_SIZE(hwmon_type_strs) - 1, 190 sizeof(hwmon_type_strs[0]), hwmon_strcmp); 191 if (!elem) { 192 pr_debug3("hwmon_pmu: not a hwmon type '%s' in file name '%s'\n", 193 fn_type, filename); 194 return false; 195 } 196 197 *type = elem - &hwmon_type_strs[0]; 198 if (!item) 199 return true; 200 201 *alarm = false; 202 fn_item_len = strlen(fn_item); 203 if (fn_item_len > 6 && !strcmp(&fn_item[fn_item_len - 6], "_alarm")) { 204 assert(strlen(LONGEST_HWMON_ITEM_STR) < sizeof(fn_type)); 205 /* fn_item_len - 5 strips "_alarm"; clamp to buffer size */ 206 strlcpy(fn_type, fn_item, min_t(size_t, fn_item_len - 5, sizeof(fn_type))); 207 fn_item = fn_type; 208 *alarm = true; 209 } 210 elem = bsearch(fn_item, hwmon_item_strs + 1, ARRAY_SIZE(hwmon_item_strs) - 1, 211 sizeof(hwmon_item_strs[0]), hwmon_strcmp); 212 if (!elem) { 213 pr_debug3("hwmon_pmu: not a hwmon item '%s' in file name '%s'\n", 214 fn_item, filename); 215 return false; 216 } 217 *item = elem - &hwmon_item_strs[0]; 218 return true; 219 } 220 221 static void fix_name(char *p) 222 { 223 char *s = strchr(p, '\n'); 224 225 if (s) 226 *s = '\0'; 227 228 while (*p != '\0') { 229 if (strchr(" :,/\n\t", *p)) 230 *p = '_'; 231 else 232 *p = tolower(*p); 233 p++; 234 } 235 } 236 237 static int hwmon_pmu__read_events(struct hwmon_pmu *pmu) 238 { 239 int err = 0; 240 struct hashmap_entry *cur, *tmp; 241 size_t bkt; 242 struct io_dirent64 *ent; 243 struct io_dir dir; 244 245 if (pmu->pmu.sysfs_aliases_loaded) 246 return 0; 247 248 /* Use openat so that the directory contents are refreshed. */ 249 io_dir__init(&dir, open(pmu->hwmon_dir, O_CLOEXEC | O_DIRECTORY | O_RDONLY)); 250 251 if (dir.dirfd < 0) 252 return -ENOENT; 253 254 while ((ent = io_dir__readdir(&dir)) != NULL) { 255 enum hwmon_type type; 256 int number; 257 enum hwmon_item item; 258 bool alarm; 259 union hwmon_pmu_event_key key = { .type_and_num = 0 }; 260 struct hwmon_pmu_event_value *value; 261 262 if (ent->d_type != DT_REG) 263 continue; 264 265 if (!parse_hwmon_filename(ent->d_name, &type, &number, &item, &alarm)) { 266 pr_debug3("Not a hwmon file '%s'\n", ent->d_name); 267 continue; 268 } 269 key.num = number; 270 key.type = type; 271 if (!hashmap__find(&pmu->events, key.type_and_num, &value)) { 272 value = zalloc(sizeof(*value)); 273 if (!value) { 274 err = -ENOMEM; 275 goto err_out; 276 } 277 err = hashmap__add(&pmu->events, key.type_and_num, value); 278 if (err) { 279 free(value); 280 err = -ENOMEM; 281 goto err_out; 282 } 283 } 284 __set_bit(item, alarm ? value->alarm_items : value->items); 285 if (item == HWMON_ITEM_LABEL) { 286 char buf[128]; 287 int fd = openat(dir.dirfd, ent->d_name, O_RDONLY); 288 ssize_t read_len; 289 290 if (fd < 0) 291 continue; 292 293 read_len = read(fd, buf, sizeof(buf) - 1); 294 295 while (read_len > 0 && buf[read_len - 1] == '\n') 296 read_len--; 297 298 if (read_len <= 0) { 299 close(fd); 300 continue; 301 } 302 buf[read_len] = '\0'; 303 304 if (buf[0] == '\0') { 305 pr_debug("hwmon_pmu: empty label file %s %s\n", 306 pmu->pmu.name, ent->d_name); 307 close(fd); 308 continue; 309 } 310 value->label = strdup(buf); 311 if (!value->label) { 312 pr_debug("hwmon_pmu: memory allocation failure\n"); 313 close(fd); 314 continue; 315 } 316 snprintf(buf, sizeof(buf), "%s_%s", hwmon_type_strs[type], value->label); 317 fix_name(buf); 318 value->name = strdup(buf); 319 if (!value->name) 320 pr_debug("hwmon_pmu: memory allocation failure\n"); 321 close(fd); 322 } 323 } 324 if (hashmap__size(&pmu->events) == 0) 325 pr_debug2("hwmon_pmu: %s has no events\n", pmu->pmu.name); 326 327 hashmap__for_each_entry_safe((&pmu->events), cur, tmp, bkt) { 328 union hwmon_pmu_event_key key = { 329 .type_and_num = cur->key, 330 }; 331 struct hwmon_pmu_event_value *value = cur->pvalue; 332 333 if (!test_bit(HWMON_ITEM_INPUT, value->items)) { 334 pr_debug("hwmon_pmu: %s removing event '%s%d' that has no input file\n", 335 pmu->pmu.name, hwmon_type_strs[key.type], key.num); 336 hashmap__delete(&pmu->events, key.type_and_num, &key, &value); 337 zfree(&value->label); 338 zfree(&value->name); 339 free(value); 340 } 341 } 342 pmu->pmu.sysfs_aliases_loaded = true; 343 344 err_out: 345 close(dir.dirfd); 346 return err; 347 } 348 349 struct perf_pmu *hwmon_pmu__new(struct list_head *pmus, const char *hwmon_dir, 350 const char *sysfs_name, const char *name) 351 { 352 char buf[64]; 353 struct hwmon_pmu *hwm; 354 __u32 type = PERF_PMU_TYPE_HWMON_START + strtoul(sysfs_name + 5, NULL, 10); 355 356 if (type > PERF_PMU_TYPE_HWMON_END) { 357 pr_err("Unable to encode hwmon type from %s in valid PMU type\n", sysfs_name); 358 return NULL; 359 } 360 361 snprintf(buf, sizeof(buf), "hwmon_%s", name); 362 fix_name(buf + 6); 363 364 hwm = zalloc(sizeof(*hwm)); 365 if (!hwm) 366 return NULL; 367 368 if (perf_pmu__init(&hwm->pmu, type, buf) != 0) { 369 perf_pmu__delete(&hwm->pmu); 370 return NULL; 371 } 372 373 hwm->hwmon_dir = strdup(hwmon_dir); 374 if (!hwm->hwmon_dir) { 375 perf_pmu__delete(&hwm->pmu); 376 return NULL; 377 } 378 hwm->pmu.alias_name = strdup(sysfs_name); 379 if (!hwm->pmu.alias_name) { 380 perf_pmu__delete(&hwm->pmu); 381 return NULL; 382 } 383 hwm->pmu.cpus = perf_cpu_map__new_int(0); 384 if (!hwm->pmu.cpus) { 385 perf_pmu__delete(&hwm->pmu); 386 return NULL; 387 } 388 INIT_LIST_HEAD(&hwm->pmu.format); 389 INIT_LIST_HEAD(&hwm->pmu.caps); 390 hashmap__init(&hwm->events, hwmon_pmu__event_hashmap_hash, 391 hwmon_pmu__event_hashmap_equal, /*ctx=*/NULL); 392 393 list_add_tail(&hwm->pmu.list, pmus); 394 return &hwm->pmu; 395 } 396 397 void hwmon_pmu__exit(struct perf_pmu *pmu) 398 { 399 struct hwmon_pmu *hwm = container_of(pmu, struct hwmon_pmu, pmu); 400 struct hashmap_entry *cur, *tmp; 401 size_t bkt; 402 403 hashmap__for_each_entry_safe((&hwm->events), cur, tmp, bkt) { 404 struct hwmon_pmu_event_value *value = cur->pvalue; 405 406 zfree(&value->label); 407 zfree(&value->name); 408 free(value); 409 } 410 hashmap__clear(&hwm->events); 411 zfree(&hwm->hwmon_dir); 412 } 413 414 static size_t hwmon_pmu__describe_items(struct hwmon_pmu *hwm, char *out_buf, size_t out_buf_len, 415 union hwmon_pmu_event_key key, 416 const unsigned long *items, bool is_alarm) 417 { 418 size_t bit; 419 char buf[64]; 420 size_t len = 0; 421 int dir = open(hwm->hwmon_dir, O_CLOEXEC | O_DIRECTORY | O_RDONLY); 422 423 if (dir < 0) 424 return 0; 425 426 for_each_set_bit(bit, items, HWMON_ITEM__MAX) { 427 int fd; 428 429 if (bit == HWMON_ITEM_LABEL || bit == HWMON_ITEM_INPUT) 430 continue; 431 432 snprintf(buf, sizeof(buf), "%s%d_%s%s", 433 hwmon_type_strs[key.type], 434 key.num, 435 hwmon_item_strs[bit], 436 is_alarm ? "_alarm" : ""); 437 fd = openat(dir, buf, O_RDONLY); 438 if (fd >= 0) { 439 ssize_t read_len = read(fd, buf, sizeof(buf) - 1); 440 441 while (read_len > 0 && buf[read_len - 1] == '\n') 442 read_len--; 443 444 if (read_len > 0) { 445 long long val; 446 447 buf[read_len] = '\0'; 448 val = strtoll(buf, /*endptr=*/NULL, 10); 449 len += scnprintf(out_buf + len, out_buf_len - len, "%s%s%s=%g%s", 450 len == 0 ? " " : ", ", 451 hwmon_item_strs[bit], 452 is_alarm ? "_alarm" : "", 453 (double)val / 1000.0, 454 hwmon_units[key.type]); 455 } 456 close(fd); 457 } 458 } 459 close(dir); 460 return len; 461 } 462 463 int hwmon_pmu__for_each_event(struct perf_pmu *pmu, void *state, pmu_event_callback cb) 464 { 465 struct hwmon_pmu *hwm = container_of(pmu, struct hwmon_pmu, pmu); 466 struct hashmap_entry *cur; 467 size_t bkt; 468 469 if (hwmon_pmu__read_events(hwm)) 470 return false; 471 472 hashmap__for_each_entry((&hwm->events), cur, bkt) { 473 static const char *const hwmon_scale_units[HWMON_TYPE_MAX] = { 474 NULL, 475 "0.001V", /* cpu */ 476 "0.001A", /* curr */ 477 "0.001J", /* energy */ 478 "1rpm", /* fan */ 479 "0.001%", /* humidity */ 480 "0.001V", /* in */ 481 NULL, /* intrusion */ 482 "0.001W", /* power */ 483 "1Hz", /* pwm */ 484 "0.001'C", /* temp */ 485 }; 486 static const char *const hwmon_desc[HWMON_TYPE_MAX] = { 487 NULL, 488 "CPU core reference voltage", /* cpu */ 489 "Current", /* curr */ 490 "Cumulative energy use", /* energy */ 491 "Fan", /* fan */ 492 "Humidity", /* humidity */ 493 "Voltage", /* in */ 494 "Chassis intrusion detection", /* intrusion */ 495 "Power use", /* power */ 496 "Pulse width modulation fan control", /* pwm */ 497 "Temperature", /* temp */ 498 }; 499 char alias_buf[64]; 500 char desc_buf[256]; 501 char encoding_buf[128]; 502 union hwmon_pmu_event_key key = { 503 .type_and_num = cur->key, 504 }; 505 struct hwmon_pmu_event_value *value = cur->pvalue; 506 struct pmu_event_info info = { 507 .pmu = pmu, 508 .name = value->name, 509 .alias = alias_buf, 510 .scale_unit = hwmon_scale_units[key.type], 511 .desc = desc_buf, 512 .long_desc = NULL, 513 .encoding_desc = encoding_buf, 514 .topic = "hwmon", 515 .pmu_name = pmu->name, 516 .event_type_desc = "Hwmon event", 517 }; 518 int ret; 519 size_t len; 520 521 scnprintf(alias_buf, sizeof(alias_buf), "%s%d", 522 hwmon_type_strs[key.type], key.num); 523 if (!info.name) { 524 info.name = info.alias; 525 info.alias = NULL; 526 } 527 528 len = scnprintf(desc_buf, sizeof(desc_buf), "%s in unit %s named %s.", 529 hwmon_desc[key.type], 530 pmu->name + 6, 531 value->label ?: info.name); 532 533 len += hwmon_pmu__describe_items(hwm, desc_buf + len, sizeof(desc_buf) - len, 534 key, value->items, /*is_alarm=*/false); 535 536 len += hwmon_pmu__describe_items(hwm, desc_buf + len, sizeof(desc_buf) - len, 537 key, value->alarm_items, /*is_alarm=*/true); 538 539 snprintf(encoding_buf, sizeof(encoding_buf), "%s/config=0x%lx/", 540 pmu->name, cur->key); 541 542 ret = cb(state, &info); 543 if (ret) 544 return ret; 545 } 546 return 0; 547 } 548 549 size_t hwmon_pmu__num_events(struct perf_pmu *pmu) 550 { 551 struct hwmon_pmu *hwm = container_of(pmu, struct hwmon_pmu, pmu); 552 553 hwmon_pmu__read_events(hwm); 554 return hashmap__size(&hwm->events); 555 } 556 557 bool hwmon_pmu__have_event(struct perf_pmu *pmu, const char *name) 558 { 559 struct hwmon_pmu *hwm = container_of(pmu, struct hwmon_pmu, pmu); 560 enum hwmon_type type; 561 int number; 562 union hwmon_pmu_event_key key = { .type_and_num = 0 }; 563 struct hashmap_entry *cur; 564 size_t bkt; 565 566 if (!parse_hwmon_filename(name, &type, &number, /*item=*/NULL, /*is_alarm=*/NULL)) 567 return false; 568 569 if (hwmon_pmu__read_events(hwm)) 570 return false; 571 572 key.type = type; 573 key.num = number; 574 if (hashmap_find(&hwm->events, key.type_and_num, /*value=*/NULL)) 575 return true; 576 if (key.num != -1) 577 return false; 578 /* Item is of form <type>_ which means we should match <type>_<label>. */ 579 hashmap__for_each_entry((&hwm->events), cur, bkt) { 580 struct hwmon_pmu_event_value *value = cur->pvalue; 581 582 key.type_and_num = cur->key; 583 if (key.type == type && value->name && !strcasecmp(name, value->name)) 584 return true; 585 } 586 return false; 587 } 588 589 static int hwmon_pmu__config_term(const struct hwmon_pmu *hwm, 590 struct perf_event_attr *attr, 591 struct parse_events_term *term, 592 struct parse_events_error *err) 593 { 594 if (term->type_term == PARSE_EVENTS__TERM_TYPE_USER) { 595 enum hwmon_type type; 596 int number; 597 598 if (parse_hwmon_filename(term->config, &type, &number, 599 /*item=*/NULL, /*is_alarm=*/NULL)) { 600 if (number == -1) { 601 /* 602 * Item is of form <type>_ which means we should 603 * match <type>_<label>. 604 */ 605 struct hashmap_entry *cur; 606 size_t bkt; 607 608 attr->config = 0; 609 hashmap__for_each_entry((&hwm->events), cur, bkt) { 610 union hwmon_pmu_event_key key = { 611 .type_and_num = cur->key, 612 }; 613 struct hwmon_pmu_event_value *value = cur->pvalue; 614 615 if (key.type == type && value->name && 616 !strcasecmp(term->config, value->name)) { 617 attr->config = key.type_and_num; 618 break; 619 } 620 } 621 if (attr->config == 0) 622 return -EINVAL; 623 } else { 624 union hwmon_pmu_event_key key = { 625 .type_and_num = 0, 626 }; 627 628 key.type = type; 629 key.num = number; 630 attr->config = key.type_and_num; 631 } 632 return 0; 633 } 634 } 635 if (err) { 636 char *err_str; 637 638 parse_events_error__handle(err, term->err_val, 639 asprintf(&err_str, 640 "unexpected hwmon event term (%s) %s", 641 parse_events__term_type_str(term->type_term), 642 term->config) < 0 643 ? strdup("unexpected hwmon event term") 644 : err_str, 645 NULL); 646 } 647 return -EINVAL; 648 } 649 650 int hwmon_pmu__config_terms(const struct perf_pmu *pmu, 651 struct perf_event_attr *attr, 652 struct parse_events_terms *terms, 653 struct parse_events_error *err) 654 { 655 struct hwmon_pmu *hwm = container_of(pmu, struct hwmon_pmu, pmu); 656 struct parse_events_term *term; 657 int ret; 658 659 ret = hwmon_pmu__read_events(hwm); 660 if (ret) 661 return ret; 662 663 list_for_each_entry(term, &terms->terms, list) { 664 if (hwmon_pmu__config_term(hwm, attr, term, err)) 665 return -EINVAL; 666 } 667 668 return 0; 669 670 } 671 672 int hwmon_pmu__check_alias(struct parse_events_terms *terms, struct perf_pmu_info *info, 673 struct parse_events_error *err) 674 { 675 struct parse_events_term *term = 676 list_first_entry(&terms->terms, struct parse_events_term, list); 677 678 if (term->type_term == PARSE_EVENTS__TERM_TYPE_USER) { 679 enum hwmon_type type; 680 int number; 681 682 if (parse_hwmon_filename(term->config, &type, &number, 683 /*item=*/NULL, /*is_alarm=*/NULL)) { 684 info->unit = hwmon_units[type]; 685 if (type == HWMON_TYPE_FAN || type == HWMON_TYPE_PWM || 686 type == HWMON_TYPE_INTRUSION) 687 info->scale = 1; 688 else 689 info->scale = 0.001; 690 } 691 return 0; 692 } 693 if (err) { 694 char *err_str; 695 696 parse_events_error__handle(err, term->err_val, 697 asprintf(&err_str, 698 "unexpected hwmon event term (%s) %s", 699 parse_events__term_type_str(term->type_term), 700 term->config) < 0 701 ? strdup("unexpected hwmon event term") 702 : err_str, 703 NULL); 704 } 705 return -EINVAL; 706 } 707 708 int perf_pmus__read_hwmon_pmus(struct list_head *pmus) 709 { 710 char *line = NULL; 711 struct io_dirent64 *class_hwmon_ent; 712 struct io_dir class_hwmon_dir; 713 char buf[PATH_MAX]; 714 const char *sysfs = sysfs__mountpoint(); 715 716 if (!sysfs) 717 return 0; 718 719 scnprintf(buf, sizeof(buf), "%s/class/hwmon/", sysfs); 720 io_dir__init(&class_hwmon_dir, open(buf, O_CLOEXEC | O_DIRECTORY | O_RDONLY)); 721 722 if (class_hwmon_dir.dirfd < 0) 723 return 0; 724 725 while ((class_hwmon_ent = io_dir__readdir(&class_hwmon_dir)) != NULL) { 726 size_t line_len; 727 int hwmon_dir, name_fd; 728 struct io io; 729 char buf2[128]; 730 731 if (class_hwmon_ent->d_type != DT_LNK) 732 continue; 733 734 scnprintf(buf, sizeof(buf), "%s/class/hwmon/%s", sysfs, class_hwmon_ent->d_name); 735 hwmon_dir = open(buf, O_DIRECTORY); 736 if (hwmon_dir == -1) { 737 pr_debug("hwmon_pmu: not a directory: '%s/class/hwmon/%s'\n", 738 sysfs, class_hwmon_ent->d_name); 739 continue; 740 } 741 name_fd = openat(hwmon_dir, "name", O_RDONLY); 742 if (name_fd == -1) { 743 pr_debug("hwmon_pmu: failure to open '%s/class/hwmon/%s/name'\n", 744 sysfs, class_hwmon_ent->d_name); 745 close(hwmon_dir); 746 continue; 747 } 748 io__init(&io, name_fd, buf2, sizeof(buf2)); 749 if (io__getline(&io, &line, &line_len) > 0 && line[line_len - 1] == '\n') 750 line[line_len - 1] = '\0'; 751 hwmon_pmu__new(pmus, buf, class_hwmon_ent->d_name, line); 752 close(name_fd); 753 close(hwmon_dir); 754 } 755 free(line); 756 close(class_hwmon_dir.dirfd); 757 return 0; 758 } 759 760 #define FD(e, x, y) (*(int *)xyarray__entry(e->core.fd, x, y)) 761 762 int evsel__hwmon_pmu_open(struct evsel *evsel, 763 struct perf_thread_map *threads, 764 int start_cpu_map_idx, int end_cpu_map_idx) 765 { 766 struct hwmon_pmu *hwm = container_of(evsel->pmu, struct hwmon_pmu, pmu); 767 union hwmon_pmu_event_key key = { 768 .type_and_num = evsel->core.attr.config, 769 }; 770 int idx = 0, thread = 0, nthreads, err = 0; 771 int dir = open(hwm->hwmon_dir, O_CLOEXEC | O_DIRECTORY | O_RDONLY); 772 773 if (dir < 0) 774 return -errno; 775 776 nthreads = perf_thread_map__nr(threads); 777 for (idx = start_cpu_map_idx; idx < end_cpu_map_idx; idx++) { 778 for (thread = 0; thread < nthreads; thread++) { 779 char buf[64]; 780 int fd; 781 782 snprintf(buf, sizeof(buf), "%s%d_input", 783 hwmon_type_strs[key.type], key.num); 784 785 fd = openat(dir, buf, O_RDONLY); 786 FD(evsel, idx, thread) = fd; 787 if (fd < 0) { 788 err = -errno; 789 goto out_close; 790 } 791 } 792 } 793 close(dir); 794 return 0; 795 out_close: 796 if (err) 797 threads->err_thread = thread; 798 799 do { 800 while (--thread >= 0) { 801 if (FD(evsel, idx, thread) >= 0) 802 close(FD(evsel, idx, thread)); 803 FD(evsel, idx, thread) = -1; 804 } 805 thread = nthreads; 806 } while (--idx >= 0); 807 close(dir); 808 return err; 809 } 810 811 int evsel__hwmon_pmu_read(struct evsel *evsel, int cpu_map_idx, int thread) 812 { 813 char buf[32]; 814 int fd; 815 ssize_t len; 816 struct perf_counts_values *count, *old_count = NULL; 817 818 if (evsel->prev_raw_counts) 819 old_count = perf_counts(evsel->prev_raw_counts, cpu_map_idx, thread); 820 821 count = perf_counts(evsel->counts, cpu_map_idx, thread); 822 fd = FD(evsel, cpu_map_idx, thread); 823 len = pread(fd, buf, sizeof(buf) - 1, 0); 824 if (len <= 0) { 825 count->lost++; 826 return -EINVAL; 827 } 828 buf[len] = '\0'; 829 if (old_count) { 830 count->val = old_count->val + strtoll(buf, NULL, 10); 831 count->run = old_count->run + 1; 832 count->ena = old_count->ena + 1; 833 } else { 834 count->val = strtoll(buf, NULL, 10); 835 count->run++; 836 count->ena++; 837 } 838 return 0; 839 } 840