1 // SPDX-License-Identifier: GPL-2.0-only
3 * Copyright 2013-2015, Michael Ellerman, IBM Corp.
6 #define _GNU_SOURCE /* For CPU_ZERO etc. */
18 #include <sys/ioctl.h>
20 #include <sys/sysinfo.h>
21 #include <sys/types.h>
22 #include <sys/utsname.h>
24 #include <asm/unistd.h>
25 #include <linux/limits.h>
29 static char auxv[4096];
31 int read_file(const char *path, char *buf, size_t count, size_t *len)
38 fd = open(path, O_RDONLY);
42 rc = read(fd, buf, count);
51 /* Overflow if there are still more bytes after filling the buffer */
53 rc = read(fd, &eof, 1);
68 int read_file_alloc(const char *path, char **buf, size_t *len)
70 size_t read_offset = 0;
71 size_t buffer_len = 0;
76 fd = open(path, O_RDONLY);
81 * We don't use stat & preallocate st_size because some non-files
82 * report 0 file size. Instead just dynamically grow the buffer
88 if (read_offset >= buffer_len / 2) {
91 buffer_len = buffer_len ? buffer_len * 2 : 4096;
92 next_buffer = realloc(buffer, buffer_len);
100 rc = read(fd, buffer + read_offset, buffer_len - read_offset);
126 int write_file(const char *path, const char *buf, size_t count)
132 fd = open(path, O_WRONLY | O_CREAT | O_TRUNC, 0644);
136 rc = write(fd, buf, count);
155 int read_auxv(char *buf, ssize_t buf_size)
159 err = read_file("/proc/self/auxv", buf, buf_size, NULL);
161 perror("Error reading /proc/self/auxv");
168 int read_debugfs_file(const char *subpath, char *buf, size_t count)
170 char path[PATH_MAX] = "/sys/kernel/debug/";
172 strncat(path, subpath, sizeof(path) - strlen(path) - 1);
174 return read_file(path, buf, count, NULL);
177 int write_debugfs_file(const char *subpath, const char *buf, size_t count)
179 char path[PATH_MAX] = "/sys/kernel/debug/";
181 strncat(path, subpath, sizeof(path) - strlen(path) - 1);
183 return write_file(path, buf, count);
186 static int validate_int_parse(const char *buffer, size_t count, char *end)
190 /* Require at least one digit */
196 /* Require all remaining characters be whitespace-ish */
197 for (; end < buffer + count; end++) {
201 if (*end != ' ' && *end != '\n') {
212 static int parse_bounded_int(const char *buffer, size_t count, intmax_t *result,
213 int base, intmax_t min, intmax_t max)
219 *result = strtoimax(buffer, &end, base);
224 err = validate_int_parse(buffer, count, end);
228 if (*result < min || *result > max)
236 static int parse_bounded_uint(const char *buffer, size_t count, uintmax_t *result,
237 int base, uintmax_t max)
243 *result = strtoumax(buffer, &end, base);
248 err = validate_int_parse(buffer, count, end);
260 int parse_intmax(const char *buffer, size_t count, intmax_t *result, int base)
262 return parse_bounded_int(buffer, count, result, base, INTMAX_MIN, INTMAX_MAX);
265 int parse_uintmax(const char *buffer, size_t count, uintmax_t *result, int base)
267 return parse_bounded_uint(buffer, count, result, base, UINTMAX_MAX);
270 int parse_int(const char *buffer, size_t count, int *result, int base)
273 int err = parse_bounded_int(buffer, count, &parsed, base, INT_MIN, INT_MAX);
279 int parse_uint(const char *buffer, size_t count, unsigned int *result, int base)
282 int err = parse_bounded_uint(buffer, count, &parsed, base, UINT_MAX);
288 int parse_long(const char *buffer, size_t count, long *result, int base)
291 int err = parse_bounded_int(buffer, count, &parsed, base, LONG_MIN, LONG_MAX);
297 int parse_ulong(const char *buffer, size_t count, unsigned long *result, int base)
300 int err = parse_bounded_uint(buffer, count, &parsed, base, ULONG_MAX);
306 int read_long(const char *path, long *result, int base)
309 char buffer[32] = {0};
311 err = read_file(path, buffer, sizeof(buffer) - 1, NULL);
315 return parse_long(buffer, sizeof(buffer), result, base);
318 int read_ulong(const char *path, unsigned long *result, int base)
321 char buffer[32] = {0};
323 err = read_file(path, buffer, sizeof(buffer) - 1, NULL);
327 return parse_ulong(buffer, sizeof(buffer), result, base);
330 int write_long(const char *path, long result, int base)
336 /* Decimal only for now: no format specifier for signed hex values */
342 len = snprintf(buffer, sizeof(buffer), "%ld", result);
343 if (len < 0 || len >= sizeof(buffer)) {
348 err = write_file(path, buffer, len);
355 int write_ulong(const char *path, unsigned long result, int base)
374 len = snprintf(buffer, sizeof(buffer), fmt, result);
375 if (len < 0 || len >= sizeof(buffer)) {
380 err = write_file(path, buffer, len);
387 void *find_auxv_entry(int type, char *auxv)
391 p = (ElfW(auxv_t) *)auxv;
393 while (p->a_type != AT_NULL) {
394 if (p->a_type == type)
403 void *get_auxv_entry(int type)
407 if (read_auxv(auxv, sizeof(auxv)))
410 p = find_auxv_entry(type, auxv);
412 return (void *)p->a_un.a_val;
417 int pick_online_cpu(void)
423 ncpus = get_nprocs_conf();
424 size = CPU_ALLOC_SIZE(ncpus);
425 mask = CPU_ALLOC(ncpus);
431 CPU_ZERO_S(size, mask);
433 if (sched_getaffinity(0, size, mask)) {
434 perror("sched_getaffinity");
438 /* We prefer a primary thread, but skip 0 */
439 for (cpu = 8; cpu < ncpus; cpu += 8)
440 if (CPU_ISSET_S(cpu, size, mask))
443 /* Search for anything, but in reverse */
444 for (cpu = ncpus - 1; cpu >= 0; cpu--)
445 if (CPU_ISSET_S(cpu, size, mask))
448 printf("No cpus in affinity mask?!\n");
455 int bind_to_cpu(int cpu)
460 if (cpu == BIND_CPU_ANY) {
461 cpu = pick_online_cpu();
466 printf("Binding to cpu %d\n", cpu);
471 err = sched_setaffinity(0, sizeof(mask), &mask);
478 bool is_ppc64le(void)
490 return strcmp(uts.machine, "ppc64le") == 0;
493 int read_sysfs_file(char *fpath, char *result, size_t result_size)
495 char path[PATH_MAX] = "/sys/";
497 strncat(path, fpath, PATH_MAX - strlen(path) - 1);
499 return read_file(path, result, result_size, NULL);
502 int read_debugfs_int(const char *debugfs_file, int *result)
505 char value[16] = {0};
507 err = read_debugfs_file(debugfs_file, value, sizeof(value) - 1);
511 return parse_int(value, sizeof(value), result, 10);
514 int write_debugfs_int(const char *debugfs_file, int result)
518 snprintf(value, 16, "%d", result);
520 return write_debugfs_file(debugfs_file, value, strlen(value));
523 static long perf_event_open(struct perf_event_attr *hw_event, pid_t pid,
524 int cpu, int group_fd, unsigned long flags)
526 return syscall(__NR_perf_event_open, hw_event, pid, cpu,
530 static void perf_event_attr_init(struct perf_event_attr *event_attr,
532 unsigned long config)
534 memset(event_attr, 0, sizeof(*event_attr));
536 event_attr->type = type;
537 event_attr->size = sizeof(struct perf_event_attr);
538 event_attr->config = config;
539 event_attr->read_format = PERF_FORMAT_GROUP;
540 event_attr->disabled = 1;
541 event_attr->exclude_kernel = 1;
542 event_attr->exclude_hv = 1;
543 event_attr->exclude_guest = 1;
546 int perf_event_open_counter(unsigned int type,
547 unsigned long config, int group_fd)
550 struct perf_event_attr event_attr;
552 perf_event_attr_init(&event_attr, type, config);
554 fd = perf_event_open(&event_attr, 0, -1, group_fd, 0);
557 perror("perf_event_open() failed");
562 int perf_event_enable(int fd)
564 if (ioctl(fd, PERF_EVENT_IOC_ENABLE, PERF_IOC_FLAG_GROUP) == -1) {
565 perror("error while enabling perf events");
572 int perf_event_disable(int fd)
574 if (ioctl(fd, PERF_EVENT_IOC_DISABLE, PERF_IOC_FLAG_GROUP) == -1) {
575 perror("error disabling perf events");
582 int perf_event_reset(int fd)
584 if (ioctl(fd, PERF_EVENT_IOC_RESET, PERF_IOC_FLAG_GROUP) == -1) {
585 perror("error resetting perf events");
592 int using_hash_mmu(bool *using_hash)
598 f = fopen("/proc/cpuinfo", "r");
602 while (fgets(line, sizeof(line), f) != NULL) {
603 if (!strcmp(line, "MMU : Hash\n") ||
604 !strcmp(line, "platform : Cell\n") ||
605 !strcmp(line, "platform : PowerMac\n")) {
610 if (strcmp(line, "MMU : Radix\n") == 0) {
622 struct sigaction push_signal_handler(int sig, void (*fn)(int, siginfo_t *, void *))
625 struct sigaction old_handler;
627 sa.sa_sigaction = fn;
628 sigemptyset(&sa.sa_mask);
629 sa.sa_flags = SA_SIGINFO;
630 FAIL_IF_EXIT_MSG(sigaction(sig, &sa, &old_handler),
631 "failed to push signal handler");
636 struct sigaction pop_signal_handler(int sig, struct sigaction old_handler)
638 struct sigaction popped;
640 FAIL_IF_EXIT_MSG(sigaction(sig, &old_handler, &popped),
641 "failed to pop signal handler");