216 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * (C) 2010,2011 Thomas Renninger <trenn@suse.de>, Novell Inc.4 *5 * Based on Len Brown's <lenb@kernel.org> turbostat tool.6 */7 8#if defined(__i386__) || defined(__x86_64__)9 10#include <stdio.h>11#include <stdint.h>12#include <stdlib.h>13#include <string.h>14 15#include "helpers/helpers.h"16#include "idle_monitor/cpupower-monitor.h"17 18#define MSR_PKG_C3_RESIDENCY 0x3F819#define MSR_PKG_C6_RESIDENCY 0x3F920#define MSR_CORE_C3_RESIDENCY 0x3FC21#define MSR_CORE_C6_RESIDENCY 0x3FD22 23#define MSR_TSC 0x1024 25#define NHM_CSTATE_COUNT 426 27enum intel_nhm_id { C3 = 0, C6, PC3, PC6, TSC = 0xFFFF };28 29static int nhm_get_count_percent(unsigned int self_id, double *percent,30 unsigned int cpu);31 32static cstate_t nhm_cstates[NHM_CSTATE_COUNT] = {33 {34 .name = "C3",35 .desc = N_("Processor Core C3"),36 .id = C3,37 .range = RANGE_CORE,38 .get_count_percent = nhm_get_count_percent,39 },40 {41 .name = "C6",42 .desc = N_("Processor Core C6"),43 .id = C6,44 .range = RANGE_CORE,45 .get_count_percent = nhm_get_count_percent,46 },47 48 {49 .name = "PC3",50 .desc = N_("Processor Package C3"),51 .id = PC3,52 .range = RANGE_PACKAGE,53 .get_count_percent = nhm_get_count_percent,54 },55 {56 .name = "PC6",57 .desc = N_("Processor Package C6"),58 .id = PC6,59 .range = RANGE_PACKAGE,60 .get_count_percent = nhm_get_count_percent,61 },62};63 64static unsigned long long tsc_at_measure_start;65static unsigned long long tsc_at_measure_end;66static unsigned long long *previous_count[NHM_CSTATE_COUNT];67static unsigned long long *current_count[NHM_CSTATE_COUNT];68/* valid flag for all CPUs. If a MSR read failed it will be zero */69static int *is_valid;70 71static int nhm_get_count(enum intel_nhm_id id, unsigned long long *val,72 unsigned int cpu)73{74 int msr;75 76 switch (id) {77 case C3:78 msr = MSR_CORE_C3_RESIDENCY;79 break;80 case C6:81 msr = MSR_CORE_C6_RESIDENCY;82 break;83 case PC3:84 msr = MSR_PKG_C3_RESIDENCY;85 break;86 case PC6:87 msr = MSR_PKG_C6_RESIDENCY;88 break;89 case TSC:90 msr = MSR_TSC;91 break;92 default:93 return -1;94 }95 if (read_msr(cpu, msr, val))96 return -1;97 98 return 0;99}100 101static int nhm_get_count_percent(unsigned int id, double *percent,102 unsigned int cpu)103{104 *percent = 0.0;105 106 if (!is_valid[cpu])107 return -1;108 109 *percent = (100.0 *110 (current_count[id][cpu] - previous_count[id][cpu])) /111 (tsc_at_measure_end - tsc_at_measure_start);112 113 dprint("%s: previous: %llu - current: %llu - (%u)\n",114 nhm_cstates[id].name, previous_count[id][cpu],115 current_count[id][cpu], cpu);116 117 dprint("%s: tsc_diff: %llu - count_diff: %llu - percent: %2.f (%u)\n",118 nhm_cstates[id].name,119 (unsigned long long) tsc_at_measure_end - tsc_at_measure_start,120 current_count[id][cpu] - previous_count[id][cpu],121 *percent, cpu);122 123 return 0;124}125 126static int nhm_start(void)127{128 int num, cpu;129 unsigned long long dbg, val;130 131 nhm_get_count(TSC, &tsc_at_measure_start, base_cpu);132 133 for (num = 0; num < NHM_CSTATE_COUNT; num++) {134 for (cpu = 0; cpu < cpu_count; cpu++) {135 is_valid[cpu] = !nhm_get_count(num, &val, cpu);136 previous_count[num][cpu] = val;137 }138 }139 nhm_get_count(TSC, &dbg, base_cpu);140 dprint("TSC diff: %llu\n", dbg - tsc_at_measure_start);141 return 0;142}143 144static int nhm_stop(void)145{146 unsigned long long val;147 unsigned long long dbg;148 int num, cpu;149 150 nhm_get_count(TSC, &tsc_at_measure_end, base_cpu);151 152 for (num = 0; num < NHM_CSTATE_COUNT; num++) {153 for (cpu = 0; cpu < cpu_count; cpu++) {154 is_valid[cpu] = !nhm_get_count(num, &val, cpu);155 current_count[num][cpu] = val;156 }157 }158 nhm_get_count(TSC, &dbg, base_cpu);159 dprint("TSC diff: %llu\n", dbg - tsc_at_measure_end);160 161 return 0;162}163 164struct cpuidle_monitor intel_nhm_monitor;165 166struct cpuidle_monitor *intel_nhm_register(void)167{168 int num;169 170 if (cpupower_cpu_info.vendor != X86_VENDOR_INTEL)171 return NULL;172 173 if (!(cpupower_cpu_info.caps & CPUPOWER_CAP_INV_TSC))174 return NULL;175 176 if (!(cpupower_cpu_info.caps & CPUPOWER_CAP_APERF))177 return NULL;178 179 /* Free this at program termination */180 is_valid = calloc(cpu_count, sizeof(int));181 for (num = 0; num < NHM_CSTATE_COUNT; num++) {182 previous_count[num] = calloc(cpu_count,183 sizeof(unsigned long long));184 current_count[num] = calloc(cpu_count,185 sizeof(unsigned long long));186 }187 188 intel_nhm_monitor.name_len = strlen(intel_nhm_monitor.name);189 return &intel_nhm_monitor;190}191 192void intel_nhm_unregister(void)193{194 int num;195 196 for (num = 0; num < NHM_CSTATE_COUNT; num++) {197 free(previous_count[num]);198 free(current_count[num]);199 }200 free(is_valid);201}202 203struct cpuidle_monitor intel_nhm_monitor = {204 .name = "Nehalem",205 .hw_states_num = NHM_CSTATE_COUNT,206 .hw_states = nhm_cstates,207 .start = nhm_start,208 .stop = nhm_stop,209 .do_register = intel_nhm_register,210 .unregister = intel_nhm_unregister,211 .flags.needs_root = 1,212 .overflow_s = 922000000 /* 922337203 seconds TSC overflow213 at 20GHz */214};215#endif216