You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
191 lines
3.9 KiB
191 lines
3.9 KiB
/*
|
|
* 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;
|
|
}
|
|
|