feat: further kernel power and power optimizations
This commit is contained in:
@@ -6,6 +6,7 @@
|
||||
|
||||
#include "ApicTimer.hpp"
|
||||
#include <atomic>
|
||||
#include <ACPI/CpuIdle.hpp>
|
||||
#include <Hal/Apic/Apic.hpp>
|
||||
#include <Hal/Apic/Interrupts.hpp>
|
||||
#include <Hal/Cpu.hpp>
|
||||
@@ -40,9 +41,10 @@ namespace Timekeeping {
|
||||
static constexpr uint32_t BSP_TICK_INTERVAL_MS = 1;
|
||||
static constexpr uint32_t BSP_TIMER_HZ = 1000 / BSP_TICK_INTERVAL_MS;
|
||||
static constexpr uint32_t AP_TICK_INTERVAL_MS = 10;
|
||||
// Without a reschedule IPI, the BSP still needs a short periodic safety net
|
||||
// while idle so input- and IPC-driven wakeups do not feel laggy.
|
||||
static constexpr uint32_t BSP_IDLE_MAX_INTERVAL_MS = 2;
|
||||
// Keep a bounded periodic wake while otherwise tickless so wall-clock
|
||||
// accounting and typematic repeat still advance even on a fully idle BSP.
|
||||
static constexpr uint32_t BSP_IDLE_MAX_INTERVAL_MS = 50;
|
||||
static constexpr uint32_t BSP_INTERACTIVE_IDLE_MAX_INTERVAL_MS = 1;
|
||||
|
||||
// Global state
|
||||
static std::atomic<uint64_t> g_tickCount{0};
|
||||
@@ -52,6 +54,7 @@ namespace Timekeeping {
|
||||
static volatile bool g_bspIdleOneShotArmed = false;
|
||||
static uint32_t g_bspIdleOneShotMs = 0;
|
||||
static uint32_t g_bspIdleInitialCount = 0;
|
||||
static std::atomic<uint64_t> g_interactiveUntilMs{0};
|
||||
|
||||
static uint32_t CountForIntervalMs(uint32_t intervalMs) {
|
||||
return g_ticksPerMs * intervalMs;
|
||||
@@ -84,14 +87,6 @@ namespace Timekeeping {
|
||||
Hal::LocalApic::WriteRegister(Hal::LocalApic::REG_TIMER_INITIAL, g_bspIdleInitialCount);
|
||||
}
|
||||
|
||||
static void WaitForInterrupt(bool hasMwait, volatile uint64_t* monitorAddr) {
|
||||
if (hasMwait && monitorAddr != nullptr) {
|
||||
Hal::IdleWait(monitorAddr);
|
||||
} else {
|
||||
asm volatile("hlt");
|
||||
}
|
||||
}
|
||||
|
||||
// Timer IRQ handler: BSP handles timekeeping and the few timer-driven
|
||||
// fallbacks that are still required; APs only run scheduler accounting.
|
||||
static void TimerHandler(uint8_t) {
|
||||
@@ -220,6 +215,20 @@ namespace Timekeeping {
|
||||
return g_tickCount.load(std::memory_order_relaxed); // 1 tick = 1 ms at 1000 Hz
|
||||
}
|
||||
|
||||
void NoteInteractiveActivity(uint32_t durationMs) {
|
||||
uint64_t until = GetMilliseconds() + durationMs;
|
||||
uint64_t current = g_interactiveUntilMs.load(std::memory_order_relaxed);
|
||||
|
||||
while (until > current &&
|
||||
!g_interactiveUntilMs.compare_exchange_weak(
|
||||
current, until, std::memory_order_relaxed, std::memory_order_relaxed)) {
|
||||
}
|
||||
}
|
||||
|
||||
static bool IsInteractiveActivityActive() {
|
||||
return GetMilliseconds() < g_interactiveUntilMs.load(std::memory_order_relaxed);
|
||||
}
|
||||
|
||||
void EnableSchedulerTick() {
|
||||
g_schedEnabled = true;
|
||||
}
|
||||
@@ -238,7 +247,7 @@ namespace Timekeeping {
|
||||
void IdleOnce(bool hasMwait, volatile uint64_t* monitorAddr) {
|
||||
auto* cpu = Smp::GetCurrentCpuData();
|
||||
if (cpu == nullptr || cpu->cpuIndex != 0 || !g_schedEnabled || g_ticksPerMs == 0) {
|
||||
WaitForInterrupt(hasMwait, monitorAddr);
|
||||
Hal::CpuIdle::Wait(BSP_TICK_INTERVAL_MS, hasMwait, monitorAddr);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -249,11 +258,19 @@ namespace Timekeeping {
|
||||
return;
|
||||
}
|
||||
|
||||
uint32_t waitMs = BSP_TICK_INTERVAL_MS;
|
||||
waitMs = BSP_IDLE_MAX_INTERVAL_MS;
|
||||
bool interactive = IsInteractiveActivityActive();
|
||||
uint32_t waitMs = interactive
|
||||
? BSP_INTERACTIVE_IDLE_MAX_INTERVAL_MS
|
||||
: BSP_IDLE_MAX_INTERVAL_MS;
|
||||
|
||||
uint64_t now = GetTicks();
|
||||
uint64_t nextDeadline = Sched::GetNextDeadlineTick();
|
||||
uint64_t keyboardDeadline = Drivers::USB::HidKeyboard::GetNextTickDeadline();
|
||||
if (keyboardDeadline != 0 &&
|
||||
(nextDeadline == 0 || keyboardDeadline < nextDeadline)) {
|
||||
nextDeadline = keyboardDeadline;
|
||||
}
|
||||
|
||||
if (nextDeadline != 0) {
|
||||
if (nextDeadline <= now) {
|
||||
waitMs = BSP_TICK_INTERVAL_MS;
|
||||
@@ -271,11 +288,12 @@ namespace Timekeeping {
|
||||
|
||||
asm volatile("cli" ::: "memory");
|
||||
ProgramBspIdleOneShotTimer(waitMs);
|
||||
asm volatile("sti" ::: "memory");
|
||||
if (interactive) {
|
||||
Hal::CpuIdle::WaitShallowWithInterruptsDisabled(hasMwait, monitorAddr);
|
||||
} else {
|
||||
Hal::CpuIdle::WaitWithInterruptsDisabled(waitMs, hasMwait, monitorAddr);
|
||||
}
|
||||
|
||||
WaitForInterrupt(hasMwait, monitorAddr);
|
||||
|
||||
asm volatile("cli" ::: "memory");
|
||||
if (g_bspIdleOneShotArmed) {
|
||||
uint32_t currentCount = Hal::LocalApic::ReadRegister(Hal::LocalApic::REG_TIMER_CURRENT);
|
||||
uint32_t elapsedTicks = (g_bspIdleInitialCount > currentCount)
|
||||
|
||||
@@ -32,6 +32,11 @@ namespace Timekeeping {
|
||||
// one-shot LAPIC timer while idle; APs keep their existing simple wait.
|
||||
void IdleOnce(bool hasMwait, volatile uint64_t* monitorAddr = nullptr);
|
||||
|
||||
// Mark recent latency-sensitive input activity. Idle policy uses this to
|
||||
// stay in shallow, high-responsiveness mode briefly while the user is
|
||||
// moving the pointer or dragging windows.
|
||||
void NoteInteractiveActivity(uint32_t durationMs = 250);
|
||||
|
||||
// Busy-wait sleep for the given number of milliseconds
|
||||
void Sleep(uint64_t ms);
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user