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:
parent
8e08046519
commit
a489be0e05
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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); }
|
||||
|
|
|
@ -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<uint64_t>(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<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_INITIAL_REG, m_lapic_timer_frequency_hz / 2 / 100);
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Reference in New Issue