From 32c5ef32e9501669aab43259fcf0ec3bec098194 Mon Sep 17 00:00:00 2001 From: Alexander Boettcher Date: Tue, 18 Jun 2024 13:50:32 +0200 Subject: [PATCH] foc: use new timer scheme Issue #5138 --- repos/base-foc/src/timer/foc/component.cc | 416 +++++++++++++++++++++ repos/base-foc/src/timer/foc/target.mk | 11 +- repos/base/src/timer/fiasco/time_source.cc | 101 ----- 3 files changed, 420 insertions(+), 108 deletions(-) create mode 100644 repos/base-foc/src/timer/foc/component.cc delete mode 100644 repos/base/src/timer/fiasco/time_source.cc diff --git a/repos/base-foc/src/timer/foc/component.cc b/repos/base-foc/src/timer/foc/component.cc new file mode 100644 index 0000000000..7058c5fde2 --- /dev/null +++ b/repos/base-foc/src/timer/foc/component.cc @@ -0,0 +1,416 @@ +/* + * \brief Timer driver for Fiasco.OC + * \author Norman Feske + * \author Alexander Boettcher + * \date 2024-06-16 + */ + +/* + * Copyright (C) 2024 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU Affero General Public License version 3. + */ + +/* Genode includes */ +#include +#include +#include +#include +#include +#include + +/* base-internal includes */ +#include + +/* Fiasco.OC includes */ +#include + +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wconversion" + +namespace Foc { + #include + #include +} + +#pragma GCC diagnostic pop + + +using namespace Foc; + + +namespace Timer { + + using namespace Genode; + + struct Tsc { uint64_t tsc; }; + struct Clock; + struct Device; + struct Alarm; + struct Root; + struct Session_component; + struct Main; + + using Alarms = Alarm_registry; +} + + +struct Timer::Clock +{ + uint64_t us; + + static constexpr uint64_t MASK = uint64_t(-1); + + uint64_t value() const { return us; } + + void print(Output &out) const { Genode::print(out, us); } +}; + + +class Timer::Device +{ + private: + + Attached_rom_dataspace _kip_ds; + + public: + + struct Wakeup_dispatcher : Interface + { + virtual void dispatch_device_wakeup() = 0; + }; + + struct Deadline : Clock { }; + + static constexpr Deadline infinite_deadline { uint64_t(-1) }; + + private: + + struct Waiter : Thread + { + l4_timeout_s mus_to_timeout(uint64_t const mus) const + { + if (mus == 0) + return L4_IPC_TIMEOUT_0; + else if (mus == ~0ULL) + return L4_IPC_TIMEOUT_NEVER; + + long e = Genode::log2((unsigned long)mus) - 7; + + if (e < 0) e = 0; + + uint64_t m = mus / (1UL << e); + + enum { M_MASK = 0x3ff }; + + /* check corner case */ + if ((e > 31 ) || (m > M_MASK)) { + Genode::warning("invalid timeout ", mus, ", using max. values"); + e = 0; + m = M_MASK; + } + + return l4_timeout_rel(m & M_MASK, (unsigned)e); + } + + Wakeup_dispatcher &_dispatcher; + + Mutex _mutex { }; /* protect '_deadline' */ + Deadline _deadline { ~0ULL }; + + l4_cap_idx_t _myself { }; + + Device &_device; + + Waiter(Env &env, Wakeup_dispatcher &dispatcher, Device &device) + : + Thread(env, "waiter", 8*1024*sizeof(addr_t)), + _dispatcher(dispatcher), + _device(device) + { + start(); + } + + void entry() override + { + _myself = native_thread().kcap; + + for (;;) { + + auto deadline_atomic = [&] + { + Mutex::Guard guard(_mutex); + return _deadline; + }; + + { + auto const deadline = deadline_atomic(); + auto const now = _device.now(); + + if (now.us < deadline.us) { + /* due to ex_regs not working, use 1ms granularity */ + auto usecs = min(deadline.us - now.us, 1000ull); + + auto const timeout = l4_timeout(L4_IPC_TIMEOUT_NEVER, + mus_to_timeout(usecs)); + l4_ipc_sleep(timeout); + } + } + + if (_device.now().us >= deadline_atomic().us) + _dispatcher.dispatch_device_wakeup(); + } + } + + void update_deadline(Deadline const deadline) + { + Mutex::Guard guard(_mutex); + + bool const sooner_than_scheduled = (deadline.us < _deadline.us); + + _deadline = deadline; + + if (sooner_than_scheduled) { + /* cancel old timeout by waking sleeping waiter */ + + /* XXX not working, syscall does not return + Foc::l4_thread_ex_regs(_myself, ~0UL, ~0UL, + L4_THREAD_EX_REGS_CANCEL); + */ + } + } + } _waiter; + + public: + + Device(Env &env, Wakeup_dispatcher &dispatcher) + : _kip_ds(env, "l4v2_kip"), _waiter(env, dispatcher, *this) { } + + Clock now() + { + auto kip = _kip_ds.local_addr(); + return { .us = Foc::l4_kip_clock(kip) }; + } + + void update_deadline(Deadline deadline) { + _waiter.update_deadline(deadline); } +}; + + +struct Timer::Alarm : Alarms::Element +{ + Session_component &session; + + Alarm(Alarms &alarms, Session_component &session, Clock time) + : + Alarms::Element(alarms, *this, time), session(session) + { } + + void print(Output &out) const; +}; + + +static Timer::Device::Deadline next_deadline(Timer::Alarms &alarms) +{ + using namespace Timer; + + return alarms.soonest(Clock { 0 }).convert( + [&] (Clock soonest) -> Device::Deadline { + + /* scan alarms for a cluster nearby the soonest */ + uint64_t const MAX_DELAY_US = 250; + Device::Deadline result { soonest.us }; + alarms.for_each_in_range(soonest, Clock { soonest.us + MAX_DELAY_US }, + [&] (Alarm const &alarm) { + result.us = max(result.us, alarm.time.us); }); + + return result; + }, + [&] (Alarms::None) { return Device::infinite_deadline; }); +} + + +struct Timer::Session_component : Session_object +{ + Alarms &_alarms; + Mutex &_alarms_mutex; + Device &_device; + + Signal_context_capability _sigh { }; + + Clock const _creation_time = _device.now(); + + uint64_t _local_now_us() const { return _device.now().us - _creation_time.us; } + + struct Period { uint64_t us; }; + + Constructible _period { }; + Constructible _alarm { }; + + Session_component(Env &env, + Resources const &resources, + Label const &label, + Diag const &diag, + Alarms &alarms, + Mutex &alarms_mutex, + Device &device) + : + Session_object(env.ep(), resources, label, diag), + _alarms(alarms), _alarms_mutex(alarms_mutex), _device(device) + { } + + ~Session_component() + { + Mutex::Guard guard(_alarms_mutex); + + _alarm.destruct(); + } + + /** + * Called by Device::Wakeup_dispatcher with '_alarms_mutex' taken + */ + void handle_wakeup() + { + if (_sigh.valid()) + Signal_transmitter(_sigh).submit(); + + if (_period.constructed()) { + Clock const next = _alarm.constructed() + ? Clock { _alarm->time.us + _period->us } + : Clock { _device.now().us + _period->us }; + + _alarm.construct(_alarms, *this, next); + + } else /* response of 'trigger_once' */ { + _alarm.destruct(); + } + } + + /****************************** + ** Timer::Session interface ** + ******************************/ + + void trigger_once(uint64_t rel_us) override + { + Mutex::Guard guard(_alarms_mutex); + + _period.destruct(); + _alarm.destruct(); + + Clock const now = _device.now(); + + rel_us = max(rel_us, 250u); + _alarm.construct(_alarms, *this, Clock { now.us + rel_us }); + + _device.update_deadline(next_deadline(_alarms)); + } + + void trigger_periodic(uint64_t period_us) override + { + Mutex::Guard guard(_alarms_mutex); + + _period.destruct(); + _alarm.destruct(); + + if (period_us) { + period_us = max(period_us, 1000u); + _period.construct(period_us); + handle_wakeup(); + } + + _device.update_deadline(next_deadline(_alarms)); + } + + void sigh(Signal_context_capability sigh) override { _sigh = sigh; } + + uint64_t elapsed_ms() const override { return _local_now_us()/1000; } + uint64_t elapsed_us() const override { return _local_now_us(); } + + void msleep(uint64_t) override { } + void usleep(uint64_t) override { } +}; + + +struct Timer::Root : public Root_component +{ + private: + + Env &_env; + Alarms &_alarms; + Mutex &_alarms_mutex; + Device &_device; + + protected: + + Session_component *_create_session(const char *args) override + { + return new (md_alloc()) + Session_component(_env, + session_resources_from_args(args), + session_label_from_args(args), + session_diag_from_args(args), + _alarms, _alarms_mutex, _device); + } + + void _upgrade_session(Session_component *s, const char *args) override + { + s->upgrade(ram_quota_from_args(args)); + s->upgrade(cap_quota_from_args(args)); + } + + void _destroy_session(Session_component *session) override + { + Genode::destroy(md_alloc(), session); + } + + public: + + Root(Env &env, Allocator &md_alloc, + Alarms &alarms, Mutex &alarms_mutex, Device &device) + : + Root_component(&env.ep().rpc_ep(), &md_alloc), + _env(env), _alarms(alarms), _alarms_mutex(alarms_mutex), _device(device) + { } +}; + + +void Timer::Alarm::print(Output &out) const { Genode::print(out, session.label()); } + + +struct Timer::Main : Device::Wakeup_dispatcher +{ + Env &_env; + + Device _device { _env, *this }; + + Mutex _alarms_mutex { }; + Alarms _alarms { }; + + Sliced_heap _sliced_heap { _env.ram(), _env.rm() }; + + Root _root { _env, _sliced_heap, _alarms, _alarms_mutex, _device }; + + /** + * Device::Wakeup_dispatcher + */ + void dispatch_device_wakeup() override + { + Mutex::Guard guard(_alarms_mutex); + + /* handle and remove pending alarms */ + while (_alarms.with_any_in_range({ 0 }, _device.now(), [&] (Alarm &alarm) { + alarm.session.handle_wakeup(); })); + + /* schedule next wakeup */ + _device.update_deadline(next_deadline(_alarms)); + } + + Main(Genode::Env &env) : _env(env) + { + _env.parent().announce(_env.ep().manage(_root)); + } +}; + + +void Component::construct(Genode::Env &env) { static Timer::Main inst(env); } diff --git a/repos/base-foc/src/timer/foc/target.mk b/repos/base-foc/src/timer/foc/target.mk index 8b1c911ad7..262f8074e7 100644 --- a/repos/base-foc/src/timer/foc/target.mk +++ b/repos/base-foc/src/timer/foc/target.mk @@ -1,9 +1,6 @@ TARGET = foc_timer -LIBS += syscall-foc -GEN_DIR := $(dir $(call select_from_repositories,src/timer/main.cc)) -INC_DIR += $(GEN_DIR)/periodic -SRC_CC += periodic/time_source.cc fiasco/time_source.cc +INC_DIR += $(PRG_DIR) +SRC_CC += component.cc +LIBS += base syscall-foc -include $(GEN_DIR)/target.inc - -vpath %.cc $(GEN_DIR) +REP_INC_DIR += src/include diff --git a/repos/base/src/timer/fiasco/time_source.cc b/repos/base/src/timer/fiasco/time_source.cc deleted file mode 100644 index efa588f775..0000000000 --- a/repos/base/src/timer/fiasco/time_source.cc +++ /dev/null @@ -1,101 +0,0 @@ -/* - * \brief Time source that uses sleeping by the means of the kernel - * \author Christian Helmuth - * \author Norman Feske - * \author Martin Stein - * \date 2006-08-30 - */ - -/* - * Copyright (C) 2006-2017 Genode Labs GmbH - * - * This file is part of the Genode OS framework, which is distributed - * under the terms of the GNU Affero General Public License version 3. - */ - -/* Genode includes */ -#include -#include - -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wconversion" - -/* Fiasco includes */ -namespace Fiasco { -#include -#include -} - -/* - * On L4/Fiasco, the KIP layout is defined in 'kernel.h', which does not exist - * on Fiasco.OC. We test for 'L4_SYS_KIP_H__' to check for the L4/Fiasco case - * and include 'kernel.h'. This works because the Fiasco.OC headers do not use - * include guards ('L4_SYS_KIP_H__' is undefined on Fiasco.OC). - */ -#ifdef L4_SYS_KIP_H__ -namespace Fiasco { -#include -} -#endif /* L4_SYS_KIP_H__ */ -#pragma GCC diagnostic pop - -/* local includes */ -#include - -using namespace Fiasco; -using Microseconds = Genode::Microseconds; -using Duration = Genode::Duration; -using Genode::uint64_t; - - -static l4_timeout_s mus_to_timeout(uint64_t mus) -{ - if (mus == 0) - return L4_IPC_TIMEOUT_0; - else if (mus == ~(uint64_t)0) - return L4_IPC_TIMEOUT_NEVER; - - long e = Genode::log2((unsigned long)mus) - 7; - - if (e < 0) e = 0; - - uint64_t m = mus / (1UL << e); - - enum { M_MASK = 0x3ff }; - - /* check corner case */ - if ((e > 31 ) || (m > M_MASK)) { - Genode::warning("invalid timeout ", mus, ", using max. values"); - e = 0; - m = M_MASK; - } - return l4_timeout_rel(m & M_MASK, (unsigned)e); -} - - -Microseconds Timer::Time_source::max_timeout() const -{ - Genode::Mutex::Guard lock_guard(_mutex); - return Microseconds(1000 * 1000 * 100); -} - - -Duration Timer::Time_source::curr_time() -{ - Genode::Mutex::Guard mutex_guard(_mutex); - static Genode::Attached_rom_dataspace kip_ds(_env, "l4v2_kip"); - static Fiasco::l4_kernel_info_t * const kip = - kip_ds.local_addr(); - -#ifdef L4_SYS_KIP_H__ - Fiasco::l4_cpu_time_t const clock = kip->clock; -#else - Fiasco::l4_cpu_time_t const clock = Fiasco::l4_kip_clock(kip); -#endif - - return Duration(Microseconds(clock)); -} - - -void Timer::Time_source::_usleep(uint64_t usecs) { - l4_ipc_sleep(l4_timeout(L4_IPC_TIMEOUT_NEVER, mus_to_timeout(usecs))); }