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