Path: blob/21.2-virgl/src/gallium/auxiliary/hud/hud_cpufreq.c
4565 views
/**************************************************************************1*2* Copyright (C) 2016 Steven Toth <[email protected]>3* Copyright (C) 2016 Zodiac Inflight Innovations4* All Rights Reserved.5*6* Permission is hereby granted, free of charge, to any person obtaining a7* copy of this software and associated documentation files (the8* "Software"), to deal in the Software without restriction, including9* without limitation the rights to use, copy, modify, merge, publish,10* distribute, sub license, and/or sell copies of the Software, and to11* permit persons to whom the Software is furnished to do so, subject to12* the following conditions:13*14* The above copyright notice and this permission notice (including the15* next paragraph) shall be included in all copies or substantial portions16* of the Software.17*18* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS19* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF20* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.21* IN NO EVENT SHALL THE AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR22* ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,23* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE24* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.25*26**************************************************************************/2728#ifdef HAVE_GALLIUM_EXTRA_HUD2930/* Purpose:31* Reading /sys/devices/system/cpu/cpu?/cpufreq/scaling_???_freq32* cpu frequency (KHz), displaying on the HUD in Hz.33*/3435#include "hud/hud_private.h"36#include "util/list.h"37#include "util/os_time.h"38#include "os/os_thread.h"39#include "util/u_memory.h"40#include <stdio.h>41#include <unistd.h>42#include <dirent.h>43#include <stdlib.h>44#include <errno.h>45#include <inttypes.h>46#include <sys/types.h>47#include <sys/stat.h>4849struct cpufreq_info50{51struct list_head list;52int mode; /* CPUFREQ_MINIMUM, CPUFREQ_CURRENT, CPUFREQ_MAXIMUM */53char name[16]; /* EG. cpu0 */54int cpu_index;5556/* EG. /sys/devices/system/cpu/cpu?/cpufreq/scaling_cur_freq */57char sysfs_filename[128];58uint64_t KHz;59uint64_t last_time;60};6162static int gcpufreq_count = 0;63static struct list_head gcpufreq_list;64static mtx_t gcpufreq_mutex = _MTX_INITIALIZER_NP;6566static struct cpufreq_info *67find_cfi_by_index(int cpu_index, int mode)68{69list_for_each_entry(struct cpufreq_info, cfi, &gcpufreq_list, list) {70if (cfi->mode != mode)71continue;72if (cfi->cpu_index == cpu_index)73return cfi;74}75return 0;76}7778static int79get_file_value(const char *fn, uint64_t *KHz)80{81FILE *fh = fopen(fn, "r");82if (!fh) {83fprintf(stderr, "%s error: %s\n", fn, strerror(errno));84return -1;85}86int ret = fscanf(fh, "%" PRIu64 "", KHz);87fclose(fh);8889return ret;90}9192static void93query_cfi_load(struct hud_graph *gr, struct pipe_context *pipe)94{95struct cpufreq_info *cfi = gr->query_data;9697uint64_t now = os_time_get();98if (cfi->last_time) {99if (cfi->last_time + gr->pane->period <= now) {100switch (cfi->mode) {101case CPUFREQ_MINIMUM:102case CPUFREQ_CURRENT:103case CPUFREQ_MAXIMUM:104get_file_value(cfi->sysfs_filename, &cfi->KHz);105hud_graph_add_value(gr, (uint64_t)cfi->KHz * 1000);106}107cfi->last_time = now;108}109} else {110/* initialize */111get_file_value(cfi->sysfs_filename, &cfi->KHz);112cfi->last_time = now;113}114}115116/**117* Create and initialize a new object for a specific CPU.118* \param pane parent context.119* \param cpu_index CPU identifier Eg. 0 (CPU0)120* \param mode query CPUFREQ_MINIMUM | CURRENT | MAXIMUM statistic.121*/122void123hud_cpufreq_graph_install(struct hud_pane *pane, int cpu_index,124unsigned int mode)125{126struct hud_graph *gr;127struct cpufreq_info *cfi;128129int num_cpus = hud_get_num_cpufreq(0);130if (num_cpus <= 0)131return;132133cfi = find_cfi_by_index(cpu_index, mode);134if (!cfi)135return;136137gr = CALLOC_STRUCT(hud_graph);138if (!gr)139return;140141cfi->mode = mode;142switch(cfi->mode) {143case CPUFREQ_MINIMUM:144snprintf(gr->name, sizeof(gr->name), "%s-Min", cfi->name);145break;146case CPUFREQ_CURRENT:147snprintf(gr->name, sizeof(gr->name), "%s-Cur", cfi->name);148break;149case CPUFREQ_MAXIMUM:150snprintf(gr->name, sizeof(gr->name), "%s-Max", cfi->name);151break;152default:153free(gr);154return;155}156157gr->query_data = cfi;158gr->query_new_value = query_cfi_load;159160hud_pane_add_graph(pane, gr);161hud_pane_set_max_value(pane, 3000000 /* 3 GHz */);162}163164static void165add_object(const char *name, const char *fn, int objmode, int cpu_index)166{167struct cpufreq_info *cfi = CALLOC_STRUCT(cpufreq_info);168169strcpy(cfi->name, name);170strcpy(cfi->sysfs_filename, fn);171cfi->mode = objmode;172cfi->cpu_index = cpu_index;173list_addtail(&cfi->list, &gcpufreq_list);174gcpufreq_count++;175}176177/**178* Initialize internal object arrays and display cpu freq HUD help.179* \param displayhelp true if the list of detected cpus should be180displayed on the console.181* \return number of detected CPU metrics (CPU count * 3)182*/183int184hud_get_num_cpufreq(bool displayhelp)185{186struct dirent *dp;187struct stat stat_buf;188char fn[128];189int cpu_index;190191/* Return the number of CPU metrics we support. */192mtx_lock(&gcpufreq_mutex);193if (gcpufreq_count) {194mtx_unlock(&gcpufreq_mutex);195return gcpufreq_count;196}197198/* Scan /sys/devices.../cpu, for every object type we support, create199* and persist an object to represent its different metrics.200*/201list_inithead(&gcpufreq_list);202DIR *dir = opendir("/sys/devices/system/cpu");203if (!dir) {204mtx_unlock(&gcpufreq_mutex);205return 0;206}207208while ((dp = readdir(dir)) != NULL) {209210size_t d_name_len = strlen(dp->d_name);211212/* Avoid 'lo' and '..' and '.', and avoid overlong names that213* would result in a buffer overflow in add_object.214*/215if (d_name_len <= 2 || d_name_len > 15)216continue;217218if (sscanf(dp->d_name, "cpu%d\n", &cpu_index) != 1)219continue;220221char basename[256];222snprintf(basename, sizeof(basename), "/sys/devices/system/cpu/%s", dp->d_name);223224snprintf(fn, sizeof(fn), "%s/cpufreq/scaling_cur_freq", basename);225if (stat(fn, &stat_buf) < 0)226continue;227228if (!S_ISREG(stat_buf.st_mode))229continue; /* Not a regular file */230231snprintf(fn, sizeof(fn), "%s/cpufreq/scaling_min_freq", basename);232add_object(dp->d_name, fn, CPUFREQ_MINIMUM, cpu_index);233234snprintf(fn, sizeof(fn), "%s/cpufreq/scaling_cur_freq", basename);235add_object(dp->d_name, fn, CPUFREQ_CURRENT, cpu_index);236237snprintf(fn, sizeof(fn), "%s/cpufreq/scaling_max_freq", basename);238add_object(dp->d_name, fn, CPUFREQ_MAXIMUM, cpu_index);239}240closedir(dir);241242if (displayhelp) {243list_for_each_entry(struct cpufreq_info, cfi, &gcpufreq_list, list) {244char line[128];245snprintf(line, sizeof(line), " cpufreq-%s-%s",246cfi->mode == CPUFREQ_MINIMUM ? "min" :247cfi->mode == CPUFREQ_CURRENT ? "cur" :248cfi->mode == CPUFREQ_MAXIMUM ? "max" : "undefined", cfi->name);249250puts(line);251}252}253254mtx_unlock(&gcpufreq_mutex);255return gcpufreq_count;256}257258#endif /* HAVE_GALLIUM_EXTRA_HUD */259260261