linux/tools/power/cpupower/utils/idle_monitor/nhm_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_C3_RESIDENCY    0x3F8
  19#define MSR_PKG_C6_RESIDENCY    0x3F9
  20#define MSR_CORE_C3_RESIDENCY   0x3FC
  21#define MSR_CORE_C6_RESIDENCY   0x3FD
  22
  23#define MSR_TSC 0x10
  24
  25#define NHM_CSTATE_COUNT 4
  26
  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        .needs_root             = 1,
 212        .overflow_s             = 922000000 /* 922337203 seconds TSC overflow
 213                                               at 20GHz */
 214};
 215#endif
 216