Merge tag 'trace-printf-v6.13' of git://git.kernel.org/pub/scm/linux/kernel/git/trace...
[drm/drm-misc.git] / tools / perf / util / hwmon_pmu.c
blobe61429b38ba7bdfbf6fbe0ab5c7ed53c419fa7a5
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>
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",
39 #define LONGEST_HWMON_TYPE_STR "intrusion"
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",
88 #define LONGEST_HWMON_ITEM_STR "average_interval_max"
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 */
104 struct hwmon_pmu {
105 struct perf_pmu pmu;
106 struct hashmap events;
107 int hwmon_dir_fd;
111 * union hwmon_pmu_event_key: Key for hwmon_pmu->events as such each key
112 * represents an event.
114 * Related hwmon files start <type><number> that this key represents.
116 union hwmon_pmu_event_key {
117 long type_and_num;
118 struct {
119 int num :16;
120 enum hwmon_type type :8;
125 * struct hwmon_pmu_event_value: Value in hwmon_pmu->events.
127 * Hwmon files are of the form <type><number>_<item> and may have a suffix
128 * _alarm.
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;
141 bool perf_pmu__is_hwmon(const struct perf_pmu *pmu)
143 return pmu && pmu->type >= PERF_PMU_TYPE_HWMON_START &&
144 pmu->type <= PERF_PMU_TYPE_HWMON_END;
147 bool evsel__is_hwmon(const struct evsel *evsel)
149 return perf_pmu__is_hwmon(evsel->pmu);
152 static size_t hwmon_pmu__event_hashmap_hash(long key, void *ctx __maybe_unused)
154 return ((union hwmon_pmu_event_key)key).type_and_num;
157 static bool hwmon_pmu__event_hashmap_equal(long key1, long key2, void *ctx __maybe_unused)
159 return ((union hwmon_pmu_event_key)key1).type_and_num ==
160 ((union hwmon_pmu_event_key)key2).type_and_num;
163 static int hwmon_strcmp(const void *a, const void *b)
165 const char *sa = a;
166 const char * const *sb = b;
168 return strcmp(sa, *sb);
171 bool parse_hwmon_filename(const char *filename,
172 enum hwmon_type *type,
173 int *number,
174 enum hwmon_item *item,
175 bool *alarm)
177 char fn_type[24];
178 const char **elem;
179 const char *fn_item = NULL;
180 size_t fn_item_len;
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;
192 if (fn_type[i] == '_') {
193 fn_type[i] = '\0';
194 *number = -1;
195 fn_item = &filename[i + 1];
196 break;
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;
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;
211 *type = elem - &hwmon_type_strs[0];
212 if (!item)
213 return true;
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;
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;
230 *item = elem - &hwmon_item_strs[0];
231 return true;
234 static void fix_name(char *p)
236 char *s = strchr(p, '\n');
238 if (s)
239 *s = '\0';
241 while (*p != '\0') {
242 if (strchr(" :,/\n\t", *p))
243 *p = '_';
244 else
245 *p = tolower(*p);
246 p++;
250 static int hwmon_pmu__read_events(struct hwmon_pmu *pmu)
252 DIR *dir;
253 struct dirent *ent;
254 int dup_fd, err = 0;
255 struct hashmap_entry *cur, *tmp;
256 size_t bkt;
258 if (pmu->pmu.sysfs_aliases_loaded)
259 return 0;
261 /* Use a dup-ed fd as closedir will close it. */
262 dup_fd = dup(pmu->hwmon_dir_fd);
263 if (dup_fd == -1)
264 return -ENOMEM;
266 dir = fdopendir(dup_fd);
267 if (!dir) {
268 close(dup_fd);
269 return -ENOMEM;
272 while ((ent = readdir(dir)) != NULL) {
273 enum hwmon_type type;
274 int number;
275 enum hwmon_item item;
276 bool alarm;
277 union hwmon_pmu_event_key key = { .type_and_num = 0 };
278 struct hwmon_pmu_event_value *value;
280 if (ent->d_type != DT_REG)
281 continue;
283 if (!parse_hwmon_filename(ent->d_name, &type, &number, &item, &alarm)) {
284 pr_debug3("Not a hwmon file '%s'\n", ent->d_name);
285 continue;
287 key.num = number;
288 key.type = type;
289 if (!hashmap__find(&pmu->events, key.type_and_num, &value)) {
290 value = zalloc(sizeof(*value));
291 if (!value) {
292 err = -ENOMEM;
293 goto err_out;
295 err = hashmap__add(&pmu->events, key.type_and_num, value);
296 if (err) {
297 free(value);
298 err = -ENOMEM;
299 goto err_out;
302 __set_bit(item, alarm ? value->alarm_items : value->items);
303 if (item == HWMON_ITEM_LABEL) {
304 char buf[128];
305 int fd = openat(pmu->hwmon_dir_fd, ent->d_name, O_RDONLY);
306 ssize_t read_len;
308 if (fd < 0)
309 continue;
311 read_len = read(fd, buf, sizeof(buf));
313 while (read_len > 0 && buf[read_len - 1] == '\n')
314 read_len--;
316 if (read_len > 0)
317 buf[read_len] = '\0';
319 if (buf[0] == '\0') {
320 pr_debug("hwmon_pmu: empty label file %s %s\n",
321 pmu->pmu.name, ent->d_name);
322 close(fd);
323 continue;
325 value->label = strdup(buf);
326 if (!value->label) {
327 pr_debug("hwmon_pmu: memory allocation failure\n");
328 close(fd);
329 continue;
331 snprintf(buf, sizeof(buf), "%s_%s", hwmon_type_strs[type], value->label);
332 fix_name(buf);
333 value->name = strdup(buf);
334 if (!value->name)
335 pr_debug("hwmon_pmu: memory allocation failure\n");
336 close(fd);
339 hashmap__for_each_entry_safe((&pmu->events), cur, tmp, bkt) {
340 union hwmon_pmu_event_key key = {
341 .type_and_num = cur->key,
343 struct hwmon_pmu_event_value *value = cur->pvalue;
345 if (!test_bit(HWMON_ITEM_INPUT, value->items)) {
346 pr_debug("hwmon_pmu: removing event '%s%d' that has no input file\n",
347 hwmon_type_strs[key.type], key.num);
348 hashmap__delete(&pmu->events, key.type_and_num, &key, &value);
349 zfree(&value->label);
350 zfree(&value->name);
351 free(value);
354 pmu->pmu.sysfs_aliases_loaded = true;
356 err_out:
357 closedir(dir);
358 return err;
361 struct perf_pmu *hwmon_pmu__new(struct list_head *pmus, int hwmon_dir, const char *sysfs_name, const char *name)
363 char buf[32];
364 struct hwmon_pmu *hwm;
366 hwm = zalloc(sizeof(*hwm));
367 if (!hwm)
368 return NULL;
370 hwm->hwmon_dir_fd = hwmon_dir;
371 hwm->pmu.type = PERF_PMU_TYPE_HWMON_START + strtoul(sysfs_name + 5, NULL, 10);
372 if (hwm->pmu.type > PERF_PMU_TYPE_HWMON_END) {
373 pr_err("Unable to encode hwmon type from %s in valid PMU type\n", sysfs_name);
374 goto err_out;
376 snprintf(buf, sizeof(buf), "hwmon_%s", name);
377 fix_name(buf + 6);
378 hwm->pmu.name = strdup(buf);
379 if (!hwm->pmu.name)
380 goto err_out;
381 hwm->pmu.alias_name = strdup(sysfs_name);
382 if (!hwm->pmu.alias_name)
383 goto err_out;
384 hwm->pmu.cpus = perf_cpu_map__new("0");
385 if (!hwm->pmu.cpus)
386 goto err_out;
387 INIT_LIST_HEAD(&hwm->pmu.format);
388 INIT_LIST_HEAD(&hwm->pmu.aliases);
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);
393 list_add_tail(&hwm->pmu.list, pmus);
394 return &hwm->pmu;
395 err_out:
396 free((char *)hwm->pmu.name);
397 free(hwm->pmu.alias_name);
398 free(hwm);
399 close(hwmon_dir);
400 return NULL;
403 void hwmon_pmu__exit(struct perf_pmu *pmu)
405 struct hwmon_pmu *hwm = container_of(pmu, struct hwmon_pmu, pmu);
406 struct hashmap_entry *cur, *tmp;
407 size_t bkt;
409 hashmap__for_each_entry_safe((&hwm->events), cur, tmp, bkt) {
410 struct hwmon_pmu_event_value *value = cur->pvalue;
412 zfree(&value->label);
413 zfree(&value->name);
414 free(value);
416 hashmap__clear(&hwm->events);
417 close(hwm->hwmon_dir_fd);
420 static size_t hwmon_pmu__describe_items(struct hwmon_pmu *hwm, char *out_buf, size_t out_buf_len,
421 union hwmon_pmu_event_key key,
422 const unsigned long *items, bool is_alarm)
424 size_t bit;
425 char buf[64];
426 size_t len = 0;
428 for_each_set_bit(bit, items, HWMON_ITEM__MAX) {
429 int fd;
431 if (bit == HWMON_ITEM_LABEL || bit == HWMON_ITEM_INPUT)
432 continue;
434 snprintf(buf, sizeof(buf), "%s%d_%s%s",
435 hwmon_type_strs[key.type],
436 key.num,
437 hwmon_item_strs[bit],
438 is_alarm ? "_alarm" : "");
439 fd = openat(hwm->hwmon_dir_fd, buf, O_RDONLY);
440 if (fd > 0) {
441 ssize_t read_len = read(fd, buf, sizeof(buf));
443 while (read_len > 0 && buf[read_len - 1] == '\n')
444 read_len--;
446 if (read_len > 0) {
447 long long val;
449 buf[read_len] = '\0';
450 val = strtoll(buf, /*endptr=*/NULL, 10);
451 len += snprintf(out_buf + len, out_buf_len - len, "%s%s%s=%g%s",
452 len == 0 ? " " : ", ",
453 hwmon_item_strs[bit],
454 is_alarm ? "_alarm" : "",
455 (double)val / 1000.0,
456 hwmon_units[key.type]);
458 close(fd);
461 return len;
464 int hwmon_pmu__for_each_event(struct perf_pmu *pmu, void *state, pmu_event_callback cb)
466 struct hwmon_pmu *hwm = container_of(pmu, struct hwmon_pmu, pmu);
467 struct hashmap_entry *cur;
468 size_t bkt;
470 if (hwmon_pmu__read_events(hwm))
471 return false;
473 hashmap__for_each_entry((&hwm->events), cur, bkt) {
474 static const char *const hwmon_scale_units[HWMON_TYPE_MAX] = {
475 NULL,
476 "0.001V", /* cpu */
477 "0.001A", /* curr */
478 "0.001J", /* energy */
479 "1rpm", /* fan */
480 "0.001%", /* humidity */
481 "0.001V", /* in */
482 NULL, /* intrusion */
483 "0.001W", /* power */
484 "1Hz", /* pwm */
485 "0.001'C", /* temp */
487 static const char *const hwmon_desc[HWMON_TYPE_MAX] = {
488 NULL,
489 "CPU core reference voltage", /* cpu */
490 "Current", /* curr */
491 "Cumulative energy use", /* energy */
492 "Fan", /* fan */
493 "Humidity", /* humidity */
494 "Voltage", /* in */
495 "Chassis intrusion detection", /* intrusion */
496 "Power use", /* power */
497 "Pulse width modulation fan control", /* pwm */
498 "Temperature", /* temp */
500 char alias_buf[64];
501 char desc_buf[256];
502 char encoding_buf[128];
503 union hwmon_pmu_event_key key = {
504 .type_and_num = cur->key,
506 struct hwmon_pmu_event_value *value = cur->pvalue;
507 struct pmu_event_info info = {
508 .pmu = pmu,
509 .name = value->name,
510 .alias = alias_buf,
511 .scale_unit = hwmon_scale_units[key.type],
512 .desc = desc_buf,
513 .long_desc = NULL,
514 .encoding_desc = encoding_buf,
515 .topic = "hwmon",
516 .pmu_name = pmu->name,
517 .event_type_desc = "Hwmon event",
519 int ret;
520 size_t len;
522 len = snprintf(alias_buf, sizeof(alias_buf), "%s%d",
523 hwmon_type_strs[key.type], key.num);
524 if (!info.name) {
525 info.name = info.alias;
526 info.alias = NULL;
529 len = snprintf(desc_buf, sizeof(desc_buf), "%s in unit %s named %s.",
530 hwmon_desc[key.type],
531 pmu->name + 6,
532 value->label ?: info.name);
534 len += hwmon_pmu__describe_items(hwm, desc_buf + len, sizeof(desc_buf) - len,
535 key, value->items, /*is_alarm=*/false);
537 len += hwmon_pmu__describe_items(hwm, desc_buf + len, sizeof(desc_buf) - len,
538 key, value->alarm_items, /*is_alarm=*/true);
540 snprintf(encoding_buf, sizeof(encoding_buf), "%s/config=0x%lx/",
541 pmu->name, cur->key);
543 ret = cb(state, &info);
544 if (ret)
545 return ret;
547 return 0;
550 size_t hwmon_pmu__num_events(struct perf_pmu *pmu)
552 struct hwmon_pmu *hwm = container_of(pmu, struct hwmon_pmu, pmu);
554 hwmon_pmu__read_events(hwm);
555 return hashmap__size(&hwm->events);
558 bool hwmon_pmu__have_event(struct perf_pmu *pmu, const char *name)
560 struct hwmon_pmu *hwm = container_of(pmu, struct hwmon_pmu, pmu);
561 enum hwmon_type type;
562 int number;
563 union hwmon_pmu_event_key key = { .type_and_num = 0 };
564 struct hashmap_entry *cur;
565 size_t bkt;
567 if (!parse_hwmon_filename(name, &type, &number, /*item=*/NULL, /*is_alarm=*/NULL))
568 return false;
570 if (hwmon_pmu__read_events(hwm))
571 return false;
573 key.type = type;
574 key.num = number;
575 if (hashmap_find(&hwm->events, key.type_and_num, /*value=*/NULL))
576 return true;
577 if (key.num != -1)
578 return false;
579 /* Item is of form <type>_ which means we should match <type>_<label>. */
580 hashmap__for_each_entry((&hwm->events), cur, bkt) {
581 struct hwmon_pmu_event_value *value = cur->pvalue;
583 key.type_and_num = cur->key;
584 if (key.type == type && value->name && !strcasecmp(name, value->name))
585 return true;
587 return false;
590 static int hwmon_pmu__config_term(const struct hwmon_pmu *hwm,
591 struct perf_event_attr *attr,
592 struct parse_events_term *term,
593 struct parse_events_error *err)
595 if (term->type_term == PARSE_EVENTS__TERM_TYPE_USER) {
596 enum hwmon_type type;
597 int number;
599 if (parse_hwmon_filename(term->config, &type, &number,
600 /*item=*/NULL, /*is_alarm=*/NULL)) {
601 if (number == -1) {
603 * Item is of form <type>_ which means we should
604 * match <type>_<label>.
606 struct hashmap_entry *cur;
607 size_t bkt;
609 attr->config = 0;
610 hashmap__for_each_entry((&hwm->events), cur, bkt) {
611 union hwmon_pmu_event_key key = {
612 .type_and_num = cur->key,
614 struct hwmon_pmu_event_value *value = cur->pvalue;
616 if (key.type == type && value->name &&
617 !strcasecmp(term->config, value->name)) {
618 attr->config = key.type_and_num;
619 break;
622 if (attr->config == 0)
623 return -EINVAL;
624 } else {
625 union hwmon_pmu_event_key key = {
626 .type_and_num = 0,
629 key.type = type;
630 key.num = number;
631 attr->config = key.type_and_num;
633 return 0;
636 if (err) {
637 char *err_str;
639 parse_events_error__handle(err, term->err_val,
640 asprintf(&err_str,
641 "unexpected hwmon event term (%s) %s",
642 parse_events__term_type_str(term->type_term),
643 term->config) < 0
644 ? strdup("unexpected hwmon event term")
645 : err_str,
646 NULL);
648 return -EINVAL;
651 int hwmon_pmu__config_terms(const struct perf_pmu *pmu,
652 struct perf_event_attr *attr,
653 struct parse_events_terms *terms,
654 struct parse_events_error *err)
656 struct hwmon_pmu *hwm = container_of(pmu, struct hwmon_pmu, pmu);
657 struct parse_events_term *term;
658 int ret;
660 ret = hwmon_pmu__read_events(hwm);
661 if (ret)
662 return ret;
664 list_for_each_entry(term, &terms->terms, list) {
665 if (hwmon_pmu__config_term(hwm, attr, term, err))
666 return -EINVAL;
669 return 0;
673 int hwmon_pmu__check_alias(struct parse_events_terms *terms, struct perf_pmu_info *info,
674 struct parse_events_error *err)
676 struct parse_events_term *term =
677 list_first_entry(&terms->terms, struct parse_events_term, list);
679 if (term->type_term == PARSE_EVENTS__TERM_TYPE_USER) {
680 enum hwmon_type type;
681 int number;
683 if (parse_hwmon_filename(term->config, &type, &number,
684 /*item=*/NULL, /*is_alarm=*/NULL)) {
685 info->unit = hwmon_units[type];
686 if (type == HWMON_TYPE_FAN || type == HWMON_TYPE_PWM ||
687 type == HWMON_TYPE_INTRUSION)
688 info->scale = 1;
689 else
690 info->scale = 0.001;
692 return 0;
694 if (err) {
695 char *err_str;
697 parse_events_error__handle(err, term->err_val,
698 asprintf(&err_str,
699 "unexpected hwmon event term (%s) %s",
700 parse_events__term_type_str(term->type_term),
701 term->config) < 0
702 ? strdup("unexpected hwmon event term")
703 : err_str,
704 NULL);
706 return -EINVAL;
709 int perf_pmus__read_hwmon_pmus(struct list_head *pmus)
711 char *line = NULL;
712 DIR *class_hwmon_dir;
713 struct dirent *class_hwmon_ent;
714 char buf[PATH_MAX];
715 const char *sysfs = sysfs__mountpoint();
717 if (!sysfs)
718 return 0;
720 scnprintf(buf, sizeof(buf), "%s/class/hwmon/", sysfs);
721 class_hwmon_dir = opendir(buf);
722 if (!class_hwmon_dir)
723 return 0;
725 while ((class_hwmon_ent = readdir(class_hwmon_dir)) != NULL) {
726 size_t line_len;
727 int hwmon_dir, name_fd;
728 struct io io;
730 if (class_hwmon_ent->d_type != DT_LNK)
731 continue;
733 scnprintf(buf, sizeof(buf), "%s/class/hwmon/%s", sysfs, class_hwmon_ent->d_name);
734 hwmon_dir = open(buf, O_DIRECTORY);
735 if (hwmon_dir == -1) {
736 pr_debug("hwmon_pmu: not a directory: '%s/class/hwmon/%s'\n",
737 sysfs, class_hwmon_ent->d_name);
738 continue;
740 name_fd = openat(hwmon_dir, "name", O_RDONLY);
741 if (name_fd == -1) {
742 pr_debug("hwmon_pmu: failure to open '%s/class/hwmon/%s/name'\n",
743 sysfs, class_hwmon_ent->d_name);
744 close(hwmon_dir);
745 continue;
747 io__init(&io, name_fd, buf, sizeof(buf));
748 io__getline(&io, &line, &line_len);
749 if (line_len > 0 && line[line_len - 1] == '\n')
750 line[line_len - 1] = '\0';
751 hwmon_pmu__new(pmus, hwmon_dir, class_hwmon_ent->d_name, line);
752 close(name_fd);
754 free(line);
755 closedir(class_hwmon_dir);
756 return 0;
759 #define FD(e, x, y) (*(int *)xyarray__entry(e->core.fd, x, y))
761 int evsel__hwmon_pmu_open(struct evsel *evsel,
762 struct perf_thread_map *threads,
763 int start_cpu_map_idx, int end_cpu_map_idx)
765 struct hwmon_pmu *hwm = container_of(evsel->pmu, struct hwmon_pmu, pmu);
766 union hwmon_pmu_event_key key = {
767 .type_and_num = evsel->core.attr.config,
769 int idx = 0, thread = 0, nthreads, err = 0;
771 nthreads = perf_thread_map__nr(threads);
772 for (idx = start_cpu_map_idx; idx < end_cpu_map_idx; idx++) {
773 for (thread = 0; thread < nthreads; thread++) {
774 char buf[64];
775 int fd;
777 snprintf(buf, sizeof(buf), "%s%d_input",
778 hwmon_type_strs[key.type], key.num);
780 fd = openat(hwm->hwmon_dir_fd, buf, O_RDONLY);
781 FD(evsel, idx, thread) = fd;
782 if (fd < 0) {
783 err = -errno;
784 goto out_close;
788 return 0;
789 out_close:
790 if (err)
791 threads->err_thread = thread;
793 do {
794 while (--thread >= 0) {
795 if (FD(evsel, idx, thread) >= 0)
796 close(FD(evsel, idx, thread));
797 FD(evsel, idx, thread) = -1;
799 thread = nthreads;
800 } while (--idx >= 0);
801 return err;
804 int evsel__hwmon_pmu_read(struct evsel *evsel, int cpu_map_idx, int thread)
806 char buf[32];
807 int fd;
808 ssize_t len;
809 struct perf_counts_values *count, *old_count = NULL;
811 if (evsel->prev_raw_counts)
812 old_count = perf_counts(evsel->prev_raw_counts, cpu_map_idx, thread);
814 count = perf_counts(evsel->counts, cpu_map_idx, thread);
815 fd = FD(evsel, cpu_map_idx, thread);
816 len = pread(fd, buf, sizeof(buf), 0);
817 if (len <= 0) {
818 count->lost++;
819 return -EINVAL;
821 buf[len] = '\0';
822 if (old_count) {
823 count->val = old_count->val + strtoll(buf, NULL, 10);
824 count->run = old_count->run + 1;
825 count->ena = old_count->ena + 1;
826 } else {
827 count->val = strtoll(buf, NULL, 10);
828 count->run++;
829 count->ena++;
831 return 0;