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
perf_pmu__is_hwmon(const struct perf_pmu * pmu)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
evsel__is_hwmon(const struct evsel * evsel)147 bool evsel__is_hwmon(const struct evsel *evsel)
148 {
149 return perf_pmu__is_hwmon(evsel->pmu);
150 }
151
hwmon_pmu__event_hashmap_hash(long key,void * ctx __maybe_unused)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
hwmon_pmu__event_hashmap_equal(long key1,long key2,void * ctx __maybe_unused)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
hwmon_strcmp(const void * a,const void * b)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
parse_hwmon_filename(const char * filename,enum hwmon_type * type,int * number,enum hwmon_item * item,bool * alarm)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
fix_name(char * p)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
hwmon_pmu__read_events(struct hwmon_pmu * pmu)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
hwmon_pmu__new(struct list_head * pmus,int hwmon_dir,const char * sysfs_name,const char * name)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
hwmon_pmu__exit(struct perf_pmu * pmu)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
hwmon_pmu__describe_items(struct hwmon_pmu * hwm,char * out_buf,size_t out_buf_len,union hwmon_pmu_event_key key,const unsigned long * items,bool is_alarm)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
hwmon_pmu__for_each_event(struct perf_pmu * pmu,void * state,pmu_event_callback cb)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
hwmon_pmu__num_events(struct perf_pmu * pmu)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
hwmon_pmu__have_event(struct perf_pmu * pmu,const char * name)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
hwmon_pmu__config_term(const struct hwmon_pmu * hwm,struct perf_event_attr * attr,struct parse_events_term * term,struct parse_events_error * err)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
hwmon_pmu__config_terms(const struct perf_pmu * pmu,struct perf_event_attr * attr,struct parse_events_terms * terms,struct parse_events_error * err)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
hwmon_pmu__check_alias(struct parse_events_terms * terms,struct perf_pmu_info * info,struct parse_events_error * err)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
perf_pmus__read_hwmon_pmus(struct list_head * pmus)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
evsel__hwmon_pmu_open(struct evsel * evsel,struct perf_thread_map * threads,int start_cpu_map_idx,int end_cpu_map_idx)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
evsel__hwmon_pmu_read(struct evsel * evsel,int cpu_map_idx,int thread)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