From 2195811a847b0a573b017750eefda19abcf0a6ec Mon Sep 17 00:00:00 2001 From: Yongcong Du Date: Wed, 4 Jul 2012 23:28:43 +0800 Subject: [PATCH] idle: introduce cpuidle generic module This module will load the various lowlevel cpuidle modules' implementations during initialiation. If it finds one available module, it will change the global gCpuIdleFunc as its own better one. When idle, cpuidle module will select the best cstate and enter it by calling the lowlevel module's implementation. Signed-off-by: Yongcong Du Signed-off-by: Fredrik Holmqvist --- headers/os/drivers/cpuidle.h | 51 +++++++++ src/add-ons/kernel/idle/Jamfile | 3 + src/add-ons/kernel/idle/generic/Jamfile | 7 ++ src/add-ons/kernel/idle/generic/cpuidle.cpp | 119 ++++++++++++++++++++ 4 files changed, 180 insertions(+) create mode 100644 headers/os/drivers/cpuidle.h create mode 100644 src/add-ons/kernel/idle/Jamfile create mode 100644 src/add-ons/kernel/idle/generic/Jamfile create mode 100644 src/add-ons/kernel/idle/generic/cpuidle.cpp diff --git a/headers/os/drivers/cpuidle.h b/headers/os/drivers/cpuidle.h new file mode 100644 index 0000000000..67d8f307a4 --- /dev/null +++ b/headers/os/drivers/cpuidle.h @@ -0,0 +1,51 @@ +/* + * Copyright 2012, Haiku, Inc. All Rights Reserved. + * Distributed under the terms of the MIT License. + */ +#ifndef _CPUIDLE_MODULE_H +#define _CPUIDLE_MODULE_H + + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define CPUIDLE_CSTATE_MAX 8 +#define CSTATE_NAME_LENGTH 32 +#define B_CPUIDLE_MODULE_NAME "idle/generic/cpuidle/v1" + + +struct CpuidleStats { + uint64 usageCount; + bigtime_t usageTime; +}; + + +struct CpuidleInfo { + int32 cstateSleep; + CpuidleStats stats[CPUIDLE_CSTATE_MAX]; +}; + + +struct CpuidleCstate { + char name[CSTATE_NAME_LENGTH]; + int32 latency; + int32 (*EnterIdle)(int32 state, CpuidleCstate *cstate); + void *pData; +}; + + +struct CpuidleModuleInfo { + module_info info; + CpuidleCstate cStates[CPUIDLE_CSTATE_MAX]; + int32 cStateCount; +}; + + +#ifdef __cplusplus +} +#endif + +#endif // _CPUIDLE_MODULE_H diff --git a/src/add-ons/kernel/idle/Jamfile b/src/add-ons/kernel/idle/Jamfile new file mode 100644 index 0000000000..27f0864029 --- /dev/null +++ b/src/add-ons/kernel/idle/Jamfile @@ -0,0 +1,3 @@ +SubDir HAIKU_TOP src add-ons kernel idle ; + +SubInclude HAIKU_TOP src add-ons kernel idle generic ; diff --git a/src/add-ons/kernel/idle/generic/Jamfile b/src/add-ons/kernel/idle/generic/Jamfile new file mode 100644 index 0000000000..f7ff039052 --- /dev/null +++ b/src/add-ons/kernel/idle/generic/Jamfile @@ -0,0 +1,7 @@ +SubDir HAIKU_TOP src add-ons kernel idle generic ; + +UsePrivateKernelHeaders ; + +KernelAddon cpuidle : + cpuidle.cpp + ; diff --git a/src/add-ons/kernel/idle/generic/cpuidle.cpp b/src/add-ons/kernel/idle/generic/cpuidle.cpp new file mode 100644 index 0000000000..4338a16835 --- /dev/null +++ b/src/add-ons/kernel/idle/generic/cpuidle.cpp @@ -0,0 +1,119 @@ +/* + * Copyright 2012, Haiku, Inc. All Rights Reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * Yongcong Du + */ + +#include +#include +#include + +#include +#include + + +static CpuidleInfo sPerCPU[B_MAX_CPU_COUNT]; +static CpuidleModuleInfo *sCpuidleModule; + +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 = sCpuidleModule->cStateCount - 1; i > 0; i--) { + CpuidleCstate *cState = &sCpuidleModule->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 = &sCpuidleModule->cStates[state]; + bigtime_t now = system_time(); + if (cstate->EnterIdle(state, cstate) > 0) { + bigtime_t diff = system_time() - now; + info->cstateSleep = diff; + info->stats[state].usageCount++; + info->stats[state].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: + { + dprintf("generic cpuidle init\n"); + void *cookie = open_module_list("idle/cpuidles"); + if (cookie == NULL) + return B_ERROR; + + char name[B_FILE_NAME_LENGTH]; + size_t nameLength = sizeof(name); + + while (read_next_module_name(cookie, name, &nameLength) == B_OK) { + if (get_module(name, (module_info **)&sCpuidleModule) == B_OK) { + break; + } + } + + close_module_list(cookie); + if (sCpuidleModule->cStateCount < 2) { + dprintf("no enough available cstates, exiting...\n"); + put_module(sCpuidleModule->info.name); + return B_ERROR; + } else { + dprintf("using %s\n", sCpuidleModule->info.name); + gCpuIdleFunc = CpuCstateIdle; + return B_OK; + } + } + case B_MODULE_UNINIT: + dprintf("generic cpuidle uninit\n"); + put_module(sCpuidleModule->info.name); + return B_OK; + } + + return B_ERROR; +} + + +static module_info sModule = { + B_CPUIDLE_MODULE_NAME, + 0, + std_ops +}; + + +module_info *modules[] = { + (module_info *)&sModule, + NULL +};