From 5f51d851840d889520c21cc4359d6db59d83ff06 Mon Sep 17 00:00:00 2001 From: Tom Date: Thu, 3 Dec 2020 22:12:50 -0700 Subject: Kernel: Improve time keeping and dramatically reduce interrupt load This implements a number of changes related to time: * If a HPET is present, it is now used only as a system timer, unless the Local APIC timer is used (in which case the HPET timer will not trigger any interrupts at all). * If a HPET is present, the current time can now be as accurate as the chip can be, independently from the system timer. We now query the HPET main counter for the current time in CPU #0's system timer interrupt, and use that as a base line. If a high precision time is queried, that base line is used in combination with quering the HPET timer directly, which should give a much more accurate time stamp at the expense of more overhead. For faster time stamps, the more coarse value based on the last interrupt will be returned. This also means that any missed interrupts should not cause the time to drift. * The default system interrupt rate is reduced to about 250 per second. * Fix calculation of Thread CPU usage by using the amount of ticks they used rather than the number of times a context switch happened. * Implement CLOCK_REALTIME_COARSE and CLOCK_MONOTONIC_COARSE and use it for most cases where precise timestamps are not needed. --- Kernel/FileSystem/ProcFS.cpp | 3 +- Kernel/Scheduler.cpp | 8 +-- Kernel/Syscalls/alarm.cpp | 4 +- Kernel/Syscalls/clock.cpp | 30 ++++----- Kernel/Thread.cpp | 12 ++-- Kernel/Thread.h | 19 +++--- Kernel/Time/APICTimer.h | 1 + Kernel/Time/HPET.cpp | 44 +++++++++--- Kernel/Time/HPET.h | 8 ++- Kernel/Time/HPETComparator.cpp | 33 ++++++--- Kernel/Time/HPETComparator.h | 4 +- Kernel/Time/HardwareTimer.h | 1 + Kernel/Time/PIT.h | 1 + Kernel/Time/RTC.h | 1 + Kernel/Time/TimeManagement.cpp | 150 +++++++++++++++++++++++++++-------------- Kernel/Time/TimeManagement.h | 34 +++++++--- Kernel/TimerQueue.cpp | 80 ++++++++++------------ Kernel/TimerQueue.h | 8 +-- Kernel/UnixTypes.h | 3 + 19 files changed, 279 insertions(+), 165 deletions(-) (limited to 'Kernel') diff --git a/Kernel/FileSystem/ProcFS.cpp b/Kernel/FileSystem/ProcFS.cpp index 8c41de2106..f223f40dd3 100644 --- a/Kernel/FileSystem/ProcFS.cpp +++ b/Kernel/FileSystem/ProcFS.cpp @@ -884,7 +884,8 @@ static OwnPtr procfs$all(InodeIdentifier) thread_object.add("tid", thread.tid().value()); thread_object.add("name", thread.name()); thread_object.add("times_scheduled", thread.times_scheduled()); - thread_object.add("ticks", thread.ticks()); + thread_object.add("ticks_user", thread.ticks_in_user()); + thread_object.add("ticks_kernel", thread.ticks_in_kernel()); thread_object.add("state", thread.state_string()); thread_object.add("cpu", thread.cpu()); thread_object.add("priority", thread.priority()); diff --git a/Kernel/Scheduler.cpp b/Kernel/Scheduler.cpp index 41395e311c..7283630f97 100644 --- a/Kernel/Scheduler.cpp +++ b/Kernel/Scheduler.cpp @@ -64,10 +64,10 @@ void Scheduler::init_thread(Thread& thread) static u32 time_slice_for(const Thread& thread) { - // One time slice unit == 1ms + // One time slice unit == 4ms (assuming 250 ticks/second) if (&thread == Processor::current().idle_thread()) return 1; - return 10; + return 2; } Thread* g_finalizer; @@ -219,6 +219,7 @@ bool Scheduler::pick_next() // but since we're still holding the scheduler lock we're still in a critical section critical.leave(); + thread_to_schedule->set_ticks_left(time_slice_for(*thread_to_schedule)); return context_switch(thread_to_schedule); } @@ -317,7 +318,6 @@ bool Scheduler::donate_to(RefPtr& beneficiary, const char* reason) bool Scheduler::context_switch(Thread* thread) { - thread->set_ticks_left(time_slice_for(*thread)); thread->did_schedule(); auto from_thread = Thread::current(); @@ -480,7 +480,7 @@ void Scheduler::timer_tick(const RegisterState& regs) } } - if (current_thread->tick()) + if (current_thread->tick((regs.cs & 3) == 0)) return; ASSERT_INTERRUPTS_DISABLED(); diff --git a/Kernel/Syscalls/alarm.cpp b/Kernel/Syscalls/alarm.cpp index f740fd0c47..8282572acc 100644 --- a/Kernel/Syscalls/alarm.cpp +++ b/Kernel/Syscalls/alarm.cpp @@ -46,9 +46,9 @@ unsigned Process::sys$alarm(unsigned seconds) } if (seconds > 0) { - auto deadline = TimeManagement::the().current_time(CLOCK_REALTIME).value(); + auto deadline = TimeManagement::the().current_time(CLOCK_REALTIME_COARSE).value(); timespec_add(deadline, { seconds, 0 }, deadline); - m_alarm_timer = TimerQueue::the().add_timer_without_id(CLOCK_REALTIME, deadline, [this]() { + m_alarm_timer = TimerQueue::the().add_timer_without_id(CLOCK_REALTIME_COARSE, deadline, [this]() { [[maybe_unused]] auto rc = send_signal(SIGALRM, nullptr); }); } diff --git a/Kernel/Syscalls/clock.cpp b/Kernel/Syscalls/clock.cpp index a48c2df98a..c28552953d 100644 --- a/Kernel/Syscalls/clock.cpp +++ b/Kernel/Syscalls/clock.cpp @@ -78,25 +78,21 @@ int Process::sys$clock_nanosleep(Userspacesleep_until(params.clock_id, requested_sleep).was_interrupted(); - } else { - timespec remaining_sleep; - was_interrupted = Thread::current()->sleep(params.clock_id, requested_sleep, &remaining_sleep).was_interrupted(); - if (was_interrupted && params.remaining_sleep && !copy_to_user(params.remaining_sleep, &remaining_sleep)) - return -EFAULT; - } - if (was_interrupted) - return -EINTR; - return 0; - } - default: + if (!TimeManagement::is_valid_clock_id(params.clock_id)) return -EINVAL; + + bool was_interrupted; + if (is_absolute) { + was_interrupted = Thread::current()->sleep_until(params.clock_id, requested_sleep).was_interrupted(); + } else { + timespec remaining_sleep; + was_interrupted = Thread::current()->sleep(params.clock_id, requested_sleep, &remaining_sleep).was_interrupted(); + if (was_interrupted && params.remaining_sleep && !copy_to_user(params.remaining_sleep, &remaining_sleep)) + return -EFAULT; } + if (was_interrupted) + return -EINTR; + return 0; } int Process::sys$adjtime(Userspace user_delta, Userspace user_old_delta) diff --git a/Kernel/Thread.cpp b/Kernel/Thread.cpp index a863780586..ddc8e8b001 100644 --- a/Kernel/Thread.cpp +++ b/Kernel/Thread.cpp @@ -400,13 +400,15 @@ void Thread::finalize_dying_threads() } } -bool Thread::tick() +bool Thread::tick(bool in_kernel) { - ++m_ticks; - if (tss().cs & 3) - ++m_process->m_ticks_in_user; - else + if (in_kernel) { ++m_process->m_ticks_in_kernel; + ++m_ticks_in_kernel; + } else { + ++m_process->m_ticks_in_user; + ++m_ticks_in_user; + } return --m_ticks_left; } diff --git a/Kernel/Thread.h b/Kernel/Thread.h index 5a1c51702c..77c543872e 100644 --- a/Kernel/Thread.h +++ b/Kernel/Thread.h @@ -211,7 +211,7 @@ public: : m_infinite(true) { } - explicit BlockTimeout(bool is_absolute, const timeval* time, const timespec* start_time = nullptr, clockid_t clock_id = CLOCK_MONOTONIC) + explicit BlockTimeout(bool is_absolute, const timeval* time, const timespec* start_time = nullptr, clockid_t clock_id = CLOCK_MONOTONIC_COARSE) : m_clock_id(clock_id) , m_infinite(!time) { @@ -225,7 +225,7 @@ public: timespec_add(m_time, m_start_time, m_time); } } - explicit BlockTimeout(bool is_absolute, const timespec* time, const timespec* start_time = nullptr, clockid_t clock_id = CLOCK_MONOTONIC) + explicit BlockTimeout(bool is_absolute, const timespec* time, const timespec* start_time = nullptr, clockid_t clock_id = CLOCK_MONOTONIC_COARSE) : m_clock_id(clock_id) , m_infinite(!time) { @@ -249,7 +249,7 @@ public: private: timespec m_time { 0, 0 }; timespec m_start_time { 0, 0 }; - clockid_t m_clock_id { CLOCK_MONOTONIC }; + clockid_t m_clock_id { CLOCK_MONOTONIC_COARSE }; bool m_infinite { false }; bool m_should_block { false }; }; @@ -756,7 +756,6 @@ public: const TSS32& tss() const { return m_tss; } State state() const { return m_state; } const char* state_string() const; - u32 ticks() const { return m_ticks; } VirtualAddress thread_specific_data() const { return m_thread_specific_data; } size_t thread_specific_region_size() const { return m_thread_specific_region_size; } @@ -921,12 +920,12 @@ public: BlockResult sleep(clockid_t, const timespec&, timespec* = nullptr); BlockResult sleep(const timespec& duration, timespec* remaining_time = nullptr) { - return sleep(CLOCK_MONOTONIC, duration, remaining_time); + return sleep(CLOCK_MONOTONIC_COARSE, duration, remaining_time); } BlockResult sleep_until(clockid_t, const timespec&); BlockResult sleep_until(const timespec& duration) { - return sleep_until(CLOCK_MONOTONIC, duration); + return sleep_until(CLOCK_MONOTONIC_COARSE, duration); } // Tell this thread to unblock if needed, @@ -937,7 +936,7 @@ public: void exit(void* = nullptr); - bool tick(); + bool tick(bool in_kernel); void set_ticks_left(u32 t) { m_ticks_left = t; } u32 ticks_left() const { return m_ticks_left; } @@ -1070,6 +1069,9 @@ public: static constexpr u32 default_kernel_stack_size = 65536; static constexpr u32 default_userspace_stack_size = 4 * MiB; + u32 ticks_in_user() const { return m_ticks_in_user; } + u32 ticks_in_kernel() const { return m_ticks_in_kernel; } + RecursiveSpinLock& get_lock() const { return m_lock; } #ifdef LOCK_DEBUG @@ -1188,9 +1190,10 @@ private: TSS32 m_tss; Atomic m_cpu { 0 }; u32 m_cpu_affinity { THREAD_AFFINITY_DEFAULT }; - u32 m_ticks { 0 }; u32 m_ticks_left { 0 }; u32 m_times_scheduled { 0 }; + u32 m_ticks_in_user { 0 }; + u32 m_ticks_in_kernel { 0 }; u32 m_pending_signals { 0 }; u32 m_signal_mask { 0 }; u32 m_kernel_stack_base { 0 }; diff --git a/Kernel/Time/APICTimer.h b/Kernel/Time/APICTimer.h index 15f12a31b1..7e9b24e630 100644 --- a/Kernel/Time/APICTimer.h +++ b/Kernel/Time/APICTimer.h @@ -44,6 +44,7 @@ public: virtual bool is_periodic_capable() const override { return true; } virtual void set_periodic() override; virtual void set_non_periodic() override; + virtual void disable() override { } virtual void reset_to_default_ticks_per_second() override; virtual bool try_to_set_frequency(size_t frequency) override; diff --git a/Kernel/Time/HPET.cpp b/Kernel/Time/HPET.cpp index ed4c5a5e1c..12014476bd 100644 --- a/Kernel/Time/HPET.cpp +++ b/Kernel/Time/HPET.cpp @@ -197,10 +197,14 @@ void HPET::update_periodic_comparator_value() auto& regs = registers(); u64 previous_main_value = (u64)regs.main_counter_value.low | ((u64)regs.main_counter_value.high << 32); + m_main_counter_drift += previous_main_value - m_main_counter_last_read; + m_main_counter_last_read = 0; regs.main_counter_value.low = 0; regs.main_counter_value.high = 0; for (auto& comparator : m_comparators) { auto& timer = regs.timers[comparator.comparator_number()]; + if (!comparator.is_enabled()) + continue; if (comparator.is_periodic()) { // Note that this means we're restarting all periodic timers. There is no // way to resume periodic timers properly because we reset the main counter @@ -242,6 +246,33 @@ void HPET::update_non_periodic_comparator_value(const HPETComparator& comparator timer.comparator_value.low = (u32)new_counter_value; } +u64 HPET::update_time(u64& seconds_since_boot, u32& ticks_this_second, bool query_only) +{ + // Should only be called by the time keeper interrupt handler! + u64 current_value = read_register_safe64(registers().main_counter_value); + u64 delta_ticks = m_main_counter_drift; + if (current_value >= m_main_counter_last_read) + delta_ticks += current_value - m_main_counter_last_read; + else + delta_ticks += m_main_counter_last_read - current_value; // the counter wrapped around + u64 ticks_since_last_second = (u64)ticks_this_second + delta_ticks; + auto ticks_per_second = frequency(); + if (ticks_since_last_second > ticks_per_second) { + seconds_since_boot += ticks_since_last_second / ticks_per_second; + ticks_this_second = ticks_since_last_second % ticks_per_second; + } else { + ticks_this_second = ticks_since_last_second; + } + + if (!query_only) { + m_main_counter_drift = 0; + m_main_counter_last_read = current_value; + } + + // Return the time passed (in ns) since last time update_time was called + return (delta_ticks * 1000000000ull) / ticks_per_second; +} + void HPET::enable_periodic_interrupt(const HPETComparator& comparator) { #ifdef HPET_DEBUG @@ -253,7 +284,8 @@ void HPET::enable_periodic_interrupt(const HPETComparator& comparator) auto capabilities = timer.capabilities; ASSERT(capabilities & (u32)HPETFlags::TimerConfiguration::PeriodicInterruptCapable); timer.capabilities = capabilities | (u32)HPETFlags::TimerConfiguration::GeneratePeriodicInterrupt; - enable(comparator); + if (comparator.is_enabled()) + enable(comparator); } void HPET::disable_periodic_interrupt(const HPETComparator& comparator) { @@ -266,7 +298,8 @@ void HPET::disable_periodic_interrupt(const HPETComparator& comparator) auto capabilities = timer.capabilities; ASSERT(capabilities & (u32)HPETFlags::TimerConfiguration::PeriodicInterruptCapable); timer.capabilities = capabilities & ~(u32)HPETFlags::TimerConfiguration::GeneratePeriodicInterrupt; - enable(comparator); + if (comparator.is_enabled()) + enable(comparator); } void HPET::disable(const HPETComparator& comparator) @@ -288,11 +321,6 @@ void HPET::enable(const HPETComparator& comparator) timer.capabilities = timer.capabilities | (u32)HPETFlags::TimerConfiguration::InterruptEnable; } -u64 HPET::frequency() const -{ - return m_frequency; -} - Vector HPET::capable_interrupt_numbers(const HPETComparator& comparator) { ASSERT(comparator.comparator_number() <= m_comparators.size()); @@ -394,7 +422,7 @@ HPET::HPET(PhysicalAddress acpi_hpet) klog() << "HPET: frequency " << m_frequency << " Hz (" << MEGAHERTZ_TO_HERTZ(m_frequency) << " MHz) resolution: " << calculate_ticks_in_nanoseconds() << "ns"; ASSERT(regs.capabilities.main_counter_tick_period <= ABSOLUTE_MAXIMUM_COUNTER_TICK_PERIOD); - // Reset the counter, just in case... + // Reset the counter, just in case... (needs to match m_main_counter_last_read) regs.main_counter_value.high = 0; regs.main_counter_value.low = 0; if (regs.capabilities.attributes & (u32)HPETFlags::Attributes::LegacyReplacementRouteCapable) diff --git a/Kernel/Time/HPET.h b/Kernel/Time/HPET.h index d60adc4839..e9460d3484 100644 --- a/Kernel/Time/HPET.h +++ b/Kernel/Time/HPET.h @@ -45,7 +45,7 @@ public: static bool check_for_exisiting_periodic_timers(); static HPET& the(); - u64 frequency() const; + u64 frequency() const { return m_frequency; } const NonnullRefPtrVector& comparators() const { return m_comparators; } void disable(const HPETComparator&); @@ -59,6 +59,8 @@ public: void enable_periodic_interrupt(const HPETComparator& comparator); void disable_periodic_interrupt(const HPETComparator& comparator); + u64 update_time(u64& seconds_since_boot, u32& ticks_this_second, bool query_only); + Vector capable_interrupt_numbers(u8 comparator_number); Vector capable_interrupt_numbers(const HPETComparator&); @@ -80,7 +82,9 @@ private: PhysicalAddress m_physical_acpi_hpet_registers; OwnPtr m_hpet_mmio_region; - u64 m_main_counter_clock_period { 0 }; + u64 m_main_counter_last_read { 0 }; + u64 m_main_counter_drift { 0 }; + u16 m_vendor_id; u16 m_minimum_tick; u64 m_frequency; diff --git a/Kernel/Time/HPETComparator.cpp b/Kernel/Time/HPETComparator.cpp index f902b92ea8..9507cee02e 100644 --- a/Kernel/Time/HPETComparator.cpp +++ b/Kernel/Time/HPETComparator.cpp @@ -41,20 +41,31 @@ HPETComparator::HPETComparator(u8 number, u8 irq, bool periodic_capable) : HardwareTimer(irq) , m_periodic(false) , m_periodic_capable(periodic_capable) + , m_enabled(false) , m_comparator_number(number) { } +void HPETComparator::disable() +{ + if (!m_enabled) + return; + m_enabled = false; + HPET::the().disable(*this); +} + void HPETComparator::set_periodic() { ASSERT(m_periodic_capable); m_periodic = true; + m_enabled = true; HPET::the().enable_periodic_interrupt(*this); } void HPETComparator::set_non_periodic() { ASSERT(m_periodic_capable); m_periodic = false; + m_enabled = true; HPET::the().disable_periodic_interrupt(*this); } @@ -79,7 +90,7 @@ size_t HPETComparator::ticks_per_second() const void HPETComparator::reset_to_default_ticks_per_second() { - ASSERT(is_capable_of_frequency(OPTIMAL_TICKS_PER_SECOND_RATE)); + dbg() << "reset_to_default_ticks_per_second"; m_frequency = OPTIMAL_TICKS_PER_SECOND_RATE; if (!is_periodic()) set_new_countdown(); @@ -89,13 +100,16 @@ void HPETComparator::reset_to_default_ticks_per_second() bool HPETComparator::try_to_set_frequency(size_t frequency) { InterruptDisabler disabler; - if (!is_capable_of_frequency(frequency)) + if (!is_capable_of_frequency(frequency)) { + dbg() << "HPETComparator: not cable of frequency: " << frequency; return false; - if (m_frequency == frequency) - return true; + } + auto hpet_frequency = HPET::the().frequency(); ASSERT(frequency <= hpet_frequency); m_frequency = frequency; + m_enabled = true; + #ifdef HPET_COMPARATOR_DEBUG dbg() << "HPET Comparator: Max frequency " << hpet_frequency << " Hz, want to set " << frequency << " Hz, periodic: " << is_periodic(); #endif @@ -112,16 +126,17 @@ bool HPETComparator::is_capable_of_frequency(size_t frequency) const { if (frequency > HPET::the().frequency()) return false; - if ((HPET::the().frequency() % frequency) != 0) - return false; + // HPET::update_periodic_comparator_value and HPET::update_non_periodic_comparator_value + // calculate the best counter based on the desired frequency. return true; } size_t HPETComparator::calculate_nearest_possible_frequency(size_t frequency) const { - if (frequency >= HPET::the().frequency()) + if (frequency > HPET::the().frequency()) return HPET::the().frequency(); - // FIXME: Use better math here - return (frequency + (HPET::the().frequency() % frequency)); + // HPET::update_periodic_comparator_value and HPET::update_non_periodic_comparator_value + // calculate the best counter based on the desired frequency. + return frequency; } } diff --git a/Kernel/Time/HPETComparator.h b/Kernel/Time/HPETComparator.h index d4c79f2a0b..771eca1887 100644 --- a/Kernel/Time/HPETComparator.h +++ b/Kernel/Time/HPETComparator.h @@ -42,6 +42,7 @@ public: virtual const char* model() const override { return "HPET"; } u8 comparator_number() const { return m_comparator_number; } + bool is_enabled() const { return m_enabled; } virtual size_t ticks_per_second() const override; @@ -49,6 +50,7 @@ public: virtual bool is_periodic_capable() const override { return m_periodic_capable; } virtual void set_periodic() override; virtual void set_non_periodic() override; + virtual void disable() override; virtual void reset_to_default_ticks_per_second() override; virtual bool try_to_set_frequency(size_t frequency) override; @@ -61,7 +63,7 @@ private: HPETComparator(u8 number, u8 irq, bool periodic_capable); bool m_periodic : 1; bool m_periodic_capable : 1; - bool m_edge_triggered : 1; + bool m_enabled : 1; u8 m_comparator_number { 0 }; }; } diff --git a/Kernel/Time/HardwareTimer.h b/Kernel/Time/HardwareTimer.h index f88435ac86..5dbbb76f35 100644 --- a/Kernel/Time/HardwareTimer.h +++ b/Kernel/Time/HardwareTimer.h @@ -57,6 +57,7 @@ public: virtual bool is_periodic_capable() const = 0; virtual void set_periodic() = 0; virtual void set_non_periodic() = 0; + virtual void disable() = 0; virtual size_t ticks_per_second() const = 0; diff --git a/Kernel/Time/PIT.h b/Kernel/Time/PIT.h index b00eee520e..3a1efaa6da 100644 --- a/Kernel/Time/PIT.h +++ b/Kernel/Time/PIT.h @@ -63,6 +63,7 @@ public: virtual bool is_periodic_capable() const override { return true; } virtual void set_periodic() override; virtual void set_non_periodic() override; + virtual void disable() override { } virtual void reset_to_default_ticks_per_second() override; virtual bool try_to_set_frequency(size_t frequency) override; diff --git a/Kernel/Time/RTC.h b/Kernel/Time/RTC.h index e2427dff01..76d263be9c 100644 --- a/Kernel/Time/RTC.h +++ b/Kernel/Time/RTC.h @@ -42,6 +42,7 @@ public: virtual bool is_periodic_capable() const override { return true; } virtual void set_periodic() override { } virtual void set_non_periodic() override { } + virtual void disable() override { } virtual void reset_to_default_ticks_per_second() override; virtual bool try_to_set_frequency(size_t frequency) override; diff --git a/Kernel/Time/TimeManagement.cpp b/Kernel/Time/TimeManagement.cpp index bcf0d8f8ea..d255b87fe7 100644 --- a/Kernel/Time/TimeManagement.cpp +++ b/Kernel/Time/TimeManagement.cpp @@ -52,13 +52,33 @@ TimeManagement& TimeManagement::the() return *s_the; } +bool TimeManagement::is_valid_clock_id(clockid_t clock_id) +{ + switch (clock_id) { + case CLOCK_MONOTONIC: + case CLOCK_MONOTONIC_COARSE: + case CLOCK_MONOTONIC_RAW: + case CLOCK_REALTIME: + case CLOCK_REALTIME_COARSE: + return true; + default: + return false; + }; +} + KResultOr TimeManagement::current_time(clockid_t clock_id) const { switch (clock_id) { case CLOCK_MONOTONIC: - return monotonic_time(); + return monotonic_time(TimePrecision::Precise); + case CLOCK_MONOTONIC_COARSE: + return monotonic_time(TimePrecision::Coarse); + case CLOCK_MONOTONIC_RAW: + return monotonic_time_raw(); case CLOCK_REALTIME: - return epoch_time(); + return epoch_time(TimePrecision::Precise); + case CLOCK_REALTIME_COARSE: + return epoch_time(TimePrecision::Coarse); default: return KResult(EINVAL); } @@ -69,22 +89,6 @@ bool TimeManagement::is_system_timer(const HardwareTimerBase& timer) const return &timer == m_system_timer.ptr(); } -timespec TimeManagement::ticks_to_time(u64 ticks, time_t ticks_per_second) -{ - timespec tspec; - tspec.tv_sec = ticks / ticks_per_second; - tspec.tv_nsec = (ticks % ticks_per_second) * (1'000'000'000 / ticks_per_second); - ASSERT(tspec.tv_nsec <= 1'000'000'000); - return tspec; -} - -u64 TimeManagement::time_to_ticks(const timespec& tspec, time_t ticks_per_second) -{ - u64 ticks = (u64)tspec.tv_sec * ticks_per_second; - ticks += ((u64)tspec.tv_nsec * ticks_per_second) / 1'000'000'000; - return ticks; -} - void TimeManagement::set_epoch_time(timespec ts) { InterruptDisabler disabler; @@ -92,27 +96,40 @@ void TimeManagement::set_epoch_time(timespec ts) m_remaining_epoch_time_adjustment = { 0, 0 }; } -u64 TimeManagement::monotonic_ticks() const +timespec TimeManagement::monotonic_time(TimePrecision precision) const { - long seconds; - u64 ticks; + // This is the time when last updated by an interrupt. + u64 seconds; + u32 ticks; + + bool do_query = precision == TimePrecision::Precise && m_can_query_precise_time; u32 update_iteration; do { update_iteration = m_update1.load(AK::MemoryOrder::memory_order_acquire); seconds = m_seconds_since_boot; ticks = m_ticks_this_second; + + if (do_query) { + // We may have to do this over again if the timer interrupt fires + // while we're trying to query the information. In that case, our + // seconds and ticks became invalid, producing an incorrect time. + // Be sure to not modify m_seconds_since_boot and m_ticks_this_second + // because this may only be modified by the interrupt handler + HPET::the().update_time(seconds, ticks, true); + } } while (update_iteration != m_update2.load(AK::MemoryOrder::memory_order_acquire)); - return ticks + (u64)seconds * (u64)ticks_per_second(); -} -timespec TimeManagement::monotonic_time() const -{ - return ticks_to_time(monotonic_ticks(), ticks_per_second()); + ASSERT(m_time_ticks_per_second > 0); + ASSERT(ticks < m_time_ticks_per_second); + u64 ns = ((u64)ticks * 1000000000ull) / m_time_ticks_per_second; + ASSERT(ns < 1000000000ull); + return { (long)seconds, (long)ns }; } -timespec TimeManagement::epoch_time() const +timespec TimeManagement::epoch_time(TimePrecision) const { + // TODO: Take into account precision timespec ts; u32 update_iteration; do { @@ -155,7 +172,9 @@ void TimeManagement::initialize(u32 cpu) void TimeManagement::set_system_timer(HardwareTimerBase& timer) { + ASSERT(Processor::current().id() == 0); // This should only be called on the BSP! auto original_callback = m_system_timer->set_callback(nullptr); + m_system_timer->disable(); timer.set_callback(move(original_callback)); m_system_timer = timer; } @@ -259,28 +278,36 @@ bool TimeManagement::probe_and_set_non_legacy_hardware_timers() if (is_hpet_periodic_mode_allowed()) ASSERT(!periodic_timers.is_empty()); - ASSERT(periodic_timers.size() + non_periodic_timers.size() >= 2); + ASSERT(periodic_timers.size() + non_periodic_timers.size() > 0); - if (periodic_timers.size() >= 2) { - m_time_keeper_timer = periodic_timers[1]; + if (periodic_timers.size() > 0) m_system_timer = periodic_timers[0]; - } else { - if (periodic_timers.size() == 1) { - m_time_keeper_timer = periodic_timers[0]; - m_system_timer = non_periodic_timers[0]; - } else { - m_time_keeper_timer = non_periodic_timers[1]; - m_system_timer = non_periodic_timers[0]; + else + m_system_timer = non_periodic_timers[0]; + + m_system_timer->set_callback([this](const RegisterState& regs) { + // Update the time. We don't really care too much about the + // frequency of the interrupt because we'll query the main + // counter to get an accurate time. + if (Processor::current().id() == 0) { + // TODO: Have the other CPUs call system_timer_tick directly + increment_time_since_boot_hpet(); } - } - m_system_timer->set_callback(TimeManagement::timer_tick); - m_time_keeper_timer->set_callback(TimeManagement::update_time); + system_timer_tick(regs); + }); - dbg() << "Reset timers"; - m_system_timer->try_to_set_frequency(m_system_timer->calculate_nearest_possible_frequency(1024)); - m_time_keeper_timer->try_to_set_frequency(OPTIMAL_TICKS_PER_SECOND_RATE); + // Use the HPET main counter frequency for time purposes. This is likely + // a much higher frequency than the interrupt itself and allows us to + // keep a more accurate time + m_can_query_precise_time = true; + m_time_ticks_per_second = HPET::the().frequency(); + m_system_timer->try_to_set_frequency(m_system_timer->calculate_nearest_possible_frequency(OPTIMAL_TICKS_PER_SECOND_RATE)); + + // We don't need an interrupt for time keeping purposes because we + // can query the timer. + m_time_keeper_timer = m_system_timer; return true; } @@ -296,18 +323,43 @@ bool TimeManagement::probe_and_set_legacy_hardware_timers() } m_hardware_timers.append(PIT::initialize(TimeManagement::update_time)); - m_hardware_timers.append(RealTimeClock::create(TimeManagement::timer_tick)); + m_hardware_timers.append(RealTimeClock::create(TimeManagement::system_timer_tick)); m_time_keeper_timer = m_hardware_timers[0]; m_system_timer = m_hardware_timers[1]; + + // The timer is only as accurate as the interrupts... + m_time_ticks_per_second = m_time_keeper_timer->ticks_per_second(); return true; } -void TimeManagement::update_time(const RegisterState& regs) +void TimeManagement::update_time(const RegisterState&) { - TimeManagement::the().increment_time_since_boot(regs); + TimeManagement::the().increment_time_since_boot(); +} + +void TimeManagement::increment_time_since_boot_hpet() +{ + ASSERT(!m_time_keeper_timer.is_null()); + ASSERT(m_time_keeper_timer->timer_type() == HardwareTimerType::HighPrecisionEventTimer); + + // NOTE: m_seconds_since_boot and m_ticks_this_second are only ever + // updated here! So we can safely read that information, query the clock, + // and when we're all done we can update the information. This reduces + // contention when other processors attempt to read the clock. + auto seconds_since_boot = m_seconds_since_boot; + auto ticks_this_second = m_ticks_this_second; + auto delta_ns = HPET::the().update_time(seconds_since_boot, ticks_this_second, false); + + // Now that we have a precise time, go update it as quickly as we can + u32 update_iteration = m_update1.fetch_add(1, AK::MemoryOrder::memory_order_acquire); + m_seconds_since_boot = seconds_since_boot; + m_ticks_this_second = ticks_this_second; + // TODO: Apply m_remaining_epoch_time_adjustment + timespec_add(m_epoch_time, { (time_t)(delta_ns / 1000000000), (long)(delta_ns % 1000000000) }, m_epoch_time); + m_update2.store(update_iteration + 1, AK::MemoryOrder::memory_order_release); } -void TimeManagement::increment_time_since_boot(const RegisterState&) +void TimeManagement::increment_time_since_boot() { ASSERT(!m_time_keeper_timer.is_null()); @@ -318,7 +370,7 @@ void TimeManagement::increment_time_since_boot(const RegisterState&) constexpr time_t MaxSlewNanos = NanosPerTick / 100; static_assert(MaxSlewNanos < NanosPerTick); - u32 update_iteration = m_update1.fetch_add(1, AK::MemoryOrder::memory_order_relaxed); + u32 update_iteration = m_update1.fetch_add(1, AK::MemoryOrder::memory_order_acquire); // Clamp twice, to make sure intermediate fits into a long. long slew_nanos = clamp(clamp(m_remaining_epoch_time_adjustment.tv_sec, (time_t)-1, (time_t)1) * 1'000'000'000 + m_remaining_epoch_time_adjustment.tv_nsec, -MaxSlewNanos, MaxSlewNanos); @@ -338,7 +390,7 @@ void TimeManagement::increment_time_since_boot(const RegisterState&) m_update2.store(update_iteration + 1, AK::MemoryOrder::memory_order_release); } -void TimeManagement::timer_tick(const RegisterState& regs) +void TimeManagement::system_timer_tick(const RegisterState& regs) { if (Processor::current().in_irq() <= 1) { // Don't expire timers while handling IRQs diff --git a/Kernel/Time/TimeManagement.h b/Kernel/Time/TimeManagement.h index 009f345742..bed0fd281d 100644 --- a/Kernel/Time/TimeManagement.h +++ b/Kernel/Time/TimeManagement.h @@ -34,10 +34,15 @@ namespace Kernel { -#define OPTIMAL_TICKS_PER_SECOND_RATE 1000 +#define OPTIMAL_TICKS_PER_SECOND_RATE 250 class HardwareTimerBase; +enum class TimePrecision { + Coarse = 0, + Precise +}; + class TimeManagement { AK_MAKE_ETERNAL; @@ -47,12 +52,15 @@ public: static void initialize(u32 cpu); static TimeManagement& the(); - static timespec ticks_to_time(u64 ticks, time_t ticks_per_second); - static u64 time_to_ticks(const timespec& tspec, time_t ticks_per_second); - - KResultOr current_time(clockid_t clock_id) const; - timespec monotonic_time() const; - timespec epoch_time() const; + static bool is_valid_clock_id(clockid_t); + KResultOr current_time(clockid_t) const; + timespec monotonic_time(TimePrecision = TimePrecision::Coarse) const; + timespec monotonic_time_raw() const + { + // TODO: implement + return monotonic_time(TimePrecision::Precise); + } + timespec epoch_time(TimePrecision = TimePrecision::Precise) const; void set_epoch_time(timespec); time_t ticks_per_second() const; time_t boot_time() const; @@ -60,12 +68,13 @@ public: bool is_system_timer(const HardwareTimerBase&) const; static void update_time(const RegisterState&); - void increment_time_since_boot(const RegisterState&); + static void update_time_hpet(const RegisterState&); + void increment_time_since_boot_hpet(); + void increment_time_since_boot(); static bool is_hpet_periodic_mode_allowed(); u64 uptime_ms() const; - u64 monotonic_ticks() const; static timeval now_as_timeval(); timespec remaining_epoch_time_adjustment() const { return m_remaining_epoch_time_adjustment; } @@ -78,16 +87,19 @@ private: Vector scan_for_non_periodic_timers(); NonnullRefPtrVector m_hardware_timers; void set_system_timer(HardwareTimerBase&); - static void timer_tick(const RegisterState&); + static void system_timer_tick(const RegisterState&); // Variables between m_update1 and m_update2 are synchronized Atomic m_update1 { 0 }; u32 m_ticks_this_second { 0 }; - u32 m_seconds_since_boot { 0 }; + u64 m_seconds_since_boot { 0 }; timespec m_epoch_time { 0, 0 }; timespec m_remaining_epoch_time_adjustment { 0, 0 }; Atomic m_update2 { 0 }; + u32 m_time_ticks_per_second { 0 }; // may be different from interrupts/second (e.g. hpet) + bool m_can_query_precise_time { false }; + RefPtr m_system_timer; RefPtr m_time_keeper_timer; }; diff --git a/Kernel/TimerQueue.cpp b/Kernel/TimerQueue.cpp index a14058ebcf..1e9face90f 100644 --- a/Kernel/TimerQueue.cpp +++ b/Kernel/TimerQueue.cpp @@ -38,16 +38,45 @@ namespace Kernel { static AK::Singleton s_the; static SpinLock g_timerqueue_lock; +ALWAYS_INLINE static u64 time_to_ns(const timespec& ts) +{ + return (u64)ts.tv_sec * 1000000000ull + ts.tv_nsec; +} + +ALWAYS_INLINE static timespec ns_to_time(u64 ns) +{ + return { (time_t)(ns / 1000000000ull), (long)(ns % 1000000000ull) }; +} + timespec Timer::remaining() const { if (m_remaining == 0) return {}; - return TimerQueue::the().ticks_to_time(m_clock_id, m_remaining); + return ns_to_time(m_remaining); } -u64 Timer::now() const +u64 Timer::now(bool is_firing) const { - return TimerQueue::the().time_to_ticks(m_clock_id, TimeManagement::the().current_time(m_clock_id).value()); + // NOTE: If is_firing is true then TimePrecision::Precise isn't really useful here. + // We already have a quite precise time stamp because we just updated the time in the + // interrupt handler. In those cases, just use coarse timestamps. + auto clock_id = m_clock_id; + if (is_firing) { + switch (clock_id) { + case CLOCK_MONOTONIC: + clock_id = CLOCK_MONOTONIC_COARSE; + break; + case CLOCK_MONOTONIC_RAW: + // TODO: use a special CLOCK_MONOTONIC_RAW_COARSE like mechanism here + break; + case CLOCK_REALTIME: + clock_id = CLOCK_REALTIME_COARSE; + break; + default: + break; + } + } + return time_to_ns(TimeManagement::the().current_time(clock_id).value()); } TimerQueue& TimerQueue::the() @@ -70,7 +99,7 @@ RefPtr TimerQueue::add_timer_without_id(clockid_t clock_id, const timespe // *must* be a RefPtr. Otherwise calling cancel_timer() could // inadvertently cancel another timer that has been created between // returning from the timer handler and a call to cancel_timer(). - auto timer = adopt(*new Timer(clock_id, time_to_ticks(clock_id, deadline), move(callback))); + auto timer = adopt(*new Timer(clock_id, time_to_ns(deadline), move(callback))); ScopedSpinLock lock(g_timerqueue_lock); timer->m_id = 0; // Don't generate a timer id @@ -91,7 +120,6 @@ TimerId TimerQueue::add_timer(NonnullRefPtr&& timer) void TimerQueue::add_timer_locked(NonnullRefPtr timer) { u64 timer_expiration = timer->m_expires; - ASSERT(timer_expiration >= time_to_ticks(timer->m_clock_id, TimeManagement::the().current_time(timer->m_clock_id).value())); ASSERT(!timer->is_queued()); @@ -123,43 +151,7 @@ TimerId TimerQueue::add_timer(clockid_t clock_id, timeval& deadline, Function now) timer.m_remaining = timer.m_expires - now; @@ -270,7 +262,7 @@ void TimerQueue::fire() ASSERT(timer); ASSERT(queue.next_timer_due == timer->m_expires); - while (timer && timer->now() > timer->m_expires) { + while (timer && timer->now(true) > timer->m_expires) { queue.list.remove(timer); timer->set_queued(false); diff --git a/Kernel/TimerQueue.h b/Kernel/TimerQueue.h index f4eda76c41..a70911e972 100644 --- a/Kernel/TimerQueue.h +++ b/Kernel/TimerQueue.h @@ -80,7 +80,7 @@ private: } bool is_queued() const { return m_queued.load(AK::MemoryOrder::memory_order_relaxed); } void set_queued(bool queued) { m_queued.store(queued, AK::MemoryOrder::memory_order_relaxed); } - u64 now() const; + u64 now(bool) const; }; class TimerQueue { @@ -114,17 +114,17 @@ private: { switch (timer.m_clock_id) { case CLOCK_MONOTONIC: + case CLOCK_MONOTONIC_COARSE: + case CLOCK_MONOTONIC_RAW: return m_timer_queue_monotonic; case CLOCK_REALTIME: + case CLOCK_REALTIME_COARSE: return m_timer_queue_realtime; default: ASSERT_NOT_REACHED(); } } - timespec ticks_to_time(clockid_t, u64 ticks) const; - u64 time_to_ticks(clockid_t, const timespec&) const; - u64 m_timer_id_count { 0 }; u64 m_ticks_per_second { 0 }; Queue m_timer_queue_monotonic; diff --git a/Kernel/UnixTypes.h b/Kernel/UnixTypes.h index d942603044..02af314b50 100644 --- a/Kernel/UnixTypes.h +++ b/Kernel/UnixTypes.h @@ -548,6 +548,9 @@ typedef int clockid_t; #define CLOCK_REALTIME 0 #define CLOCK_MONOTONIC 1 +#define CLOCK_MONOTONIC_RAW 4 +#define CLOCK_REALTIME_COARSE 5 +#define CLOCK_MONOTONIC_COARSE 6 #define TIMER_ABSTIME 99 #define UTSNAME_ENTRY_LEN 65 -- cgit v1.2.3