162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci 362306a36Sopenharmony_ci#define _GNU_SOURCE 462306a36Sopenharmony_ci#include <errno.h> 562306a36Sopenharmony_ci#include <stdio.h> 662306a36Sopenharmony_ci#include <stdlib.h> 762306a36Sopenharmony_ci#include <signal.h> 862306a36Sopenharmony_ci#include <sched.h> 962306a36Sopenharmony_ci#include <string.h> 1062306a36Sopenharmony_ci#include <unistd.h> 1162306a36Sopenharmony_ci#include <fcntl.h> 1262306a36Sopenharmony_ci#include <locale.h> 1362306a36Sopenharmony_ci#include <sys/types.h> 1462306a36Sopenharmony_ci#include <sys/stat.h> 1562306a36Sopenharmony_ci#include <sys/time.h> 1662306a36Sopenharmony_ci#include <sys/wait.h> 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_ci#include <bpf/bpf.h> 1962306a36Sopenharmony_ci#include <bpf/libbpf.h> 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_cistatic int cstate_map_fd, pstate_map_fd; 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_ci#define MAX_CPU 8 2462306a36Sopenharmony_ci#define MAX_PSTATE_ENTRIES 5 2562306a36Sopenharmony_ci#define MAX_CSTATE_ENTRIES 3 2662306a36Sopenharmony_ci#define MAX_STARS 40 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_ci#define CPUFREQ_MAX_SYSFS_PATH "/sys/devices/system/cpu/cpu0/cpufreq/scaling_max_freq" 2962306a36Sopenharmony_ci#define CPUFREQ_LOWEST_FREQ "208000" 3062306a36Sopenharmony_ci#define CPUFREQ_HIGHEST_FREQ "12000000" 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_cistruct cpu_stat_data { 3362306a36Sopenharmony_ci unsigned long cstate[MAX_CSTATE_ENTRIES]; 3462306a36Sopenharmony_ci unsigned long pstate[MAX_PSTATE_ENTRIES]; 3562306a36Sopenharmony_ci}; 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_cistatic struct cpu_stat_data stat_data[MAX_CPU]; 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_cistatic void cpu_stat_print(void) 4062306a36Sopenharmony_ci{ 4162306a36Sopenharmony_ci int i, j; 4262306a36Sopenharmony_ci char state_str[sizeof("cstate-9")]; 4362306a36Sopenharmony_ci struct cpu_stat_data *data; 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ci /* Clear screen */ 4662306a36Sopenharmony_ci printf("\033[2J"); 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_ci /* Header */ 4962306a36Sopenharmony_ci printf("\nCPU states statistics:\n"); 5062306a36Sopenharmony_ci printf("%-10s ", "state(ms)"); 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci for (i = 0; i < MAX_CSTATE_ENTRIES; i++) { 5362306a36Sopenharmony_ci sprintf(state_str, "cstate-%d", i); 5462306a36Sopenharmony_ci printf("%-11s ", state_str); 5562306a36Sopenharmony_ci } 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci for (i = 0; i < MAX_PSTATE_ENTRIES; i++) { 5862306a36Sopenharmony_ci sprintf(state_str, "pstate-%d", i); 5962306a36Sopenharmony_ci printf("%-11s ", state_str); 6062306a36Sopenharmony_ci } 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ci printf("\n"); 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_ci for (j = 0; j < MAX_CPU; j++) { 6562306a36Sopenharmony_ci data = &stat_data[j]; 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_ci printf("CPU-%-6d ", j); 6862306a36Sopenharmony_ci for (i = 0; i < MAX_CSTATE_ENTRIES; i++) 6962306a36Sopenharmony_ci printf("%-11ld ", data->cstate[i] / 1000000); 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_ci for (i = 0; i < MAX_PSTATE_ENTRIES; i++) 7262306a36Sopenharmony_ci printf("%-11ld ", data->pstate[i] / 1000000); 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_ci printf("\n"); 7562306a36Sopenharmony_ci } 7662306a36Sopenharmony_ci} 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_cistatic void cpu_stat_update(int cstate_fd, int pstate_fd) 7962306a36Sopenharmony_ci{ 8062306a36Sopenharmony_ci unsigned long key, value; 8162306a36Sopenharmony_ci int c, i; 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci for (c = 0; c < MAX_CPU; c++) { 8462306a36Sopenharmony_ci for (i = 0; i < MAX_CSTATE_ENTRIES; i++) { 8562306a36Sopenharmony_ci key = c * MAX_CSTATE_ENTRIES + i; 8662306a36Sopenharmony_ci bpf_map_lookup_elem(cstate_fd, &key, &value); 8762306a36Sopenharmony_ci stat_data[c].cstate[i] = value; 8862306a36Sopenharmony_ci } 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_ci for (i = 0; i < MAX_PSTATE_ENTRIES; i++) { 9162306a36Sopenharmony_ci key = c * MAX_PSTATE_ENTRIES + i; 9262306a36Sopenharmony_ci bpf_map_lookup_elem(pstate_fd, &key, &value); 9362306a36Sopenharmony_ci stat_data[c].pstate[i] = value; 9462306a36Sopenharmony_ci } 9562306a36Sopenharmony_ci } 9662306a36Sopenharmony_ci} 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci/* 9962306a36Sopenharmony_ci * This function is copied from 'idlestat' tool function 10062306a36Sopenharmony_ci * idlestat_wake_all() in idlestate.c. 10162306a36Sopenharmony_ci * 10262306a36Sopenharmony_ci * It sets the self running task affinity to cpus one by one so can wake up 10362306a36Sopenharmony_ci * the specific CPU to handle scheduling; this results in all cpus can be 10462306a36Sopenharmony_ci * waken up once and produce ftrace event 'trace_cpu_idle'. 10562306a36Sopenharmony_ci */ 10662306a36Sopenharmony_cistatic int cpu_stat_inject_cpu_idle_event(void) 10762306a36Sopenharmony_ci{ 10862306a36Sopenharmony_ci int rcpu, i, ret; 10962306a36Sopenharmony_ci cpu_set_t cpumask; 11062306a36Sopenharmony_ci cpu_set_t original_cpumask; 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci ret = sysconf(_SC_NPROCESSORS_CONF); 11362306a36Sopenharmony_ci if (ret < 0) 11462306a36Sopenharmony_ci return -1; 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ci rcpu = sched_getcpu(); 11762306a36Sopenharmony_ci if (rcpu < 0) 11862306a36Sopenharmony_ci return -1; 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci /* Keep track of the CPUs we will run on */ 12162306a36Sopenharmony_ci sched_getaffinity(0, sizeof(original_cpumask), &original_cpumask); 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci for (i = 0; i < ret; i++) { 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci /* Pointless to wake up ourself */ 12662306a36Sopenharmony_ci if (i == rcpu) 12762306a36Sopenharmony_ci continue; 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci /* Pointless to wake CPUs we will not run on */ 13062306a36Sopenharmony_ci if (!CPU_ISSET(i, &original_cpumask)) 13162306a36Sopenharmony_ci continue; 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci CPU_ZERO(&cpumask); 13462306a36Sopenharmony_ci CPU_SET(i, &cpumask); 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_ci sched_setaffinity(0, sizeof(cpumask), &cpumask); 13762306a36Sopenharmony_ci } 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci /* Enable all the CPUs of the original mask */ 14062306a36Sopenharmony_ci sched_setaffinity(0, sizeof(original_cpumask), &original_cpumask); 14162306a36Sopenharmony_ci return 0; 14262306a36Sopenharmony_ci} 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_ci/* 14562306a36Sopenharmony_ci * It's possible to have no any frequency change for long time and cannot 14662306a36Sopenharmony_ci * get ftrace event 'trace_cpu_frequency' for long period, this introduces 14762306a36Sopenharmony_ci * big deviation for pstate statistics. 14862306a36Sopenharmony_ci * 14962306a36Sopenharmony_ci * To solve this issue, below code forces to set 'scaling_max_freq' to 208MHz 15062306a36Sopenharmony_ci * for triggering ftrace event 'trace_cpu_frequency' and then recovery back to 15162306a36Sopenharmony_ci * the maximum frequency value 1.2GHz. 15262306a36Sopenharmony_ci */ 15362306a36Sopenharmony_cistatic int cpu_stat_inject_cpu_frequency_event(void) 15462306a36Sopenharmony_ci{ 15562306a36Sopenharmony_ci int len, fd; 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_ci fd = open(CPUFREQ_MAX_SYSFS_PATH, O_WRONLY); 15862306a36Sopenharmony_ci if (fd < 0) { 15962306a36Sopenharmony_ci printf("failed to open scaling_max_freq, errno=%d\n", errno); 16062306a36Sopenharmony_ci return fd; 16162306a36Sopenharmony_ci } 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ci len = write(fd, CPUFREQ_LOWEST_FREQ, strlen(CPUFREQ_LOWEST_FREQ)); 16462306a36Sopenharmony_ci if (len < 0) { 16562306a36Sopenharmony_ci printf("failed to open scaling_max_freq, errno=%d\n", errno); 16662306a36Sopenharmony_ci goto err; 16762306a36Sopenharmony_ci } 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_ci len = write(fd, CPUFREQ_HIGHEST_FREQ, strlen(CPUFREQ_HIGHEST_FREQ)); 17062306a36Sopenharmony_ci if (len < 0) { 17162306a36Sopenharmony_ci printf("failed to open scaling_max_freq, errno=%d\n", errno); 17262306a36Sopenharmony_ci goto err; 17362306a36Sopenharmony_ci } 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_cierr: 17662306a36Sopenharmony_ci close(fd); 17762306a36Sopenharmony_ci return len; 17862306a36Sopenharmony_ci} 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_cistatic void int_exit(int sig) 18162306a36Sopenharmony_ci{ 18262306a36Sopenharmony_ci cpu_stat_inject_cpu_idle_event(); 18362306a36Sopenharmony_ci cpu_stat_inject_cpu_frequency_event(); 18462306a36Sopenharmony_ci cpu_stat_update(cstate_map_fd, pstate_map_fd); 18562306a36Sopenharmony_ci cpu_stat_print(); 18662306a36Sopenharmony_ci exit(0); 18762306a36Sopenharmony_ci} 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_ciint main(int argc, char **argv) 19062306a36Sopenharmony_ci{ 19162306a36Sopenharmony_ci struct bpf_link *link = NULL; 19262306a36Sopenharmony_ci struct bpf_program *prog; 19362306a36Sopenharmony_ci struct bpf_object *obj; 19462306a36Sopenharmony_ci char filename[256]; 19562306a36Sopenharmony_ci int ret; 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ci snprintf(filename, sizeof(filename), "%s_kern.o", argv[0]); 19862306a36Sopenharmony_ci obj = bpf_object__open_file(filename, NULL); 19962306a36Sopenharmony_ci if (libbpf_get_error(obj)) { 20062306a36Sopenharmony_ci fprintf(stderr, "ERROR: opening BPF object file failed\n"); 20162306a36Sopenharmony_ci return 0; 20262306a36Sopenharmony_ci } 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_ci prog = bpf_object__find_program_by_name(obj, "bpf_prog1"); 20562306a36Sopenharmony_ci if (!prog) { 20662306a36Sopenharmony_ci printf("finding a prog in obj file failed\n"); 20762306a36Sopenharmony_ci goto cleanup; 20862306a36Sopenharmony_ci } 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_ci /* load BPF program */ 21162306a36Sopenharmony_ci if (bpf_object__load(obj)) { 21262306a36Sopenharmony_ci fprintf(stderr, "ERROR: loading BPF object file failed\n"); 21362306a36Sopenharmony_ci goto cleanup; 21462306a36Sopenharmony_ci } 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_ci cstate_map_fd = bpf_object__find_map_fd_by_name(obj, "cstate_duration"); 21762306a36Sopenharmony_ci pstate_map_fd = bpf_object__find_map_fd_by_name(obj, "pstate_duration"); 21862306a36Sopenharmony_ci if (cstate_map_fd < 0 || pstate_map_fd < 0) { 21962306a36Sopenharmony_ci fprintf(stderr, "ERROR: finding a map in obj file failed\n"); 22062306a36Sopenharmony_ci goto cleanup; 22162306a36Sopenharmony_ci } 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_ci link = bpf_program__attach(prog); 22462306a36Sopenharmony_ci if (libbpf_get_error(link)) { 22562306a36Sopenharmony_ci fprintf(stderr, "ERROR: bpf_program__attach failed\n"); 22662306a36Sopenharmony_ci link = NULL; 22762306a36Sopenharmony_ci goto cleanup; 22862306a36Sopenharmony_ci } 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_ci ret = cpu_stat_inject_cpu_idle_event(); 23162306a36Sopenharmony_ci if (ret < 0) 23262306a36Sopenharmony_ci return 1; 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_ci ret = cpu_stat_inject_cpu_frequency_event(); 23562306a36Sopenharmony_ci if (ret < 0) 23662306a36Sopenharmony_ci return 1; 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_ci signal(SIGINT, int_exit); 23962306a36Sopenharmony_ci signal(SIGTERM, int_exit); 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_ci while (1) { 24262306a36Sopenharmony_ci cpu_stat_update(cstate_map_fd, pstate_map_fd); 24362306a36Sopenharmony_ci cpu_stat_print(); 24462306a36Sopenharmony_ci sleep(5); 24562306a36Sopenharmony_ci } 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_cicleanup: 24862306a36Sopenharmony_ci bpf_link__destroy(link); 24962306a36Sopenharmony_ci bpf_object__close(obj); 25062306a36Sopenharmony_ci return 0; 25162306a36Sopenharmony_ci} 252