intel_pstates: enable HWP when available

* don't reset the APERF and MPERF MSRs, they could be in use otherwise.
* use EPP or EPB to select power-save or performance mode
* HWP_REQUEST_PACKAGE disabled (as other OSes seem to do).

Change-Id: I26eb0b05c402b131a4ab174ad6f9ddf8e9367709
Reviewed-on: https://review.haiku-os.org/c/haiku/+/3222
Reviewed-by: Jérôme Duval <jerome.duval@gmail.com>
This commit is contained in:
Jérôme Duval 2020-09-10 22:35:12 +02:00
parent 80ad523bc2
commit dfed08e0de

View File

@ -1,4 +1,5 @@
/*
* Copyright 2020, Jérôme Duval, jerome.duval@gmail.com.
* Copyright 2013, Haiku, Inc. All Rights Reserved.
* Distributed under the terms of the MIT License.
*
@ -24,6 +25,13 @@ const int kMinimalInterval = 50000;
static uint16 sMinPState;
static uint16 sMaxPState;
static uint16 sBoostPState;
static bool sHWPActive;
static bool sHWPEPP;
static uint8 sHWPLowest;
static uint8 sHWPGuaranteed;
static uint8 sHWPEfficient;
static uint8 sHWPHighest;
static bool sHWPPackage;
static bool sAvoidBoost;
@ -34,6 +42,9 @@ struct CPUEntry {
uint16 fCurrentPState;
bigtime_t fLastUpdate;
uint64 fPrevAperf;
uint64 fPrevMperf;
} CACHE_LINE_ALIGN;
static CPUEntry* sCPUEntries;
@ -46,10 +57,15 @@ CPUEntry::CPUEntry()
}
static void set_normal_pstate(void* /* dummy */, int cpu);
static void
pstates_set_scheduler_mode(scheduler_mode mode)
{
sAvoidBoost = mode == SCHEDULER_MODE_POWER_SAVING;
if (sHWPActive)
call_all_cpus(set_normal_pstate, NULL);
}
@ -61,16 +77,16 @@ measure_pstate(CPUEntry* entry)
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)
if (mperf == 0 || mperf == entry->fPrevMperf)
return sMinPState;
int oldPState = sMaxPState * aperf / mperf;
int oldPState = sMaxPState * (aperf - entry->fPrevAperf)
/ (mperf - entry->fPrevMperf);
oldPState = min_c(max_c(oldPState, sMinPState), sBoostPState);
entry->fPrevAperf = aperf;
entry->fPrevMperf = mperf;
return oldPState;
}
@ -96,6 +112,9 @@ pstates_increase_performance(int delta)
{
CPUEntry* entry = &sCPUEntries[smp_get_current_cpu()];
if (sHWPActive)
return B_NOT_SUPPORTED;
if (system_time() - entry->fLastUpdate < kMinimalInterval)
return B_OK;
@ -115,6 +134,9 @@ pstates_decrease_performance(int delta)
{
CPUEntry* entry = &sCPUEntries[smp_get_current_cpu()];
if (sHWPActive)
return B_NOT_SUPPORTED;
if (system_time() - entry->fLastUpdate < kMinimalInterval)
return B_OK;
@ -161,8 +183,46 @@ is_cpu_model_supported(cpu_ent* cpu)
static void
set_normal_pstate(void* /* dummy */, int cpu)
{
measure_pstate(&sCPUEntries[cpu]);
set_pstate(sMaxPState);
if (sHWPActive) {
if (x86_check_feature(IA32_FEATURE_HWP_NOTIFY, FEATURE_6_EAX))
x86_write_msr(IA32_MSR_HWP_INTERRUPT, 0);
x86_write_msr(IA32_MSR_PM_ENABLE, 1);
uint64 hwpRequest = x86_read_msr(IA32_MSR_HWP_REQUEST);
uint64 caps = x86_read_msr(IA32_MSR_HWP_CAPABILITIES);
sHWPLowest = IA32_HWP_CAPS_LOWEST_PERFORMANCE(caps);
sHWPEfficient = IA32_HWP_CAPS_EFFICIENT_PERFORMANCE(caps);
sHWPGuaranteed = IA32_HWP_CAPS_GUARANTEED_PERFORMANCE(caps);
sHWPHighest = IA32_HWP_CAPS_HIGHEST_PERFORMANCE(caps);
hwpRequest &= ~IA32_HWP_REQUEST_DESIRED_PERFORMANCE;
hwpRequest &= ~IA32_HWP_REQUEST_ACTIVITY_WINDOW;
hwpRequest &= ~IA32_HWP_REQUEST_MINIMUM_PERFORMANCE;
hwpRequest |= sHWPLowest;
hwpRequest &= ~IA32_HWP_REQUEST_MAXIMUM_PERFORMANCE;
hwpRequest |= sHWPHighest << 8;
if (x86_check_feature(IA32_FEATURE_HWP_EPP, FEATURE_6_EAX)) {
hwpRequest &= ~IA32_HWP_REQUEST_ENERGY_PERFORMANCE_PREFERENCE;
hwpRequest |= (sAvoidBoost ? 0x80ULL : 0x0ULL) << 24;
} else if (x86_check_feature(IA32_FEATURE_EPB, FEATURE_6_ECX)) {
uint64 perfBias = x86_read_msr(IA32_MSR_ENERGY_PERF_BIAS);
perfBias &= ~(0xfULL << 0);
perfBias |= (sAvoidBoost ? 0xfULL : 0x0ULL) << 0;
x86_write_msr(IA32_MSR_ENERGY_PERF_BIAS, perfBias);
}
if (sHWPPackage) {
x86_write_msr(IA32_MSR_HWP_REQUEST, hwpRequest
| IA32_HWP_REQUEST_PACKAGE_CONTROL);
x86_write_msr(IA32_MSR_HWP_REQUEST_PKG, hwpRequest);
} else
x86_write_msr(IA32_MSR_HWP_REQUEST, hwpRequest);
} else {
measure_pstate(&sCPUEntries[cpu]);
set_pstate(sMaxPState);
}
}
@ -181,13 +241,20 @@ init_pstates()
return B_ERROR;
}
sMinPState = (x86_read_msr(IA32_MSR_PLATFORM_INFO) >> 40) & 0xff;
sMaxPState = (x86_read_msr(IA32_MSR_PLATFORM_INFO) >> 8) & 0xff;
uint64 platformInfo = x86_read_msr(IA32_MSR_PLATFORM_INFO);
sHWPEPP = x86_check_feature(IA32_FEATURE_HWP_EPP, FEATURE_6_EAX);
sHWPActive = (x86_check_feature(IA32_FEATURE_HWP, FEATURE_6_EAX)
&& sHWPEPP);
sMinPState = (platformInfo >> 40) & 0xff;
sMaxPState = (platformInfo >> 8) & 0xff;
sBoostPState
= max_c(x86_read_msr(IA32_MSR_TURBO_RATIO_LIMIT) & 0xff, sMaxPState);
/* x86_check_feature(IA32_FEATURE_HWP_PLR, FEATURE_6_EAX)) */
sHWPPackage = false;
dprintf("using Intel P-States: min %" B_PRIu16 ", max %" B_PRIu16
", boost %" B_PRIu16 "\n", sMinPState, sMaxPState, sBoostPState);
", boost %" B_PRIu16 "%s\n", sMinPState, sMaxPState, sBoostPState,
sHWPActive ? ", HWP active" : "");
if (sMaxPState <= sMinPState || sMaxPState == 0) {
dprintf("unexpected or invalid Intel P-States limits, aborting\n");