feat: further kernel power and power optimizations

This commit is contained in:
2026-04-21 20:51:12 +02:00
parent d1fffecff6
commit 0dfb3e8dbb
31 changed files with 895 additions and 52 deletions
+36 -18
View File
@@ -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)
+5
View File
@@ -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);
};