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 d6f61f58..57471d6a 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 96d99178..2c26ed5f 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 65b61343..1a44be2a 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 69bc0c9d..0b11a621 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 d4075bdf..ca9cb056 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);