Home | History | Annotate | Download | only in eas
      1 /*
      2  * Copyright (c) 2018 Google, Inc.
      3  *
      4  * SPDX-License-Identifier: GPL-2.0-or-later
      5  */
      6 
      7 #define _GNU_SOURCE
      8 #include <errno.h>
      9 #include <fcntl.h>
     10 #include <sched.h>
     11 #include <string.h>
     12 #include <stdio.h>
     13 #include <stdlib.h>
     14 #include <time.h>
     15 #include <unistd.h>
     16 
     17 #include "tst_cpu.h"
     18 #include "tst_safe_file_ops.h"
     19 
     20 #include "util.h"
     21 
     22 void affine(int cpu)
     23 {
     24 	cpu_set_t cpuset;
     25 	CPU_ZERO(&cpuset);
     26 	CPU_SET(cpu, &cpuset);
     27 	ERROR_CHECK(sched_setaffinity(0, sizeof(cpu_set_t), &cpuset));
     28 }
     29 
     30 /*
     31  * Busywait for a certain amount of wallclock time.
     32  * If sleep is nonzero, sleep for 1ms between each check.
     33  */
     34 void burn(unsigned int usec, int sleep)
     35 {
     36 	unsigned long long now_usec, end_usec;
     37 	struct timespec ts;
     38 
     39 	if (clock_gettime(CLOCK_MONOTONIC, &ts)) {
     40 		printf("clock_gettime() reported an error\n");
     41 		return;
     42 	}
     43 	end_usec = (ts.tv_sec) * USEC_PER_SEC + (ts.tv_nsec / 1000) + usec;
     44 	while(1) {
     45 		if (clock_gettime(CLOCK_MONOTONIC, &ts)) {
     46 			printf("clock_gettime() reported an error\n");
     47 			return;
     48 		}
     49 		now_usec = ts.tv_sec * USEC_PER_SEC + ts.tv_nsec / 1000;
     50 		if (now_usec > end_usec)
     51 			return;
     52 		if (sleep)
     53 			usleep(1000);
     54 	}
     55 }
     56 
     57 #define CAP_STATE_FILE_SIZE 1024
     58 static int read_capacity_sched_domains(int cpu, unsigned int *cap)
     59 {
     60 	char *filebuf, *tmp1, *tmp2;
     61 	char cap_states_file[100];
     62 	int cap_states_fd;
     63 	int bytes, rv;
     64 
     65 	sprintf(cap_states_file,
     66 			"/proc/sys/kernel/sched_domain/cpu%d/domain0/group0/energy/cap_states",
     67 			cpu);
     68 	cap_states_fd = open(cap_states_file, O_RDONLY);
     69 	if (cap_states_fd == -1)
     70 		return -ENOENT;
     71 
     72 	bytes = CAP_STATE_FILE_SIZE;
     73 	filebuf = calloc(1, CAP_STATE_FILE_SIZE + 1);
     74 	if (!filebuf) {
     75 		printf("Failed to calloc buffer for cap_states\n");
     76 		return -1;
     77 	}
     78 	tmp1 = filebuf;
     79 	while (bytes) {
     80 		rv = read(cap_states_fd, tmp1, bytes);
     81 		if (rv == -1) {
     82 			printf("Could not read cap_states\n");
     83 			return -1;
     84 		}
     85 		if (rv == 0)
     86 			break;
     87 		tmp1 += rv;
     88 		bytes -= rv;
     89 	}
     90 	if (tmp1 - filebuf == CAP_STATE_FILE_SIZE) {
     91 		printf("CAP_STATE_FILE_SIZE exhausted, increase\n");
     92 		return -1;
     93 	}
     94 	tmp1 = strrchr(filebuf, '\t');
     95 	if (!tmp1 || tmp1 == filebuf ) {
     96 		printf("Malformatted cap_states_file (1).\n%s\n", filebuf);
     97 		return -1;
     98 	}
     99 	tmp1 = strrchr(tmp1 - 1, '\t');
    100 	if (!tmp1 || tmp1 == filebuf) {
    101 		printf("Malformatted cap_states_file (2).\n%s\n", filebuf);
    102 		return -1;
    103 	}
    104 	tmp1 = strrchr(tmp1 - 1, '\t');
    105 	if (!tmp1 || tmp1 == filebuf) {
    106 		printf("Malformatted cap_states_file (3).\n%s\n", filebuf);
    107 		return -1;
    108 	}
    109 	/* tmp1 now points to tab after the capacity we want. */
    110 	*tmp1 = 0;
    111 	tmp2 = strrchr(tmp1 - 1, '\t');
    112 	if (!tmp2)
    113 		tmp2 = filebuf;
    114 	else
    115 		tmp2++;
    116 	if (sscanf(tmp2,"%d", cap) != 1) {
    117 		printf("Failed to parse capacity from cap_states.\n");
    118 		return -1;
    119 	}
    120 	free(filebuf);
    121 	if (close(cap_states_fd)) {
    122 		printf("Failed to close cap_states file.\n");
    123 		return -1;
    124 	}
    125 
    126 	return 0;
    127 }
    128 
    129 static int read_capacity_sysfs(int cpu, unsigned int *cap)
    130 {
    131 	char path[100];
    132 
    133 	sprintf(path, "/sys/devices/system/cpu/cpu%d/cpu_capacity", cpu);
    134 
    135 	return SAFE_FILE_LINES_SCANF(path, "%u", cap);
    136 }
    137 
    138 static int read_cpu_capacity(int cpu, unsigned int *cap)
    139 {
    140 	int ret = read_capacity_sched_domains(cpu, cap);
    141 
    142 	/*
    143 	 * Capacities are exposed in sched debug for android 4.9 and older.
    144 	 * Otherwise, upstream uses a sysfs interface.
    145 	 */
    146 	if (ret == -ENOENT)
    147 		ret = read_capacity_sysfs(cpu, cap);
    148 
    149 	if (ret)
    150 		perror(NULL);
    151 
    152 	return ret;
    153 }
    154 
    155 /*
    156  * get_bigs = 0, search for smallest CPUs
    157  * get_bigs = 1, search for CPUs other than the smallest CPUs
    158  */
    159 int find_cpus_with_capacity(int get_bigs, cpu_set_t *cpuset)
    160 {
    161 	unsigned int cap, smallest = -1;
    162 	int i;
    163 
    164 	CPU_ZERO(cpuset);
    165 
    166 	for (i = 0; i < tst_ncpus(); i++) {
    167 		if (read_cpu_capacity(i, &cap))
    168 			return -1;
    169 
    170 		if (cap < smallest) {
    171 			smallest = cap;
    172 			CPU_ZERO(cpuset);
    173 			CPU_SET(i, cpuset);
    174 		} else if (cap == smallest) {
    175 			CPU_SET(i, cpuset);
    176 		}
    177 	}
    178 
    179 	if (!get_bigs)
    180 		return 0;
    181 
    182 	for (i = 0; i < tst_ncpus(); i++)
    183 		if (CPU_ISSET(i, cpuset))
    184 			CPU_CLR(i, cpuset);
    185 		else
    186 			CPU_SET(i, cpuset);
    187 
    188 	return 0;
    189 }
    190 
    191