Kernel: All processors use LAPIC timer when running with APIC
This makes scheduler preemption much cleaner as bsb does not have to send smp messages to notify other processes about timer interrupt. Also PIT percision is now "full" 0.8 us instead of 1 ms that I was using before.
This commit is contained in:
@@ -254,9 +254,9 @@ namespace Kernel
|
||||
|
||||
void HPET::handle_irq()
|
||||
{
|
||||
auto& regs = registers();
|
||||
|
||||
{
|
||||
auto& regs = registers();
|
||||
|
||||
SpinLockGuard _(m_lock);
|
||||
|
||||
uint64_t current_ticks;
|
||||
@@ -272,7 +272,8 @@ namespace Kernel
|
||||
m_last_ticks = current_ticks;
|
||||
}
|
||||
|
||||
Processor::scheduler().timer_interrupt();
|
||||
if (should_invoke_scheduler())
|
||||
Processor::scheduler().timer_interrupt();
|
||||
}
|
||||
|
||||
uint64_t HPET::ms_since_boot() const
|
||||
@@ -303,4 +304,33 @@ namespace Kernel
|
||||
};
|
||||
}
|
||||
|
||||
void HPET::pre_scheduler_sleep_ns(uint64_t ns)
|
||||
{
|
||||
auto& regs = registers();
|
||||
|
||||
const uint64_t target_ticks = BAN::Math::div_round_up<uint64_t>(ns * FS_PER_NS, regs.counter_clk_period);
|
||||
|
||||
if (m_is_64bit)
|
||||
{
|
||||
const uint64_t target_counter = regs.main_counter.full + target_ticks;
|
||||
while (regs.main_counter.full < target_counter)
|
||||
__builtin_ia32_pause();
|
||||
}
|
||||
else
|
||||
{
|
||||
uint64_t elapsed_ticks = 0;
|
||||
uint64_t last_counter = regs.main_counter.low;
|
||||
while (elapsed_ticks < target_ticks)
|
||||
{
|
||||
const uint64_t current_counter = regs.main_counter.low;
|
||||
if (last_counter <= current_counter)
|
||||
elapsed_ticks += current_counter - last_counter;
|
||||
else
|
||||
elapsed_ticks += 0xFFFFFFFF + current_counter - last_counter;
|
||||
last_counter = current_counter;
|
||||
__builtin_ia32_pause();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -18,17 +18,19 @@
|
||||
#define ACCESS_HI 0x10
|
||||
#define ACCESS_LO 0x20
|
||||
|
||||
#define MODE_SQUARE_WAVE 0x06
|
||||
#define MODE_RATE_GENERATOR 0x05
|
||||
|
||||
#define BASE_FREQUENCY 1193182
|
||||
#define TICKS_PER_SECOND 1000
|
||||
|
||||
#define MS_PER_S 1'000
|
||||
#define NS_PER_MS 1'000'000
|
||||
#define NS_PER_S 1'000'000'000
|
||||
|
||||
namespace Kernel
|
||||
{
|
||||
|
||||
constexpr uint16_t s_ticks_per_ms = BASE_FREQUENCY / 1000;
|
||||
|
||||
BAN::ErrorOr<BAN::UniqPtr<PIT>> PIT::create()
|
||||
{
|
||||
PIT* pit = new PIT();
|
||||
@@ -40,12 +42,9 @@ namespace Kernel
|
||||
|
||||
void PIT::initialize()
|
||||
{
|
||||
constexpr uint16_t timer_reload = BASE_FREQUENCY / TICKS_PER_SECOND;
|
||||
|
||||
IO::outb(PIT_CTL, SELECT_CHANNEL0 | ACCESS_LO | ACCESS_HI | MODE_SQUARE_WAVE);
|
||||
|
||||
IO::outb(TIMER0_CTL, (timer_reload >> 0) & 0xff);
|
||||
IO::outb(TIMER0_CTL, (timer_reload >> 8) & 0xff);
|
||||
IO::outb(PIT_CTL, SELECT_CHANNEL0 | ACCESS_LO | ACCESS_HI | MODE_RATE_GENERATOR);
|
||||
IO::outb(TIMER0_CTL, (s_ticks_per_ms >> 0) & 0xff);
|
||||
IO::outb(TIMER0_CTL, (s_ticks_per_ms >> 8) & 0xff);
|
||||
|
||||
MUST(InterruptController::get().reserve_irq(PIT_IRQ));
|
||||
set_irq(PIT_IRQ);
|
||||
@@ -56,34 +55,77 @@ namespace Kernel
|
||||
{
|
||||
{
|
||||
SpinLockGuard _(m_lock);
|
||||
m_system_time++;
|
||||
m_system_time_ms++;
|
||||
}
|
||||
Processor::scheduler().timer_interrupt();
|
||||
|
||||
if (should_invoke_scheduler())
|
||||
Processor::scheduler().timer_interrupt();
|
||||
}
|
||||
|
||||
uint64_t PIT::read_counter() const
|
||||
uint64_t PIT::read_counter_ns() const
|
||||
{
|
||||
SpinLockGuard _(m_lock);
|
||||
return m_system_time;
|
||||
|
||||
// send latch command
|
||||
IO::outb(PIT_CTL, SELECT_CHANNEL0);
|
||||
|
||||
// read ticks
|
||||
uint64_t ticks_this_ms { 0 };
|
||||
ticks_this_ms |= IO::inb(TIMER0_CTL);
|
||||
ticks_this_ms |= IO::inb(TIMER0_CTL) << 8;
|
||||
ticks_this_ms = s_ticks_per_ms - ticks_this_ms;
|
||||
|
||||
const uint64_t ns_this_ms = ticks_this_ms * NS_PER_S / BASE_FREQUENCY;
|
||||
return (m_system_time_ms * NS_PER_MS) + ns_this_ms;
|
||||
}
|
||||
|
||||
uint64_t PIT::ms_since_boot() const
|
||||
{
|
||||
return read_counter() * (MS_PER_S / TICKS_PER_SECOND);
|
||||
return read_counter_ns() / NS_PER_MS;
|
||||
}
|
||||
|
||||
uint64_t PIT::ns_since_boot() const
|
||||
{
|
||||
return read_counter() * (NS_PER_S / TICKS_PER_SECOND);
|
||||
return read_counter_ns();
|
||||
}
|
||||
|
||||
timespec PIT::time_since_boot() const
|
||||
{
|
||||
uint64_t ticks = read_counter();
|
||||
uint64_t ns = read_counter_ns();
|
||||
return timespec {
|
||||
.tv_sec = ticks / TICKS_PER_SECOND,
|
||||
.tv_nsec = (long)((ticks % TICKS_PER_SECOND) * (NS_PER_S / TICKS_PER_SECOND))
|
||||
.tv_sec = static_cast<time_t>(ns / NS_PER_S),
|
||||
.tv_nsec = static_cast<long>(ns % NS_PER_S)
|
||||
};
|
||||
}
|
||||
|
||||
void PIT::pre_scheduler_sleep_ns(uint64_t ns)
|
||||
{
|
||||
const uint64_t target_ticks = BAN::Math::div_round_up<uint64_t>(ns * BASE_FREQUENCY, NS_PER_S);
|
||||
|
||||
SpinLockGuard _(m_lock);
|
||||
|
||||
IO::outb(PIT_CTL, SELECT_CHANNEL0 | ACCESS_LO | ACCESS_HI | MODE_RATE_GENERATOR);
|
||||
IO::outb(TIMER0_CTL, 0);
|
||||
IO::outb(TIMER0_CTL, 0);
|
||||
|
||||
IO::outb(PIT_CTL, SELECT_CHANNEL0 | ACCESS_LO | MODE_RATE_GENERATOR);
|
||||
IO::outb(TIMER0_CTL, 0xFF);
|
||||
|
||||
uint64_t elapsed_ticks = 0;
|
||||
uint8_t last_ticks = IO::inb(TIMER0_CTL);
|
||||
while (elapsed_ticks < target_ticks)
|
||||
{
|
||||
const uint8_t current_ticks = IO::inb(TIMER0_CTL);
|
||||
if (last_ticks <= current_ticks)
|
||||
elapsed_ticks += current_ticks - last_ticks;
|
||||
else
|
||||
elapsed_ticks += 0xFF + current_ticks - last_ticks;
|
||||
last_ticks = current_ticks;
|
||||
}
|
||||
|
||||
IO::outb(PIT_CTL, SELECT_CHANNEL0 | ACCESS_LO | ACCESS_HI | MODE_RATE_GENERATOR);
|
||||
IO::outb(TIMER0_CTL, (s_ticks_per_ms >> 0) & 0xFF);
|
||||
IO::outb(TIMER0_CTL, (s_ticks_per_ms >> 8) & 0xFF);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -69,6 +69,11 @@ namespace Kernel
|
||||
return m_timer->time_since_boot();
|
||||
}
|
||||
|
||||
void SystemTimer::pre_scheduler_sleep_ns(uint64_t ns)
|
||||
{
|
||||
return m_timer->pre_scheduler_sleep_ns(ns);
|
||||
}
|
||||
|
||||
void SystemTimer::sleep_ns(uint64_t ns) const
|
||||
{
|
||||
if (ns == 0)
|
||||
@@ -76,10 +81,6 @@ namespace Kernel
|
||||
|
||||
const uint64_t wake_time_ns = ns_since_boot() + ns;
|
||||
Processor::scheduler().block_current_thread(nullptr, wake_time_ns);
|
||||
|
||||
//const uint64_t current_time_ns = ns_since_boot();
|
||||
//if (current_time_ns < wake_time_ns)
|
||||
// dwarnln("sleep woke {} ms too soon", BAN::Math::div_round_up<uint64_t>(wake_time_ns - current_time_ns, 1'000'000));
|
||||
}
|
||||
|
||||
timespec SystemTimer::real_time() const
|
||||
|
||||
Reference in New Issue
Block a user