1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-05-31 13:38:11 +00:00

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.
This commit is contained in:
Tom 2020-12-03 22:12:50 -07:00 committed by Andreas Kling
parent a3fdf5148b
commit 5f51d85184
32 changed files with 318 additions and 190 deletions

View file

@ -355,12 +355,12 @@ void ProcessModel::update()
auto previous_pid_count = m_pids.size(); auto previous_pid_count = m_pids.size();
auto all_processes = Core::ProcessStatisticsReader::get_all(); auto all_processes = Core::ProcessStatisticsReader::get_all();
unsigned last_sum_times_scheduled = 0; u64 last_sum_ticks_scheduled = 0;
for (auto& it : m_threads) for (auto& it : m_threads)
last_sum_times_scheduled += it.value->current_state.times_scheduled; last_sum_ticks_scheduled += it.value->current_state.ticks_user + it.value->current_state.ticks_kernel;
HashTable<PidAndTid> live_pids; HashTable<PidAndTid> live_pids;
unsigned sum_times_scheduled = 0; u64 sum_ticks_scheduled = 0;
for (auto& it : all_processes) { for (auto& it : all_processes) {
for (auto& thread : it.value.threads) { for (auto& thread : it.value.threads) {
ThreadState state; ThreadState state;
@ -393,12 +393,14 @@ void ProcessModel::update()
state.pgid = it.value.pgid; state.pgid = it.value.pgid;
state.sid = it.value.sid; state.sid = it.value.sid;
state.times_scheduled = thread.times_scheduled; state.times_scheduled = thread.times_scheduled;
state.ticks_user = thread.ticks_user;
state.ticks_kernel = thread.ticks_kernel;
state.cpu = thread.cpu; state.cpu = thread.cpu;
state.cpu_percent = 0; state.cpu_percent = 0;
state.priority = thread.priority; state.priority = thread.priority;
state.effective_priority = thread.effective_priority; state.effective_priority = thread.effective_priority;
state.state = thread.state; state.state = thread.state;
sum_times_scheduled += thread.times_scheduled; sum_ticks_scheduled += thread.ticks_user + thread.ticks_kernel;
{ {
auto pit = m_threads.find({ it.value.pid, thread.tid }); auto pit = m_threads.find({ it.value.pid, thread.tid });
if (pit == m_threads.end()) if (pit == m_threads.end())
@ -423,8 +425,9 @@ void ProcessModel::update()
continue; continue;
} }
auto& process = *it.value; auto& process = *it.value;
u32 times_scheduled_diff = process.current_state.times_scheduled - process.previous_state.times_scheduled; u32 times_scheduled_diff = (process.current_state.ticks_user + process.current_state.ticks_kernel)
process.current_state.cpu_percent = ((float)times_scheduled_diff * 100) / (float)(sum_times_scheduled - last_sum_times_scheduled); - (process.previous_state.ticks_user + process.previous_state.ticks_kernel);
process.current_state.cpu_percent = ((float)times_scheduled_diff * 100) / (float)(sum_ticks_scheduled - last_sum_ticks_scheduled);
if (it.key.pid != 0) { if (it.key.pid != 0) {
m_cpus[process.current_state.cpu].total_cpu_percent += process.current_state.cpu_percent; m_cpus[process.current_state.cpu].total_cpu_percent += process.current_state.cpu_percent;
m_pids.append(it.key); m_pids.append(it.key);

View file

@ -116,6 +116,8 @@ private:
pid_t pgid; pid_t pgid;
pid_t sid; pid_t sid;
unsigned times_scheduled; unsigned times_scheduled;
unsigned ticks_user;
unsigned ticks_kernel;
String name; String name;
String state; String state;
String user; String user;

View file

@ -884,7 +884,8 @@ static OwnPtr<KBuffer> procfs$all(InodeIdentifier)
thread_object.add("tid", thread.tid().value()); thread_object.add("tid", thread.tid().value());
thread_object.add("name", thread.name()); thread_object.add("name", thread.name());
thread_object.add("times_scheduled", thread.times_scheduled()); 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("state", thread.state_string());
thread_object.add("cpu", thread.cpu()); thread_object.add("cpu", thread.cpu());
thread_object.add("priority", thread.priority()); thread_object.add("priority", thread.priority());

View file

@ -64,10 +64,10 @@ void Scheduler::init_thread(Thread& thread)
static u32 time_slice_for(const 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()) if (&thread == Processor::current().idle_thread())
return 1; return 1;
return 10; return 2;
} }
Thread* g_finalizer; 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 // but since we're still holding the scheduler lock we're still in a critical section
critical.leave(); critical.leave();
thread_to_schedule->set_ticks_left(time_slice_for(*thread_to_schedule));
return context_switch(thread_to_schedule); return context_switch(thread_to_schedule);
} }
@ -317,7 +318,6 @@ bool Scheduler::donate_to(RefPtr<Thread>& beneficiary, const char* reason)
bool Scheduler::context_switch(Thread* thread) bool Scheduler::context_switch(Thread* thread)
{ {
thread->set_ticks_left(time_slice_for(*thread));
thread->did_schedule(); thread->did_schedule();
auto from_thread = Thread::current(); 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; return;
ASSERT_INTERRUPTS_DISABLED(); ASSERT_INTERRUPTS_DISABLED();

View file

@ -46,9 +46,9 @@ unsigned Process::sys$alarm(unsigned seconds)
} }
if (seconds > 0) { 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); 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); [[maybe_unused]] auto rc = send_signal(SIGALRM, nullptr);
}); });
} }

