added 1 changeset to branch 'refs/remotes/pdziepak-github/scheduler' old head: 0e94a12f8e0e5fe5ff5b2e3f83384f3586396c92 new head: 7db89e8dc395db73368479fd9817b2b67899f3f6 overview: https://github.com/pdziepak/Haiku/compare/0e94a12...7db89e8 ---------------------------------------------------------------------------- 7db89e8: kernel: Rework cpuidle module * Create new interface for cpuidle modules (similar to the cpufreq interface) * Generic cpuidle module is no longer needed * Fix and update Intel C-State module [ Pawel Dziepak <pdziepak@xxxxxxxxxxx> ] ---------------------------------------------------------------------------- Commit: 7db89e8dc395db73368479fd9817b2b67899f3f6 Author: Pawel Dziepak <pdziepak@xxxxxxxxxxx> Date: Mon Nov 25 22:50:27 2013 UTC ---------------------------------------------------------------------------- 22 files changed, 356 insertions(+), 339 deletions(-) build/jam/packages/Haiku | 1 + build/jam/packages/HaikuBootstrap | 1 + headers/os/drivers/cpuidle.h | 46 +---- headers/private/kernel/arch/cpu.h | 1 - headers/private/kernel/arch/x86/arch_cpu.h | 29 ++- headers/private/kernel/cpu.h | 17 +- .../drivers/power/x86_cpuidle/intel_cpuidle.cpp | 121 ------------ src/add-ons/kernel/generic/Jamfile | 1 - src/add-ons/kernel/generic/cpuidle/Jamfile | 7 - src/add-ons/kernel/generic/cpuidle/cpuidle.cpp | 105 ---------- src/add-ons/kernel/power/Jamfile | 1 + src/add-ons/kernel/power/cpuidle/Jamfile | 4 + .../kernel/power/cpuidle/intel_cstates/Jamfile | 8 + .../cpuidle/intel_cstates/intel_cstates.cpp | 195 +++++++++++++++++++ src/system/kernel/UserTimer.cpp | 2 +- src/system/kernel/arch/x86/arch_cpu.cpp | 37 ++-- .../kernel/arch/x86/arch_debug_console.cpp | 4 +- src/system/kernel/cpu.cpp | 65 ++++++- src/system/kernel/debug/debug.cpp | 4 +- src/system/kernel/main.cpp | 2 +- src/system/kernel/smp.cpp | 34 ++-- src/system/kernel/timer.cpp | 10 +- ---------------------------------------------------------------------------- diff --git a/build/jam/packages/Haiku b/build/jam/packages/Haiku index 46df98a..5511bc4 100644 --- a/build/jam/packages/Haiku +++ b/build/jam/packages/Haiku @@ -53,6 +53,7 @@ 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 ; +AddFilesToPackage add-ons kernel power cpuidle : intel_cstates@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 1fafbb2..24d6ce6 100644 --- a/build/jam/packages/HaikuBootstrap +++ b/build/jam/packages/HaikuBootstrap @@ -52,6 +52,7 @@ 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 ; +AddFilesToPackage add-ons kernel power cpuidle : intel_cstates@x86,x86_64 ; if $(TARGET_ARCH) = x86 || $(TARGET_ARCH) = x86_64 { AddFilesToPackage add-ons kernel cpu : generic_x86 ; diff --git a/headers/os/drivers/cpuidle.h b/headers/os/drivers/cpuidle.h index 7649cda..c22b3e6 100644 --- a/headers/os/drivers/cpuidle.h +++ b/headers/os/drivers/cpuidle.h @@ -8,50 +8,18 @@ #include <module.h> -#ifdef __cplusplus -extern "C" { -#endif -#define CPUIDLE_CSTATE_MAX 8 -#define CSTATE_NAME_LENGTH 32 -#define B_CPUIDLE_MODULE_NAME "generic/cpuidle/v1" +#define CPUIDLE_MODULES_PREFIX "power/cpuidle" -struct CpuidleStat { - uint64 usageCount; - bigtime_t usageTime; -}; +typedef struct cpuidle_module_info { + module_info info; + float rank; -struct CpuidleInfo { - int32 cstateSleep; - CpuidleStat stats[CPUIDLE_CSTATE_MAX]; -}; + void (*idle)(void); + void (*wait)(int32* variable, int32 test); +} cpuidle_module_info; -struct CpuidleDevice; - -struct CpuidleCstate { - char name[CSTATE_NAME_LENGTH]; - int32 latency; - int32 (*EnterIdle)(int32 state, CpuidleDevice *device); - void *pData; -}; - - -struct CpuidleDevice { - CpuidleCstate cStates[CPUIDLE_CSTATE_MAX]; - int32 cStateCount; -}; - - -struct CpuidleModuleInfo { - module_info info; - status_t (*AddDevice)(CpuidleDevice *device); -}; - - -#ifdef __cplusplus -} -#endif #endif // _CPUIDLE_MODULE_H diff --git a/headers/private/kernel/arch/cpu.h b/headers/private/kernel/arch/cpu.h index 4d5754e..d5ea2de 100644 --- a/headers/private/kernel/arch/cpu.h +++ b/headers/private/kernel/arch/cpu.h @@ -41,7 +41,6 @@ ssize_t arch_cpu_user_strlcpy(char *to, const char *from, size_t size, status_t arch_cpu_user_memset(void *s, char c, size_t count, addr_t *faultHandler); -void arch_cpu_idle(void); void arch_cpu_sync_icache(void *address, size_t length); void arch_cpu_memory_read_barrier(void); diff --git a/headers/private/kernel/arch/x86/arch_cpu.h b/headers/private/kernel/arch/x86/arch_cpu.h index eb2e24a..f4ccaf4 100644 --- a/headers/private/kernel/arch/x86/arch_cpu.h +++ b/headers/private/kernel/arch/x86/arch_cpu.h @@ -185,6 +185,10 @@ | IA32_FEATURE_AMD_EXT_RDTSCP \ | IA32_FEATURE_AMD_EXT_LONG) +// x86 defined features from cpuid eax 5, ecx register +#define IA32_FEATURE_POWER_MWAIT (1 << 0) +#define IA32_FEATURE_INTERRUPT_MWAIT (1 << 1) + // x86 defined features from cpuid eax 6, eax register // reference http://www.intel.com/Assets/en_US/PDF/appnote/241618.pdf (Table 5-11) #define IA32_FEATURE_DTS (1 << 0) //Digital Thermal Sensor @@ -199,6 +203,9 @@ #define IA32_FEATURE_APERFMPERF (1 << 0) //IA32_APERF, IA32_MPERF #define IA32_FEATURE_EPB (1 << 3) //IA32_ENERGY_PERF_BIAS +// x86 defined features from cpuid eax 0x80000007, edx register +#define IA32_FEATURE_INVARIANT_TSC (1 << 8) + // cr4 flags #define IA32_CR4_PAE (1UL << 5) #define IA32_CR4_GLOBAL_PAGES (1UL << 7) @@ -282,8 +289,10 @@ enum x86_feature_type { FEATURE_EXT, // cpuid eax=1, edx register FEATURE_EXT_AMD_ECX, // cpuid eax=0x80000001, ecx register (AMD) FEATURE_EXT_AMD, // cpuid eax=0x80000001, edx register (AMD) + FEATURE_5_ECX, // cpuid eax=5, ecx register FEATURE_6_EAX, // cpuid eax=6, eax registers FEATURE_6_ECX, // cpuid eax=6, ecx registers + FEATURE_EXT_7_EDX, // cpuid eax=0x80000007, edx register FEATURE_NUM }; @@ -330,9 +339,6 @@ typedef struct arch_cpu_info { } arch_cpu_info; -#undef PAUSE -#define PAUSE() asm volatile ("pause;") - #define nop() __asm__ ("nop"::) #define x86_read_cr0() ({ \ @@ -426,6 +432,9 @@ typedef struct arch_cpu_info { }) +extern void (*gCpuIdleFunc)(void); + + #ifdef __cplusplus extern "C" { #endif @@ -478,6 +487,20 @@ void x86_fnsave_swap(void* oldFpuState, const void* newFpuState); #endif +static inline void +arch_cpu_idle(void) +{ + gCpuIdleFunc(); +} + + +static inline void +arch_cpu_pause(void) +{ + asm volatile("pause"); +} + + #ifdef __cplusplus } // extern "C" { #endif diff --git a/headers/private/kernel/cpu.h b/headers/private/kernel/cpu.h index 27fb6ff..3337d38 100644 --- a/headers/private/kernel/cpu.h +++ b/headers/private/kernel/cpu.h @@ -17,12 +17,6 @@ #include <arch/cpu.h> -// define PAUSE, if not done in arch/cpu.h -#ifndef PAUSE -# define PAUSE() -#endif - - struct kernel_args; namespace BKernel { @@ -114,6 +108,17 @@ cpu_topology_node* get_cpu_topology(void); status_t increase_cpu_performance(int delta, bool allowBoost); status_t decrease_cpu_performance(int delta); +void cpu_idle(void); +void cpu_wait(int32* variable, int32 test); + + +static inline void +cpu_pause(void) +{ + arch_cpu_pause(); +} + + void _user_clear_caches(void *address, size_t length, uint32 flags); bool _user_cpu_enabled(int32 cpu); status_t _user_set_cpu_enabled(int32 cpu, bool enabled); diff --git a/src/add-ons/kernel/drivers/power/x86_cpuidle/intel_cpuidle.cpp b/src/add-ons/kernel/drivers/power/x86_cpuidle/intel_cpuidle.cpp deleted file mode 100644 index cbbd1bc..0000000 --- a/src/add-ons/kernel/drivers/power/x86_cpuidle/intel_cpuidle.cpp +++ /dev/null @@ -1,121 +0,0 @@ -/* - * Copyright 2012, Haiku, Inc. All Rights Reserved. - * Distributed under the terms of the MIT License. - * - * Authors: - * Yongcong Du <ycdu.vmcore@xxxxxxxxx> - */ - -#include <KernelExport.h> -#include <cpu.h> -#include <arch_system_info.h> - -#include <stdio.h> - -#include <cpuidle.h> - -#include "x86_cpuidle.h" - -static CpuidleDevice sIntelDevice; - -static void *kMwaitEax[] = { - // C0, we never use it - (void *)0x00, - // MWAIT C1 - (void *)0x00, - // MWAIT C2 - (void *)0x10, - // MWAIT C3 - (void *)0x20, - // MWAIT C4 - (void *)0x30, - // MWAIT C5 - (void *)0x40, - // MWAIT C6, 0x2 is used to fully shrink L2 cache - (void *)0x52 -}; - - -static int32 -IntelCstateIdleEnter(int32 state, CpuidleDevice *device) -{ - cpu_ent *cpu = get_cpu_struct(); - if (cpu->invoke_scheduler) - return 0; - - CpuidleCstate *cState = &device->cStates[state]; - x86_monitor((void *)&cpu->invoke_scheduler, 0, 0); - if (!cpu->invoke_scheduler) - x86_mwait((unsigned long)cState->pData, 1); - - return state; -} - - -static CpuidleCstate sSnbcStates[CPUIDLE_CSTATE_MAX] = { - {}, - { - "C1-SNB", - 1, - IntelCstateIdleEnter, - }, - { - "C3-SNB", - 80, - IntelCstateIdleEnter, - }, - { - "C6-SNB", - 104, - IntelCstateIdleEnter, - }, - { - "C7-SNB", - 109, - IntelCstateIdleEnter, - }, -}; - - -status_t -intel_cpuidle_init(void) -{ - dprintf("intel idle init\n"); - cpu_ent *cpu = get_cpu_struct(); - if (cpu->arch.vendor != VENDOR_INTEL || cpu->arch.family != 6) - return B_ERROR; - - // Calculated Model Value: M = (Extended Model << 4) + Model - uint32 model = (cpu->arch.extended_model << 4) + cpu->arch.model; - if (model != 0x2a && model != 0x2d) - return B_ERROR; - - cpuid_info cpuid; - get_current_cpuid(&cpuid, 5, 0); - /* ecx[0] monitor/mwait extension supported - * ecx[1] support for treating interrupts as break-events for mwait - * edx number of sub-states - */ - if ((cpuid.regs.ecx & 0x1) == 0 || - (cpuid.regs.ecx & 0x2) == 0 || - cpuid.regs.edx == 0) { - return B_ERROR; - } - - sIntelDevice.cStateCount = 1; - for (int32 i = 1; i < CPUIDLE_CSTATE_MAX; i++) { - int32 subStates = (cpuid.regs.edx >> ((i) * 4)) & 0xf; - // no sub-states means the state is not available - if (!subStates) - continue; - sIntelDevice.cStates[sIntelDevice.cStateCount] = - sSnbcStates[i]; - sIntelDevice.cStates[sIntelDevice.cStateCount].pData = - kMwaitEax[i]; - sIntelDevice.cStateCount++; - } - status_t status = gIdle->AddDevice(&sIntelDevice); - if (status == B_OK) - dprintf("using intel idle\n"); - return status; -} diff --git a/src/add-ons/kernel/generic/Jamfile b/src/add-ons/kernel/generic/Jamfile index 9a1677d..1ac20ba 100644 --- a/src/add-ons/kernel/generic/Jamfile +++ b/src/add-ons/kernel/generic/Jamfile @@ -3,7 +3,6 @@ SubDir HAIKU_TOP src add-ons kernel generic ; SubInclude HAIKU_TOP src add-ons kernel generic ata_adapter ; SubInclude HAIKU_TOP src add-ons kernel generic atomizer ; SubInclude HAIKU_TOP src add-ons kernel generic bios ; -SubInclude HAIKU_TOP src add-ons kernel generic cpuidle ; SubInclude HAIKU_TOP src add-ons kernel generic dpc ; SubInclude HAIKU_TOP src add-ons kernel generic ide_adapter ; SubInclude HAIKU_TOP src add-ons kernel generic locked_pool ; diff --git a/src/add-ons/kernel/generic/cpuidle/Jamfile b/src/add-ons/kernel/generic/cpuidle/Jamfile deleted file mode 100644 index bb7cdd2..0000000 --- a/src/add-ons/kernel/generic/cpuidle/Jamfile +++ /dev/null @@ -1,7 +0,0 @@ -SubDir HAIKU_TOP src add-ons kernel generic cpuidle ; - -UsePrivateKernelHeaders ; - -KernelAddon cpuidle : - cpuidle.cpp - ; diff --git a/src/add-ons/kernel/generic/cpuidle/cpuidle.cpp b/src/add-ons/kernel/generic/cpuidle/cpuidle.cpp deleted file mode 100644 index cc17be8..0000000 --- a/src/add-ons/kernel/generic/cpuidle/cpuidle.cpp +++ /dev/null @@ -1,105 +0,0 @@ -/* - * Copyright 2012, Haiku, Inc. All Rights Reserved. - * Distributed under the terms of the MIT License. - * - * Authors: - * Yongcong Du <ycdu.vmcore@xxxxxxxxx> - */ - -#include <KernelExport.h> -#include <stdio.h> -#include <string.h> - -#include <smp.h> -#include <cpuidle.h> - - -static CpuidleInfo sPerCPU[B_MAX_CPU_COUNT]; -static CpuidleDevice *sDevice; -extern void (*gCpuIdleFunc)(void); - - -/* - * next cstate selection algorithm is based on NetBSD's - * it's simple, stupid - */ -static int32 -SelectCstate(CpuidleInfo *info) -{ - static const int32 csFactor = 3; - - for (int32 i = sDevice->cStateCount - 1; i > 0; i--) { - CpuidleCstate *cState = &sDevice->cStates[i]; - if (info->cstateSleep > cState->latency * csFactor) - return i; - } - - /* Choose C1 if there's no state found */ - return 1; -} - - -static inline void -EnterCstate(int32 state, CpuidleInfo *info) -{ - CpuidleCstate *cstate = &sDevice->cStates[state]; - bigtime_t now = system_time(); - int32 finalState = cstate->EnterIdle(state, sDevice); - if (finalState > 0) { - bigtime_t diff = system_time() - now; - info->cstateSleep = diff; - info->stats[finalState].usageCount++; - info->stats[finalState].usageTime += diff; - } else { - info->cstateSleep = 0; - } -} - - -static void -CpuCstateIdle(void) -{ - CpuidleInfo *info = &sPerCPU[smp_get_current_cpu()]; - int32 state = SelectCstate(info); - EnterCstate(state, info); -} - - -static status_t -std_ops(int32 op, ...) -{ - switch (op) { - case B_MODULE_INIT: - return B_OK; - case B_MODULE_UNINIT: - return B_OK; - } - - return B_ERROR; -} - - -status_t AddDevice(CpuidleDevice *device) -{ - sDevice = device; - memory_write_barrier(); - gCpuIdleFunc = CpuCstateIdle; - return B_OK; -} - - -static CpuidleModuleInfo sCpuidleModule = { - { - B_CPUIDLE_MODULE_NAME, - 0, - std_ops - }, - - AddDevice, -}; - - -module_info *modules[] = { - (module_info *)&sCpuidleModule, - NULL -}; diff --git a/src/add-ons/kernel/power/Jamfile b/src/add-ons/kernel/power/Jamfile index 84032da..289cb1f2 100644 --- a/src/add-ons/kernel/power/Jamfile +++ b/src/add-ons/kernel/power/Jamfile @@ -1,4 +1,5 @@ SubDir HAIKU_TOP src add-ons kernel power ; SubInclude HAIKU_TOP src add-ons kernel power cpufreq ; +SubInclude HAIKU_TOP src add-ons kernel power cpuidle ; diff --git a/src/add-ons/kernel/power/cpuidle/Jamfile b/src/add-ons/kernel/power/cpuidle/Jamfile new file mode 100644 index 0000000..702f538 --- /dev/null +++ b/src/add-ons/kernel/power/cpuidle/Jamfile @@ -0,0 +1,4 @@ +SubDir HAIKU_TOP src add-ons kernel power cpuidle ; + +SubInclude HAIKU_TOP src add-ons kernel power cpuidle intel_cstates ; + diff --git a/src/add-ons/kernel/power/cpuidle/intel_cstates/Jamfile b/src/add-ons/kernel/power/cpuidle/intel_cstates/Jamfile new file mode 100644 index 0000000..4c8ee48 --- /dev/null +++ b/src/add-ons/kernel/power/cpuidle/intel_cstates/Jamfile @@ -0,0 +1,8 @@ +SubDir HAIKU_TOP src add-ons kernel power cpuidle intel_cstates ; + +UsePrivateKernelHeaders ; + +KernelAddon intel_cstates : + intel_cstates.cpp + ; + diff --git a/src/add-ons/kernel/power/cpuidle/intel_cstates/intel_cstates.cpp b/src/add-ons/kernel/power/cpuidle/intel_cstates/intel_cstates.cpp new file mode 100644 index 0000000..d401b45 --- /dev/null +++ b/src/add-ons/kernel/power/cpuidle/intel_cstates/intel_cstates.cpp @@ -0,0 +1,195 @@ +/* + * Copyright 2012-2013, Haiku, Inc. All Rights Reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * Yongcong Du <ycdu.vmcore@xxxxxxxxx> + * Paweł Dziepak, <pdziepak@xxxxxxxxxxx> + */ + + +#include <arch_system_info.h> +#include <cpu.h> +#include <debug.h> +#include <smp.h> +#include <thread.h> +#include <util/AutoLock.h> + +#include <cpuidle.h> +#include <KernelExport.h> + +#include <stdio.h> + + +#define CPUIDLE_CSTATE_MAX 8 + +#define MWAIT_INTERRUPTS_BREAK (1 << 0) + +#define INTEL_CSTATES_MODULE_NAME CPUIDLE_MODULES_PREFIX "/intel_cstates/v1" + +#define TIME_STEP 250 + +struct CState { + uint32 fCode; + int fSubStatesCount; + int fSubStatesStep; +}; + +static CState sCStates[CPUIDLE_CSTATE_MAX]; +static int sCStateCount; + +static bigtime_t* sIdleTime; + + +static inline void +x86_monitor(void* address, uint32 ecx, uint32 edx) +{ + asm volatile("monitor" : : "a" (address), "c" (ecx), "d"(edx)); +} + + +static inline void +x86_mwait(uint32 eax, uint32 ecx) +{ + asm volatile("mwait" : : "a" (eax), "c" (ecx)); +} + + +static void +idle(void) +{ + ASSERT(thread_get_current_thread()->pinned_to_cpu > 0); + int32 cpu = smp_get_current_cpu(); + + bigtime_t idleTime = sIdleTime[cpu]; + int state = min_c(idleTime / TIME_STEP, sCStateCount - 1); + + ASSERT(state >= 0 && state < sCStateCount); + + int subState = idleTime % TIME_STEP; + subState /= sCStates[state].fSubStatesStep; + + ASSERT(subState >= 0 && subState < sCStates[state].fSubStatesCount); + + InterruptsLocker locker; + int dummy; + bigtime_t start = system_time(); + x86_monitor(&dummy, 0, 0); + x86_mwait(sCStates[state].fCode | subState, MWAIT_INTERRUPTS_BREAK); + bigtime_t delta = system_time() - start; + locker.Unlock(); + + sIdleTime[cpu] = (idleTime + delta) / 2; +} + + +static void +wait(int32* variable, int32 test) +{ + InterruptsLocker _; + x86_monitor(variable, 0, 0); + if (*variable != test) + x86_mwait(sCStates[0].fCode, MWAIT_INTERRUPTS_BREAK); +} + + +static status_t +init_cstates() +{ + if (!x86_check_feature(IA32_FEATURE_EXT_MONITOR, FEATURE_EXT)) + return B_ERROR; + if (!x86_check_feature(IA32_FEATURE_POWER_MWAIT, FEATURE_5_ECX)) + return B_ERROR; + if (!x86_check_feature(IA32_FEATURE_INTERRUPT_MWAIT, FEATURE_5_ECX)) + return B_ERROR; + + // we need invariant TSC + if (!x86_check_feature(IA32_FEATURE_INVARIANT_TSC, FEATURE_EXT_7_EDX)) + return B_ERROR; + + // get C-state data + cpuid_info cpuid; + get_current_cpuid(&cpuid, 0, 0); + uint32 maxBasicLeaf = cpuid.eax_0.max_eax; + if (maxBasicLeaf < 5) + return B_ERROR; + + get_current_cpuid(&cpuid, 5, 0); + if ((cpuid.regs.eax & 0xffff) < sizeof(int32)) + return B_ERROR; + + char cStates[64]; + unsigned int offset = 0; + for (int32 i = 1; i < CPUIDLE_CSTATE_MAX; i++) { + int32 subStates = (cpuid.regs.edx >> (i * 4)) & 0xf; + // no sub-states means the state is not available + if (subStates == 0) + continue; + + if (offset < sizeof(cStates)) { + offset += snprintf(cStates + offset, sizeof(cStates) - offset, + ", C%" B_PRId32, i); + } + + sCStates[sCStateCount].fCode = sCStateCount * 0x10; + sCStates[sCStateCount].fSubStatesCount = subStates; + sCStates[sCStateCount].fSubStatesStep = TIME_STEP / subStates; + sCStateCount++; + } + + if (sCStateCount == 0) + return B_ERROR; + + sIdleTime = new(std::nothrow) bigtime_t[smp_get_num_cpus()]; + if (sIdleTime == NULL) + return B_NO_MEMORY; + memset(sIdleTime, 0, sizeof(bigtime_t) * smp_get_num_cpus()); + + dprintf("using Intel C-States: C0%s\n", cStates); + return B_OK; +} + + +static status_t +uninit_cstates() +{ + delete[] sIdleTime; + return B_OK; +} + + +static status_t +std_ops(int32 op, ...) +{ + switch (op) { + case B_MODULE_INIT: + return init_cstates(); + + case B_MODULE_UNINIT: + uninit_cstates(); + return B_OK; + } + + return B_ERROR; +} + + +static cpuidle_module_info sIntelCStates = { + { + INTEL_CSTATES_MODULE_NAME, + 0, + std_ops, + }, + + 0.8f, + + idle, + wait +}; + + +module_info* modules[] = { + (module_info*)&sIntelCStates, + NULL +}; + diff --git a/src/system/kernel/UserTimer.cpp b/src/system/kernel/UserTimer.cpp index b3da871..db816a4 100644 --- a/src/system/kernel/UserTimer.cpp +++ b/src/system/kernel/UserTimer.cpp @@ -196,7 +196,7 @@ UserTimer::HandleTimerHook(struct timer* timer) while (!locked && atomic_get(&userTimer->fSkip) == 0) { locked = try_acquire_write_seqlock(&sUserTimerLock); if (!locked) - PAUSE(); + cpu_pause(); } if (locked) { diff --git a/src/system/kernel/arch/x86/arch_cpu.cpp b/src/system/kernel/arch/x86/arch_cpu.cpp index be1afda..16d60bf 100644 --- a/src/system/kernel/arch/x86/arch_cpu.cpp +++ b/src/system/kernel/arch/x86/arch_cpu.cpp @@ -1,5 +1,6 @@ /* * Copyright 2002-2010, Axel Dörfler, axeld@xxxxxxxxxxxxxxxx. + * Copyright 2013, Paweł Dziepak, pdziepak@xxxxxxxxxxx. * Copyright 2012, Alex Smith, alex@xxxxxxxxxxxxxxxx. * Distributed under the terms of the MIT License. * @@ -69,16 +70,6 @@ static const struct cpu_vendor_info vendor_info[VENDOR_NUM] = { #define K8_CMPHALT (K8_SMIONCMPHALT | K8_C1EONCMPHALT) -/* - * 0 favors highest performance while 15 corresponds to the maximum energy - * savings. 7 means balance between performance and energy savings. - * Refer to Section 14.3.4 in <Intel 64 and IA-32 Architectures Software - * Developer's Manual Volume 3> for details - */ -#define ENERGY_PERF_BIAS_PERFORMANCE 0 -#define ENERGY_PERF_BIAS_BALANCE 7 -#define ENERGY_PERF_BIAS_POWERSAVE 15 - struct set_mtrr_parameter { int32 index; uint64 base; @@ -929,12 +920,22 @@ detect_cpu(int currentCPU) cpu->arch.feature[FEATURE_EXT_AMD] &= IA32_FEATURES_INTEL_EXT; } + if (maxBasicLeaf >= 5) { + get_current_cpuid(&cpuid, 5, 0); + cpu->arch.feature[FEATURE_5_ECX] = cpuid.regs.ecx; + } + if (maxBasicLeaf >= 6) { get_current_cpuid(&cpuid, 6, 0); cpu->arch.feature[FEATURE_6_EAX] = cpuid.regs.eax; cpu->arch.feature[FEATURE_6_ECX] = cpuid.regs.ecx; } + if (maxExtendedLeaf >= 0x80000007) { + get_current_cpuid(&cpuid, 0x80000007, 0); + cpu->arch.feature[FEATURE_EXT_7_EDX] = cpuid.regs.edx; + } + detect_cpu_topology(currentCPU, cpu, maxBasicLeaf, maxExtendedLeaf); #if DUMP_FEATURE_STRING @@ -1060,15 +1061,6 @@ arch_cpu_init_percpu(kernel_args* args, int cpu) gCpuIdleFunc = halt_idle; } - if (x86_check_feature(IA32_FEATURE_EPB, FEATURE_6_ECX)) { - uint64 msr = x86_read_msr(IA32_MSR_ENERGY_PERF_BIAS); - if ((msr & 0xf) == ENERGY_PERF_BIAS_PERFORMANCE) { - msr &= ~0xf; - msr |= ENERGY_PERF_BIAS_BALANCE; - x86_write_msr(IA32_MSR_ENERGY_PERF_BIAS, msr); - } - } - return B_OK; } @@ -1288,13 +1280,6 @@ arch_cpu_shutdown(bool rebootSystem) void -arch_cpu_idle(void) -{ - gCpuIdleFunc(); -} - - -void arch_cpu_sync_icache(void* address, size_t length) { // instruction cache is always consistent on x86 diff --git a/src/system/kernel/arch/x86/arch_debug_console.cpp b/src/system/kernel/arch/x86/arch_debug_console.cpp index 8309a99..1f226db 100644 --- a/src/system/kernel/arch/x86/arch_debug_console.cpp +++ b/src/system/kernel/arch/x86/arch_debug_console.cpp @@ -316,7 +316,7 @@ arch_debug_blue_screen_getchar(void) if (c >= 0) return (char)c; - PAUSE(); + arch_cpu_pause(); } } @@ -352,7 +352,7 @@ arch_debug_serial_getchar(void) if ((lineStatus & 0x1) != 0) break; - PAUSE(); + arch_cpu_pause(); } return in8(sSerialBasePort + SERIAL_RECEIVE_BUFFER); diff --git a/src/system/kernel/cpu.cpp b/src/system/kernel/cpu.cpp index 3b7a5ad..2fdf6fd 100644 --- a/src/system/kernel/cpu.cpp +++ b/src/system/kernel/cpu.cpp @@ -16,6 +16,7 @@ #include <string.h> #include <cpufreq.h> +#include <cpuidle.h> #include <boot/kernel_args.h> #include <kscheduler.h> @@ -30,6 +31,7 @@ uint32 gCPUCacheLevelCount; static cpu_topology_node sCPUTopology; static cpufreq_module_info* sCPUPerformanceModule; +static cpuidle_module_info* sCPUIdleModule; static spinlock sSetCpuLock; @@ -89,6 +91,40 @@ load_cpufreq_module() } +static void +load_cpuidle_module() +{ + void* cookie = open_module_list(CPUIDLE_MODULES_PREFIX); + + while (true) { + char name[B_FILE_NAME_LENGTH]; + size_t nameLength = sizeof(name); + cpuidle_module_info* current = NULL; + + if (read_next_module_name(cookie, name, &nameLength) != B_OK) + break; + + if (get_module(name, (module_info**)¤t) == B_OK) { + dprintf("found cpuidle module: %s\n", name); + + if (sCPUIdleModule != NULL) { + if (sCPUIdleModule->rank < current->rank) { + put_module(sCPUIdleModule->info.name); + sCPUIdleModule = current; + } else + put_module(name); + } else + sCPUIdleModule = current; + } + } + + close_module_list(cookie); + + if (sCPUIdleModule == NULL) + dprintf("no valid cpuidle module found\n"); +} + + status_t cpu_init_post_modules(kernel_args *args) { @@ -97,6 +133,7 @@ cpu_init_post_modules(kernel_args *args) return result; load_cpufreq_module(); + load_cpuidle_module(); return B_OK; } @@ -232,6 +269,8 @@ cpu_build_topology_tree(void) int32 lastID[CPU_TOPOLOGY_LEVELS]; memset(&lastID, 0, sizeof(lastID)); cpu_rebuild_topology_tree(&sCPUTopology, lastID); + + return B_OK; } @@ -260,6 +299,31 @@ decrease_cpu_performance(int delta) } +void +cpu_idle(void) +{ +#if KDEBUG + if (!are_interrupts_enabled()) + panic("cpu_idle() called with interrupts disabled."); +#endif + + if (sCPUIdleModule != NULL) + sCPUIdleModule->idle(); + else + arch_cpu_idle(); +} + + +void +cpu_wait(int32* variable, int32 test) +{ + if (sCPUIdleModule != NULL) + sCPUIdleModule->wait(variable, test); + else + arch_cpu_pause(); +} + + // #pragma mark - @@ -283,7 +347,6 @@ _user_cpu_enabled(int32 cpu) status_t _user_set_cpu_enabled(int32 cpu, bool enabled) { - cpu_status state; int32 i, count; if (cpu < 0 || cpu >= smp_get_num_cpus()) diff --git a/src/system/kernel/debug/debug.cpp b/src/system/kernel/debug/debug.cpp index e75acd6..b56145b 100644 --- a/src/system/kernel/debug/debug.cpp +++ b/src/system/kernel/debug/debug.cpp @@ -703,7 +703,7 @@ kgetc(void) } } - PAUSE(); + cpu_pause(); } } @@ -1008,7 +1008,7 @@ hand_over_kernel_debugger() // initiated by the user, it is harmless, though. sHandOverKDL = true; while (atomic_get(&sHandOverKDLToCPU) >= 0) - PAUSE(); + cpu_wait(&sHandOverKDLToCPU, -1); } diff --git a/src/system/kernel/main.cpp b/src/system/kernel/main.cpp index 9dedf47..f135265 100644 --- a/src/system/kernel/main.cpp +++ b/src/system/kernel/main.cpp @@ -262,7 +262,7 @@ _start(kernel_args *bootKernelArgs, int currentCPU) #endif for (;;) - arch_cpu_idle(); + cpu_idle(); return 0; } diff --git a/src/system/kernel/smp.cpp b/src/system/kernel/smp.cpp index a07df3f..0477b85 100644 --- a/src/system/kernel/smp.cpp +++ b/src/system/kernel/smp.cpp @@ -358,7 +358,7 @@ acquire_spinlock(spinlock* lock) } process_all_pending_ici(currentCPU); - PAUSE(); + cpu_wait(lock, 0); } if (atomic_get_and_set((int32*)lock, 1) == 0) break; @@ -411,7 +411,7 @@ acquire_spinlock_nocheck(spinlock *lock) count = 0; } - PAUSE(); + cpu_wait(lock, 0); } if (atomic_get_and_set((int32*)lock, 1) == 0) @@ -455,7 +455,7 @@ acquire_spinlock_cpu(int32 currentCPU, spinlock *lock) } process_all_pending_ici(currentCPU); - PAUSE(); + cpu_wait(lock, 0); } if (atomic_get_and_set((int32*)lock, 1) == 0) break; @@ -570,7 +570,7 @@ acquire_write_spinlock(rw_spinlock* lock) } process_all_pending_ici(currentCPU); - PAUSE(); + cpu_wait(&lock->lock, 0); } } } @@ -583,7 +583,7 @@ release_write_spinlock(rw_spinlock* lock) uint32 previous = atomic_get_and_set(&lock->lock, 0); if ((previous & 1u << 31) == 0) { panic("release_write_spinlock: lock %p was already released (value: " - "%x)\n", lock, previous); + "%#" B_PRIx32 ")\n", lock, previous); } #else atomic_set(&lock->lock, 0); @@ -639,7 +639,7 @@ acquire_read_spinlock(rw_spinlock* lock) } process_all_pending_ici(currentCPU); - PAUSE(); + cpu_wait(&lock->lock, 0); } } } @@ -652,7 +652,7 @@ release_read_spinlock(rw_spinlock* lock) uint32 previous = atomic_add(&lock->lock, -1); if ((previous & 1 << 31) != 0) { panic("release_read_spinlock: lock %p was already released (value:" - " %x)\n", lock, previous); + " %#" B_PRIx32 ")\n", lock, previous); } #else atomic_add(&lock->lock, -1); @@ -695,7 +695,7 @@ release_read_seqlock(seqlock* lock, uint32 count) { uint32 current = atomic_get((int32*)&lock->count); if (count % 2 == 1 || current != count) { - PAUSE(); + cpu_pause(); return false; } @@ -719,7 +719,7 @@ retry: state = disable_interrupts(); process_all_pending_ici(smp_get_current_cpu()); restore_interrupts(state); - PAUSE(); + cpu_pause(); } state = disable_interrupts(); acquire_spinlock(&sFreeMessageSpinlock); @@ -757,7 +757,7 @@ find_free_message_interrupts_disabled(int32 currentCPU, while (sFreeMessageCount <= 0) { release_spinlock(&sFreeMessageSpinlock); process_all_pending_ici(currentCPU); - PAUSE(); + cpu_pause(); acquire_spinlock_cpu(currentCPU, &sFreeMessageSpinlock); } @@ -1014,7 +1014,7 @@ call_all_cpus_early(void (*function)(void*, int), void* cookie) // wait for all CPUs to finish while ((atomic_get(&sEarlyCPUCall) & cpuMask) != 0) - PAUSE(); + cpu_pause(); } function(cookie, 0); @@ -1085,7 +1085,7 @@ smp_send_ici(int32 targetCPU, int32 message, addr_t data, addr_t data2, // if the message is sync after it has removed it from the mailbox while (msg->done == false) { process_all_pending_ici(currentCPU); - PAUSE(); + cpu_pause(); } // for SYNC messages, it's our responsibility to put it // back into the free list @@ -1148,7 +1148,7 @@ smp_send_multicast_ici(cpu_mask_t cpuMask, int32 message, addr_t data, // if the message is sync after it has removed it from the mailbox while (msg->done == false) { process_all_pending_ici(currentCPU); - PAUSE(); + cpu_pause(); } // for SYNC messages, it's our responsibility to put it @@ -1210,7 +1210,7 @@ smp_send_broadcast_ici(int32 message, addr_t data, addr_t data2, addr_t data3, while (msg->done == false) { process_all_pending_ici(currentCPU); - PAUSE(); + cpu_pause(); } TRACE(("smp_send_broadcast_ici: returning message to free list\n")); @@ -1274,7 +1274,7 @@ smp_send_broadcast_ici_interrupts_disabled(int32 currentCPU, int32 message, while (msg->done == false) { process_all_pending_ici(currentCPU); - PAUSE(); + cpu_pause(); } TRACE(("smp_send_broadcast_ici_interrupts_disabled %ld: returning " @@ -1310,7 +1310,7 @@ smp_trap_non_boot_cpus(int32 cpu, uint32* rendezVous) if ((atomic_get(&sEarlyCPUCall) & (1 << cpu)) != 0) process_early_cpu_call(cpu); - PAUSE(); + cpu_pause(); } return false; @@ -1346,7 +1346,7 @@ smp_cpu_rendezvous(uint32* var, int current_cpu) uint32 allReady = ((uint32)1 << sNumCPUs) - 1; while ((uint32)atomic_get((int32*)var) != allReady) - PAUSE(); + cpu_wait((int32*)var, allReady); } diff --git a/src/system/kernel/timer.cpp b/src/system/kernel/timer.cpp index d10e3c3..5a3ea20 100644 --- a/src/system/kernel/timer.cpp +++ b/src/system/kernel/timer.cpp @@ -447,9 +447,8 @@ cancel_timer(timer* event) if (cpu != smp_get_current_cpu()) { spinLocker.Unlock(); - while (atomic_get(&cpuData.current_event_in_progress) == 1) { - PAUSE(); - } + while (atomic_get(&cpuData.current_event_in_progress) == 1) + cpu_wait(&cpuData.current_event_in_progress, 0); } return true; @@ -461,7 +460,6 @@ spin(bigtime_t microseconds) { bigtime_t time = system_time(); - while ((system_time() - time) < microseconds) { - PAUSE(); - } + while ((system_time() - time) < microseconds) + cpu_pause(); }