This repository has been archived by the owner on Jan 23, 2020. It is now read-only.
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Browse files
Browse the repository at this point in the history
cpupower: Add Haswell family 0x45 specific idle monitor to show PC8,9…
…,10 states This specific processor supports 3 new package sleep states. Provide a monitor, so that the user can see their usage. Signed-off-by: Thomas Renninger <trenn@suse.de> Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
- Loading branch information
Showing
3 changed files
with
198 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,196 @@ | ||
/* | ||
* (C) 2010,2011 Thomas Renninger <trenn@suse.de>, Novell Inc. | ||
* | ||
* Licensed under the terms of the GNU GPL License version 2. | ||
* | ||
* Based on SandyBridge monitor. Implements the new package C-states | ||
* (PC8, PC9, PC10) coming with a specific Haswell (family 0x45) CPU. | ||
*/ | ||
|
||
#if defined(__i386__) || defined(__x86_64__) | ||
|
||
#include <stdio.h> | ||
#include <stdint.h> | ||
#include <stdlib.h> | ||
#include <string.h> | ||
|
||
#include "helpers/helpers.h" | ||
#include "idle_monitor/cpupower-monitor.h" | ||
|
||
#define MSR_PKG_C8_RESIDENCY 0x00000630 | ||
#define MSR_PKG_C9_RESIDENCY 0x00000631 | ||
#define MSR_PKG_C10_RESIDENCY 0x00000632 | ||
|
||
#define MSR_TSC 0x10 | ||
|
||
enum intel_hsw_ext_id { PC8 = 0, PC9, PC10, HSW_EXT_CSTATE_COUNT, | ||
TSC = 0xFFFF }; | ||
|
||
static int hsw_ext_get_count_percent(unsigned int self_id, double *percent, | ||
unsigned int cpu); | ||
|
||
static cstate_t hsw_ext_cstates[HSW_EXT_CSTATE_COUNT] = { | ||
{ | ||
.name = "PC8", | ||
.desc = N_("Processor Package C8"), | ||
.id = PC8, | ||
.range = RANGE_PACKAGE, | ||
.get_count_percent = hsw_ext_get_count_percent, | ||
}, | ||
{ | ||
.name = "PC9", | ||
.desc = N_("Processor Package C9"), | ||
.desc = N_("Processor Package C2"), | ||
.id = PC9, | ||
.range = RANGE_PACKAGE, | ||
.get_count_percent = hsw_ext_get_count_percent, | ||
}, | ||
{ | ||
.name = "PC10", | ||
.desc = N_("Processor Package C10"), | ||
.id = PC10, | ||
.range = RANGE_PACKAGE, | ||
.get_count_percent = hsw_ext_get_count_percent, | ||
}, | ||
}; | ||
|
||
static unsigned long long tsc_at_measure_start; | ||
static unsigned long long tsc_at_measure_end; | ||
static unsigned long long *previous_count[HSW_EXT_CSTATE_COUNT]; | ||
static unsigned long long *current_count[HSW_EXT_CSTATE_COUNT]; | ||
/* valid flag for all CPUs. If a MSR read failed it will be zero */ | ||
static int *is_valid; | ||
|
||
static int hsw_ext_get_count(enum intel_hsw_ext_id id, unsigned long long *val, | ||
unsigned int cpu) | ||
{ | ||
int msr; | ||
|
||
switch (id) { | ||
case PC8: | ||
msr = MSR_PKG_C8_RESIDENCY; | ||
break; | ||
case PC9: | ||
msr = MSR_PKG_C9_RESIDENCY; | ||
break; | ||
case PC10: | ||
msr = MSR_PKG_C10_RESIDENCY; | ||
break; | ||
case TSC: | ||
msr = MSR_TSC; | ||
break; | ||
default: | ||
return -1; | ||
}; | ||
if (read_msr(cpu, msr, val)) | ||
return -1; | ||
return 0; | ||
} | ||
|
||
static int hsw_ext_get_count_percent(unsigned int id, double *percent, | ||
unsigned int cpu) | ||
{ | ||
*percent = 0.0; | ||
|
||
if (!is_valid[cpu]) | ||
return -1; | ||
|
||
*percent = (100.0 * | ||
(current_count[id][cpu] - previous_count[id][cpu])) / | ||
(tsc_at_measure_end - tsc_at_measure_start); | ||
|
||
dprint("%s: previous: %llu - current: %llu - (%u)\n", | ||
hsw_ext_cstates[id].name, previous_count[id][cpu], | ||
current_count[id][cpu], cpu); | ||
|
||
dprint("%s: tsc_diff: %llu - count_diff: %llu - percent: %2.f (%u)\n", | ||
hsw_ext_cstates[id].name, | ||
(unsigned long long) tsc_at_measure_end - tsc_at_measure_start, | ||
current_count[id][cpu] - previous_count[id][cpu], | ||
*percent, cpu); | ||
|
||
return 0; | ||
} | ||
|
||
static int hsw_ext_start(void) | ||
{ | ||
int num, cpu; | ||
unsigned long long val; | ||
|
||
for (num = 0; num < HSW_EXT_CSTATE_COUNT; num++) { | ||
for (cpu = 0; cpu < cpu_count; cpu++) { | ||
hsw_ext_get_count(num, &val, cpu); | ||
previous_count[num][cpu] = val; | ||
} | ||
} | ||
hsw_ext_get_count(TSC, &tsc_at_measure_start, 0); | ||
return 0; | ||
} | ||
|
||
static int hsw_ext_stop(void) | ||
{ | ||
unsigned long long val; | ||
int num, cpu; | ||
|
||
hsw_ext_get_count(TSC, &tsc_at_measure_end, 0); | ||
|
||
for (num = 0; num < HSW_EXT_CSTATE_COUNT; num++) { | ||
for (cpu = 0; cpu < cpu_count; cpu++) { | ||
is_valid[cpu] = !hsw_ext_get_count(num, &val, cpu); | ||
current_count[num][cpu] = val; | ||
} | ||
} | ||
return 0; | ||
} | ||
|
||
struct cpuidle_monitor intel_hsw_ext_monitor; | ||
|
||
static struct cpuidle_monitor *hsw_ext_register(void) | ||
{ | ||
int num; | ||
|
||
if (cpupower_cpu_info.vendor != X86_VENDOR_INTEL | ||
|| cpupower_cpu_info.family != 6) | ||
return NULL; | ||
|
||
switch (cpupower_cpu_info.model) { | ||
case 0x45: /* HSW */ | ||
break; | ||
default: | ||
return NULL; | ||
} | ||
|
||
is_valid = calloc(cpu_count, sizeof(int)); | ||
for (num = 0; num < HSW_EXT_CSTATE_COUNT; num++) { | ||
previous_count[num] = calloc(cpu_count, | ||
sizeof(unsigned long long)); | ||
current_count[num] = calloc(cpu_count, | ||
sizeof(unsigned long long)); | ||
} | ||
intel_hsw_ext_monitor.name_len = strlen(intel_hsw_ext_monitor.name); | ||
return &intel_hsw_ext_monitor; | ||
} | ||
|
||
void hsw_ext_unregister(void) | ||
{ | ||
int num; | ||
free(is_valid); | ||
for (num = 0; num < HSW_EXT_CSTATE_COUNT; num++) { | ||
free(previous_count[num]); | ||
free(current_count[num]); | ||
} | ||
} | ||
|
||
struct cpuidle_monitor intel_hsw_ext_monitor = { | ||
.name = "HaswellExtended", | ||
.hw_states = hsw_ext_cstates, | ||
.hw_states_num = HSW_EXT_CSTATE_COUNT, | ||
.start = hsw_ext_start, | ||
.stop = hsw_ext_stop, | ||
.do_register = hsw_ext_register, | ||
.unregister = hsw_ext_unregister, | ||
.needs_root = 1, | ||
.overflow_s = 922000000 /* 922337203 seconds TSC overflow | ||
at 20GHz */ | ||
}; | ||
#endif /* defined(__i386__) || defined(__x86_64__) */ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,6 +2,7 @@ | |
DEF(amd_fam14h) | ||
DEF(intel_nhm) | ||
DEF(intel_snb) | ||
DEF(intel_hsw_ext) | ||
DEF(mperf) | ||
#endif | ||
DEF(cpuidle_sysfs) |