linux/tools/power/cpupower/utils/idle_monitor/snb_idle.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2/*
   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    0x60D
  19#define MSR_PKG_C7_RESIDENCY    0x3FA
  20#define MSR_CORE_C7_RESIDENCY   0x3FE
  21
  22#define MSR_TSC 0x10
  23
  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_INTEL
 149            || 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 overflow
 197                                               at 20GHz */
 198};
 199#endif /* defined(__i386__) || defined(__x86_64__) */
 200