200 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_C2_RESIDENCY 0x60D19#define MSR_PKG_C7_RESIDENCY 0x3FA20#define MSR_CORE_C7_RESIDENCY 0x3FE21 22#define MSR_TSC 0x1023 24enum intel_snb_id { C7 = 0, PC2, PC7, SNB_CSTATE_COUNT, TSC = 0xFFFF };25 26static int snb_get_count_percent(unsigned int self_id, double *percent,27 unsigned int cpu);28 29static cstate_t snb_cstates[SNB_CSTATE_COUNT] = {30 {31 .name = "C7",32 .desc = N_("Processor Core C7"),33 .id = C7,34 .range = RANGE_CORE,35 .get_count_percent = snb_get_count_percent,36 },37 {38 .name = "PC2",39 .desc = N_("Processor Package C2"),40 .id = PC2,41 .range = RANGE_PACKAGE,42 .get_count_percent = snb_get_count_percent,43 },44 {45 .name = "PC7",46 .desc = N_("Processor Package C7"),47 .id = PC7,48 .range = RANGE_PACKAGE,49 .get_count_percent = snb_get_count_percent,50 },51};52 53static unsigned long long tsc_at_measure_start;54static unsigned long long tsc_at_measure_end;55static unsigned long long *previous_count[SNB_CSTATE_COUNT];56static unsigned long long *current_count[SNB_CSTATE_COUNT];57/* valid flag for all CPUs. If a MSR read failed it will be zero */58static int *is_valid;59 60static int snb_get_count(enum intel_snb_id id, unsigned long long *val,61 unsigned int cpu)62{63 int msr;64 65 switch (id) {66 case C7:67 msr = MSR_CORE_C7_RESIDENCY;68 break;69 case PC2:70 msr = MSR_PKG_C2_RESIDENCY;71 break;72 case PC7:73 msr = MSR_PKG_C7_RESIDENCY;74 break;75 case TSC:76 msr = MSR_TSC;77 break;78 default:79 return -1;80 }81 if (read_msr(cpu, msr, val))82 return -1;83 return 0;84}85 86static int snb_get_count_percent(unsigned int id, double *percent,87 unsigned int cpu)88{89 *percent = 0.0;90 91 if (!is_valid[cpu])92 return -1;93 94 *percent = (100.0 *95 (current_count[id][cpu] - previous_count[id][cpu])) /96 (tsc_at_measure_end - tsc_at_measure_start);97 98 dprint("%s: previous: %llu - current: %llu - (%u)\n",99 snb_cstates[id].name, previous_count[id][cpu],100 current_count[id][cpu], cpu);101 102 dprint("%s: tsc_diff: %llu - count_diff: %llu - percent: %2.f (%u)\n",103 snb_cstates[id].name,104 (unsigned long long) tsc_at_measure_end - tsc_at_measure_start,105 current_count[id][cpu] - previous_count[id][cpu],106 *percent, cpu);107 108 return 0;109}110 111static int snb_start(void)112{113 int num, cpu;114 unsigned long long val;115 116 for (num = 0; num < SNB_CSTATE_COUNT; num++) {117 for (cpu = 0; cpu < cpu_count; cpu++) {118 snb_get_count(num, &val, cpu);119 previous_count[num][cpu] = val;120 }121 }122 snb_get_count(TSC, &tsc_at_measure_start, base_cpu);123 return 0;124}125 126static int snb_stop(void)127{128 unsigned long long val;129 int num, cpu;130 131 snb_get_count(TSC, &tsc_at_measure_end, base_cpu);132 133 for (num = 0; num < SNB_CSTATE_COUNT; num++) {134 for (cpu = 0; cpu < cpu_count; cpu++) {135 is_valid[cpu] = !snb_get_count(num, &val, cpu);136 current_count[num][cpu] = val;137 }138 }139 return 0;140}141 142struct cpuidle_monitor intel_snb_monitor;143 144static struct cpuidle_monitor *snb_register(void)145{146 int num;147 148 if (cpupower_cpu_info.vendor != X86_VENDOR_INTEL149 || cpupower_cpu_info.family != 6)150 return NULL;151 152 switch (cpupower_cpu_info.model) {153 case 0x2A: /* SNB */154 case 0x2D: /* SNB Xeon */155 case 0x3A: /* IVB */156 case 0x3E: /* IVB Xeon */157 case 0x3C: /* HSW */158 case 0x3F: /* HSW */159 case 0x45: /* HSW */160 case 0x46: /* HSW */161 break;162 default:163 return NULL;164 }165 166 is_valid = calloc(cpu_count, sizeof(int));167 for (num = 0; num < SNB_CSTATE_COUNT; num++) {168 previous_count[num] = calloc(cpu_count,169 sizeof(unsigned long long));170 current_count[num] = calloc(cpu_count,171 sizeof(unsigned long long));172 }173 intel_snb_monitor.name_len = strlen(intel_snb_monitor.name);174 return &intel_snb_monitor;175}176 177void snb_unregister(void)178{179 int num;180 free(is_valid);181 for (num = 0; num < SNB_CSTATE_COUNT; num++) {182 free(previous_count[num]);183 free(current_count[num]);184 }185}186 187struct cpuidle_monitor intel_snb_monitor = {188 .name = "SandyBridge",189 .hw_states = snb_cstates,190 .hw_states_num = SNB_CSTATE_COUNT,191 .start = snb_start,192 .stop = snb_stop,193 .do_register = snb_register,194 .unregister = snb_unregister,195 .flags.needs_root = 1,196 .overflow_s = 922000000 /* 922337203 seconds TSC overflow197 at 20GHz */198};199#endif /* defined(__i386__) || defined(__x86_64__) */200