linux/tools/power/cpupower/utils/idle_monitor/hsw_ext_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 SandyBridge monitor. Implements the new package C-states
   6 *  (PC8, PC9, PC10) coming with a specific Haswell (family 0x45) CPU.
   7 */
   8
   9#if defined(__i386__) || defined(__x86_64__)
  10
  11#include <stdio.h>
  12#include <stdint.h>
  13#include <stdlib.h>
  14#include <string.h>
  15
  16#include "helpers/helpers.h"
  17#include "idle_monitor/cpupower-monitor.h"
  18
  19#define MSR_PKG_C8_RESIDENCY           0x00000630
  20#define MSR_PKG_C9_RESIDENCY           0x00000631
  21#define MSR_PKG_C10_RESIDENCY          0x00000632
  22
  23#define MSR_TSC 0x10
  24
  25enum intel_hsw_ext_id { PC8 = 0, PC9, PC10, HSW_EXT_CSTATE_COUNT,
  26                        TSC = 0xFFFF };
  27
  28static int hsw_ext_get_count_percent(unsigned int self_id, double *percent,
  29                                 unsigned int cpu);
  30
  31static cstate_t hsw_ext_cstates[HSW_EXT_CSTATE_COUNT] = {
  32        {
  33                .name                   = "PC8",
  34                .desc                   = N_("Processor Package C8"),
  35                .id                     = PC8,
  36                .range                  = RANGE_PACKAGE,
  37                .get_count_percent      = hsw_ext_get_count_percent,
  38        },
  39        {
  40                .name                   = "PC9",
  41                .desc                   = N_("Processor Package C9"),
  42                .desc                   = N_("Processor Package C2"),
  43                .id                     = PC9,
  44                .range                  = RANGE_PACKAGE,
  45                .get_count_percent      = hsw_ext_get_count_percent,
  46        },
  47        {
  48                .name                   = "PC10",
  49                .desc                   = N_("Processor Package C10"),
  50                .id                     = PC10,
  51                .range                  = RANGE_PACKAGE,
  52                .get_count_percent      = hsw_ext_get_count_percent,
  53        },
  54};
  55
  56static unsigned long long tsc_at_measure_start;
  57static unsigned long long tsc_at_measure_end;
  58static unsigned long long *previous_count[HSW_EXT_CSTATE_COUNT];
  59static unsigned long long *current_count[HSW_EXT_CSTATE_COUNT];
  60/* valid flag for all CPUs. If a MSR read failed it will be zero */
  61static int *is_valid;
  62
  63static int hsw_ext_get_count(enum intel_hsw_ext_id id, unsigned long long *val,
  64                        unsigned int cpu)
  65{
  66        int msr;
  67
  68        switch (id) {
  69        case PC8:
  70                msr = MSR_PKG_C8_RESIDENCY;
  71                break;
  72        case PC9:
  73                msr = MSR_PKG_C9_RESIDENCY;
  74                break;
  75        case PC10:
  76                msr = MSR_PKG_C10_RESIDENCY;
  77                break;
  78        case TSC:
  79                msr = MSR_TSC;
  80                break;
  81        default:
  82                return -1;
  83        };
  84        if (read_msr(cpu, msr, val))
  85                return -1;
  86        return 0;
  87}
  88
  89static int hsw_ext_get_count_percent(unsigned int id, double *percent,
  90                                 unsigned int cpu)
  91{
  92        *percent = 0.0;
  93
  94        if (!is_valid[cpu])
  95                return -1;
  96
  97        *percent = (100.0 *
  98                (current_count[id][cpu] - previous_count[id][cpu])) /
  99                (tsc_at_measure_end - tsc_at_measure_start);
 100
 101        dprint("%s: previous: %llu - current: %llu - (%u)\n",
 102                hsw_ext_cstates[id].name, previous_count[id][cpu],
 103                current_count[id][cpu], cpu);
 104
 105        dprint("%s: tsc_diff: %llu - count_diff: %llu - percent: %2.f (%u)\n",
 106               hsw_ext_cstates[id].name,
 107               (unsigned long long) tsc_at_measure_end - tsc_at_measure_start,
 108               current_count[id][cpu] - previous_count[id][cpu],
 109               *percent, cpu);
 110
 111        return 0;
 112}
 113
 114static int hsw_ext_start(void)
 115{
 116        int num, cpu;
 117        unsigned long long val;
 118
 119        for (num = 0; num < HSW_EXT_CSTATE_COUNT; num++) {
 120                for (cpu = 0; cpu < cpu_count; cpu++) {
 121                        hsw_ext_get_count(num, &val, cpu);
 122                        previous_count[num][cpu] = val;
 123                }
 124        }
 125        hsw_ext_get_count(TSC, &tsc_at_measure_start, base_cpu);
 126        return 0;
 127}
 128
 129static int hsw_ext_stop(void)
 130{
 131        unsigned long long val;
 132        int num, cpu;
 133
 134        hsw_ext_get_count(TSC, &tsc_at_measure_end, base_cpu);
 135
 136        for (num = 0; num < HSW_EXT_CSTATE_COUNT; num++) {
 137                for (cpu = 0; cpu < cpu_count; cpu++) {
 138                        is_valid[cpu] = !hsw_ext_get_count(num, &val, cpu);
 139                        current_count[num][cpu] = val;
 140                }
 141        }
 142        return 0;
 143}
 144
 145struct cpuidle_monitor intel_hsw_ext_monitor;
 146
 147static struct cpuidle_monitor *hsw_ext_register(void)
 148{
 149        int num;
 150
 151        if (cpupower_cpu_info.vendor != X86_VENDOR_INTEL
 152            || cpupower_cpu_info.family != 6)
 153                return NULL;
 154
 155        switch (cpupower_cpu_info.model) {
 156        case 0x45: /* HSW */
 157                break;
 158        default:
 159                return NULL;
 160        }
 161
 162        is_valid = calloc(cpu_count, sizeof(int));
 163        for (num = 0; num < HSW_EXT_CSTATE_COUNT; num++) {
 164                previous_count[num] = calloc(cpu_count,
 165                                        sizeof(unsigned long long));
 166                current_count[num]  = calloc(cpu_count,
 167                                        sizeof(unsigned long long));
 168        }
 169        intel_hsw_ext_monitor.name_len = strlen(intel_hsw_ext_monitor.name);
 170        return &intel_hsw_ext_monitor;
 171}
 172
 173void hsw_ext_unregister(void)
 174{
 175        int num;
 176        free(is_valid);
 177        for (num = 0; num < HSW_EXT_CSTATE_COUNT; num++) {
 178                free(previous_count[num]);
 179                free(current_count[num]);
 180        }
 181}
 182
 183struct cpuidle_monitor intel_hsw_ext_monitor = {
 184        .name                   = "HaswellExtended",
 185        .hw_states              = hsw_ext_cstates,
 186        .hw_states_num          = HSW_EXT_CSTATE_COUNT,
 187        .start                  = hsw_ext_start,
 188        .stop                   = hsw_ext_stop,
 189        .do_register            = hsw_ext_register,
 190        .unregister             = hsw_ext_unregister,
 191        .needs_root             = 1,
 192        .overflow_s             = 922000000 /* 922337203 seconds TSC overflow
 193                                               at 20GHz */
 194};
 195#endif /* defined(__i386__) || defined(__x86_64__) */
 196