/* * Copyright (c) 2018 Google, Inc. * * SPDX-License-Identifier: GPL-2.0-or-later */ #define _GNU_SOURCE #include <errno.h> #include <fcntl.h> #include <sched.h> #include <string.h> #include <stdio.h> #include <stdlib.h> #include <time.h> #include <unistd.h> #include "tst_cpu.h" #include "tst_safe_file_ops.h" #include "util.h" void affine(int cpu) { cpu_set_t cpuset; CPU_ZERO(&cpuset); CPU_SET(cpu, &cpuset); ERROR_CHECK(sched_setaffinity(0, sizeof(cpu_set_t), &cpuset)); } /* * Busywait for a certain amount of wallclock time. * If sleep is nonzero, sleep for 1ms between each check. */ void burn(unsigned int usec, int sleep) { unsigned long long now_usec, end_usec; struct timespec ts; if (clock_gettime(CLOCK_MONOTONIC, &ts)) { printf("clock_gettime() reported an error\n"); return; } end_usec = (ts.tv_sec) * USEC_PER_SEC + (ts.tv_nsec / 1000) + usec; while(1) { if (clock_gettime(CLOCK_MONOTONIC, &ts)) { printf("clock_gettime() reported an error\n"); return; } now_usec = ts.tv_sec * USEC_PER_SEC + ts.tv_nsec / 1000; if (now_usec > end_usec) return; if (sleep) usleep(1000); } } #define CAP_STATE_FILE_SIZE 1024 static int read_capacity_sched_domains(int cpu, unsigned int *cap) { char *filebuf, *tmp1, *tmp2; char cap_states_file[100]; int cap_states_fd; int bytes, rv; sprintf(cap_states_file, "/proc/sys/kernel/sched_domain/cpu%d/domain0/group0/energy/cap_states", cpu); cap_states_fd = open(cap_states_file, O_RDONLY); if (cap_states_fd == -1) return -ENOENT; bytes = CAP_STATE_FILE_SIZE; filebuf = calloc(1, CAP_STATE_FILE_SIZE + 1); if (!filebuf) { printf("Failed to calloc buffer for cap_states\n"); return -1; } tmp1 = filebuf; while (bytes) { rv = read(cap_states_fd, tmp1, bytes); if (rv == -1) { printf("Could not read cap_states\n"); return -1; } if (rv == 0) break; tmp1 += rv; bytes -= rv; } if (tmp1 - filebuf == CAP_STATE_FILE_SIZE) { printf("CAP_STATE_FILE_SIZE exhausted, increase\n"); return -1; } tmp1 = strrchr(filebuf, '\t'); if (!tmp1 || tmp1 == filebuf ) { printf("Malformatted cap_states_file (1).\n%s\n", filebuf); return -1; } tmp1 = strrchr(tmp1 - 1, '\t'); if (!tmp1 || tmp1 == filebuf) { printf("Malformatted cap_states_file (2).\n%s\n", filebuf); return -1; } tmp1 = strrchr(tmp1 - 1, '\t'); if (!tmp1 || tmp1 == filebuf) { printf("Malformatted cap_states_file (3).\n%s\n", filebuf); return -1; } /* tmp1 now points to tab after the capacity we want. */ *tmp1 = 0; tmp2 = strrchr(tmp1 - 1, '\t'); if (!tmp2) tmp2 = filebuf; else tmp2++; if (sscanf(tmp2,"%d", cap) != 1) { printf("Failed to parse capacity from cap_states.\n"); return -1; } free(filebuf); if (close(cap_states_fd)) { printf("Failed to close cap_states file.\n"); return -1; } return 0; } static int read_capacity_sysfs(int cpu, unsigned int *cap) { char path[100]; sprintf(path, "/sys/devices/system/cpu/cpu%d/cpu_capacity", cpu); return SAFE_FILE_LINES_SCANF(path, "%u", cap); } static int read_cpu_capacity(int cpu, unsigned int *cap) { int ret = read_capacity_sched_domains(cpu, cap); /* * Capacities are exposed in sched debug for android 4.9 and older. * Otherwise, upstream uses a sysfs interface. */ if (ret == -ENOENT) ret = read_capacity_sysfs(cpu, cap); if (ret) perror(NULL); return ret; } /* * get_bigs = 0, search for smallest CPUs * get_bigs = 1, search for CPUs other than the smallest CPUs */ int find_cpus_with_capacity(int get_bigs, cpu_set_t *cpuset) { unsigned int cap, smallest = -1; int i; CPU_ZERO(cpuset); for (i = 0; i < tst_ncpus(); i++) { if (read_cpu_capacity(i, &cap)) return -1; if (cap < smallest) { smallest = cap; CPU_ZERO(cpuset); CPU_SET(i, cpuset); } else if (cap == smallest) { CPU_SET(i, cpuset); } } if (!get_bigs) return 0; for (i = 0; i < tst_ncpus(); i++) if (CPU_ISSET(i, cpuset)) CPU_CLR(i, cpuset); else CPU_SET(i, cpuset); return 0; }