View file

@ -78,25 +78,21 @@ int Process::sys$clock_nanosleep(Userspace<const Syscall::SC_clock_nanosleep_par
bool is_absolute = params.flags & TIMER_ABSTIME; bool is_absolute = params.flags & TIMER_ABSTIME;
switch (params.clock_id) { if (!TimeManagement::is_valid_clock_id(params.clock_id))
case CLOCK_MONOTONIC:
case CLOCK_REALTIME: {
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;
}
default:
return -EINVAL; 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<const timeval*> user_delta, Userspace<timeval*> user_old_delta) int Process::sys$adjtime(Userspace<const timeval*> user_delta, Userspace<timeval*> user_old_delta)

View file

@ -400,13 +400,15 @@ void Thread::finalize_dying_threads()
} }
} }
bool Thread::tick() bool Thread::tick(bool in_kernel)
{ {
++m_ticks; if (in_kernel) {
if (tss().cs & 3)
++m_process->m_ticks_in_user;
else
++m_process->m_ticks_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; return --m_ticks_left;
} }

View file

@ -211,7 +211,7 @@ public:
: m_infinite(true) : 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_clock_id(clock_id)
, m_infinite(!time) , m_infinite(!time)
{ {
@ -225,7 +225,7 @@ public:
timespec_add(m_time, m_start_time, m_time); 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_clock_id(clock_id)
, m_infinite(!time) , m_infinite(!time)
{ {
@ -249,7 +249,7 @@ public:
private: private:
timespec m_time { 0, 0 }; timespec m_time { 0, 0 };
timespec m_start_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_infinite { false };
bool m_should_block { false }; bool m_should_block { false };
}; };
@ -756,7 +756,6 @@ public:
const TSS32& tss() const { return m_tss; } const TSS32& tss() const { return m_tss; }
State state() const { return m_state; } State state() const { return m_state; }
const char* state_string() const; const char* state_string() const;
u32 ticks() const { return m_ticks; }
VirtualAddress thread_specific_data() const { return m_thread_specific_data; } VirtualAddress thread_specific_data() const { return m_thread_specific_data; }
size_t thread_specific_region_size() const { return m_thread_specific_region_size; } 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(clockid_t, const timespec&, timespec* = nullptr);
BlockResult sleep(const timespec& duration, timespec* remaining_time = 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(clockid_t, const timespec&);
BlockResult sleep_until(const timespec& duration) 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, // Tell this thread to unblock if needed,
@ -937,7 +936,7 @@ public:
void exit(void* = nullptr); void exit(void* = nullptr);
bool tick(); bool tick(bool in_kernel);
void set_ticks_left(u32 t) { m_ticks_left = t; } void set_ticks_left(u32 t) { m_ticks_left = t; }
u32 ticks_left() const { return m_ticks_left; } 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_kernel_stack_size = 65536;
static constexpr u32 default_userspace_stack_size = 4 * MiB; 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; } RecursiveSpinLock& get_lock() const { return m_lock; }
#ifdef LOCK_DEBUG #ifdef LOCK_DEBUG
@ -1188,9 +1190,10 @@ private:
TSS32 m_tss; TSS32 m_tss;
Atomic<u32> m_cpu { 0 }; Atomic<u32> m_cpu { 0 };
u32 m_cpu_affinity { THREAD_AFFINITY_DEFAULT }; u32 m_cpu_affinity { THREAD_AFFINITY_DEFAULT };
u32 m_ticks { 0 };
u32 m_ticks_left { 0 }; u32 m_ticks_left { 0 };
u32 m_times_scheduled { 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_pending_signals { 0 };
u32 m_signal_mask { 0 }; u32 m_signal_mask { 0 };
u32 m_kernel_stack_base { 0 }; u32 m_kernel_stack_base { 0 };

View file

@ -44,6 +44,7 @@ public:
virtual bool is_periodic_capable() const override { return true; } virtual bool is_periodic_capable() const override { return true; }
virtual void set_periodic() override; virtual void set_periodic() override;
virtual void set_non_periodic() override; virtual void set_non_periodic() override;
virtual void disable() override { }
virtual void reset_to_default_ticks_per_second() override; virtual void reset_to_default_ticks_per_second() override;
virtual bool try_to_set_frequency(size_t frequency) override; virtual bool try_to_set_frequency(size_t frequency) override;

View file

@ -197,10 +197,14 @@ void HPET::update_periodic_comparator_value()
auto& regs = registers(); auto& regs = registers();
u64 previous_main_value = (u64)regs.main_counter_value.low | ((u64)regs.main_counter_value.high << 32); 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.low = 0;
regs.main_counter_value.high = 0; regs.main_counter_value.high = 0;
for (auto& comparator : m_comparators) { for (auto& comparator : m_comparators) {
auto& timer = regs.timers[comparator.comparator_number()]; auto& timer = regs.timers[comparator.comparator_number()];
if (!comparator.is_enabled())
continue;
if (comparator.is_periodic()) { if (comparator.is_periodic()) {
// Note that this means we're restarting all periodic timers. There is no // 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 // 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; 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) void HPET::enable_periodic_interrupt(const HPETComparator& comparator)
{ {
#ifdef HPET_DEBUG #ifdef HPET_DEBUG
@ -253,7 +284,8 @@ void HPET::enable_periodic_interrupt(const HPETComparator& comparator)
auto capabilities = timer.capabilities; auto capabilities = timer.capabilities;
ASSERT(capabilities & (u32)HPETFlags::TimerConfiguration::PeriodicInterruptCapable); ASSERT(capabilities & (u32)HPETFlags::TimerConfiguration::PeriodicInterruptCapable);
timer.capabilities = capabilities | (u32)HPETFlags::TimerConfiguration::GeneratePeriodicInterrupt; timer.capabilities = capabilities | (u32)HPETFlags::TimerConfiguration::GeneratePeriodicInterrupt;
enable(comparator); if (comparator.is_enabled())
enable(comparator);
} }
void HPET::disable_periodic_interrupt(const HPETComparator& 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; auto capabilities = timer.capabilities;
ASSERT(capabilities & (u32)HPETFlags::TimerConfiguration::PeriodicInterruptCapable); ASSERT(capabilities & (u32)HPETFlags::TimerConfiguration::PeriodicInterruptCapable);
timer.capabilities = capabilities & ~(u32)HPETFlags::TimerConfiguration::GeneratePeriodicInterrupt; timer.capabilities = capabilities & ~(u32)HPETFlags::TimerConfiguration::GeneratePeriodicInterrupt;
enable(comparator); if (comparator.is_enabled())
enable(comparator);
} }
void HPET::disable(const HPETComparator& 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; timer.capabilities = timer.capabilities | (u32)HPETFlags::TimerConfiguration::InterruptEnable;
} }
u64 HPET::frequency() const
{
return m_frequency;
}
Vector<unsigned> HPET::capable_interrupt_numbers(const HPETComparator& comparator) Vector<unsigned> HPET::capable_interrupt_numbers(const HPETComparator& comparator)
{ {
ASSERT(comparator.comparator_number() <= m_comparators.size()); 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"; 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); 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.high = 0;
regs.main_counter_value.low = 0; regs.main_counter_value.low = 0;
if (regs.capabilities.attributes & (u32)HPETFlags::Attributes::LegacyReplacementRouteCapable) if (regs.capabilities.attributes & (u32)HPETFlags::Attributes::LegacyReplacementRouteCapable)

View file

@ -45,7 +45,7 @@ public:
static bool check_for_exisiting_periodic_timers(); static bool check_for_exisiting_periodic_timers();
static HPET& the(); static HPET& the();
u64 frequency() const; u64 frequency() const { return m_frequency; }
const NonnullRefPtrVector<HPETComparator>& comparators() const { return m_comparators; } const NonnullRefPtrVector<HPETComparator>& comparators() const { return m_comparators; }
void disable(const HPETComparator&); void disable(const HPETComparator&);
@ -59,6 +59,8 @@ public:
void enable_periodic_interrupt(const HPETComparator& comparator); void enable_periodic_interrupt(const HPETComparator& comparator);
void disable_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<unsigned> capable_interrupt_numbers(u8 comparator_number); Vector<unsigned> capable_interrupt_numbers(u8 comparator_number);
Vector<unsigned> capable_interrupt_numbers(const HPETComparator&); Vector<unsigned> capable_interrupt_numbers(const HPETComparator&);
@ -80,7 +82,9 @@ private:
PhysicalAddress m_physical_acpi_hpet_registers; PhysicalAddress m_physical_acpi_hpet_registers;
OwnPtr<Region> m_hpet_mmio_region; OwnPtr<Region> 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_vendor_id;
u16 m_minimum_tick; u16 m_minimum_tick;
u64 m_frequency; u64 m_frequency;

View file

@ -41,20 +41,31 @@ HPETComparator::HPETComparator(u8 number, u8 irq, bool periodic_capable)
: HardwareTimer(irq) : HardwareTimer(irq)
, m_periodic(false) , m_periodic(false)
, m_periodic_capable(periodic_capable) , m_periodic_capable(periodic_capable)
, m_enabled(false)
, m_comparator_number(number) , m_comparator_number(number)
{ {
} }
void HPETComparator::disable()
{
if (!m_enabled)
return;
m_enabled = false;
HPET::the().disable(*this);
}
void HPETComparator::set_periodic() void HPETComparator::set_periodic()
{ {
ASSERT(m_periodic_capable); ASSERT(m_periodic_capable);
m_periodic = true; m_periodic = true;
m_enabled = true;
HPET::the().enable_periodic_interrupt(*this); HPET::the().enable_periodic_interrupt(*this);
} }
void HPETComparator::set_non_periodic() void HPETComparator::set_non_periodic()
{ {
ASSERT(m_periodic_capable); ASSERT(m_periodic_capable);
m_periodic = false; m_periodic = false;
m_enabled = true;
HPET::the().disable_periodic_interrupt(*this); 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() 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; m_frequency = OPTIMAL_TICKS_PER_SECOND_RATE;
if (!is_periodic()) if (!is_periodic())
set_new_countdown(); set_new_countdown();
@ -89,13 +100,16 @@ void HPETComparator::reset_to_default_ticks_per_second()
bool HPETComparator::try_to_set_frequency(size_t frequency) bool HPETComparator::try_to_set_frequency(size_t frequency)
{ {
InterruptDisabler disabler; InterruptDisabler disabler;
if (!is_capable_of_frequency(frequency)) if (!is_capable_of_frequency(frequency)) {
dbg() << "HPETComparator: not cable of frequency: " << frequency;
return false; return false;
if (m_frequency == frequency) }
return true;
auto hpet_frequency = HPET::the().frequency(); auto hpet_frequency = HPET::the().frequency();
ASSERT(frequency <= hpet_frequency); ASSERT(frequency <= hpet_frequency);
m_frequency = frequency; m_frequency = frequency;
m_enabled = true;
#ifdef HPET_COMPARATOR_DEBUG #ifdef HPET_COMPARATOR_DEBUG
dbg() << "HPET Comparator: Max frequency " << hpet_frequency << " Hz, want to set " << frequency << " Hz, periodic: " << is_periodic(); dbg() << "HPET Comparator: Max frequency " << hpet_frequency << " Hz, want to set " << frequency << " Hz, periodic: " << is_periodic();
#endif #endif
@ -112,16 +126,17 @@ bool HPETComparator::is_capable_of_frequency(size_t frequency) const
{ {
if (frequency > HPET::the().frequency()) if (frequency > HPET::the().frequency())
return false; return false;
if ((HPET::the().frequency() % frequency) != 0) // HPET::update_periodic_comparator_value and HPET::update_non_periodic_comparator_value
return false; // calculate the best counter based on the desired frequency.
return true; return true;
} }
size_t HPETComparator::calculate_nearest_possible_frequency(size_t frequency) const 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(); return HPET::the().frequency();
// FIXME: Use better math here // HPET::update_periodic_comparator_value and HPET::update_non_periodic_comparator_value
return (frequency + (HPET::the().frequency() % frequency)); // calculate the best counter based on the desired frequency.
return frequency;
} }
} }

View file

@ -42,6 +42,7 @@ public:
virtual const char* model() const override { return "HPET"; } virtual const char* model() const override { return "HPET"; }
u8 comparator_number() const { return m_comparator_number; } u8 comparator_number() const { return m_comparator_number; }
bool is_enabled() const { return m_enabled; }
virtual size_t ticks_per_second() const override; 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 bool is_periodic_capable() const override { return m_periodic_capable; }
virtual void set_periodic() override; virtual void set_periodic() override;
virtual void set_non_periodic() override; virtual void set_non_periodic() override;
virtual void disable() override;
virtual void reset_to_default_ticks_per_second() override; virtual void reset_to_default_ticks_per_second() override;
virtual bool try_to_set_frequency(size_t frequency) override; virtual bool try_to_set_frequency(size_t frequency) override;
@ -61,7 +63,7 @@ private:
HPETComparator(u8 number, u8 irq, bool periodic_capable); HPETComparator(u8 number, u8 irq, bool periodic_capable);
bool m_periodic : 1; bool m_periodic : 1;
bool m_periodic_capable : 1; bool m_periodic_capable : 1;
bool m_edge_triggered : 1; bool m_enabled : 1;
u8 m_comparator_number { 0 }; u8 m_comparator_number { 0 };
}; };
} }

View file

@ -57,6 +57,7 @@ public:
virtual bool is_periodic_capable() const = 0; virtual bool is_periodic_capable() const = 0;
virtual void set_periodic() = 0; virtual void set_periodic() = 0;
virtual void set_non_periodic() = 0; virtual void set_non_periodic() = 0;
virtual void disable() = 0;
virtual size_t ticks_per_second() const = 0; virtual size_t ticks_per_second() const = 0;

View file

@ -63,6 +63,7 @@ public:
virtual bool is_periodic_capable() const override { return true; } virtual bool is_periodic_capable() const override { return true; }
virtual void set_periodic() override; virtual void set_periodic() override;
virtual void set_non_periodic() override; virtual void set_non_periodic() override;
virtual void disable() override { }
virtual void reset_to_default_ticks_per_second() override; virtual void reset_to_default_ticks_per_second() override;
virtual bool try_to_set_frequency(size_t frequency) override; virtual bool try_to_set_frequency(size_t frequency) override;

View file

@ -42,6 +42,7 @@ public:
virtual bool is_periodic_capable() const override { return true; } virtual bool is_periodic_capable() const override { return true; }
virtual void set_periodic() override { } virtual void set_periodic() override { }
virtual void set_non_periodic() override { } virtual void set_non_periodic() override { }
virtual void disable() override { }
virtual void reset_to_default_ticks_per_second() override; virtual void reset_to_default_ticks_per_second() override;
virtual bool try_to_set_frequency(size_t frequency) override; virtual bool try_to_set_frequency(size_t frequency) override;

View file

@ -52,13 +52,33 @@ TimeManagement& TimeManagement::the()
return *s_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<timespec> TimeManagement::current_time(clockid_t clock_id) const KResultOr<timespec> TimeManagement::current_time(clockid_t clock_id) const
{ {
switch (clock_id) { switch (clock_id) {
case CLOCK_MONOTONIC: 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: case CLOCK_REALTIME:
return epoch_time(); return epoch_time(TimePrecision::Precise);
case CLOCK_REALTIME_COARSE:
return epoch_time(TimePrecision::Coarse);
default: default:
return KResult(EINVAL); return KResult(EINVAL);
} }
@ -69,22 +89,6 @@ bool TimeManagement::is_system_timer(const HardwareTimerBase& timer) const
return &timer == m_system_timer.ptr(); 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) void TimeManagement::set_epoch_time(timespec ts)
{ {
InterruptDisabler disabler; InterruptDisabler disabler;
@ -92,27 +96,40 @@ void TimeManagement::set_epoch_time(timespec ts)
m_remaining_epoch_time_adjustment = { 0, 0 }; m_remaining_epoch_time_adjustment = { 0, 0 };
} }
u64 TimeManagement::monotonic_ticks() const timespec TimeManagement::monotonic_time(TimePrecision precision) const
{ {
long seconds; // This is the time when last updated by an interrupt.
u64 ticks; u64 seconds;
u32 ticks;
bool do_query = precision == TimePrecision::Precise && m_can_query_precise_time;
u32 update_iteration; u32 update_iteration;
do { do {
update_iteration = m_update1.load(AK::MemoryOrder::memory_order_acquire); update_iteration = m_update1.load(AK::MemoryOrder::memory_order_acquire);
seconds = m_seconds_since_boot; seconds = m_seconds_since_boot;
ticks = m_ticks_this_second; 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)); } while (update_iteration != m_update2.load(AK::MemoryOrder::memory_order_acquire));
return ticks + (u64)seconds * (u64)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::monotonic_time() const timespec TimeManagement::epoch_time(TimePrecision) const
{
return ticks_to_time(monotonic_ticks(), ticks_per_second());
}
timespec TimeManagement::epoch_time() const
{ {
// TODO: Take into account precision
timespec ts; timespec ts;
u32 update_iteration; u32 update_iteration;
do { do {
@ -155,7 +172,9 @@ void TimeManagement::initialize(u32 cpu)
void TimeManagement::set_system_timer(HardwareTimerBase& timer) 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); auto original_callback = m_system_timer->set_callback(nullptr);
m_system_timer->disable();
timer.set_callback(move(original_callback)); timer.set_callback(move(original_callback));
m_system_timer = timer; m_system_timer = timer;
} }
@ -259,28 +278,36 @@ bool TimeManagement::probe_and_set_non_legacy_hardware_timers()
if (is_hpet_periodic_mode_allowed()) if (is_hpet_periodic_mode_allowed())
ASSERT(!periodic_timers.is_empty()); 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) { if (periodic_timers.size() > 0)
m_time_keeper_timer = periodic_timers[1];
m_system_timer = periodic_timers[0]; m_system_timer = periodic_timers[0];
} else { else
if (periodic_timers.size() == 1) { m_system_timer = non_periodic_timers[0];
m_time_keeper_timer = periodic_timers[0];
m_system_timer = non_periodic_timers[0]; m_system_timer->set_callback([this](const RegisterState& regs) {
} else { // Update the time. We don't really care too much about the
m_time_keeper_timer = non_periodic_timers[1]; // frequency of the interrupt because we'll query the main
m_system_timer = non_periodic_timers[0]; // 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); system_timer_tick(regs);
m_time_keeper_timer->set_callback(TimeManagement::update_time); });
dbg() << "Reset timers"; // Use the HPET main counter frequency for time purposes. This is likely
m_system_timer->try_to_set_frequency(m_system_timer->calculate_nearest_possible_frequency(1024)); // a much higher frequency than the interrupt itself and allows us to
m_time_keeper_timer->try_to_set_frequency(OPTIMAL_TICKS_PER_SECOND_RATE); // 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; 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(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_time_keeper_timer = m_hardware_timers[0];
m_system_timer = m_hardware_timers[1]; 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; 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(const RegisterState&) 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()
{ {
ASSERT(!m_time_keeper_timer.is_null()); 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; constexpr time_t MaxSlewNanos = NanosPerTick / 100;
static_assert(MaxSlewNanos < NanosPerTick); 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. // 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); 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); 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) { if (Processor::current().in_irq() <= 1) {
// Don't expire timers while handling IRQs // Don't expire timers while handling IRQs

View file

@ -34,10 +34,15 @@
namespace Kernel { namespace Kernel {
#define OPTIMAL_TICKS_PER_SECOND_RATE 1000 #define OPTIMAL_TICKS_PER_SECOND_RATE 250
class HardwareTimerBase; class HardwareTimerBase;
enum class TimePrecision {
Coarse = 0,
Precise
};
class TimeManagement { class TimeManagement {
AK_MAKE_ETERNAL; AK_MAKE_ETERNAL;
@ -47,12 +52,15 @@ public:
static void initialize(u32 cpu); static void initialize(u32 cpu);
static TimeManagement& the(); static TimeManagement& the();
static timespec ticks_to_time(u64 ticks, time_t ticks_per_second); static bool is_valid_clock_id(clockid_t);
static u64 time_to_ticks(const timespec& tspec, time_t ticks_per_second); KResultOr<timespec> current_time(clockid_t) const;
timespec monotonic_time(TimePrecision = TimePrecision::Coarse) const;
KResultOr<timespec> current_time(clockid_t clock_id) const; timespec monotonic_time_raw() const
timespec monotonic_time() const; {
timespec epoch_time() const; // TODO: implement
return monotonic_time(TimePrecision::Precise);
}
timespec epoch_time(TimePrecision = TimePrecision::Precise) const;
void set_epoch_time(timespec); void set_epoch_time(timespec);
time_t ticks_per_second() const; time_t ticks_per_second() const;
time_t boot_time() const; time_t boot_time() const;
@ -60,12 +68,13 @@ public:
bool is_system_timer(const HardwareTimerBase&) const; bool is_system_timer(const HardwareTimerBase&) const;
static void update_time(const RegisterState&); 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(); static bool is_hpet_periodic_mode_allowed();
u64 uptime_ms() const; u64 uptime_ms() const;
u64 monotonic_ticks() const;
static timeval now_as_timeval(); static timeval now_as_timeval();
timespec remaining_epoch_time_adjustment() const { return m_remaining_epoch_time_adjustment; } timespec remaining_epoch_time_adjustment() const { return m_remaining_epoch_time_adjustment; }
@ -78,16 +87,19 @@ private:
Vector<HardwareTimerBase*> scan_for_non_periodic_timers(); Vector<HardwareTimerBase*> scan_for_non_periodic_timers();
NonnullRefPtrVector<HardwareTimerBase> m_hardware_timers; NonnullRefPtrVector<HardwareTimerBase> m_hardware_timers;
void set_system_timer(HardwareTimerBase&); 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 // Variables between m_update1 and m_update2 are synchronized
Atomic<u32> m_update1 { 0 }; Atomic<u32> m_update1 { 0 };
u32 m_ticks_this_second { 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_epoch_time { 0, 0 };
timespec m_remaining_epoch_time_adjustment { 0, 0 }; timespec m_remaining_epoch_time_adjustment { 0, 0 };
Atomic<u32> m_update2 { 0 }; Atomic<u32> 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<HardwareTimerBase> m_system_timer; RefPtr<HardwareTimerBase> m_system_timer;
RefPtr<HardwareTimerBase> m_time_keeper_timer; RefPtr<HardwareTimerBase> m_time_keeper_timer;
}; };

View file

@ -38,16 +38,45 @@ namespace Kernel {
static AK::Singleton<TimerQueue> s_the; static AK::Singleton<TimerQueue> s_the;
static SpinLock<u8> g_timerqueue_lock; static SpinLock<u8> 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 timespec Timer::remaining() const
{ {
if (m_remaining == 0) if (m_remaining == 0)
return {}; 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() TimerQueue& TimerQueue::the()
@ -70,7 +99,7 @@ RefPtr<Timer> TimerQueue::add_timer_without_id(clockid_t clock_id, const timespe
// *must* be a RefPtr<Timer>. Otherwise calling cancel_timer() could // *must* be a RefPtr<Timer>. Otherwise calling cancel_timer() could
// inadvertently cancel another timer that has been created between // inadvertently cancel another timer that has been created between
// returning from the timer handler and a call to cancel_timer(). // 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); ScopedSpinLock lock(g_timerqueue_lock);
timer->m_id = 0; // Don't generate a timer id timer->m_id = 0; // Don't generate a timer id
@ -91,7 +120,6 @@ TimerId TimerQueue::add_timer(NonnullRefPtr<Timer>&& timer)
void TimerQueue::add_timer_locked(NonnullRefPtr<Timer> timer) void TimerQueue::add_timer_locked(NonnullRefPtr<Timer> timer)
{ {
u64 timer_expiration = timer->m_expires; 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()); ASSERT(!timer->is_queued());
@ -123,43 +151,7 @@ TimerId TimerQueue::add_timer(clockid_t clock_id, timeval& deadline, Function<vo
{ {
auto expires = TimeManagement::the().current_time(clock_id).value(); auto expires = TimeManagement::the().current_time(clock_id).value();
timespec_add_timeval(expires, deadline, expires); timespec_add_timeval(expires, deadline, expires);
return add_timer(adopt(*new Timer(clock_id, time_to_ticks(clock_id, expires), move(callback)))); return add_timer(adopt(*new Timer(clock_id, time_to_ns(expires), move(callback))));
}
timespec TimerQueue::ticks_to_time(clockid_t clock_id, u64 ticks) const
{
timespec tspec;
switch (clock_id) {
case CLOCK_MONOTONIC:
tspec.tv_sec = ticks / m_ticks_per_second;
tspec.tv_nsec = (ticks % m_ticks_per_second) * (1'000'000'000 / m_ticks_per_second);
break;
case CLOCK_REALTIME:
tspec.tv_sec = ticks / 1'000'000'000;
tspec.tv_nsec = ticks % 1'000'000'000;
break;
default:
ASSERT_NOT_REACHED();
}
ASSERT(tspec.tv_nsec <= 1'000'000'000);
return tspec;
}
u64 TimerQueue::time_to_ticks(clockid_t clock_id, const timespec& tspec) const
{
u64 ticks;
switch (clock_id) {
case CLOCK_MONOTONIC:
ticks = (u64)tspec.tv_sec * m_ticks_per_second;
ticks += ((u64)tspec.tv_nsec * m_ticks_per_second) / 1'000'000'000;
break;
case CLOCK_REALTIME:
ticks = (u64)tspec.tv_sec * 1'000'000'000 + tspec.tv_nsec;
break;
default:
ASSERT_NOT_REACHED();
}
return ticks;
} }
bool TimerQueue::cancel_timer(TimerId id) bool TimerQueue::cancel_timer(TimerId id)
@ -249,7 +241,7 @@ void TimerQueue::remove_timer_locked(Queue& queue, Timer& timer)
bool was_next_timer = (queue.list.head() == &timer); bool was_next_timer = (queue.list.head() == &timer);
queue.list.remove(&timer); queue.list.remove(&timer);
timer.set_queued(false); timer.set_queued(false);
auto now = timer.now(); auto now = timer.now(false);
if (timer.m_expires > now) if (timer.m_expires > now)
timer.m_remaining = timer.m_expires - now; timer.m_remaining = timer.m_expires - now;
@ -270,7 +262,7 @@ void TimerQueue::fire()
ASSERT(timer); ASSERT(timer);
ASSERT(queue.next_timer_due == timer->m_expires); 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); queue.list.remove(timer);
timer->set_queued(false); timer->set_queued(false);

View file

@ -80,7 +80,7 @@ private:
} }
bool is_queued() const { return m_queued.load(AK::MemoryOrder::memory_order_relaxed); } 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); } void set_queued(bool queued) { m_queued.store(queued, AK::MemoryOrder::memory_order_relaxed); }
u64 now() const; u64 now(bool) const;
}; };
class TimerQueue { class TimerQueue {
@ -114,17 +114,17 @@ private:
{ {
switch (timer.m_clock_id) { switch (timer.m_clock_id) {
case CLOCK_MONOTONIC: case CLOCK_MONOTONIC:
case CLOCK_MONOTONIC_COARSE:
case CLOCK_MONOTONIC_RAW:
return m_timer_queue_monotonic; return m_timer_queue_monotonic;
case CLOCK_REALTIME: case CLOCK_REALTIME:
case CLOCK_REALTIME_COARSE:
return m_timer_queue_realtime; return m_timer_queue_realtime;
default: default:
ASSERT_NOT_REACHED(); 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_timer_id_count { 0 };
u64 m_ticks_per_second { 0 }; u64 m_ticks_per_second { 0 };
Queue m_timer_queue_monotonic; Queue m_timer_queue_monotonic;

View file

@ -548,6 +548,9 @@ typedef int clockid_t;
#define CLOCK_REALTIME 0 #define CLOCK_REALTIME 0
#define CLOCK_MONOTONIC 1 #define CLOCK_MONOTONIC 1
#define CLOCK_MONOTONIC_RAW 4
#define CLOCK_REALTIME_COARSE 5
#define CLOCK_MONOTONIC_COARSE 6
#define TIMER_ABSTIME 99 #define TIMER_ABSTIME 99
#define UTSNAME_ENTRY_LEN 65 #define UTSNAME_ENTRY_LEN 65

View file

@ -72,6 +72,9 @@ typedef int clockid_t;
#define CLOCK_REALTIME 0 #define CLOCK_REALTIME 0
#define CLOCK_MONOTONIC 1 #define CLOCK_MONOTONIC 1
#define CLOCK_MONOTONIC_RAW 4
#define CLOCK_REALTIME_COARSE 5
#define CLOCK_MONOTONIC_COARSE 6
#define TIMER_ABSTIME 99 #define TIMER_ABSTIME 99
int clock_gettime(clockid_t, struct timespec*); int clock_gettime(clockid_t, struct timespec*);

View file

@ -339,7 +339,7 @@ char* getwd(char* buf)
int sleep(unsigned seconds) int sleep(unsigned seconds)
{ {
struct timespec ts = { seconds, 0 }; struct timespec ts = { seconds, 0 };
if (clock_nanosleep(CLOCK_MONOTONIC, 0, &ts, nullptr) < 0) if (clock_nanosleep(CLOCK_MONOTONIC_COARSE, 0, &ts, nullptr) < 0)
return ts.tv_sec; return ts.tv_sec;
return 0; return 0;
} }
@ -347,7 +347,7 @@ int sleep(unsigned seconds)
int usleep(useconds_t usec) int usleep(useconds_t usec)
{ {
struct timespec ts = { (long)(usec / 1000000), (long)(usec % 1000000) * 1000 }; struct timespec ts = { (long)(usec / 1000000), (long)(usec % 1000000) * 1000 };
return clock_nanosleep(CLOCK_MONOTONIC, 0, &ts, nullptr); return clock_nanosleep(CLOCK_MONOTONIC_COARSE, 0, &ts, nullptr);
} }
int gethostname(char* buffer, size_t size) int gethostname(char* buffer, size_t size)

View file

@ -36,7 +36,7 @@ void ElapsedTimer::start()
{ {
m_valid = true; m_valid = true;
timespec now_spec; timespec now_spec;
clock_gettime(CLOCK_MONOTONIC, &now_spec); clock_gettime(m_precise ? CLOCK_MONOTONIC : CLOCK_MONOTONIC_COARSE, &now_spec);
m_origin_time.tv_sec = now_spec.tv_sec; m_origin_time.tv_sec = now_spec.tv_sec;
m_origin_time.tv_usec = now_spec.tv_nsec / 1000; m_origin_time.tv_usec = now_spec.tv_nsec / 1000;
} }
@ -46,7 +46,7 @@ int ElapsedTimer::elapsed() const
ASSERT(is_valid()); ASSERT(is_valid());
struct timeval now; struct timeval now;
timespec now_spec; timespec now_spec;
clock_gettime(CLOCK_MONOTONIC, &now_spec); clock_gettime(m_precise ? CLOCK_MONOTONIC : CLOCK_MONOTONIC_COARSE, &now_spec);
now.tv_sec = now_spec.tv_sec; now.tv_sec = now_spec.tv_sec;
now.tv_usec = now_spec.tv_nsec / 1000; now.tv_usec = now_spec.tv_nsec / 1000;
struct timeval diff; struct timeval diff;

View file

@ -32,7 +32,10 @@ namespace Core {
class ElapsedTimer { class ElapsedTimer {
public: public:
ElapsedTimer() { } ElapsedTimer(bool precise = false)
: m_precise(precise)
{
}
bool is_valid() const { return m_valid; } bool is_valid() const { return m_valid; }
void start(); void start();
@ -41,6 +44,7 @@ public:
const struct timeval& origin_time() const { return m_origin_time; } const struct timeval& origin_time() const { return m_origin_time; }
private: private:
bool m_precise { false };
bool m_valid { false }; bool m_valid { false };
struct timeval m_origin_time { struct timeval m_origin_time {
0, 0 0, 0

View file

@ -580,7 +580,7 @@ retry:
auto next_timer_expiration = get_next_timer_expiration(); auto next_timer_expiration = get_next_timer_expiration();
if (next_timer_expiration.has_value()) { if (next_timer_expiration.has_value()) {
timespec now_spec; timespec now_spec;
clock_gettime(CLOCK_MONOTONIC, &now_spec); clock_gettime(CLOCK_MONOTONIC_COARSE, &now_spec);
now.tv_sec = now_spec.tv_sec; now.tv_sec = now_spec.tv_sec;
now.tv_usec = now_spec.tv_nsec / 1000; now.tv_usec = now_spec.tv_nsec / 1000;
timeval_sub(next_timer_expiration.value(), now, timeout); timeval_sub(next_timer_expiration.value(), now, timeout);
@ -631,7 +631,7 @@ try_select_again:
if (!s_timers->is_empty()) { if (!s_timers->is_empty()) {
timespec now_spec; timespec now_spec;
clock_gettime(CLOCK_MONOTONIC, &now_spec); clock_gettime(CLOCK_MONOTONIC_COARSE, &now_spec);
now.tv_sec = now_spec.tv_sec; now.tv_sec = now_spec.tv_sec;
now.tv_usec = now_spec.tv_nsec / 1000; now.tv_usec = now_spec.tv_nsec / 1000;
} }
@ -709,7 +709,7 @@ int EventLoop::register_timer(Object& object, int milliseconds, bool should_relo
timer->interval = milliseconds; timer->interval = milliseconds;
timeval now; timeval now;
timespec now_spec; timespec now_spec;
clock_gettime(CLOCK_MONOTONIC, &now_spec); clock_gettime(CLOCK_MONOTONIC_COARSE, &now_spec);
now.tv_sec = now_spec.tv_sec; now.tv_sec = now_spec.tv_sec;
now.tv_usec = now_spec.tv_nsec / 1000; now.tv_usec = now_spec.tv_nsec / 1000;
timer->reload(now); timer->reload(now);

View file

@ -85,7 +85,8 @@ HashMap<pid_t, Core::ProcessStatistics> ProcessStatisticsReader::get_all()
thread.times_scheduled = thread_object.get("times_scheduled").to_u32(); thread.times_scheduled = thread_object.get("times_scheduled").to_u32();
thread.name = thread_object.get("name").to_string(); thread.name = thread_object.get("name").to_string();
thread.state = thread_object.get("state").to_string(); thread.state = thread_object.get("state").to_string();
thread.ticks = thread_object.get("ticks").to_u32(); thread.ticks_user = thread_object.get("ticks_user").to_u32();
thread.ticks_kernel = thread_object.get("ticks_kernel").to_u32();
thread.cpu = thread_object.get("cpu").to_u32(); thread.cpu = thread_object.get("cpu").to_u32();
thread.priority = thread_object.get("priority").to_u32(); thread.priority = thread_object.get("priority").to_u32();
thread.effective_priority = thread_object.get("effective_priority").to_u32(); thread.effective_priority = thread_object.get("effective_priority").to_u32();

View file

@ -35,7 +35,8 @@ namespace Core {
struct ThreadStatistics { struct ThreadStatistics {
pid_t tid; pid_t tid;
unsigned times_scheduled; unsigned times_scheduled;
unsigned ticks; unsigned ticks_user;
unsigned ticks_kernel;
unsigned syscall_count; unsigned syscall_count;
unsigned inode_faults; unsigned inode_faults;
unsigned zero_faults; unsigned zero_faults;

View file

@ -474,7 +474,7 @@ int pthread_cond_init(pthread_cond_t* cond, const pthread_condattr_t* attr)
{ {
cond->value = 0; cond->value = 0;
cond->previous = 0; cond->previous = 0;
cond->clockid = attr ? attr->clockid : CLOCK_MONOTONIC; cond->clockid = attr ? attr->clockid : CLOCK_MONOTONIC_COARSE;
return 0; return 0;
} }
@ -502,7 +502,7 @@ int pthread_cond_wait(pthread_cond_t* cond, pthread_mutex_t* mutex)
int pthread_condattr_init(pthread_condattr_t* attr) int pthread_condattr_init(pthread_condattr_t* attr)
{ {
attr->clockid = CLOCK_MONOTONIC; attr->clockid = CLOCK_MONOTONIC_COARSE;
return 0; return 0;
} }

View file

@ -82,9 +82,9 @@ int pthread_setschedparam(pthread_t thread, int policy, const struct sched_param
{ \ { \
0, 0, 0, PTHREAD_MUTEX_DEFAULT \ 0, 0, 0, PTHREAD_MUTEX_DEFAULT \
} }
#define PTHREAD_COND_INITIALIZER \ #define PTHREAD_COND_INITIALIZER \
{ \ { \
0, 0, CLOCK_MONOTONIC \ 0, 0, CLOCK_MONOTONIC_COARSE \
} }
int pthread_key_create(pthread_key_t* key, void (*destructor)(void*)); int pthread_key_create(pthread_key_t* key, void (*destructor)(void*));

View file

@ -135,9 +135,9 @@ private:
for (auto& it : all_processes) { for (auto& it : all_processes) {
for (auto& jt : it.value.threads) { for (auto& jt : it.value.threads) {
if (it.value.pid == 0) if (it.value.pid == 0)
idle += jt.times_scheduled; idle += jt.ticks_user + jt.ticks_kernel;
else else
busy += jt.times_scheduled; busy += jt.ticks_user + jt.ticks_kernel;
} }
} }
} }

View file

@ -56,7 +56,7 @@ static struct timeval get_current_time()
{ {
struct timespec ts; struct timespec ts;
struct timeval tv; struct timeval tv;
clock_gettime(CLOCK_MONOTONIC, &ts); clock_gettime(CLOCK_MONOTONIC_COARSE, &ts);
timespec_to_timeval(ts, tv); timespec_to_timeval(ts, tv);
return tv; return tv;
} }