Skip to content

Commit

Permalink
Kernel: Improve time keeping and dramatically reduce interrupt load
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
tomuta authored and awesomekling committed Dec 21, 2020
1 parent a3fdf51 commit 5f51d85
Show file tree
Hide file tree
Showing 32 changed files with 317 additions and 189 deletions.
15 changes: 9 additions & 6 deletions Applications/SystemMonitor/ProcessModel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -355,12 +355,12 @@ void ProcessModel::update()
auto previous_pid_count = m_pids.size();
auto all_processes = Core::ProcessStatisticsReader::get_all();

unsigned last_sum_times_scheduled = 0;
u64 last_sum_ticks_scheduled = 0;
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;
unsigned sum_times_scheduled = 0;
u64 sum_ticks_scheduled = 0;
for (auto& it : all_processes) {
for (auto& thread : it.value.threads) {
ThreadState state;
Expand Down Expand Up @@ -393,12 +393,14 @@ void ProcessModel::update()
state.pgid = it.value.pgid;
state.sid = it.value.sid;
state.times_scheduled = thread.times_scheduled;
state.ticks_user = thread.ticks_user;
state.ticks_kernel = thread.ticks_kernel;
state.cpu = thread.cpu;
state.cpu_percent = 0;
state.priority = thread.priority;
state.effective_priority = thread.effective_priority;
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 });
if (pit == m_threads.end())
Expand All @@ -423,8 +425,9 @@ void ProcessModel::update()
continue;
}
auto& process = *it.value;
u32 times_scheduled_diff = process.current_state.times_scheduled - process.previous_state.times_scheduled;
process.current_state.cpu_percent = ((float)times_scheduled_diff * 100) / (float)(sum_times_scheduled - last_sum_times_scheduled);
u32 times_scheduled_diff = (process.current_state.ticks_user + process.current_state.ticks_kernel)
- (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) {
m_cpus[process.current_state.cpu].total_cpu_percent += process.current_state.cpu_percent;
m_pids.append(it.key);
Expand Down
2 changes: 2 additions & 0 deletions Applications/SystemMonitor/ProcessModel.h
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,8 @@ class ProcessModel final : public GUI::Model {
pid_t pgid;
pid_t sid;
unsigned times_scheduled;
unsigned ticks_user;
unsigned ticks_kernel;
String name;
String state;
String user;
Expand Down
3 changes: 2 additions & 1 deletion Kernel/FileSystem/ProcFS.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -884,7 +884,8 @@ static OwnPtr<KBuffer> 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());
Expand Down
8 changes: 4 additions & 4 deletions Kernel/Scheduler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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);
}

Expand Down Expand Up @@ -317,7 +318,6 @@ bool Scheduler::donate_to(RefPtr<Thread>& 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();
Expand Down Expand Up @@ -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();
Expand Down
4 changes: 2 additions & 2 deletions Kernel/Syscalls/alarm.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
});
}
Expand Down
30 changes: 13 additions & 17 deletions Kernel/Syscalls/clock.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -78,25 +78,21 @@ int Process::sys$clock_nanosleep(Userspace<const Syscall::SC_clock_nanosleep_par

bool is_absolute = params.flags & TIMER_ABSTIME;

switch (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:
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<const timeval*> user_delta, Userspace<timeval*> user_old_delta)
Expand Down
12 changes: 7 additions & 5 deletions Kernel/Thread.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}

Expand Down
19 changes: 11 additions & 8 deletions Kernel/Thread.h
Original file line number Diff line number Diff line change
Expand Up @@ -211,7 +211,7 @@ class Thread
: 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)
{
Expand All @@ -225,7 +225,7 @@ class Thread
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)
{
Expand All @@ -249,7 +249,7 @@ class Thread
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 };
};
Expand Down Expand Up @@ -756,7 +756,6 @@ class Thread
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; }
Expand Down Expand Up @@ -921,12 +920,12 @@ class Thread
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,
Expand All @@ -937,7 +936,7 @@ class Thread

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; }

Expand Down Expand Up @@ -1070,6 +1069,9 @@ class Thread
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
Expand Down Expand Up @@ -1188,9 +1190,10 @@ class Thread
TSS32 m_tss;
Atomic<u32> 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 };
Expand Down
1 change: 1 addition & 0 deletions Kernel/Time/APICTimer.h
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ class APICTimer final : public HardwareTimer<GenericInterruptHandler> {
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;
Expand Down
44 changes: 36 additions & 8 deletions Kernel/Time/HPET.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand All @@ -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)
{
Expand All @@ -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)
Expand All @@ -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<unsigned> HPET::capable_interrupt_numbers(const HPETComparator& comparator)
{
ASSERT(comparator.comparator_number() <= m_comparators.size());
Expand Down Expand Up @@ -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)
Expand Down
8 changes: 6 additions & 2 deletions Kernel/Time/HPET.h
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ class HPET {
static bool check_for_exisiting_periodic_timers();
static HPET& the();

u64 frequency() const;
u64 frequency() const { return m_frequency; }

const NonnullRefPtrVector<HPETComparator>& comparators() const { return m_comparators; }
void disable(const HPETComparator&);
Expand All @@ -59,6 +59,8 @@ class HPET {
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<unsigned> capable_interrupt_numbers(u8 comparator_number);
Vector<unsigned> capable_interrupt_numbers(const HPETComparator&);

Expand All @@ -80,7 +82,9 @@ class HPET {
PhysicalAddress m_physical_acpi_hpet_registers;
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_minimum_tick;
u64 m_frequency;
Expand Down
Loading

0 comments on commit 5f51d85

Please sign in to comment.