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