mirror of
https://github.com/autc04/Retro68.git
synced 2024-12-01 11:52:47 +00:00
304 lines
9.2 KiB
C
304 lines
9.2 KiB
C
/* Copyright (C) 2021 Free Software Foundation, Inc.
|
|
Contributed by Oracle.
|
|
|
|
This file is part of GNU Binutils.
|
|
|
|
This program is free software; you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation; either version 3, or (at your option)
|
|
any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program; if not, write to the Free Software
|
|
Foundation, 51 Franklin Street - Fifth Floor, Boston,
|
|
MA 02110-1301, USA. */
|
|
|
|
#ifndef _CPU_FREQUENCY_H
|
|
#define _CPU_FREQUENCY_H
|
|
|
|
#ifdef __cplusplus
|
|
extern "C"
|
|
{
|
|
#endif
|
|
|
|
#include <alloca.h>
|
|
#include <unistd.h> /* processor_info_t */
|
|
#include <fcntl.h>
|
|
|
|
typedef unsigned char uint8_t;
|
|
|
|
#define MAXSTRLEN 1024
|
|
/*
|
|
* This file provide the api to detect Intel CPU frequency variation features
|
|
*/
|
|
|
|
#define COL_CPUFREQ_NONE 0x0000
|
|
#define COL_CPUFREQ_SCALING 0x0001
|
|
#define COL_CPUFREQ_TURBO 0x0002
|
|
|
|
#if defined(__i386__) || defined(__x86_64)
|
|
// XXXX This is a rough table to estimate frequency increment due to intel turbo boost.
|
|
// CPU with different stepping and different core number have different turbo increment.
|
|
// It is used internally here, and is not implemented on SPARC
|
|
|
|
// YLM: one can use cputrack to estimate max turbo frequency
|
|
// example: for a cpu-bound app that runs for > 10 seconds, count cycles for 10 seconds:
|
|
// cputrack -T 10 -v -c cpu_clk_unhalted.thread_p a.out
|
|
|
|
static int
|
|
get_max_turbo_freq (int model)
|
|
{
|
|
switch (model)
|
|
{
|
|
// Nehalem
|
|
case 30:// Core i7-870: 2/2/4/5
|
|
return 2 * 133333;
|
|
case 26:// Xeon L5520: 1/1/1/2
|
|
return 2 * 133333;
|
|
case 46:// Xeon E7540: 2
|
|
return 2 * 133333;
|
|
// Westmere
|
|
case 37:// Core i5-520M: 2/4
|
|
return 2 * 133333;
|
|
case 44:// Xeon E5620: 1/1/2/2
|
|
return 2 * 133333;
|
|
case 47:// Xeon E7-2820: 1/1/1/2
|
|
return 1 * 133333;
|
|
// Sandy Bridge
|
|
case 42:// Core i5-2500: 1/2/3/4
|
|
return 3 * 100000;
|
|
// http://ark.intel.com/products/64584/Intel-Xeon-Processor-E5-2660-20M-Cache-2_20-GHz-8_00-GTs-Intel-QPI
|
|
case 45:// Xeon E5-2660 GenuineIntel 206D7 family 6 model 45 step 7 clock 2200 MHz
|
|
return 8 * 100000;
|
|
// Ivy Bridge
|
|
case 58:// Core i7-3770: 3/4/5/5
|
|
return 4 * 100000;
|
|
case 62:// Xeon E5-2697: 3/3/3/3/3/3/3/4/5/6/7/8
|
|
return 7 * 100000;
|
|
// Haswell
|
|
case 60:
|
|
return 789000; // empirically we see 3189 MHz - 2400 MHz
|
|
case 63:
|
|
return 1280000; // empirically we see 3580 MHz - 2300 MHz for single-threaded
|
|
// return 500000; // empirically we see 2800 MHz - 2300 MHz for large throughput
|
|
// Broadwell
|
|
// where are these values listed?
|
|
// maybe try https://en.wikipedia.org/wiki/Broadwell_%28microarchitecture%29#Server_processors
|
|
case 61:
|
|
return 400000;
|
|
case 71:
|
|
return 400000;
|
|
case 79:
|
|
return 950000; // empirically we see (3550-2600) MHz for single-threaded on x6-2a
|
|
case 85:
|
|
return 1600000; // X7: empirically see ~3.7GHz with single thread, baseline is 2.1Ghz Return 3,700,000-2,100,000
|
|
case 31: // Nehalem?
|
|
case 28: // Atom
|
|
case 69: // Haswell
|
|
case 70: // Haswell
|
|
case 78: // Skylake
|
|
case 94: // Skylake
|
|
default:
|
|
return 0;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
* parameter: mode, pointer to a 8bit mode indicator
|
|
* return: max cpu frequency in MHz
|
|
*/
|
|
//YXXX Updating this function? Check similar cut/paste code in:
|
|
// collctrl.cc::Coll_Ctrl()
|
|
// collector.c::log_header_write()
|
|
// cpu_frequency.h::get_cpu_frequency()
|
|
|
|
static int
|
|
get_cpu_frequency (uint8_t *mode)
|
|
{
|
|
int ret_freq = 0;
|
|
if (mode != NULL)
|
|
*mode = COL_CPUFREQ_NONE;
|
|
FILE *procf = fopen ("/proc/cpuinfo", "r");
|
|
if (procf != NULL)
|
|
{
|
|
char temp[1024];
|
|
int cpu = -1;
|
|
#if defined(__i386__) || defined(__x86_64)
|
|
int model = -1;
|
|
int family = -1;
|
|
#endif
|
|
while (fgets (temp, 1024, procf) != NULL)
|
|
{
|
|
if (strncmp (temp, "processor", strlen ("processor")) == 0)
|
|
{
|
|
char *val = strchr (temp, ':');
|
|
cpu = val ? atoi (val + 1) : -1;
|
|
}
|
|
#if defined(__i386__) || defined(__x86_64)
|
|
else if (strncmp (temp, "model", strlen ("model")) == 0
|
|
&& strstr (temp, "name") == 0)
|
|
{
|
|
char *val = strchr (temp, ':');
|
|
model = val ? atoi (val + 1) : -1;
|
|
}
|
|
else if (strncmp (temp, "cpu family", strlen ("cpu family")) == 0)
|
|
{
|
|
char *val = strchr (temp, ':');
|
|
family = val ? atoi (val + 1) : -1;
|
|
}
|
|
#endif
|
|
else if (strncmp (temp, "cpu MHz", strlen ("cpu MHz")) == 0)
|
|
{
|
|
char *val = strchr (temp, ':');
|
|
int mhz = val ? atoi (val + 1) : 0; /* reading it as int is fine */
|
|
char scaling_freq_file[MAXSTRLEN + 1];
|
|
snprintf (scaling_freq_file, sizeof (scaling_freq_file),
|
|
"/sys/devices/system/cpu/cpu%d/cpufreq/scaling_driver", cpu);
|
|
int intel_pstate = 0;
|
|
int no_turbo = 0;
|
|
if (access (scaling_freq_file, R_OK) == 0)
|
|
{
|
|
FILE *cpufreqd = fopen (scaling_freq_file, "r");
|
|
if (cpufreqd != NULL)
|
|
{
|
|
if (fgets (temp, 1024, cpufreqd) != NULL
|
|
&& strncmp (temp, "intel_pstate", sizeof ("intel_pstate") - 1) == 0)
|
|
intel_pstate = 1;
|
|
fclose (cpufreqd);
|
|
}
|
|
}
|
|
snprintf (scaling_freq_file, sizeof (scaling_freq_file),
|
|
"/sys/devices/system/cpu/intel_pstate/no_turbo");
|
|
if (access (scaling_freq_file, R_OK) == 0)
|
|
{
|
|
FILE *pstatent = fopen (scaling_freq_file, "r");
|
|
if (pstatent != NULL)
|
|
{
|
|
if (fgets (temp, 1024, pstatent) != NULL)
|
|
if (strncmp (temp, "1", sizeof ("1") - 1) == 0)
|
|
no_turbo = 1;
|
|
fclose (pstatent);
|
|
}
|
|
}
|
|
|
|
snprintf (scaling_freq_file, sizeof (scaling_freq_file),
|
|
"/sys/devices/system/cpu/cpu%d/cpufreq/scaling_governor", cpu);
|
|
int frequency_scaling = 0;
|
|
int turbo_mode = 0;
|
|
if (access (scaling_freq_file, R_OK) == 0)
|
|
{
|
|
FILE *cpufreqf = fopen (scaling_freq_file, "r");
|
|
if (cpufreqf != NULL)
|
|
{
|
|
if (fgets (temp, 1024, cpufreqf) != NULL)
|
|
{
|
|
int ondemand = 0;
|
|
if (strncmp (temp, "ondemand", sizeof ("ondemand") - 1) == 0)
|
|
ondemand = 1;
|
|
int performance = 0;
|
|
if (strncmp (temp, "performance", sizeof ("performance") - 1) == 0)
|
|
performance = 1;
|
|
int powersave = 0;
|
|
if (strncmp (temp, "powersave", sizeof ("powersave") - 1) == 0)
|
|
powersave = 1;
|
|
if (intel_pstate || ondemand || performance)
|
|
{
|
|
snprintf (scaling_freq_file, sizeof (scaling_freq_file),
|
|
"/sys/devices/system/cpu/cpu%d/cpufreq/scaling_max_freq", cpu);
|
|
if (access (scaling_freq_file, R_OK) == 0)
|
|
{
|
|
FILE * cpufreqf_max;
|
|
if ((cpufreqf_max = fopen (scaling_freq_file, "r")) != NULL)
|
|
{
|
|
if (fgets (temp, 1024, cpufreqf_max) != NULL)
|
|
{
|
|
int tmpmhz = atoi (temp);
|
|
snprintf (scaling_freq_file, sizeof (scaling_freq_file),
|
|
"/sys/devices/system/cpu/cpu%d/cpufreq/scaling_available_frequencies", cpu);
|
|
if (intel_pstate)
|
|
{
|
|
frequency_scaling = 1;
|
|
turbo_mode = !no_turbo;
|
|
if (powersave)
|
|
// the system might have been relatively cold
|
|
// so we might do better with scaling_max_freq
|
|
mhz = (int) (((double) tmpmhz / 1000.0) + 0.5);
|
|
}
|
|
else if (access (scaling_freq_file, R_OK) == 0)
|
|
{
|
|
FILE * cpufreqf_ava;
|
|
if ((cpufreqf_ava = fopen (scaling_freq_file, "r")) != NULL)
|
|
{
|
|
if (fgets (temp, 1024, cpufreqf_ava) != NULL)
|
|
{
|
|
if (strchr (temp, ' ') != strrchr (temp, ' ') && ondemand)
|
|
frequency_scaling = 1;
|
|
if (tmpmhz > 1000)
|
|
{
|
|
#if defined(__i386__) || defined(__x86_64)
|
|
if (family == 6)
|
|
{
|
|
// test turbo mode
|
|
char non_turbo_max_freq[1024];
|
|
snprintf (non_turbo_max_freq, sizeof (non_turbo_max_freq),
|
|
"%d", tmpmhz - 1000);
|
|
if (strstr (temp, non_turbo_max_freq))
|
|
{
|
|
turbo_mode = 1;
|
|
tmpmhz = (tmpmhz - 1000) + get_max_turbo_freq (model);
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
fclose (cpufreqf_ava);
|
|
}
|
|
mhz = (int) (((double) tmpmhz / 1000.0) + 0.5);
|
|
}
|
|
}
|
|
fclose (cpufreqf_max);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
fclose (cpufreqf);
|
|
}
|
|
}
|
|
if (mhz > ret_freq)
|
|
ret_freq = mhz;
|
|
if (frequency_scaling && mode != NULL)
|
|
*mode |= COL_CPUFREQ_SCALING;
|
|
if (turbo_mode && mode != NULL)
|
|
*mode |= COL_CPUFREQ_TURBO;
|
|
}
|
|
else if (strncmp (temp, "Cpu", 3) == 0 && temp[3] != '\0' &&
|
|
strncmp (strchr (temp + 1, 'C') ? strchr (temp + 1, 'C') : (temp + 4), "ClkTck", 6) == 0)
|
|
{ // sparc-Linux
|
|
char *val = strchr (temp, ':');
|
|
if (val)
|
|
{
|
|
unsigned long long freq;
|
|
sscanf (val + 2, "%llx", &freq);
|
|
int mhz = (unsigned int) (((double) freq) / 1000000.0 + 0.5);
|
|
if (mhz > ret_freq)
|
|
ret_freq = mhz;
|
|
}
|
|
}
|
|
}
|
|
fclose (procf);
|
|
}
|
|
return ret_freq;
|
|
}
|
|
|
|
#ifdef __cplusplus
|
|
}
|
|
#endif
|
|
|
|
#endif /*_CPU_FREQUENCY_H*/
|