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