diff --git a/repos/base-linux/src/timer/linux/component.cc b/repos/base-linux/src/timer/linux/component.cc new file mode 100644 index 0000000000..60f04eb3a3 --- /dev/null +++ b/repos/base-linux/src/timer/linux/component.cc @@ -0,0 +1,377 @@ +/* + * \brief Timer driver for Linux + * \author Norman Feske + * \author Alexander Boettcher + * \date 2024-06-18 + */ + +/* + * 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 + +/* Linux includes */ +#include +#include + + +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 +{ + 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 + { + Wakeup_dispatcher &_dispatcher; + + Mutex _mutex { }; /* protect '_deadline' */ + Deadline _deadline { ~0ULL }; + + 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 + { + 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) { + /* no support to cancel sleep, use 1ms granularity */ + auto usecs = min(deadline.us - now.us, 1000ull); + + struct timespec ts { + .tv_sec = long(usecs) / (1000 * 1000), + .tv_nsec = (long(usecs) % (1000 * 1000)) * 1000, + }; + + lx_nanosleep(&ts, &ts); + } + } + + 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 supported to cancel nanosleep */ + } + } + } _waiter; + + int lx_gettimeofday(struct timeval *tv, struct timeval *tz) const { + return int(lx_syscall(SYS_gettimeofday, tv, tz)); } + + public: + + Device(Env &env, Wakeup_dispatcher &dispatcher) + : _waiter(env, dispatcher, *this) { } + + Clock now() const + { + struct timeval tv { }; + + lx_gettimeofday(&tv, 0); + + return { .us = uint64_t(tv.tv_sec) * 1000 * 1000 + tv.tv_usec }; + } + + 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 */ + Genode::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-linux/src/timer/linux/target.mk b/repos/base-linux/src/timer/linux/target.mk index 5eb1696b0a..9438921997 100644 --- a/repos/base-linux/src/timer/linux/target.mk +++ b/repos/base-linux/src/timer/linux/target.mk @@ -1,9 +1,6 @@ TARGET = linux_timer -GEN_DIR := $(call select_from_repositories,src/timer/periodic)/.. -INC_DIR += $(GEN_DIR)/periodic -SRC_CC += periodic/time_source.cc time_source.cc -LIBS += syscall-linux +INC_DIR += $(PRG_DIR) +SRC_CC += component.cc +LIBS += base syscall-linux -include $(GEN_DIR)/target.inc - -vpath periodic/time_source.cc $(GEN_DIR) +REP_INC_DIR += src/include diff --git a/repos/base-linux/src/timer/linux/time_source.cc b/repos/base-linux/src/timer/linux/time_source.cc deleted file mode 100644 index 80979e981a..0000000000 --- a/repos/base-linux/src/timer/linux/time_source.cc +++ /dev/null @@ -1,51 +0,0 @@ -/* - * \brief Time source that uses sleeping by the means of the kernel - * \author Norman Feske - * \author Martin Stein - * \date 2006-08-15 - */ - -/* - * 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. - */ - -/* Linux includes */ -#include -#include - -/* local includes */ -#include - -using namespace Genode; - - -inline int lx_gettimeofday(struct timeval *tv, struct timeval *tz) { - return (int)lx_syscall(SYS_gettimeofday, tv, tz); } - - -Microseconds Timer::Time_source::max_timeout() const -{ - Mutex::Guard mutex_guard(_mutex); - return Microseconds(1000 * 1000); -} - - -Duration Timer::Time_source::curr_time() -{ - struct timeval tv; - lx_gettimeofday(&tv, 0); - return Duration(Microseconds((uint64_t)tv.tv_sec * 1000 * 1000 + tv.tv_usec)); -} - - -void Timer::Time_source::_usleep(uint64_t us) -{ - struct timespec ts; - ts.tv_sec = (long)us / (1000 * 1000); - ts.tv_nsec = ((long)us % (1000 * 1000)) * 1000; - - lx_nanosleep(&ts, &ts); -} diff --git a/repos/base/src/timer/periodic/time_source.cc b/repos/base/src/timer/periodic/time_source.cc deleted file mode 100644 index 327fffd774..0000000000 --- a/repos/base/src/timer/periodic/time_source.cc +++ /dev/null @@ -1,53 +0,0 @@ -/* - * \brief Time source that uses sleeping by the means of the kernel - * \author Norman Feske - * \author Martin Stein - * \date 2009-06-16 - */ - -/* - * Copyright (C) 2009-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. - */ - -/* local includes */ -#include - -using namespace Genode; - - -void Timer::Time_source::set_timeout(Microseconds duration, - Timeout_handler &handler) -{ - Mutex::Guard mutex_guard(_mutex); - Threaded_time_source::handler(handler); - _next_timeout_us = duration.value; -} - - -Timer::Time_source::Result_of_wait_for_irq -Timer::Time_source::_wait_for_irq() -{ - enum { SLEEP_GRANULARITY_US = 1000 }; - uint64_t last_time_us = curr_time().trunc_to_plain_us().value; - _mutex.acquire(); - while (_next_timeout_us > 0) { - _mutex.release(); - - _usleep(SLEEP_GRANULARITY_US); - - uint64_t curr_time_us = curr_time().trunc_to_plain_us().value; - uint64_t sleep_duration_us = curr_time_us - last_time_us; - last_time_us = curr_time_us; - - _mutex.acquire(); - if (_next_timeout_us >= sleep_duration_us) - _next_timeout_us -= sleep_duration_us; - else - break; - } - _mutex.release(); - return IRQ_TRIGGERED; -} diff --git a/repos/base/src/timer/periodic/time_source.h b/repos/base/src/timer/periodic/time_source.h deleted file mode 100644 index eb69db96fd..0000000000 --- a/repos/base/src/timer/periodic/time_source.h +++ /dev/null @@ -1,62 +0,0 @@ -/* - * \brief Time source that uses sleeping by the means of the kernel - * \author Norman Feske - * \author Martin Stein - * \date 2009-06-16 - */ - -/* - * Copyright (C) 2009-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. - */ - -#ifndef _TIME_SOURCE_H_ -#define _TIME_SOURCE_H_ - -/* local includes */ -#include - -namespace Timer { - - using Genode::uint64_t; - class Time_source; -} - - -class Timer::Time_source : public Threaded_time_source -{ - private: - - Genode::Env &_env; - - Genode::Mutex mutable _mutex { }; - uint64_t _curr_time_us = 0; - uint64_t _next_timeout_us = max_timeout().value; - - void _usleep(uint64_t us); - - - /************************** - ** Threaded_time_source ** - **************************/ - - Result_of_wait_for_irq _wait_for_irq() override; - - public: - - Time_source(Genode::Env &env) - : Threaded_time_source(env), _env(env) { start(); } - - - /************************* - ** Genode::Time_source ** - *************************/ - - Duration curr_time() override; - Microseconds max_timeout() const override; - void set_timeout(Microseconds duration, Genode::Timeout_handler &handler) override; -}; - -#endif /* _TIME_SOURCE_H_ */