fix: reduce idle CPU wakeups and deferred-work polling
This commit is contained in:
1 parent
f7677ac3f1
commit
788b662d44
10 files changed
+109
-14
No files matched your search
@@ -41,8 +41,8 @@ namespace Timekeeping {
|
||||
static constexpr uint32_t DIVIDE_BY_16 = 0x03;
|
||||
|
||||
// The BSP keeps a 1 ms tick for timekeeping and sleep deadlines.
|
||||
// APs use a coarser 10 ms scheduler tick to avoid waking idle cores
|
||||
// 1000 times per second with no useful work to do.
|
||||
// Running APs use a 10 ms scheduler tick. Idle APs mask it entirely and
|
||||
// rely on reschedule IPIs, avoiding periodic package wakeups.
|
||||
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;
|
||||
@@ -220,8 +220,28 @@ namespace Timekeeping {
|
||||
// identical. This avoids PIT contention during AP boot.
|
||||
if (g_ticksPerMs == 0) return;
|
||||
|
||||
// Configure a coarser periodic timer on APs. The scheduler still gets
|
||||
// a 10 ms time slice, but idle APs stop taking 1000 timer interrupts/sec.
|
||||
// Configure the 10 ms scheduler timer for running APs. Their idle loop
|
||||
// masks it after initialization and rearms it when dispatching work.
|
||||
ProgramTimer(true, AP_TICK_INTERVAL_MS);
|
||||
}
|
||||
|
||||
void ApicTimerEnterApIdle() {
|
||||
auto* cpu = Smp::GetCurrentCpuData();
|
||||
if (cpu == nullptr || cpu->cpuIndex == 0 || g_ticksPerMs == 0) return;
|
||||
|
||||
uint32_t lvt = Hal::LocalApic::ReadRegister(Hal::LocalApic::REG_TIMER_LVT);
|
||||
if ((lvt & LVT_MASKED) == 0) {
|
||||
Hal::LocalApic::WriteRegister(Hal::LocalApic::REG_TIMER_LVT,
|
||||
lvt | LVT_MASKED);
|
||||
}
|
||||
}
|
||||
|
||||
void ApicTimerLeaveApIdle() {
|
||||
auto* cpu = Smp::GetCurrentCpuData();
|
||||
if (cpu == nullptr || cpu->cpuIndex == 0 || g_ticksPerMs == 0) return;
|
||||
|
||||
// Reprogram the initial count as well as unmasking. A deep idle state
|
||||
// may have stopped the local timer at an arbitrary point in its period.
|
||||
ProgramTimer(true, AP_TICK_INTERVAL_MS);
|
||||
}
|
||||
|
||||
@@ -239,19 +259,24 @@ namespace Timekeeping {
|
||||
bool wasReserved = cpu->reservedForKernelWork;
|
||||
cpu->reservedForKernelWork = true;
|
||||
|
||||
// Drain USB hot-plug deferred work from any idle core, not just the BSP.
|
||||
if (Drivers::USB::Xhci::HasDeferredWork()) {
|
||||
// Drain USB work only when the MSI path has actually queued something.
|
||||
// Bluetooth shares this controller, so service its protocol queues in
|
||||
// the same pass after xHCI has delivered completion callbacks.
|
||||
bool usbWork = Drivers::USB::Xhci::HasDeferredWork();
|
||||
if (usbWork) {
|
||||
Drivers::USB::Xhci::ProcessDeferredWork();
|
||||
}
|
||||
|
||||
// NIC hard IRQs only acknowledge/mask and queue RX work. Dispatching
|
||||
// Ethernet/TCP/UDP here keeps process-context IPC mutexes out of IRQs.
|
||||
Drivers::Net::E1000::ProcessDeferredWork();
|
||||
Drivers::Net::E1000E::ProcessDeferredWork();
|
||||
if (Drivers::Net::E1000::HasDeferredWork())
|
||||
Drivers::Net::E1000::ProcessDeferredWork();
|
||||
if (Drivers::Net::E1000E::HasDeferredWork())
|
||||
Drivers::Net::E1000E::ProcessDeferredWork();
|
||||
|
||||
// HDA completion IRQs only acknowledge/mask. Resampling and DMA-ring
|
||||
// refill are far too expensive for hard interrupt context.
|
||||
if (cpu->cpuIndex == 0) {
|
||||
if (cpu->cpuIndex == 0 && Drivers::Audio::IntelHda::HasDeferredWork()) {
|
||||
Drivers::Audio::IntelHda::ProcessDeferredWork();
|
||||
}
|
||||
|
||||
@@ -260,7 +285,10 @@ namespace Timekeeping {
|
||||
// seconds and used to stall kmain before the first process spawned.
|
||||
// Cheap no-op unless an adapter is waiting; self-claiming, and safe
|
||||
// to preempt (the scheduler saves/resumes the idle context).
|
||||
Drivers::USB::Bluetooth::ServiceDeferredInit();
|
||||
bool bluetoothWork = usbWork ||
|
||||
Drivers::USB::Bluetooth::HasDeferredWork();
|
||||
if (bluetoothWork)
|
||||
Drivers::USB::Bluetooth::ServiceDeferredInit();
|
||||
|
||||
// Service Bluetooth inbound traffic (the headset's SDP/AVRCP queries,
|
||||
// AVDTP commands, ACL flow-control credits) whenever a core idles.
|
||||
@@ -269,13 +297,16 @@ namespace Timekeeping {
|
||||
// writes got silence (observed: Bose re-dialing SDP during playback,
|
||||
// queries never answered). Self-serializing and a cheap no-op when
|
||||
// the adapter is down.
|
||||
Drivers::USB::Bluetooth::ServiceEvents();
|
||||
if (bluetoothWork)
|
||||
Drivers::USB::Bluetooth::ServiceEvents();
|
||||
|
||||
// Wi-Fi mirrors the Bluetooth split: the firmware load needs the
|
||||
// ramdisk, and the RX/notification ring must be drained outside hard
|
||||
// interrupt context (the MSI handler only latches a flag).
|
||||
Drivers::Net::Wifi::ServiceDeferredInit();
|
||||
Drivers::Net::Wifi::ServiceEvents();
|
||||
if (Drivers::Net::Wifi::HasDeferredWork()) {
|
||||
Drivers::Net::Wifi::ServiceDeferredInit();
|
||||
Drivers::Net::Wifi::ServiceEvents();
|
||||
}
|
||||
|
||||
// Thermal policy records transitions during BSP maintenance; print
|
||||
// them from this explicitly non-interrupt idle path.
|
||||
|
||||
@@ -11,9 +11,14 @@ namespace Timekeeping {
|
||||
// Initialize the APIC timer: calibrate against PIT, start periodic interrupts
|
||||
void ApicTimerInitialize();
|
||||
|
||||
// Initialize the APIC timer on an AP (calibrate + start, no IRQ handler registration)
|
||||
// Initialize the scheduler timer on an AP using the BSP calibration.
|
||||
void ApicTimerInitializeAP();
|
||||
|
||||
// Idle APs are woken for runnable work by the reschedule IPI, so their
|
||||
// periodic scheduler timer can remain masked until a process is dispatched.
|
||||
void ApicTimerEnterApIdle();
|
||||
void ApicTimerLeaveApIdle();
|
||||
|
||||
// Reinitialize the APIC timer after S3 resume using the previously
|
||||
// calibrated tick rate. Skips PIT calibration and IRQ registration
|
||||
// (both survive in RAM). Only reprograms the timer hardware registers.
|
||||
|
||||
Reference in new issue
Block a user