From 9c0ff0eed12150e4b27b266af581b7d4758019a3 Mon Sep 17 00:00:00 2001 From: Pawel Dziepak Date: Wed, 30 Oct 2013 00:55:03 +0100 Subject: [PATCH] kernel: Add cpufreq module for Intel P-states Since Sandy Bridge managing P-states on Intel processors is much easier and more powerful than when using previous versions of EIST. --- build/jam/packages/Haiku | 1 + build/jam/packages/HaikuBootstrap | 1 + headers/private/kernel/arch/x86/arch_cpu.h | 6 + src/add-ons/kernel/Jamfile | 1 + src/add-ons/kernel/power/Jamfile | 4 + src/add-ons/kernel/power/cpufreq/Jamfile | 4 + .../power/cpufreq/intel_pstates/Jamfile | 8 + .../cpufreq/intel_pstates/intel_pstates.cpp | 234 ++++++++++++++++++ 8 files changed, 259 insertions(+) create mode 100644 src/add-ons/kernel/power/Jamfile create mode 100644 src/add-ons/kernel/power/cpufreq/Jamfile create mode 100644 src/add-ons/kernel/power/cpufreq/intel_pstates/Jamfile create mode 100644 src/add-ons/kernel/power/cpufreq/intel_pstates/intel_pstates.cpp diff --git a/build/jam/packages/Haiku b/build/jam/packages/Haiku index fa75457e8e..2ca840a6ff 100644 --- a/build/jam/packages/Haiku +++ b/build/jam/packages/Haiku @@ -52,6 +52,7 @@ AddFilesToPackage add-ons kernel generic AddFilesToPackage add-ons kernel partitioning_systems : amiga_rdb apple efi_gpt intel session ; AddFilesToPackage add-ons kernel interrupt_controllers : openpic@ppc ; +AddFilesToPackage add-ons kernel power cpufreq : intel_pstates@x86,x86_64 ; if $(TARGET_ARCH) = x86 || $(TARGET_ARCH) = x86_64 { AddFilesToPackage add-ons kernel cpu : generic_x86 ; diff --git a/build/jam/packages/HaikuBootstrap b/build/jam/packages/HaikuBootstrap index 491ebaeeb4..f4a419d7d7 100644 --- a/build/jam/packages/HaikuBootstrap +++ b/build/jam/packages/HaikuBootstrap @@ -51,6 +51,7 @@ AddFilesToPackage add-ons kernel generic AddFilesToPackage add-ons kernel partitioning_systems : amiga_rdb apple efi_gpt intel session ; AddFilesToPackage add-ons kernel interrupt_controllers : openpic@ppc ; +AddFilesToPackage add-ons kernel power cpufreq : intel_pstates@x86,x86_64 ; if $(TARGET_ARCH) = x86 || $(TARGET_ARCH) = x86_64 { AddFilesToPackage add-ons kernel cpu : generic_x86 ; diff --git a/headers/private/kernel/arch/x86/arch_cpu.h b/headers/private/kernel/arch/x86/arch_cpu.h index c51ee6f2fd..33cf17e4ae 100644 --- a/headers/private/kernel/arch/x86/arch_cpu.h +++ b/headers/private/kernel/arch/x86/arch_cpu.h @@ -31,10 +31,16 @@ #define IA32_MSR_TSC 0x10 #define IA32_MSR_APIC_BASE 0x1b +#define IA32_MSR_PLATFORM_INFO 0xce +#define IA32_MSR_MPERF 0xe7 +#define IA32_MSR_APERF 0xe8 #define IA32_MSR_MTRR_CAPABILITIES 0xfe #define IA32_MSR_SYSENTER_CS 0x174 #define IA32_MSR_SYSENTER_ESP 0x175 #define IA32_MSR_SYSENTER_EIP 0x176 +#define IA32_MSR_PERF_STATUS 0x198 +#define IA32_MSR_PERF_CTL 0x199 +#define IA32_MSR_TURBO_RATIO_LIMIT 0x1ad #define IA32_MSR_ENERGY_PERF_BIAS 0x1b0 #define IA32_MSR_MTRR_DEFAULT_TYPE 0x2ff #define IA32_MSR_MTRR_PHYSICAL_BASE_0 0x200 diff --git a/src/add-ons/kernel/Jamfile b/src/add-ons/kernel/Jamfile index 5a5b275cb3..a58d6382b9 100644 --- a/src/add-ons/kernel/Jamfile +++ b/src/add-ons/kernel/Jamfile @@ -14,3 +14,4 @@ SubInclude HAIKU_TOP src add-ons kernel media ; SubInclude HAIKU_TOP src add-ons kernel partitioning_systems ; SubInclude HAIKU_TOP src add-ons kernel generic ; SubInclude HAIKU_TOP src add-ons kernel bluetooth ; +SubInclude HAIKU_TOP src add-ons kernel power ; diff --git a/src/add-ons/kernel/power/Jamfile b/src/add-ons/kernel/power/Jamfile new file mode 100644 index 0000000000..84032da183 --- /dev/null +++ b/src/add-ons/kernel/power/Jamfile @@ -0,0 +1,4 @@ +SubDir HAIKU_TOP src add-ons kernel power ; + +SubInclude HAIKU_TOP src add-ons kernel power cpufreq ; + diff --git a/src/add-ons/kernel/power/cpufreq/Jamfile b/src/add-ons/kernel/power/cpufreq/Jamfile new file mode 100644 index 0000000000..163027559b --- /dev/null +++ b/src/add-ons/kernel/power/cpufreq/Jamfile @@ -0,0 +1,4 @@ +SubDir HAIKU_TOP src add-ons kernel power cpufreq ; + +SubInclude HAIKU_TOP src add-ons kernel power cpufreq intel_pstates ; + diff --git a/src/add-ons/kernel/power/cpufreq/intel_pstates/Jamfile b/src/add-ons/kernel/power/cpufreq/intel_pstates/Jamfile new file mode 100644 index 0000000000..22b8211d94 --- /dev/null +++ b/src/add-ons/kernel/power/cpufreq/intel_pstates/Jamfile @@ -0,0 +1,8 @@ +SubDir HAIKU_TOP src add-ons kernel power cpufreq intel_pstates ; + +UsePrivateKernelHeaders ; + +KernelAddon intel_pstates : + intel_pstates.cpp + ; + diff --git a/src/add-ons/kernel/power/cpufreq/intel_pstates/intel_pstates.cpp b/src/add-ons/kernel/power/cpufreq/intel_pstates/intel_pstates.cpp new file mode 100644 index 0000000000..7af294bf9c --- /dev/null +++ b/src/add-ons/kernel/power/cpufreq/intel_pstates/intel_pstates.cpp @@ -0,0 +1,234 @@ +/* + * Copyright 2013, Haiku, Inc. All Rights Reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * Paweł Dziepak, + */ + + +#include +#include + +#include +#include +#include +#include + + +#define INTEL_PSTATES_MODULE_NAME CPUFREQ_MODULES_PREFIX "/intel_pstates/v1" + + +const int kMinimalInterval = 50000; + +static uint16 sMinPState; +static uint16 sMaxPState; +static uint16 sBoostPState; + + +struct CPUEntry { + CPUEntry(); + + uint16 fCurrentPState; + + bigtime_t fLastUpdate; +}; +static CPUEntry* sCPUEntries; + + +CPUEntry::CPUEntry() + : + fCurrentPState(sMinPState - 1), + fLastUpdate(0) +{ +} + + +static int +measure_pstate(CPUEntry* entry) +{ + InterruptsLocker locker; + + uint64 mperf = x86_read_msr(IA32_MSR_MPERF); + uint64 aperf = x86_read_msr(IA32_MSR_APERF); + + x86_write_msr(IA32_MSR_MPERF, 0); + x86_write_msr(IA32_MSR_APERF, 0); + + locker.Unlock(); + + if (mperf == 0) + return sMinPState; + + int oldPState = sMaxPState * aperf / mperf; + oldPState = min_c(max_c(oldPState, sMinPState), sBoostPState); + + return oldPState; +} + + +static inline void +set_pstate(uint16 pstate) +{ + CPUEntry* entry = &sCPUEntries[smp_get_current_cpu()]; + pstate = min_c(max_c(sMinPState, pstate), sBoostPState); + + if (entry->fCurrentPState != pstate) { + entry->fLastUpdate = system_time(); + entry->fCurrentPState = pstate; + + x86_write_msr(IA32_MSR_PERF_CTL, pstate << 8); + } +} + + +static status_t +increase_performance(int delta, bool allowBoost) +{ + CPUEntry* entry = &sCPUEntries[smp_get_current_cpu()]; + + if (system_time() - entry->fLastUpdate < kMinimalInterval) + return B_OK; + + int pState = measure_pstate(entry); + pState += (sBoostPState - pState) * delta / kCPUPerformanceScaleMax; + + if (!allowBoost) + pState = min_c(pState, sMaxPState); + + set_pstate(pState); + return B_OK; +} + + +static status_t +decrease_performance(int delta) +{ + CPUEntry* entry = &sCPUEntries[smp_get_current_cpu()]; + + if (system_time() - entry->fLastUpdate < kMinimalInterval) + return B_OK; + + int pState = measure_pstate(entry); + pState -= (pState - sMinPState) * delta / kCPUPerformanceScaleMax; + + set_pstate(pState); + return B_OK; +} + + +static bool +is_cpu_model_supported(cpu_ent* cpu) +{ + uint8 model = cpu->arch.model + (cpu->arch.extended_model << 4); + + if (cpu->arch.vendor != VENDOR_INTEL) + return false; + + if (cpu->arch.family != 6) + return false; + + const uint8 kSupportedFamily6Models[] = { + 0x2a, 0x2d, 0x2e, 0x3a, 0x3c, 0x3e, 0x3f, 0x45, 0x46, + }; + const int kSupportedFamily6ModelsCount + = sizeof(kSupportedFamily6Models) / sizeof(uint8); + + int i; + for (i = 0; i < kSupportedFamily6ModelsCount; i++) { + if (model == kSupportedFamily6Models[i]) + break; + } + + return i != kSupportedFamily6ModelsCount; +} + + +static void +set_normal_pstate(void* /* dummy */, int cpu) +{ + measure_pstate(&sCPUEntries[cpu]); + set_pstate(sMaxPState); +} + + +static status_t +init_pstates() +{ + if (!x86_check_feature(IA32_FEATURE_MSR, FEATURE_COMMON)) + return B_ERROR; + + if (!x86_check_feature(IA32_FEATURE_APERFMPERF, FEATURE_6_ECX)) + return B_ERROR; + + int32 cpuCount = smp_get_num_cpus(); + for (int32 i = 0; i < cpuCount; i++) { + if (!is_cpu_model_supported(&gCPU[i])) + return B_ERROR; + } + + sMinPState = (x86_read_msr(IA32_MSR_PLATFORM_INFO) >> 40) & 0xff; + sMaxPState = (x86_read_msr(IA32_MSR_PLATFORM_INFO) >> 8) & 0xff; + sBoostPState + = max_c(x86_read_msr(IA32_MSR_TURBO_RATIO_LIMIT) & 0xff, sMaxPState); + + dprintf("using Intel P-States: min %" B_PRIu16 ", max %" B_PRIu16 + ", boost %" B_PRIu16 "\n", sMinPState, sMaxPState, sBoostPState); + + if (sMaxPState <= sMinPState || sMaxPState == 0) { + dprintf("unexpected or invalid Intel P-States limits, aborting\n"); + return B_ERROR; + } + + sCPUEntries = new(std::nothrow) CPUEntry[cpuCount]; + if (sCPUEntries == NULL) + return B_NO_MEMORY; + + call_all_cpus_sync(set_normal_pstate, NULL); + return B_OK; +} + + +static status_t +uninit_pstates() +{ + call_all_cpus_sync(set_normal_pstate, NULL); + delete[] sCPUEntries; + + return B_OK; +} + + +static status_t +std_ops(int32 op, ...) +{ + switch (op) { + case B_MODULE_INIT: + return init_pstates(); + + case B_MODULE_UNINIT: + uninit_pstates(); + return B_OK; + } + + return B_ERROR; +} + + +static cpufreq_module_info sIntelPStates = { + { + INTEL_PSTATES_MODULE_NAME, + 0, + std_ops, + }, + + increase_performance, + decrease_performance, +}; + + +module_info* modules[] = { + (module_info*)&sIntelPStates, + NULL +}; +