1bf215546Sopenharmony_ci/************************************************************************** 2bf215546Sopenharmony_ci * 3bf215546Sopenharmony_ci * Copyright (C) 2016 Steven Toth <stoth@kernellabs.com> 4bf215546Sopenharmony_ci * Copyright (C) 2016 Zodiac Inflight Innovations 5bf215546Sopenharmony_ci * All Rights Reserved. 6bf215546Sopenharmony_ci * 7bf215546Sopenharmony_ci * Permission is hereby granted, free of charge, to any person obtaining a 8bf215546Sopenharmony_ci * copy of this software and associated documentation files (the 9bf215546Sopenharmony_ci * "Software"), to deal in the Software without restriction, including 10bf215546Sopenharmony_ci * without limitation the rights to use, copy, modify, merge, publish, 11bf215546Sopenharmony_ci * distribute, sub license, and/or sell copies of the Software, and to 12bf215546Sopenharmony_ci * permit persons to whom the Software is furnished to do so, subject to 13bf215546Sopenharmony_ci * the following conditions: 14bf215546Sopenharmony_ci * 15bf215546Sopenharmony_ci * The above copyright notice and this permission notice (including the 16bf215546Sopenharmony_ci * next paragraph) shall be included in all copies or substantial portions 17bf215546Sopenharmony_ci * of the Software. 18bf215546Sopenharmony_ci * 19bf215546Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 20bf215546Sopenharmony_ci * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 21bf215546Sopenharmony_ci * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. 22bf215546Sopenharmony_ci * IN NO EVENT SHALL THE AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR 23bf215546Sopenharmony_ci * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 24bf215546Sopenharmony_ci * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 25bf215546Sopenharmony_ci * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 26bf215546Sopenharmony_ci * 27bf215546Sopenharmony_ci **************************************************************************/ 28bf215546Sopenharmony_ci 29bf215546Sopenharmony_ci#ifdef HAVE_GALLIUM_EXTRA_HUD 30bf215546Sopenharmony_ci 31bf215546Sopenharmony_ci/* Purpose: 32bf215546Sopenharmony_ci * Reading /sys/devices/system/cpu/cpu?/cpufreq/scaling_???_freq 33bf215546Sopenharmony_ci * cpu frequency (KHz), displaying on the HUD in Hz. 34bf215546Sopenharmony_ci */ 35bf215546Sopenharmony_ci 36bf215546Sopenharmony_ci#include "hud/hud_private.h" 37bf215546Sopenharmony_ci#include "util/list.h" 38bf215546Sopenharmony_ci#include "util/os_time.h" 39bf215546Sopenharmony_ci#include "os/os_thread.h" 40bf215546Sopenharmony_ci#include "util/u_memory.h" 41bf215546Sopenharmony_ci#include <stdio.h> 42bf215546Sopenharmony_ci#include <unistd.h> 43bf215546Sopenharmony_ci#include <dirent.h> 44bf215546Sopenharmony_ci#include <stdlib.h> 45bf215546Sopenharmony_ci#include <errno.h> 46bf215546Sopenharmony_ci#include <inttypes.h> 47bf215546Sopenharmony_ci#include <sys/types.h> 48bf215546Sopenharmony_ci#include <sys/stat.h> 49bf215546Sopenharmony_ci 50bf215546Sopenharmony_cistruct cpufreq_info 51bf215546Sopenharmony_ci{ 52bf215546Sopenharmony_ci struct list_head list; 53bf215546Sopenharmony_ci int mode; /* CPUFREQ_MINIMUM, CPUFREQ_CURRENT, CPUFREQ_MAXIMUM */ 54bf215546Sopenharmony_ci char name[16]; /* EG. cpu0 */ 55bf215546Sopenharmony_ci int cpu_index; 56bf215546Sopenharmony_ci 57bf215546Sopenharmony_ci /* EG. /sys/devices/system/cpu/cpu?/cpufreq/scaling_cur_freq */ 58bf215546Sopenharmony_ci char sysfs_filename[128]; 59bf215546Sopenharmony_ci uint64_t KHz; 60bf215546Sopenharmony_ci uint64_t last_time; 61bf215546Sopenharmony_ci}; 62bf215546Sopenharmony_ci 63bf215546Sopenharmony_cistatic int gcpufreq_count = 0; 64bf215546Sopenharmony_cistatic struct list_head gcpufreq_list; 65bf215546Sopenharmony_cistatic mtx_t gcpufreq_mutex = _MTX_INITIALIZER_NP; 66bf215546Sopenharmony_ci 67bf215546Sopenharmony_cistatic struct cpufreq_info * 68bf215546Sopenharmony_cifind_cfi_by_index(int cpu_index, int mode) 69bf215546Sopenharmony_ci{ 70bf215546Sopenharmony_ci list_for_each_entry(struct cpufreq_info, cfi, &gcpufreq_list, list) { 71bf215546Sopenharmony_ci if (cfi->mode != mode) 72bf215546Sopenharmony_ci continue; 73bf215546Sopenharmony_ci if (cfi->cpu_index == cpu_index) 74bf215546Sopenharmony_ci return cfi; 75bf215546Sopenharmony_ci } 76bf215546Sopenharmony_ci return 0; 77bf215546Sopenharmony_ci} 78bf215546Sopenharmony_ci 79bf215546Sopenharmony_cistatic int 80bf215546Sopenharmony_ciget_file_value(const char *fn, uint64_t *KHz) 81bf215546Sopenharmony_ci{ 82bf215546Sopenharmony_ci FILE *fh = fopen(fn, "r"); 83bf215546Sopenharmony_ci if (!fh) { 84bf215546Sopenharmony_ci fprintf(stderr, "%s error: %s\n", fn, strerror(errno)); 85bf215546Sopenharmony_ci return -1; 86bf215546Sopenharmony_ci } 87bf215546Sopenharmony_ci int ret = fscanf(fh, "%" PRIu64 "", KHz); 88bf215546Sopenharmony_ci fclose(fh); 89bf215546Sopenharmony_ci 90bf215546Sopenharmony_ci return ret; 91bf215546Sopenharmony_ci} 92bf215546Sopenharmony_ci 93bf215546Sopenharmony_cistatic void 94bf215546Sopenharmony_ciquery_cfi_load(struct hud_graph *gr, struct pipe_context *pipe) 95bf215546Sopenharmony_ci{ 96bf215546Sopenharmony_ci struct cpufreq_info *cfi = gr->query_data; 97bf215546Sopenharmony_ci 98bf215546Sopenharmony_ci uint64_t now = os_time_get(); 99bf215546Sopenharmony_ci if (cfi->last_time) { 100bf215546Sopenharmony_ci if (cfi->last_time + gr->pane->period <= now) { 101bf215546Sopenharmony_ci switch (cfi->mode) { 102bf215546Sopenharmony_ci case CPUFREQ_MINIMUM: 103bf215546Sopenharmony_ci case CPUFREQ_CURRENT: 104bf215546Sopenharmony_ci case CPUFREQ_MAXIMUM: 105bf215546Sopenharmony_ci get_file_value(cfi->sysfs_filename, &cfi->KHz); 106bf215546Sopenharmony_ci hud_graph_add_value(gr, (uint64_t)cfi->KHz * 1000); 107bf215546Sopenharmony_ci } 108bf215546Sopenharmony_ci cfi->last_time = now; 109bf215546Sopenharmony_ci } 110bf215546Sopenharmony_ci } else { 111bf215546Sopenharmony_ci /* initialize */ 112bf215546Sopenharmony_ci get_file_value(cfi->sysfs_filename, &cfi->KHz); 113bf215546Sopenharmony_ci cfi->last_time = now; 114bf215546Sopenharmony_ci } 115bf215546Sopenharmony_ci} 116bf215546Sopenharmony_ci 117bf215546Sopenharmony_ci/** 118bf215546Sopenharmony_ci * Create and initialize a new object for a specific CPU. 119bf215546Sopenharmony_ci * \param pane parent context. 120bf215546Sopenharmony_ci * \param cpu_index CPU identifier Eg. 0 (CPU0) 121bf215546Sopenharmony_ci * \param mode query CPUFREQ_MINIMUM | CURRENT | MAXIMUM statistic. 122bf215546Sopenharmony_ci */ 123bf215546Sopenharmony_civoid 124bf215546Sopenharmony_cihud_cpufreq_graph_install(struct hud_pane *pane, int cpu_index, 125bf215546Sopenharmony_ci unsigned int mode) 126bf215546Sopenharmony_ci{ 127bf215546Sopenharmony_ci struct hud_graph *gr; 128bf215546Sopenharmony_ci struct cpufreq_info *cfi; 129bf215546Sopenharmony_ci 130bf215546Sopenharmony_ci int num_cpus = hud_get_num_cpufreq(0); 131bf215546Sopenharmony_ci if (num_cpus <= 0) 132bf215546Sopenharmony_ci return; 133bf215546Sopenharmony_ci 134bf215546Sopenharmony_ci cfi = find_cfi_by_index(cpu_index, mode); 135bf215546Sopenharmony_ci if (!cfi) 136bf215546Sopenharmony_ci return; 137bf215546Sopenharmony_ci 138bf215546Sopenharmony_ci gr = CALLOC_STRUCT(hud_graph); 139bf215546Sopenharmony_ci if (!gr) 140bf215546Sopenharmony_ci return; 141bf215546Sopenharmony_ci 142bf215546Sopenharmony_ci cfi->mode = mode; 143bf215546Sopenharmony_ci switch(cfi->mode) { 144bf215546Sopenharmony_ci case CPUFREQ_MINIMUM: 145bf215546Sopenharmony_ci snprintf(gr->name, sizeof(gr->name), "%s-Min", cfi->name); 146bf215546Sopenharmony_ci break; 147bf215546Sopenharmony_ci case CPUFREQ_CURRENT: 148bf215546Sopenharmony_ci snprintf(gr->name, sizeof(gr->name), "%s-Cur", cfi->name); 149bf215546Sopenharmony_ci break; 150bf215546Sopenharmony_ci case CPUFREQ_MAXIMUM: 151bf215546Sopenharmony_ci snprintf(gr->name, sizeof(gr->name), "%s-Max", cfi->name); 152bf215546Sopenharmony_ci break; 153bf215546Sopenharmony_ci default: 154bf215546Sopenharmony_ci free(gr); 155bf215546Sopenharmony_ci return; 156bf215546Sopenharmony_ci } 157bf215546Sopenharmony_ci 158bf215546Sopenharmony_ci gr->query_data = cfi; 159bf215546Sopenharmony_ci gr->query_new_value = query_cfi_load; 160bf215546Sopenharmony_ci 161bf215546Sopenharmony_ci hud_pane_add_graph(pane, gr); 162bf215546Sopenharmony_ci hud_pane_set_max_value(pane, 3000000 /* 3 GHz */); 163bf215546Sopenharmony_ci} 164bf215546Sopenharmony_ci 165bf215546Sopenharmony_cistatic void 166bf215546Sopenharmony_ciadd_object(const char *name, const char *fn, int objmode, int cpu_index) 167bf215546Sopenharmony_ci{ 168bf215546Sopenharmony_ci struct cpufreq_info *cfi = CALLOC_STRUCT(cpufreq_info); 169bf215546Sopenharmony_ci 170bf215546Sopenharmony_ci strcpy(cfi->name, name); 171bf215546Sopenharmony_ci strcpy(cfi->sysfs_filename, fn); 172bf215546Sopenharmony_ci cfi->mode = objmode; 173bf215546Sopenharmony_ci cfi->cpu_index = cpu_index; 174bf215546Sopenharmony_ci list_addtail(&cfi->list, &gcpufreq_list); 175bf215546Sopenharmony_ci gcpufreq_count++; 176bf215546Sopenharmony_ci} 177bf215546Sopenharmony_ci 178bf215546Sopenharmony_ci/** 179bf215546Sopenharmony_ci * Initialize internal object arrays and display cpu freq HUD help. 180bf215546Sopenharmony_ci * \param displayhelp true if the list of detected cpus should be 181bf215546Sopenharmony_ci displayed on the console. 182bf215546Sopenharmony_ci * \return number of detected CPU metrics (CPU count * 3) 183bf215546Sopenharmony_ci */ 184bf215546Sopenharmony_ciint 185bf215546Sopenharmony_cihud_get_num_cpufreq(bool displayhelp) 186bf215546Sopenharmony_ci{ 187bf215546Sopenharmony_ci struct dirent *dp; 188bf215546Sopenharmony_ci struct stat stat_buf; 189bf215546Sopenharmony_ci char fn[128]; 190bf215546Sopenharmony_ci int cpu_index; 191bf215546Sopenharmony_ci 192bf215546Sopenharmony_ci /* Return the number of CPU metrics we support. */ 193bf215546Sopenharmony_ci mtx_lock(&gcpufreq_mutex); 194bf215546Sopenharmony_ci if (gcpufreq_count) { 195bf215546Sopenharmony_ci mtx_unlock(&gcpufreq_mutex); 196bf215546Sopenharmony_ci return gcpufreq_count; 197bf215546Sopenharmony_ci } 198bf215546Sopenharmony_ci 199bf215546Sopenharmony_ci /* Scan /sys/devices.../cpu, for every object type we support, create 200bf215546Sopenharmony_ci * and persist an object to represent its different metrics. 201bf215546Sopenharmony_ci */ 202bf215546Sopenharmony_ci list_inithead(&gcpufreq_list); 203bf215546Sopenharmony_ci DIR *dir = opendir("/sys/devices/system/cpu"); 204bf215546Sopenharmony_ci if (!dir) { 205bf215546Sopenharmony_ci mtx_unlock(&gcpufreq_mutex); 206bf215546Sopenharmony_ci return 0; 207bf215546Sopenharmony_ci } 208bf215546Sopenharmony_ci 209bf215546Sopenharmony_ci while ((dp = readdir(dir)) != NULL) { 210bf215546Sopenharmony_ci 211bf215546Sopenharmony_ci size_t d_name_len = strlen(dp->d_name); 212bf215546Sopenharmony_ci 213bf215546Sopenharmony_ci /* Avoid 'lo' and '..' and '.', and avoid overlong names that 214bf215546Sopenharmony_ci * would result in a buffer overflow in add_object. 215bf215546Sopenharmony_ci */ 216bf215546Sopenharmony_ci if (d_name_len <= 2 || d_name_len > 15) 217bf215546Sopenharmony_ci continue; 218bf215546Sopenharmony_ci 219bf215546Sopenharmony_ci if (sscanf(dp->d_name, "cpu%d\n", &cpu_index) != 1) 220bf215546Sopenharmony_ci continue; 221bf215546Sopenharmony_ci 222bf215546Sopenharmony_ci char basename[256]; 223bf215546Sopenharmony_ci snprintf(basename, sizeof(basename), "/sys/devices/system/cpu/%s", dp->d_name); 224bf215546Sopenharmony_ci 225bf215546Sopenharmony_ci snprintf(fn, sizeof(fn), "%s/cpufreq/scaling_cur_freq", basename); 226bf215546Sopenharmony_ci if (stat(fn, &stat_buf) < 0) 227bf215546Sopenharmony_ci continue; 228bf215546Sopenharmony_ci 229bf215546Sopenharmony_ci if (!S_ISREG(stat_buf.st_mode)) 230bf215546Sopenharmony_ci continue; /* Not a regular file */ 231bf215546Sopenharmony_ci 232bf215546Sopenharmony_ci snprintf(fn, sizeof(fn), "%s/cpufreq/scaling_min_freq", basename); 233bf215546Sopenharmony_ci add_object(dp->d_name, fn, CPUFREQ_MINIMUM, cpu_index); 234bf215546Sopenharmony_ci 235bf215546Sopenharmony_ci snprintf(fn, sizeof(fn), "%s/cpufreq/scaling_cur_freq", basename); 236bf215546Sopenharmony_ci add_object(dp->d_name, fn, CPUFREQ_CURRENT, cpu_index); 237bf215546Sopenharmony_ci 238bf215546Sopenharmony_ci snprintf(fn, sizeof(fn), "%s/cpufreq/scaling_max_freq", basename); 239bf215546Sopenharmony_ci add_object(dp->d_name, fn, CPUFREQ_MAXIMUM, cpu_index); 240bf215546Sopenharmony_ci } 241bf215546Sopenharmony_ci closedir(dir); 242bf215546Sopenharmony_ci 243bf215546Sopenharmony_ci if (displayhelp) { 244bf215546Sopenharmony_ci list_for_each_entry(struct cpufreq_info, cfi, &gcpufreq_list, list) { 245bf215546Sopenharmony_ci char line[128]; 246bf215546Sopenharmony_ci snprintf(line, sizeof(line), " cpufreq-%s-%s", 247bf215546Sopenharmony_ci cfi->mode == CPUFREQ_MINIMUM ? "min" : 248bf215546Sopenharmony_ci cfi->mode == CPUFREQ_CURRENT ? "cur" : 249bf215546Sopenharmony_ci cfi->mode == CPUFREQ_MAXIMUM ? "max" : "undefined", cfi->name); 250bf215546Sopenharmony_ci 251bf215546Sopenharmony_ci puts(line); 252bf215546Sopenharmony_ci } 253bf215546Sopenharmony_ci } 254bf215546Sopenharmony_ci 255bf215546Sopenharmony_ci mtx_unlock(&gcpufreq_mutex); 256bf215546Sopenharmony_ci return gcpufreq_count; 257bf215546Sopenharmony_ci} 258bf215546Sopenharmony_ci 259bf215546Sopenharmony_ci#endif /* HAVE_GALLIUM_EXTRA_HUD */ 260