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.
This commit is contained in:
Bananymous 2024-09-19 14:41:59 +03:00
parent 8e08046519
commit a489be0e05
5 changed files with 27 additions and 13 deletions

View File

@ -19,6 +19,7 @@ namespace Kernel
virtual uint64_t ns_since_boot() const override; virtual uint64_t ns_since_boot() const override;
virtual timespec time_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 pre_scheduler_sleep_ns(uint64_t) override;
virtual void handle_irq() override; virtual void handle_irq() override;

View File

@ -16,6 +16,7 @@ namespace Kernel
virtual uint64_t ns_since_boot() const override; virtual uint64_t ns_since_boot() const override;
virtual timespec time_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 pre_scheduler_sleep_ns(uint64_t) override;
virtual void handle_irq() override; virtual void handle_irq() override;

View File

@ -17,6 +17,7 @@ namespace Kernel
virtual uint64_t ns_since_boot() const = 0; virtual uint64_t ns_since_boot() const = 0;
virtual timespec time_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; virtual void pre_scheduler_sleep_ns(uint64_t) = 0;
void dont_invoke_scheduler() { m_should_invoke_scheduler = false; } void dont_invoke_scheduler() { m_should_invoke_scheduler = false; }
@ -39,6 +40,7 @@ namespace Kernel
virtual uint64_t ns_since_boot() const override; virtual uint64_t ns_since_boot() const override;
virtual timespec time_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; virtual void pre_scheduler_sleep_ns(uint64_t) override;
void sleep_ms(uint64_t ms) const { return sleep_ns(ms * 1'000'000); } void sleep_ms(uint64_t ms) const { return sleep_ns(ms * 1'000'000); }

View File

@ -253,11 +253,11 @@ namespace Kernel
const auto send_ipi = const auto send_ipi =
[&](uint8_t processor, uint32_t data, uint64_t ud) [&](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_HI_REG, (read_from_local_apic(LAPIC_ICR_HI_REG) & 0x00FFFFFF) | (processor << 24));
write_to_local_apic(LAPIC_ICR_LO_REG, data); write_to_local_apic(LAPIC_ICR_LO_REG, data);
udelay(ud); 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()); dprintln("System has {} processors", m_processors.size());
@ -402,21 +402,26 @@ namespace Kernel
void APIC::initialize_timer() void APIC::initialize_timer()
{ {
{ ASSERT(Kernel::Processor::get_interrupt_state() == InterruptState::Disabled);
constexpr uint64_t measuring_duration_ms = 100;
SpinLockGuard _(s_timer_init_lock); constexpr uint64_t measuring_duration_ms = 100;
write_to_local_apic(LAPIC_TIMER_LVT, TimerModeOneShot | TimerMask); const bool needs_lock = SystemTimer::get().pre_scheduler_sleep_needs_lock();
write_to_local_apic(LAPIC_TIMER_DIVIDE_REG, DivideBy2); if (needs_lock)
write_to_local_apic(LAPIC_TIMER_INITIAL_REG, 0xFFFFFFFF); s_timer_init_lock.lock();
SystemTimer::get().pre_scheduler_sleep_ns(measuring_duration_ms * 1'000'000);
const uint32_t counter = read_from_local_apic(LAPIC_TIMER_CURRENT_REG); write_to_local_apic(LAPIC_TIMER_LVT, TimerModeOneShot | TimerMask);
m_lapic_timer_frequency_hz = static_cast<uint64_t>(0xFFFFFFFF - counter) * 2 * (1000 / measuring_duration_ms); 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<uint64_t>(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_LVT, TimerModePeriodic | (IRQ_VECTOR_BASE + IRQ_TIMER));
write_to_local_apic(LAPIC_TIMER_INITIAL_REG, m_lapic_timer_frequency_hz / 2 / 100); write_to_local_apic(LAPIC_TIMER_INITIAL_REG, m_lapic_timer_frequency_hz / 2 / 100);

View File

@ -69,6 +69,11 @@ namespace Kernel
return m_timer->time_since_boot(); 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) void SystemTimer::pre_scheduler_sleep_ns(uint64_t ns)
{ {
return m_timer->pre_scheduler_sleep_ns(ns); return m_timer->pre_scheduler_sleep_ns(ns);