diff --git a/Kernel/API/Syscall.h b/Kernel/API/Syscall.h index b44680b6dc..70ca9cfac2 100644 --- a/Kernel/API/Syscall.h +++ b/Kernel/API/Syscall.h @@ -118,6 +118,7 @@ enum class NeedsBigProcessLock { S(listen, NeedsBigProcessLock::Yes) \ S(lseek, NeedsBigProcessLock::Yes) \ S(madvise, NeedsBigProcessLock::Yes) \ + S(map_time_page, NeedsBigProcessLock::Yes) \ S(mkdir, NeedsBigProcessLock::Yes) \ S(mknod, NeedsBigProcessLock::Yes) \ S(mmap, NeedsBigProcessLock::Yes) \ diff --git a/Kernel/API/TimePage.h b/Kernel/API/TimePage.h new file mode 100644 index 0000000000..a72c262258 --- /dev/null +++ b/Kernel/API/TimePage.h @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2021, Andreas Kling + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include + +#ifdef KERNEL +# include +#else +# include +#endif + +namespace Kernel { + +inline bool time_page_supports(clockid_t clock_id) +{ + return clock_id == CLOCK_REALTIME; +} + +struct TimePage { + volatile u32 update1; + struct timespec clocks[CLOCK_ID_COUNT]; + volatile u32 update2; +}; + +} diff --git a/Kernel/Process.h b/Kernel/Process.h index 9ae0d27e0b..b0e56a3ad7 100644 --- a/Kernel/Process.h +++ b/Kernel/Process.h @@ -412,6 +412,7 @@ public: KResultOr sys$anon_create(size_t, int options); KResultOr sys$statvfs(Userspace user_params); KResultOr sys$fstatvfs(int fd, statvfs* buf); + KResultOr sys$map_time_page(); template int get_sock_or_peer_name(const Params&); diff --git a/Kernel/Syscalls/clock.cpp b/Kernel/Syscalls/clock.cpp index d0511e656d..89f0a6e68b 100644 --- a/Kernel/Syscalls/clock.cpp +++ b/Kernel/Syscalls/clock.cpp @@ -10,6 +10,24 @@ namespace Kernel { +KResultOr Process::sys$map_time_page() +{ + VERIFY_PROCESS_BIG_LOCK_ACQUIRED(this); + REQUIRE_PROMISE(stdio); + + auto& vmobject = TimeManagement::the().time_page_vmobject(); + + auto range = address_space().page_directory().range_allocator().allocate_randomized(PAGE_SIZE, PAGE_SIZE); + if (!range.has_value()) + return ENOMEM; + + auto region_or_error = address_space().allocate_region_with_vmobject(range.value(), vmobject, 0, "Kernel time page"sv, PROT_READ, true); + if (region_or_error.is_error()) + return region_or_error.error(); + + return region_or_error.value()->vaddr().get(); +} + KResultOr Process::sys$clock_gettime(clockid_t clock_id, Userspace user_ts) { VERIFY_NO_PROCESS_BIG_LOCK(this); diff --git a/Kernel/Time/TimeManagement.cpp b/Kernel/Time/TimeManagement.cpp index b7a56a6f8e..e6cdabe562 100644 --- a/Kernel/Time/TimeManagement.cpp +++ b/Kernel/Time/TimeManagement.cpp @@ -145,6 +145,9 @@ UNMAP_AFTER_INIT void TimeManagement::initialize(u32 cpu) dmesgln("Time: Using APIC timer as system timer"); s_the->set_system_timer(*apic_timer); } + + s_the->m_time_page_region = MM.allocate_kernel_region(PAGE_SIZE, "Time page"sv, Memory::Region::Access::ReadWrite, AllocationStrategy::AllocateNow); + VERIFY(s_the->m_time_page_region); } else { VERIFY(s_the.is_initialized()); if (auto* apic_timer = APIC::the().get_timer()) { @@ -359,6 +362,9 @@ void TimeManagement::increment_time_since_boot_hpet() 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); + + update_time_page(); + m_update2.store(update_iteration + 1, AK::MemoryOrder::memory_order_release); } @@ -389,6 +395,8 @@ void TimeManagement::increment_time_since_boot() ++m_seconds_since_boot; m_ticks_this_second = 0; } + + update_time_page(); m_update2.store(update_iteration + 1, AK::MemoryOrder::memory_order_release); } @@ -419,4 +427,22 @@ bool TimeManagement::disable_profile_timer() return true; } +void TimeManagement::update_time_page() +{ + auto* page = time_page(); + u32 update_iteration = AK::atomic_fetch_add(&page->update1, 1u, AK::MemoryOrder::memory_order_acquire); + page->clocks[CLOCK_REALTIME] = m_epoch_time; + AK::atomic_store(&page->update2, update_iteration + 1u, AK::MemoryOrder::memory_order_release); +} + +TimePage* TimeManagement::time_page() +{ + return static_cast((void*)m_time_page_region->vaddr().as_ptr()); +} + +Memory::VMObject& TimeManagement::time_page_vmobject() +{ + return m_time_page_region->vmobject(); +} + } diff --git a/Kernel/Time/TimeManagement.h b/Kernel/Time/TimeManagement.h index 9b8f2dfa13..a8519b4b20 100644 --- a/Kernel/Time/TimeManagement.h +++ b/Kernel/Time/TimeManagement.h @@ -7,9 +7,11 @@ #pragma once #include +#include #include #include #include +#include #include #include #include @@ -71,7 +73,12 @@ public: bool can_query_precise_time() const { return m_can_query_precise_time; } + Memory::VMObject& time_page_vmobject(); + private: + TimePage* time_page(); + void update_time_page(); + bool probe_and_set_legacy_hardware_timers(); bool probe_and_set_non_legacy_hardware_timers(); Vector scan_and_initialize_periodic_timers(); @@ -100,6 +107,8 @@ private: Atomic m_profile_enable_count { 0 }; RefPtr m_profile_timer; + + OwnPtr m_time_page_region; }; } diff --git a/Userland/Libraries/LibC/time.cpp b/Userland/Libraries/LibC/time.cpp index ec361e439f..45273d6661 100644 --- a/Userland/Libraries/LibC/time.cpp +++ b/Userland/Libraries/LibC/time.cpp @@ -7,6 +7,7 @@ #include #include #include +#include #include #include #include @@ -362,8 +363,39 @@ clock_t clock() return tms.tms_utime + tms.tms_stime; } +static Kernel::TimePage* get_kernel_time_page() +{ + static Kernel::TimePage* s_kernel_time_page; + // FIXME: Thread safety + if (!s_kernel_time_page) { + auto rc = syscall(SC_map_time_page); + if ((int)rc < 0 && (int)rc > -EMAXERRNO) { + errno = -(int)rc; + return nullptr; + } + s_kernel_time_page = (Kernel::TimePage*)rc; + } + return s_kernel_time_page; +} + int clock_gettime(clockid_t clock_id, struct timespec* ts) { + if (Kernel::time_page_supports(clock_id)) { + if (!ts) { + errno = EFAULT; + return -1; + } + + if (auto* kernel_time_page = get_kernel_time_page()) { + u32 update_iteration; + do { + update_iteration = AK::atomic_load(&kernel_time_page->update1, AK::memory_order_acquire); + *ts = kernel_time_page->clocks[clock_id]; + } while (update_iteration != AK::atomic_load(&kernel_time_page->update2, AK::memory_order_acquire)); + return 0; + } + } + int rc = syscall(SC_clock_gettime, clock_id, ts); __RETURN_WITH_ERRNO(rc, rc, -1); }