1 // SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause)
8 #include <internal/xyarray.h>
9 #include <internal/threadmap.h>
10 #include <perf/threadmap.h>
11 #include <sys/types.h>
19 #include <api/fs/fs.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
] = {
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
] = {
50 "average_interval_max",
51 "average_interval_min",
88 #define LONGEST_HWMON_ITEM_STR "average_interval_max"
90 static const char *const hwmon_units
[HWMON_TYPE_MAX
] = {
106 struct hashmap events
;
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
{
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
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. */
137 /** @name: name computed from label of the form <type>_<label>. */
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
)
166 const char * const *sb
= b
;
168 return strcmp(sa
, *sb
);
171 bool parse_hwmon_filename(const char *filename
,
172 enum hwmon_type
*type
,
174 enum hwmon_item
*item
,
179 const char *fn_item
= NULL
;
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') {
187 *number
= strtoul(&filename
[i
], (char **)&fn_item
, 10);
192 if (fn_type
[i
] == '_') {
195 fn_item
= &filename
[i
+ 1];
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
);
203 elem
= bsearch(&fn_type
, hwmon_type_strs
+ 1, ARRAY_SIZE(hwmon_type_strs
) - 1,
204 sizeof(hwmon_type_strs
[0]), hwmon_strcmp
);
206 pr_debug3("hwmon_pmu: not a hwmon type '%s' in file name '%s'\n",
211 *type
= elem
- &hwmon_type_strs
[0];
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);
223 elem
= bsearch(fn_item
, hwmon_item_strs
+ 1, ARRAY_SIZE(hwmon_item_strs
) - 1,
224 sizeof(hwmon_item_strs
[0]), hwmon_strcmp
);
226 pr_debug3("hwmon_pmu: not a hwmon item '%s' in file name '%s'\n",
230 *item
= elem
- &hwmon_item_strs
[0];
234 static void fix_name(char *p
)
236 char *s
= strchr(p
, '\n');
242 if (strchr(" :,/\n\t", *p
))
250 static int hwmon_pmu__read_events(struct hwmon_pmu
*pmu
)
255 struct hashmap_entry
*cur
, *tmp
;
258 if (pmu
->pmu
.sysfs_aliases_loaded
)
261 /* Use a dup-ed fd as closedir will close it. */
262 dup_fd
= dup(pmu
->hwmon_dir_fd
);
266 dir
= fdopendir(dup_fd
);
272 while ((ent
= readdir(dir
)) != NULL
) {
273 enum hwmon_type type
;
275 enum hwmon_item item
;
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
)
283 if (!parse_hwmon_filename(ent
->d_name
, &type
, &number
, &item
, &alarm
)) {
284 pr_debug3("Not a hwmon file '%s'\n", ent
->d_name
);
289 if (!hashmap__find(&pmu
->events
, key
.type_and_num
, &value
)) {
290 value
= zalloc(sizeof(*value
));
295 err
= hashmap__add(&pmu
->events
, key
.type_and_num
, value
);
302 __set_bit(item
, alarm
? value
->alarm_items
: value
->items
);
303 if (item
== HWMON_ITEM_LABEL
) {
305 int fd
= openat(pmu
->hwmon_dir_fd
, ent
->d_name
, O_RDONLY
);
311 read_len
= read(fd
, buf
, sizeof(buf
));
313 while (read_len
> 0 && buf
[read_len
- 1] == '\n')
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
);
325 value
->label
= strdup(buf
);
327 pr_debug("hwmon_pmu: memory allocation failure\n");
331 snprintf(buf
, sizeof(buf
), "%s_%s", hwmon_type_strs
[type
], value
->label
);
333 value
->name
= strdup(buf
);
335 pr_debug("hwmon_pmu: memory allocation failure\n");
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
);
354 pmu
->pmu
.sysfs_aliases_loaded
= true;
361 struct perf_pmu
*hwmon_pmu__new(struct list_head
*pmus
, int hwmon_dir
, const char *sysfs_name
, const char *name
)
364 struct hwmon_pmu
*hwm
;
366 hwm
= zalloc(sizeof(*hwm
));
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
);
376 snprintf(buf
, sizeof(buf
), "hwmon_%s", name
);
378 hwm
->pmu
.name
= strdup(buf
);
381 hwm
->pmu
.alias_name
= strdup(sysfs_name
);
382 if (!hwm
->pmu
.alias_name
)
384 hwm
->pmu
.cpus
= perf_cpu_map__new("0");
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
);
396 free((char *)hwm
->pmu
.name
);
397 free(hwm
->pmu
.alias_name
);
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
;
409 hashmap__for_each_entry_safe((&hwm
->events
), cur
, tmp
, bkt
) {
410 struct hwmon_pmu_event_value
*value
= cur
->pvalue
;
412 zfree(&value
->label
);
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
)
428 for_each_set_bit(bit
, items
, HWMON_ITEM__MAX
) {
431 if (bit
== HWMON_ITEM_LABEL
|| bit
== HWMON_ITEM_INPUT
)
434 snprintf(buf
, sizeof(buf
), "%s%d_%s%s",
435 hwmon_type_strs
[key
.type
],
437 hwmon_item_strs
[bit
],
438 is_alarm
? "_alarm" : "");
439 fd
= openat(hwm
->hwmon_dir_fd
, buf
, O_RDONLY
);
441 ssize_t read_len
= read(fd
, buf
, sizeof(buf
));
443 while (read_len
> 0 && buf
[read_len
- 1] == '\n')
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
]);
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
;
470 if (hwmon_pmu__read_events(hwm
))
473 hashmap__for_each_entry((&hwm
->events
), cur
, bkt
) {
474 static const char *const hwmon_scale_units
[HWMON_TYPE_MAX
] = {
478 "0.001J", /* energy */
480 "0.001%", /* humidity */
482 NULL
, /* intrusion */
483 "0.001W", /* power */
485 "0.001'C", /* temp */
487 static const char *const hwmon_desc
[HWMON_TYPE_MAX
] = {
489 "CPU core reference voltage", /* cpu */
490 "Current", /* curr */
491 "Cumulative energy use", /* energy */
493 "Humidity", /* humidity */
495 "Chassis intrusion detection", /* intrusion */
496 "Power use", /* power */
497 "Pulse width modulation fan control", /* pwm */
498 "Temperature", /* temp */
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
= {
511 .scale_unit
= hwmon_scale_units
[key
.type
],
514 .encoding_desc
= encoding_buf
,
516 .pmu_name
= pmu
->name
,
517 .event_type_desc
= "Hwmon event",
522 len
= snprintf(alias_buf
, sizeof(alias_buf
), "%s%d",
523 hwmon_type_strs
[key
.type
], key
.num
);
525 info
.name
= info
.alias
;
529 len
= snprintf(desc_buf
, sizeof(desc_buf
), "%s in unit %s named %s.",
530 hwmon_desc
[key
.type
],
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
);
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
;
563 union hwmon_pmu_event_key key
= { .type_and_num
= 0 };
564 struct hashmap_entry
*cur
;
567 if (!parse_hwmon_filename(name
, &type
, &number
, /*item=*/NULL
, /*is_alarm=*/NULL
))
570 if (hwmon_pmu__read_events(hwm
))
575 if (hashmap_find(&hwm
->events
, key
.type_and_num
, /*value=*/NULL
))
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
))
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
;
599 if (parse_hwmon_filename(term
->config
, &type
, &number
,
600 /*item=*/NULL
, /*is_alarm=*/NULL
)) {
603 * Item is of form <type>_ which means we should
604 * match <type>_<label>.
606 struct hashmap_entry
*cur
;
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
;
622 if (attr
->config
== 0)
625 union hwmon_pmu_event_key key
= {
631 attr
->config
= key
.type_and_num
;
639 parse_events_error__handle(err
, term
->err_val
,
641 "unexpected hwmon event term (%s) %s",
642 parse_events__term_type_str(term
->type_term
),
644 ? strdup("unexpected hwmon event term")
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
;
660 ret
= hwmon_pmu__read_events(hwm
);
664 list_for_each_entry(term
, &terms
->terms
, list
) {
665 if (hwmon_pmu__config_term(hwm
, attr
, term
, err
))
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
;
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
)
697 parse_events_error__handle(err
, term
->err_val
,
699 "unexpected hwmon event term (%s) %s",
700 parse_events__term_type_str(term
->type_term
),
702 ? strdup("unexpected hwmon event term")
709 int perf_pmus__read_hwmon_pmus(struct list_head
*pmus
)
712 DIR *class_hwmon_dir
;
713 struct dirent
*class_hwmon_ent
;
715 const char *sysfs
= sysfs__mountpoint();
720 scnprintf(buf
, sizeof(buf
), "%s/class/hwmon/", sysfs
);
721 class_hwmon_dir
= opendir(buf
);
722 if (!class_hwmon_dir
)
725 while ((class_hwmon_ent
= readdir(class_hwmon_dir
)) != NULL
) {
727 int hwmon_dir
, name_fd
;
730 if (class_hwmon_ent
->d_type
!= DT_LNK
)
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
);
740 name_fd
= openat(hwmon_dir
, "name", O_RDONLY
);
742 pr_debug("hwmon_pmu: failure to open '%s/class/hwmon/%s/name'\n",
743 sysfs
, class_hwmon_ent
->d_name
);
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
);
755 closedir(class_hwmon_dir
);
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
++) {
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
;
791 threads
->err_thread
= thread
;
794 while (--thread
>= 0) {
795 if (FD(evsel
, idx
, thread
) >= 0)
796 close(FD(evsel
, idx
, thread
));
797 FD(evsel
, idx
, thread
) = -1;
800 } while (--idx
>= 0);
804 int evsel__hwmon_pmu_read(struct evsel
*evsel
, int cpu_map_idx
, int thread
)
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);
823 count
->val
= old_count
->val
+ strtoll(buf
, NULL
, 10);
824 count
->run
= old_count
->run
+ 1;
825 count
->ena
= old_count
->ena
+ 1;
827 count
->val
= strtoll(buf
, NULL
, 10);