From a489be0e05ea2f6976f6d4cc4f78ade8fd108cf5 Mon Sep 17 00:00:00 2001 From: Bananymous Date: Thu, 19 Sep 2024 14:41:59 +0300 Subject: [PATCH] Kernel: Allow parallel LAPIC timer initialization with HPET HPET supports reading LAPIC counter without locks, so it can be done in parallel. This makes booting much faster. Previously initializing every timer took 100 ms, so 16 CPUs took total of 1.6 seconds. This allows doing it all in 100 ms. --- kernel/include/kernel/Timer/HPET.h | 1 + kernel/include/kernel/Timer/PIT.h | 1 + kernel/include/kernel/Timer/Timer.h | 2 ++ kernel/kernel/APIC.cpp | 31 +++++++++++++++++------------ kernel/kernel/Timer/Timer.cpp | 5 +++++ 5 files changed, 27 insertions(+), 13 deletions(-) diff --git a/kernel/include/kernel/Timer/HPET.h b/kernel/include/kernel/Timer/HPET.h index d6f61f5801..57471d6a63 100644 --- a/kernel/include/kernel/Timer/HPET.h +++ b/kernel/include/kernel/Timer/HPET.h @@ -19,6 +19,7 @@ namespace Kernel virtual uint64_t ns_since_boot() const override; virtual timespec time_since_boot() const override; + virtual bool pre_scheduler_sleep_needs_lock() const override { return false; } virtual void pre_scheduler_sleep_ns(uint64_t) override; virtual void handle_irq() override; diff --git a/kernel/include/kernel/Timer/PIT.h b/kernel/include/kernel/Timer/PIT.h index 96d991782e..2c26ed5f5a 100644 --- a/kernel/include/kernel/Timer/PIT.h +++ b/kernel/include/kernel/Timer/PIT.h @@ -16,6 +16,7 @@ namespace Kernel virtual uint64_t ns_since_boot() const override; virtual timespec time_since_boot() const override; + virtual bool pre_scheduler_sleep_needs_lock() const override { return true; } virtual void pre_scheduler_sleep_ns(uint64_t) override; virtual void handle_irq() override; diff --git a/kernel/include/kernel/Timer/Timer.h b/kernel/include/kernel/Timer/Timer.h index 65b6134352..1a44be2a45 100644 --- a/kernel/include/kernel/Timer/Timer.h +++ b/kernel/include/kernel/Timer/Timer.h @@ -17,6 +17,7 @@ namespace Kernel virtual uint64_t ns_since_boot() const = 0; virtual timespec time_since_boot() const = 0; + virtual bool pre_scheduler_sleep_needs_lock() const = 0; virtual void pre_scheduler_sleep_ns(uint64_t) = 0; void dont_invoke_scheduler() { m_should_invoke_scheduler = false; } @@ -39,6 +40,7 @@ namespace Kernel virtual uint64_t ns_since_boot() const override; virtual timespec time_since_boot() const override; + virtual bool pre_scheduler_sleep_needs_lock() const override; virtual void pre_scheduler_sleep_ns(uint64_t) override; void sleep_ms(uint64_t ms) const { return sleep_ns(ms * 1'000'000); } diff --git a/kernel/kernel/APIC.cpp b/kernel/kernel/APIC.cpp index 69bc0c9dc3..0b11a621d5 100644 --- a/kernel/kernel/APIC.cpp +++ b/kernel/kernel/APIC.cpp @@ -253,11 +253,11 @@ namespace Kernel const auto send_ipi = [&](uint8_t processor, uint32_t data, uint64_t ud) { + while ((read_from_local_apic(LAPIC_ICR_LO_REG) & ICR_LO_delivery_status_send_pending) == ICR_LO_delivery_status_send_pending) + __builtin_ia32_pause(); write_to_local_apic(LAPIC_ICR_HI_REG, (read_from_local_apic(LAPIC_ICR_HI_REG) & 0x00FFFFFF) | (processor << 24)); write_to_local_apic(LAPIC_ICR_LO_REG, data); udelay(ud); - while ((read_from_local_apic(LAPIC_ICR_LO_REG) & ICR_LO_delivery_status_send_pending) == ICR_LO_delivery_status_send_pending) - __builtin_ia32_pause(); }; dprintln("System has {} processors", m_processors.size()); @@ -402,21 +402,26 @@ namespace Kernel void APIC::initialize_timer() { - { - constexpr uint64_t measuring_duration_ms = 100; + ASSERT(Kernel::Processor::get_interrupt_state() == InterruptState::Disabled); - SpinLockGuard _(s_timer_init_lock); + constexpr uint64_t measuring_duration_ms = 100; - write_to_local_apic(LAPIC_TIMER_LVT, TimerModeOneShot | TimerMask); - write_to_local_apic(LAPIC_TIMER_DIVIDE_REG, DivideBy2); - write_to_local_apic(LAPIC_TIMER_INITIAL_REG, 0xFFFFFFFF); - SystemTimer::get().pre_scheduler_sleep_ns(measuring_duration_ms * 1'000'000); + const bool needs_lock = SystemTimer::get().pre_scheduler_sleep_needs_lock(); + if (needs_lock) + s_timer_init_lock.lock(); - const uint32_t counter = read_from_local_apic(LAPIC_TIMER_CURRENT_REG); - m_lapic_timer_frequency_hz = static_cast(0xFFFFFFFF - counter) * 2 * (1000 / measuring_duration_ms); + write_to_local_apic(LAPIC_TIMER_LVT, TimerModeOneShot | TimerMask); + write_to_local_apic(LAPIC_TIMER_DIVIDE_REG, DivideBy2); + write_to_local_apic(LAPIC_TIMER_INITIAL_REG, 0xFFFFFFFF); + SystemTimer::get().pre_scheduler_sleep_ns(measuring_duration_ms * 1'000'000); - dprintln("CPU {}: lapic timer frequency: {} Hz", Kernel::Processor::current_id(), m_lapic_timer_frequency_hz); - } + const uint32_t counter = read_from_local_apic(LAPIC_TIMER_CURRENT_REG); + m_lapic_timer_frequency_hz = static_cast(0xFFFFFFFF - counter) * 2 * (1000 / measuring_duration_ms); + + if (needs_lock) + s_timer_init_lock.unlock(InterruptState::Disabled); + + dprintln("CPU {}: lapic timer frequency: {} Hz", Kernel::Processor::current_id(), m_lapic_timer_frequency_hz); write_to_local_apic(LAPIC_TIMER_LVT, TimerModePeriodic | (IRQ_VECTOR_BASE + IRQ_TIMER)); write_to_local_apic(LAPIC_TIMER_INITIAL_REG, m_lapic_timer_frequency_hz / 2 / 100); diff --git a/kernel/kernel/Timer/Timer.cpp b/kernel/kernel/Timer/Timer.cpp index d4075bdfc9..ca9cb05622 100644 --- a/kernel/kernel/Timer/Timer.cpp +++ b/kernel/kernel/Timer/Timer.cpp @@ -69,6 +69,11 @@ namespace Kernel return m_timer->time_since_boot(); } + bool SystemTimer::pre_scheduler_sleep_needs_lock() const + { + return m_timer->pre_scheduler_sleep_needs_lock(); + } + void SystemTimer::pre_scheduler_sleep_ns(uint64_t ns) { return m_timer->pre_scheduler_sleep_ns(ns);