From c70fed29f715ebbb3af12a2f9946ae2f3a8995d5 Mon Sep 17 00:00:00 2001 From: Martin Stein Date: Sat, 22 Apr 2017 00:52:23 +0200 Subject: [PATCH] os/timer: interpolate time via timestamps Previously, the Genode::Timer::curr_time always used the Timer_session::elapsed_ms RPC as back end. Now, Genode::Timer reads this remote time only in a periodic fashion independently from the calls to Genode::Timer::curr_time. If now one calls Genode::Timer::curr_time, the function takes the last read remote time value and adapts it using the timestamp difference since the remote-time read. The conversion factor from timestamps to time is estimated on every remote-time read using the last read remote-time value and the timestamp difference since the last remote time read. This commit also re-works the timeout test. The test now has two stages. In the first stage, it tests fast polling of the Genode::Timer::curr_time. This stage checks the error between locally interpolated and timer-driver time as well as wether the locally interpolated time is monotone and sufficiently homogeneous. In the second stage several periodic and one-shot timeouts are scheduled at once. This stage checks if the timeouts trigger sufficiently precise. This commit adds the new Kernel::time syscall to base-hw. The syscall is solely used by the Genode::Timer on base-hw as substitute for the timestamp. This is because on ARM, the timestamp function uses the ARM performance counter that stops counting when the WFI (wait for interrupt) instruction is active. This instruction, however is used by the base-hw idle contexts that get active when no user thread needs to be scheduled. Thus, the ARM performance counter is not a good choice for time interpolation and we use the kernel internal time instead. With this commit, the timeout library becomes a basic library. That means that it is linked against the LDSO which then provides it to the program it serves. Furthermore, you can't use the timeout library anymore without the LDSO because through the kernel-dependent LDSO make-files we can achieve a kernel-dependent timeout implementation. This commit introduces a structured Duration type that shall successively replace the use of Microseconds, Milliseconds, and integer types for duration values. Open issues: * The timeout test fails on Raspberry PI because of precision errors in the first stage. However, this does not render the framework unusable in general on the RPI but merely is an issue when speaking of microseconds precision. * If we run on ARM with another Kernel than HW the timestamp speed may continuously vary from almost 0 up to CPU speed. The Timer, however, only uses interpolation if the timestamp speed remained stable (12.5% tolerance) for at least 3 observation periods. Currently, one period is 100ms, so its 300ms. As long as this is not the case, Timer_session::elapsed_ms is called instead. Anyway, it might happen that the CPU load was stable for some time so interpolation becomes active and now the timestamp speed drops. In the worst case, we would now have 100ms of slowed down time. The bad thing about it would be, that this also affects the timeout of the period. Thus, it might "freeze" the local time for more than 100ms. On the other hand, if the timestamp speed suddenly raises after some stable time, interpolated time can get too fast. This would shorten the period but nonetheless may result in drifting away into the far future. Now we would have the problem that we can't deliver the real time anymore until it has caught up because the output of Timer::curr_time shall be monotone. So, effectively local time might "freeze" again for more than 100ms. It would be a solution to not use the Trace::timestamp on ARM w/o HW but a function whose return value causes the Timer to never use interpolation because of its stability policy. Fixes #2400 --- repos/base-fiasco/lib/mk/base-fiasco.mk | 2 +- repos/base-foc/lib/mk/base-foc.inc | 2 +- repos/base-hw/include/kernel/interface.h | 13 + repos/base-hw/lib/mk/base-hw.mk | 2 +- repos/base-hw/lib/mk/timeout-hw.mk | 10 + repos/base-hw/src/core/kernel/cpu.cc | 3 + repos/base-hw/src/core/kernel/cpu.h | 4 + repos/base-hw/src/core/kernel/thread.cc | 1 + repos/base-hw/src/core/kernel/timer.h | 2 + repos/base-linux/lib/mk/base-linux.mk | 2 +- repos/base-linux/lib/mk/lx_hybrid.mk | 2 +- repos/base-nova/lib/mk/base-nova.mk | 2 +- repos/base-okl4/lib/mk/base-okl4.mk | 2 +- repos/base-pistachio/lib/mk/base-pistachio.mk | 2 +- repos/base-sel4/lib/mk/base-sel4.mk | 2 +- repos/base/lib/symbols/ld | 51 +- repos/dde_linux/src/lib/lxip/lxcc_emul.cc | 2 +- repos/gems/src/server/terminal/main.cc | 20 +- repos/gems/src/server/terminal/target.mk | 2 +- repos/libports/lib/mk/libc.mk | 2 +- repos/libports/src/lib/libc/nanosleep.cc | 2 - repos/libports/src/lib/libc/task.cc | 28 +- repos/libports/src/lib/libc/task.h | 4 - repos/os/include/os/duration.h | 84 +++ repos/os/include/os/time_source.h | 64 -- repos/os/include/os/timer.h | 99 ---- repos/os/include/{os => timer}/timeout.h | 181 +++--- repos/os/include/timer_session/connection.h | 277 ++++++++- repos/os/lib/mk/timeout.mk | 5 + repos/os/run/timeout.run | 55 +- .../src/drivers/timer/fiasco/time_source.cc | 7 +- repos/os/src/drivers/timer/hw/time_source.cc | 7 +- repos/os/src/drivers/timer/hw/time_source.h | 15 +- .../drivers/timer/include/root_component.h | 4 +- .../drivers/timer/include/session_component.h | 17 +- .../timer/include/signalled_time_source.h | 2 +- .../timer/include/threaded_time_source.h | 10 +- .../os/src/drivers/timer/linux/time_source.cc | 5 +- .../os/src/drivers/timer/nova/time_source.cc | 1 - repos/os/src/drivers/timer/nova/time_source.h | 4 +- .../src/drivers/timer/periodic/time_source.cc | 4 +- .../src/drivers/timer/periodic/time_source.h | 2 +- repos/os/src/drivers/timer/pit/time_source.cc | 5 +- repos/os/src/drivers/timer/pit/time_source.h | 10 +- repos/os/src/drivers/timer/target.inc | 2 +- repos/os/src/lib/timeout/duration.cc | 67 +++ .../timeout/hw/timer_connection_timestamp.cc | 24 + repos/os/src/lib/timeout/timeout.cc | 33 +- repos/os/src/lib/timeout/timer_connection.cc | 268 +++++++++ .../lib/timeout/timer_connection_timestamp.cc | 24 + .../src/server/input_filter/chargen_source.h | 18 +- repos/os/src/server/input_filter/main.cc | 7 +- repos/os/src/server/input_filter/target.mk | 2 +- .../src/server/input_filter/timer_accessor.h | 4 +- repos/os/src/server/nic_dump/component.cc | 38 +- repos/os/src/server/nic_dump/component.h | 6 +- repos/os/src/server/nic_dump/interface.cc | 14 +- repos/os/src/server/nic_dump/interface.h | 6 +- repos/os/src/server/nic_dump/main.cc | 6 +- repos/os/src/server/nic_dump/target.mk | 2 +- repos/os/src/server/nic_dump/uplink.cc | 10 +- repos/os/src/server/nic_dump/uplink.h | 2 +- repos/os/src/server/nic_router/component.cc | 4 +- repos/os/src/server/nic_router/component.h | 6 +- repos/os/src/server/nic_router/interface.cc | 2 +- repos/os/src/server/nic_router/interface.h | 4 +- repos/os/src/server/nic_router/link.cc | 12 +- repos/os/src/server/nic_router/link.h | 16 +- repos/os/src/server/nic_router/main.cc | 14 +- repos/os/src/server/nic_router/target.mk | 2 +- repos/os/src/server/nic_router/uplink.cc | 2 +- repos/os/src/server/nic_router/uplink.h | 2 +- repos/os/src/test/timeout/main.cc | 557 +++++++++++++++++- repos/os/src/test/timeout/target.mk | 2 +- 74 files changed, 1640 insertions(+), 536 deletions(-) create mode 100644 repos/base-hw/lib/mk/timeout-hw.mk create mode 100644 repos/os/include/os/duration.h delete mode 100644 repos/os/include/os/time_source.h delete mode 100644 repos/os/include/os/timer.h rename repos/os/include/{os => timer}/timeout.h (58%) create mode 100644 repos/os/src/lib/timeout/duration.cc create mode 100644 repos/os/src/lib/timeout/hw/timer_connection_timestamp.cc create mode 100644 repos/os/src/lib/timeout/timer_connection.cc create mode 100644 repos/os/src/lib/timeout/timer_connection_timestamp.cc diff --git a/repos/base-fiasco/lib/mk/base-fiasco.mk b/repos/base-fiasco/lib/mk/base-fiasco.mk index dfca0008e..6a594d41a 100644 --- a/repos/base-fiasco/lib/mk/base-fiasco.mk +++ b/repos/base-fiasco/lib/mk/base-fiasco.mk @@ -1,6 +1,6 @@ include $(BASE_DIR)/lib/mk/base.inc -LIBS += syscall-fiasco +LIBS += syscall-fiasco timeout SRC_CC += thread_start.cc SRC_CC += cache.cc diff --git a/repos/base-foc/lib/mk/base-foc.inc b/repos/base-foc/lib/mk/base-foc.inc index 37538f73f..3ad06fce1 100644 --- a/repos/base-foc/lib/mk/base-foc.inc +++ b/repos/base-foc/lib/mk/base-foc.inc @@ -1,6 +1,6 @@ include $(BASE_DIR)/lib/mk/base.inc -LIBS += base-foc-common syscall-foc cxx +LIBS += base-foc-common syscall-foc cxx timeout SRC_CC += cap_map_remove.cc cap_alloc.cc SRC_CC += thread_start.cc diff --git a/repos/base-hw/include/kernel/interface.h b/repos/base-hw/include/kernel/interface.h index dcd8befcb..a99db6997 100644 --- a/repos/base-hw/include/kernel/interface.h +++ b/repos/base-hw/include/kernel/interface.h @@ -42,6 +42,7 @@ namespace Kernel constexpr Call_arg call_id_timeout() { return 16; } constexpr Call_arg call_id_timeout_age_us() { return 17; } constexpr Call_arg call_id_timeout_max_us() { return 18; } + constexpr Call_arg call_id_time() { return 19; } /***************************************************************** @@ -106,6 +107,18 @@ namespace Kernel } + /** + * Return value of a free-running, uniform counter + * + * The counter has a constant frequency and does not wrap twice during + * a time period of 'timeout_max_us()' microseconds. + */ + inline time_t time() + { + return call(call_id_time()); + } + + /** * Return the constant maximum installable timeout in microseconds * diff --git a/repos/base-hw/lib/mk/base-hw.mk b/repos/base-hw/lib/mk/base-hw.mk index dae3cbdfc..c861eadcd 100644 --- a/repos/base-hw/lib/mk/base-hw.mk +++ b/repos/base-hw/lib/mk/base-hw.mk @@ -6,4 +6,4 @@ SRC_CC += capability.cc SRC_CC += cache.cc SRC_CC += raw_write_string.cc -LIBS += startup-hw base-hw-common +LIBS += startup-hw base-hw-common timeout-hw diff --git a/repos/base-hw/lib/mk/timeout-hw.mk b/repos/base-hw/lib/mk/timeout-hw.mk new file mode 100644 index 000000000..235f50ec7 --- /dev/null +++ b/repos/base-hw/lib/mk/timeout-hw.mk @@ -0,0 +1,10 @@ +SRC_CC += timeout.cc +SRC_CC += timer_connection.cc +SRC_CC += hw/timer_connection_timestamp.cc +SRC_CC += duration.cc + +LIBS += alarm + +INC_DIR += $(BASE_DIR)/src/include + +vpath % $(BASE_DIR)/../os/src/lib/timeout diff --git a/repos/base-hw/src/core/kernel/cpu.cc b/repos/base-hw/src/core/kernel/cpu.cc index f66bfd43b..6efe8aba3 100644 --- a/repos/base-hw/src/core/kernel/cpu.cc +++ b/repos/base-hw/src/core/kernel/cpu.cc @@ -42,6 +42,9 @@ time_t Cpu_job::timeout_age_us(Timeout const * const timeout) const } +time_t Cpu_job::time() const { return _cpu->time(); } + + time_t Cpu_job::timeout_max_us() const { return _cpu->timeout_max_us(); diff --git a/repos/base-hw/src/core/kernel/cpu.h b/repos/base-hw/src/core/kernel/cpu.h index e81f78c63..5f052217e 100644 --- a/repos/base-hw/src/core/kernel/cpu.h +++ b/repos/base-hw/src/core/kernel/cpu.h @@ -196,6 +196,8 @@ class Kernel::Cpu_job : public Genode::Cpu::User_context, public Cpu_share time_t timeout_max_us() const; + time_t time() const; + /*************** ** Accessors ** ***************/ @@ -323,6 +325,8 @@ class Kernel::Cpu : public Genode::Cpu, public Irq::Pool, private Timeout time_t timeout_max_us() const; + time_t time() const { return _timer.time(); } + /*************** ** Accessors ** ***************/ diff --git a/repos/base-hw/src/core/kernel/thread.cc b/repos/base-hw/src/core/kernel/thread.cc index 1fde96c25..0cc20312b 100644 --- a/repos/base-hw/src/core/kernel/thread.cc +++ b/repos/base-hw/src/core/kernel/thread.cc @@ -564,6 +564,7 @@ void Thread::_call() case call_id_timeout(): _call_timeout(); return; case call_id_timeout_age_us(): _call_timeout_age_us(); return; case call_id_timeout_max_us(): _call_timeout_max_us(); return; + case call_id_time(): user_arg_0(Cpu_job::time()); return; default: /* check wether this is a core thread */ if (!_core()) { diff --git a/repos/base-hw/src/core/kernel/timer.h b/repos/base-hw/src/core/kernel/timer.h index d3c0e45c8..f51c5aeac 100644 --- a/repos/base-hw/src/core/kernel/timer.h +++ b/repos/base-hw/src/core/kernel/timer.h @@ -99,6 +99,8 @@ class Kernel::Timer unsigned interrupt_id() const; static void init_cpu_local(); + + time_t time() const { return _time; } }; #endif /* _CORE__KERNEL__TIMER_H_ */ diff --git a/repos/base-linux/lib/mk/base-linux.mk b/repos/base-linux/lib/mk/base-linux.mk index f020b24ae..767921c27 100644 --- a/repos/base-linux/lib/mk/base-linux.mk +++ b/repos/base-linux/lib/mk/base-linux.mk @@ -6,7 +6,7 @@ include $(REP_DIR)/lib/mk/base-linux.inc -LIBS += startup-linux base-linux-common +LIBS += startup-linux base-linux-common timeout SRC_CC += thread.cc thread_myself.cc thread_linux.cc SRC_CC += capability_space.cc capability_raw.cc SRC_CC += attach_stack_area.cc diff --git a/repos/base-linux/lib/mk/lx_hybrid.mk b/repos/base-linux/lib/mk/lx_hybrid.mk index 2ffba5194..5e2907de7 100644 --- a/repos/base-linux/lib/mk/lx_hybrid.mk +++ b/repos/base-linux/lib/mk/lx_hybrid.mk @@ -4,7 +4,7 @@ vpath new_delete.cc $(BASE_DIR)/src/lib/cxx vpath lx_hybrid.cc $(REP_DIR)/src/lib/lx_hybrid # add parts of the base library that are shared with core -LIBS += base-linux-common +LIBS += base-linux-common timeout # non-core parts of the base library (except for the startup code) include $(REP_DIR)/lib/mk/base-linux.inc diff --git a/repos/base-nova/lib/mk/base-nova.mk b/repos/base-nova/lib/mk/base-nova.mk index d8ebb9f4d..45aa1d988 100644 --- a/repos/base-nova/lib/mk/base-nova.mk +++ b/repos/base-nova/lib/mk/base-nova.mk @@ -1,5 +1,5 @@ include $(BASE_DIR)/lib/mk/base.inc -LIBS += base-nova-common cxx +LIBS += base-nova-common cxx timeout SRC_CC += thread_start.cc SRC_CC += cache.cc diff --git a/repos/base-okl4/lib/mk/base-okl4.mk b/repos/base-okl4/lib/mk/base-okl4.mk index b2b05fccb..4d6223e9c 100644 --- a/repos/base-okl4/lib/mk/base-okl4.mk +++ b/repos/base-okl4/lib/mk/base-okl4.mk @@ -1,6 +1,6 @@ include $(BASE_DIR)/lib/mk/base.inc -LIBS += base-okl4-common syscall-okl4 +LIBS += base-okl4-common syscall-okl4 timeout SRC_CC += thread_start.cc SRC_CC += cache.cc SRC_CC += capability_space.cc diff --git a/repos/base-pistachio/lib/mk/base-pistachio.mk b/repos/base-pistachio/lib/mk/base-pistachio.mk index 7ceb8bd9e..37a55542d 100644 --- a/repos/base-pistachio/lib/mk/base-pistachio.mk +++ b/repos/base-pistachio/lib/mk/base-pistachio.mk @@ -1,6 +1,6 @@ include $(BASE_DIR)/lib/mk/base.inc -LIBS += base-pistachio-common syscall-pistachio cxx +LIBS += base-pistachio-common syscall-pistachio cxx timeout SRC_CC += thread_start.cc SRC_CC += cache.cc diff --git a/repos/base-sel4/lib/mk/base-sel4.mk b/repos/base-sel4/lib/mk/base-sel4.mk index e9faa7f35..3ebad650f 100644 --- a/repos/base-sel4/lib/mk/base-sel4.mk +++ b/repos/base-sel4/lib/mk/base-sel4.mk @@ -4,4 +4,4 @@ SRC_CC += capability_space.cc SRC_CC += thread_start.cc thread_init.cc SRC_CC += cache.cc -LIBS += syscall-sel4 base-sel4-common +LIBS += syscall-sel4 base-sel4-common timeout diff --git a/repos/base/lib/symbols/ld b/repos/base/lib/symbols/ld index 6994b5eab..dcc787218 100644 --- a/repos/base/lib/symbols/ld +++ b/repos/base/lib/symbols/ld @@ -46,8 +46,18 @@ _Z13genode_atexitPFvvE T _Z16main_thread_utcbv T _Z21genode___cxa_finalizePv T _Z22__ldso_raise_exceptionv T +_ZN5Timer10Connection16schedule_timeoutEN6Genode12MicrosecondsERNS1_11Time_source15Timeout_handlerE T +_ZN5Timer10Connection18_schedule_one_shotERN6Genode7TimeoutENS1_12MicrosecondsE T +_ZN5Timer10Connection18_schedule_periodicERN6Genode7TimeoutENS1_12MicrosecondsE T +_ZN5Timer10Connection8_discardERN6Genode7TimeoutE T +_ZN5Timer10Connection9curr_timeEv T +_ZN5Timer10ConnectionC1ERN6Genode3EnvEPKc T +_ZN5Timer10ConnectionC1Ev T +_ZN5Timer10ConnectionC2ERN6Genode3EnvEPKc T +_ZN5Timer10ConnectionC2Ev T _ZN6Genode10Entrypoint16_dispatch_signalERNS_6SignalE T _ZN6Genode10Entrypoint16schedule_suspendEPFvvES2_ T +_ZN6Genode10Entrypoint22Signal_proxy_component6signalEv T _ZN6Genode10Entrypoint25_process_incoming_signalsEv T _ZN6Genode10Entrypoint31wait_and_dispatch_one_io_signalEv T _ZN6Genode10Entrypoint6manageERNS_22Signal_dispatcher_baseE T @@ -56,7 +66,6 @@ _ZN6Genode10EntrypointC1ERNS_3EnvE T _ZN6Genode10EntrypointC1ERNS_3EnvEmPKc T _ZN6Genode10EntrypointC2ERNS_3EnvE T _ZN6Genode10EntrypointC2ERNS_3EnvEmPKc T -_ZN6Genode10Entrypoint22Signal_proxy_component6signalEv T _ZN6Genode10Ipc_serverC1Ev T _ZN6Genode10Ipc_serverC2Ev T _ZN6Genode10Ipc_serverD1Ev T @@ -116,6 +125,7 @@ _ZN6Genode14Signal_contextD2Ev T _ZN6Genode14Timeout_thread11alarm_timerEv T _ZN6Genode14Timeout_thread5entryEv T _ZN6Genode14cache_coherentEmm T +_ZN6Genode14env_deprecatedEv T _ZN6Genode14ipc_reply_waitERKNS_17Native_capabilityENS_18Rpc_exception_codeERNS_11Msgbuf_baseES5_ T _ZN6Genode15Alarm_scheduler12_setup_alarmERNS_5AlarmEmm T _ZN6Genode15Alarm_scheduler13next_deadlineEPm T @@ -186,6 +196,12 @@ _ZN6Genode18Signal_transmitterC1ENS_10CapabilityINS_14Signal_contextEEE T _ZN6Genode18Signal_transmitterC2ENS_10CapabilityINS_14Signal_contextEEE T _ZN6Genode18server_socket_pairEv T _ZN6Genode20env_session_id_spaceEv T +_ZN6Genode23Alarm_timeout_scheduler14handle_timeoutENS_8DurationE T +_ZN6Genode23Alarm_timeout_scheduler18_schedule_one_shotERNS_7TimeoutENS_12MicrosecondsE T +_ZN6Genode23Alarm_timeout_scheduler18_schedule_periodicERNS_7TimeoutENS_12MicrosecondsE T +_ZN6Genode23Alarm_timeout_scheduler7_enableEv T +_ZN6Genode23Alarm_timeout_schedulerC1ERNS_11Time_sourceE T +_ZN6Genode23Alarm_timeout_schedulerC2ERNS_11Time_sourceE T _ZN6Genode25env_stack_area_region_mapE B 4 _ZN6Genode26env_stack_area_ram_sessionE B 4 _ZN6Genode29upgrade_pd_quota_non_blockingENS_9Ram_quotaENS_9Cap_quotaE T @@ -195,7 +211,6 @@ _ZN6Genode3Log8_releaseEv T _ZN6Genode3Raw7_outputEv T _ZN6Genode3Raw8_acquireEv T _ZN6Genode3Raw8_releaseEv T -_ZN6Genode14env_deprecatedEv T _ZN6Genode4Heap11quota_limitEm T _ZN6Genode4Heap4freeEPvm T _ZN6Genode4Heap5allocEmPPv T @@ -307,10 +322,18 @@ _ZN6Genode7Console7vprintfEPKcP13__va_list_tag T _ZN6Genode7Console7vprintfEPKcPc T _ZN6Genode7Console7vprintfEPKcPv T _ZN6Genode7Console7vprintfEPKcSt9__va_list T +_ZN6Genode7Timeout17schedule_one_shotENS_12MicrosecondsERNS0_7HandlerE T +_ZN6Genode7Timeout17schedule_periodicENS_12MicrosecondsERNS0_7HandlerE T +_ZN6Genode7Timeout5AlarmD0Ev W +_ZN6Genode7Timeout5AlarmD1Ev W +_ZN6Genode7Timeout5AlarmD2Ev W +_ZN6Genode7Timeout7discardEv T _ZN6Genode7cap_mapEv T _ZN6Genode7vprintfEPKcP13__va_list_tag T _ZN6Genode7vprintfEPKcPc T _ZN6Genode7vprintfEPKcSt9__va_list T +_ZN6Genode8DurationpLENS_12MicrosecondsE T +_ZN6Genode8DurationpLENS_12MillisecondsE T _ZN6Genode8ipc_callENS_17Native_capabilityERNS_11Msgbuf_baseES2_m T _ZN6Genode9ipc_replyENS_17Native_capabilityENS_18Rpc_exception_codeERNS_11Msgbuf_baseE T _ZNK6Genode11Sliced_heap8overheadEm T @@ -335,6 +358,7 @@ _ZNK6Genode5Child21notify_resource_availEv T _ZNK6Genode6Thread10stack_baseEv T _ZNK6Genode6Thread4nameEv T _ZNK6Genode6Thread9stack_topEv T +_ZNK6Genode8Duration17trunc_to_plain_usEv T _ZNKSt13bad_exception4whatEv T _ZNKSt9exception4whatEv T _ZNSt13bad_exceptionD0Ev T @@ -361,6 +385,7 @@ _ZTIN10__cxxabiv120__function_type_infoE D 12 _ZTIN10__cxxabiv120__si_class_type_infoE D 12 _ZTIN10__cxxabiv121__vmi_class_type_infoE D 12 _ZTIN10__cxxabiv123__fundamental_type_infoE D 12 +_ZTIN5Timer10ConnectionE D 48 _ZTIN6Genode11Sliced_heapE D 12 _ZTIN6Genode14Rpc_entrypointE D 2 _ZTIN6Genode14Rpc_entrypointE D 32 @@ -370,6 +395,7 @@ _ZTIN6Genode14Timeout_threadE D 32 _ZTIN6Genode17Region_map_clientE D 12 _ZTIN6Genode17Rm_session_clientE D 12 _ZTIN6Genode18Allocator_avl_baseE D 12 +_ZTIN6Genode23Alarm_timeout_schedulerE D 80 _ZTIN6Genode4HeapE D 12 _ZTIN6Genode4SlabE D 12 _ZTIN6Genode5AlarmE D 8 @@ -379,6 +405,7 @@ _ZTIN6Genode5ChildE D 40 _ZTIN6Genode6OutputE D 8 _ZTIN6Genode6ThreadE D 8 _ZTIN6Genode7ConsoleE D 8 +_ZTIN6Genode7Timeout5AlarmE D 24 _ZTIPDd D 16 _ZTIPDe D 16 _ZTIPDf D 16 @@ -484,6 +511,7 @@ _ZTSN10__cxxabiv120__function_type_infoE R _ZTSN10__cxxabiv120__si_class_type_infoE R _ZTSN10__cxxabiv121__vmi_class_type_infoE R _ZTSN10__cxxabiv123__fundamental_type_infoE R +_ZTSN5Timer10ConnectionE R _ZTSN6Genode11Sliced_heapE R _ZTSN6Genode14Rpc_entrypointE R _ZTSN6Genode14Signal_contextE R @@ -491,6 +519,7 @@ _ZTSN6Genode14Timeout_threadE R _ZTSN6Genode17Region_map_clientE R _ZTSN6Genode17Rm_session_clientE R _ZTSN6Genode18Allocator_avl_baseE R +_ZTSN6Genode23Alarm_timeout_schedulerE R _ZTSN6Genode4HeapE R _ZTSN6Genode4SlabE R _ZTSN6Genode5AlarmE R @@ -499,6 +528,7 @@ _ZTSN6Genode5ChildE R _ZTSN6Genode6OutputE R _ZTSN6Genode6ThreadE R _ZTSN6Genode7ConsoleE R +_ZTSN6Genode7Timeout5AlarmE R _ZTSSt10bad_typeid R _ZTSSt13bad_exception R _ZTSSt16bad_array_length R @@ -527,6 +557,7 @@ _ZTVN10__cxxabiv121__vmi_class_type_infoE D 2 _ZTVN10__cxxabiv121__vmi_class_type_infoE D 44 _ZTVN10__cxxabiv123__fundamental_type_infoE D 2 _ZTVN10__cxxabiv123__fundamental_type_infoE D 32 +_ZTVN5Timer10ConnectionE D 116 _ZTVN6Genode11Sliced_heapE D 2 _ZTVN6Genode11Sliced_heapE D 36 _ZTVN6Genode14Rpc_entrypointE D 1 @@ -541,6 +572,7 @@ _ZTVN6Genode17Rm_session_clientE D 1 _ZTVN6Genode17Rm_session_clientE D 24 _ZTVN6Genode18Allocator_avl_baseE D 4 _ZTVN6Genode18Allocator_avl_baseE D 64 +_ZTVN6Genode23Alarm_timeout_schedulerE D 80 _ZTVN6Genode4HeapE D 2 _ZTVN6Genode4HeapE D 36 _ZTVN6Genode4SlabE D 2 @@ -557,6 +589,7 @@ _ZTVN6Genode6ThreadE D 1 _ZTVN6Genode6ThreadE D 24 _ZTVN6Genode7ConsoleE D 1 _ZTVN6Genode7ConsoleE D 24 +_ZTVN6Genode7Timeout5AlarmE D 40 _ZTVSt10bad_typeid D 1 _ZTVSt10bad_typeid D 20 _ZTVSt13bad_exception D 1 @@ -573,6 +606,20 @@ _ZTVSt9exception D 1 _ZTVSt9exception D 20 _ZTVSt9type_info D 2 _ZTVSt9type_info D 32 +_ZThn232_N5Timer10Connection16schedule_timeoutEN6Genode12MicrosecondsERNS1_11Time_source15Timeout_handlerE T +_ZThn232_N5Timer10Connection9curr_timeEv T +_ZThn236_N5Timer10Connection18_schedule_one_shotERN6Genode7TimeoutENS1_12MicrosecondsE T +_ZThn236_N5Timer10Connection18_schedule_periodicERN6Genode7TimeoutENS1_12MicrosecondsE T +_ZThn236_N5Timer10Connection8_discardERN6Genode7TimeoutE T +_ZThn236_N5Timer10Connection9curr_timeEv T +_ZThn280_N5Timer10Connection16schedule_timeoutEN6Genode12MicrosecondsERNS1_11Time_source15Timeout_handlerE T +_ZThn280_N5Timer10Connection9curr_timeEv T +_ZThn288_N5Timer10Connection18_schedule_one_shotERN6Genode7TimeoutENS1_12MicrosecondsE T +_ZThn288_N5Timer10Connection18_schedule_periodicERN6Genode7TimeoutENS1_12MicrosecondsE T +_ZThn288_N5Timer10Connection8_discardERN6Genode7TimeoutE T +_ZThn288_N5Timer10Connection9curr_timeEv T +_ZThn4_N6Genode23Alarm_timeout_scheduler14handle_timeoutENS_8DurationE T +_ZThn8_N6Genode23Alarm_timeout_scheduler14handle_timeoutENS_8DurationE T _ZZN6Genode18Allocator_avl_base5BlockC4EmmbE10num_blocks V _ZdlPv W _ZdlPvPN6Genode11DeallocatorE T diff --git a/repos/dde_linux/src/lib/lxip/lxcc_emul.cc b/repos/dde_linux/src/lib/lxip/lxcc_emul.cc index 87f26e656..416f98bd4 100644 --- a/repos/dde_linux/src/lib/lxip/lxcc_emul.cc +++ b/repos/dde_linux/src/lib/lxip/lxcc_emul.cc @@ -344,7 +344,7 @@ static Genode::Signal_context_capability tick_sig_cap; void Lx::event_init(Genode::Env &env, Genode::Entrypoint &ep, void (*ticker)()) { - static Timeout handler(env, ep, ticker); + static ::Timeout handler(env, ep, ticker); _timeout = &handler; } diff --git a/repos/gems/src/server/terminal/main.cc b/repos/gems/src/server/terminal/main.cc index c0dd9de57..f81b9f494 100644 --- a/repos/gems/src/server/terminal/main.cc +++ b/repos/gems/src/server/terminal/main.cc @@ -24,7 +24,6 @@ #include #include #include -#include /* terminal includes */ #include @@ -531,8 +530,7 @@ struct Terminal::Main Framebuffer::Connection _framebuffer { _env, Framebuffer::Mode() }; Input::Connection _input { _env }; - Timer::Connection _timer_conection { _env }; - Genode::Timer _timer { _timer_conection, _env.ep() }; + Timer::Connection _timer { _env }; Sliced_heap _sliced_heap { _env.ram(), _env.rm() }; @@ -556,12 +554,12 @@ struct Terminal::Main Signal_handler
_input_handler { _env.ep(), *this, &Main::_handle_input }; - void _handle_key_repeat(Time_source::Microseconds); + void _handle_key_repeat(Duration); - One_shot_timeout
_key_repeat_timeout { + Timer::One_shot_timeout
_key_repeat_timeout { _timer, *this, &Main::_handle_key_repeat }; - void _handle_flush(Time_source::Microseconds); + void _handle_flush(Duration); /* * Time in milliseconds between a change of the terminal content and the @@ -575,7 +573,7 @@ struct Terminal::Main void _trigger_flush() { if (!_flush_scheduled) { - _flush_timeout.start(Time_source::Microseconds{1000*_flush_delay}); + _flush_timeout.schedule(Microseconds{1000*_flush_delay}); _flush_scheduled = true; } } @@ -590,7 +588,7 @@ struct Terminal::Main Trigger_flush(Main &main) : _main(main) { } } _trigger_flush_callback { *this }; - One_shot_timeout
_flush_timeout { + Timer::One_shot_timeout
_flush_timeout { _timer, *this, &Main::_handle_flush }; Main(Genode::Env &env, @@ -657,11 +655,11 @@ void Terminal::Main::_handle_input() }); if (_repeat_next) - _key_repeat_timeout.start(Time_source::Microseconds{1000*_repeat_next}); + _key_repeat_timeout.schedule(Microseconds{1000*_repeat_next}); } -void Terminal::Main::_handle_key_repeat(Time_source::Microseconds) +void Terminal::Main::_handle_key_repeat(Duration) { if (_repeat_next) { @@ -675,7 +673,7 @@ void Terminal::Main::_handle_key_repeat(Time_source::Microseconds) } -void Terminal::Main::_handle_flush(Time_source::Microseconds) +void Terminal::Main::_handle_flush(Duration) { _flush_scheduled = false; _flush_callback_registry.flush(); diff --git a/repos/gems/src/server/terminal/target.mk b/repos/gems/src/server/terminal/target.mk index dba8b32b5..46b6e93be 100644 --- a/repos/gems/src/server/terminal/target.mk +++ b/repos/gems/src/server/terminal/target.mk @@ -1,4 +1,4 @@ TARGET = terminal SRC_CC = main.cc -LIBS = base timeout +LIBS = base SRC_BIN = $(notdir $(wildcard $(PRG_DIR)/*.tff)) diff --git a/repos/libports/lib/mk/libc.mk b/repos/libports/lib/mk/libc.mk index 7eb2cc223..b06f95b15 100644 --- a/repos/libports/lib/mk/libc.mk +++ b/repos/libports/lib/mk/libc.mk @@ -4,7 +4,7 @@ LIBS = libc-string libc-locale libc-stdlib libc-stdio libc-gen libc-gdtoa \ libc-inet libc-stdtime libc-regex libc-compat libc-setjmp libc-mem -LIBS += base vfs timeout +LIBS += base vfs # # Back end diff --git a/repos/libports/src/lib/libc/nanosleep.cc b/repos/libports/src/lib/libc/nanosleep.cc index df6de9e90..9c0738a73 100644 --- a/repos/libports/src/lib/libc/nanosleep.cc +++ b/repos/libports/src/lib/libc/nanosleep.cc @@ -16,8 +16,6 @@ #include "task.h" -using namespace Genode; - extern "C" __attribute__((weak)) int _nanosleep(const struct timespec *req, struct timespec *rem) { diff --git a/repos/libports/src/lib/libc/task.cc b/repos/libports/src/lib/libc/task.cc index 4494fa294..77f366b45 100644 --- a/repos/libports/src/lib/libc/task.cc +++ b/repos/libports/src/lib/libc/task.cc @@ -23,7 +23,6 @@ #include #include #include -#include /* libc includes */ #include @@ -49,7 +48,7 @@ namespace Libc { class Timeout_handler; class Io_response_handler; - using Microseconds = Genode::Time_source::Microseconds; + using Genode::Microseconds; } @@ -167,18 +166,13 @@ class Libc::Env_implementation : public Libc::Env struct Libc::Timer { - ::Timer::Connection _timer_connection; - Genode::Timer _timer; + ::Timer::Connection _timer; - Timer(Genode::Env &env) - : - _timer_connection(env), - _timer(_timer_connection, env.ep()) - { } + Timer(Genode::Env &env) : _timer(env) { } - unsigned long curr_time() const + unsigned long curr_time() { - return _timer.curr_time().value/1000; + return _timer.curr_time().trunc_to_plain_us().value/1000; } static Microseconds microseconds(unsigned long timeout_ms) @@ -188,7 +182,7 @@ struct Libc::Timer static unsigned long max_timeout() { - return Genode::Timer::Microseconds::max().value/1000; + return ~0UL/1000; } }; @@ -217,14 +211,14 @@ struct Libc::Timeout_handler */ struct Libc::Timeout { - Libc::Timer_accessor &_timer_accessor; - Timeout_handler &_handler; - Genode::One_shot_timeout _timeout; + Libc::Timer_accessor &_timer_accessor; + Timeout_handler &_handler; + ::Timer::One_shot_timeout _timeout; bool _expired = true; unsigned long _absolute_timeout_ms = 0; - void _handle(Microseconds now) + void _handle(Duration now) { _expired = true; _absolute_timeout_ms = 0; @@ -245,7 +239,7 @@ struct Libc::Timeout _expired = false; _absolute_timeout_ms = now + timeout_ms; - _timeout.start(_timer_accessor.timer().microseconds(timeout_ms)); + _timeout.schedule(_timer_accessor.timer().microseconds(timeout_ms)); } unsigned long duration_left() const diff --git a/repos/libports/src/lib/libc/task.h b/repos/libports/src/lib/libc/task.h index d6f1395ab..00397e283 100644 --- a/repos/libports/src/lib/libc/task.h +++ b/repos/libports/src/lib/libc/task.h @@ -21,10 +21,6 @@ #ifndef _LIBC__TASK_H_ #define _LIBC__TASK_H_ -/* Genode includes */ -#include - - namespace Libc { /** diff --git a/repos/os/include/os/duration.h b/repos/os/include/os/duration.h new file mode 100644 index 000000000..16d94b3d6 --- /dev/null +++ b/repos/os/include/os/duration.h @@ -0,0 +1,84 @@ +/* + * \brief A duration type for both highly precise and long durations + * \author Martin Stein + * \date 2017-03-21 + */ + +/* + * Copyright (C) 2016-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 _OS__DURATION_H_ +#define _OS__DURATION_H_ + +/* Genode includes */ +#include +#include + +namespace Genode { + + class Microseconds; + class Milliseconds; + class Duration; +} + + +/** + * Makes it clear that a given integer value stands for microseconds + */ +struct Genode::Microseconds +{ + unsigned long value; + + explicit Microseconds(unsigned long value) : value(value) { } +}; + + +/** + * Makes it clear that a given integer value stands for milliseconds + */ +struct Genode::Milliseconds +{ + unsigned long value; + + explicit Milliseconds(unsigned long value) : value(value) { } +}; + + +/** + * A duration type that combines high precision and large intervals + */ +struct Genode::Duration +{ + public: + + struct Overflow : Exception { }; + + private: + + enum { US_PER_MS = 1000UL }; + enum { MS_PER_HOUR = 1000UL * 60 * 60 }; + enum { US_PER_HOUR = 1000UL * 1000 * 60 * 60 }; + + unsigned long _microseconds { 0 }; + unsigned long _hours { 0 }; + + void _raise_hours(unsigned long hours); + + public: + + void operator += (Microseconds us); + void operator += (Milliseconds ms); + + bool operator < (Duration &other) const; + + explicit Duration(Milliseconds ms) { *this += ms; } + explicit Duration(Microseconds us) { *this += us; } + + Microseconds trunc_to_plain_us() const; +}; + +#endif /* _OS__DURATION_H_ */ diff --git a/repos/os/include/os/time_source.h b/repos/os/include/os/time_source.h deleted file mode 100644 index 78d77cf18..000000000 --- a/repos/os/include/os/time_source.h +++ /dev/null @@ -1,64 +0,0 @@ -/* - * \brief Interface of a time source that can handle one timeout at a time - * \author Martin Stein - * \date 2016-11-04 - */ - -/* - * Copyright (C) 2016-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 _OS__TIME_SOURCE_H_ -#define _OS__TIME_SOURCE_H_ - -namespace Genode { class Time_source; } - -/** - * Interface of a time source that can handle one timeout at a time - */ -struct Genode::Time_source -{ - /** - * Makes it clear which time unit an interfaces takes - */ - struct Microseconds - { - unsigned long value; - - explicit Microseconds(unsigned long const value) : value(value) { } - - static Microseconds max() { return Microseconds(~0UL); } - }; - - /** - * Interface of a timeout callback - */ - struct Timeout_handler - { - virtual void handle_timeout(Microseconds curr_time) = 0; - }; - - /** - * Return the current time of the source - */ - virtual Microseconds curr_time() const = 0; - - /** - * Return the maximum timeout duration that the source can handle - */ - virtual Microseconds max_timeout() const = 0; - - /** - * Install a timeout, overrides the last timeout if any - * - * \param duration timeout duration - * \param handler timeout callback - */ - virtual void schedule_timeout(Microseconds duration, - Timeout_handler &handler) = 0; -}; - -#endif /* _OS__TIME_SOURCE_H_ */ diff --git a/repos/os/include/os/timer.h b/repos/os/include/os/timer.h deleted file mode 100644 index 4f6481f2f..000000000 --- a/repos/os/include/os/timer.h +++ /dev/null @@ -1,99 +0,0 @@ -/* - * \brief Multiplexes a timer session amongst different timeouts - * \author Martin Stein - * \date 2016-11-04 - */ - -/* - * Copyright (C) 2016-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 _TIMER_H_ -#define _TIMER_H_ - -/* Genode includes */ -#include -#include -#include - -namespace Genode { - class Timer; - class Timer_time_source; -} - - -/** - * Implementation helper for 'Timer' - * - * \noapi - */ -class Genode::Timer_time_source : public Genode::Time_source -{ - private: - - enum { MIN_TIMEOUT_US = 5000 }; - - using Signal_handler = Genode::Io_signal_handler; - - ::Timer::Session &_session; - Signal_handler _signal_handler; - Timeout_handler *_handler = nullptr; - - void _handle_timeout() - { - if (_handler) - _handler->handle_timeout(curr_time()); - } - - public: - - Timer_time_source(::Timer::Session &session, Entrypoint &ep) - : - _session(session), - _signal_handler(ep, *this, &Timer_time_source::_handle_timeout) - { - _session.sigh(_signal_handler); - } - - Microseconds curr_time() const { - return Microseconds(1000UL * _session.elapsed_ms()); } - - void schedule_timeout(Microseconds duration, - Timeout_handler &handler) - { - if (duration.value < MIN_TIMEOUT_US) - duration.value = MIN_TIMEOUT_US; - - if (duration.value > max_timeout().value) - duration.value = max_timeout().value; - - _handler = &handler; - _session.trigger_once(duration.value); - } - - Microseconds max_timeout() const { return Microseconds::max(); } -}; - - -/** - * Timer-session based timeout scheduler - * - * Multiplexes a timer session amongst different timeouts. - */ -struct Genode::Timer : private Genode::Timer_time_source, - public Genode::Alarm_timeout_scheduler -{ - using Time_source::Microseconds; - using Alarm_timeout_scheduler::curr_time; - - Timer(::Timer::Session &session, Entrypoint &ep) - : - Timer_time_source(session, ep), - Alarm_timeout_scheduler(*(Time_source*)this) - { } -}; - -#endif /* _TIMER_H_ */ diff --git a/repos/os/include/os/timeout.h b/repos/os/include/timer/timeout.h similarity index 58% rename from repos/os/include/os/timeout.h rename to repos/os/include/timer/timeout.h index 4fed98d11..ac75445cd 100644 --- a/repos/os/include/os/timeout.h +++ b/repos/os/include/timer/timeout.h @@ -2,6 +2,11 @@ * \brief Multiplexing one time source amongst different timeouts * \author Martin Stein * \date 2016-11-04 + * + * These classes are not meant to be used directly. They merely exist to share + * the generic parts of timeout-scheduling between the Timer::Connection and the + * Timer driver. For user-level timeout-scheduling you should use the interface + * in timer_session/connection.h instead. */ /* @@ -11,23 +16,72 @@ * under the terms of the GNU Affero General Public License version 3. */ -#ifndef _OS__TIMEOUT_H_ -#define _OS__TIMEOUT_H_ +#ifndef _TIMER__TIMEOUT_H_ +#define _TIMER__TIMEOUT_H_ /* Genode includes */ #include -#include #include +#include +#include namespace Genode { + class Time_source; class Timeout_scheduler; class Timeout; class Alarm_timeout_scheduler; - template class Periodic_timeout; - template class One_shot_timeout; } +namespace Timer +{ + class Connection; + class Root_component; +} + + +/** + * Interface of a time source that can handle one timeout at a time + */ +struct Genode::Time_source +{ + /** + * Interface of a timeout callback + */ + struct Timeout_handler + { + virtual void handle_timeout(Duration curr_time) = 0; + }; + + /** + * Return the current time of the source + */ + virtual Duration curr_time() = 0; + + /** + * Return the maximum timeout duration that the source can handle + */ + virtual Microseconds max_timeout() const = 0; + + /** + * Install a timeout, overrides the last timeout if any + * + * \param duration timeout duration + * \param handler timeout callback + */ + virtual void schedule_timeout(Microseconds duration, + Timeout_handler &handler) = 0; + + /** + * Tell the time source which scheduler to use for its own timeouts + * + * This method enables a time source for example to synchronize with an + * accurate but expensive timer only on a periodic basis while using a + * cheaper interpolation in general. + */ + virtual void scheduler(Timeout_scheduler &scheduler) { }; +}; + /** * Interface of a time-source multiplexer @@ -38,10 +92,6 @@ namespace Genode { */ class Genode::Timeout_scheduler { - public: - - using Microseconds = Time_source::Microseconds; - private: friend Timeout; @@ -74,7 +124,7 @@ class Genode::Timeout_scheduler /** * Read out the now time of the scheduler */ - virtual Microseconds curr_time() const = 0; + virtual Duration curr_time() = 0; }; @@ -97,16 +147,11 @@ class Genode::Timeout : private Noncopyable */ struct Handler { - using Microseconds = Time_source::Microseconds; - - virtual void handle_timeout(Microseconds curr_time) = 0; + virtual void handle_timeout(Duration curr_time) = 0; }; private: - using Microseconds = Time_source::Microseconds; - - struct Alarm : Genode::Alarm { Timeout_scheduler &timeout_scheduler; @@ -137,98 +182,8 @@ class Genode::Timeout : private Noncopyable void schedule_one_shot(Microseconds duration, Handler &handler); void discard(); -}; - -/** - * Periodic timeout that is linked to a custom handler, starts when constructed - */ -template -struct Genode::Periodic_timeout : private Noncopyable -{ - public: - - using Microseconds = Timeout_scheduler::Microseconds; - - private: - - typedef void (HANDLER::*Handler_method)(Microseconds); - - Timeout _timeout; - - struct Handler : Timeout::Handler - { - HANDLER &object; - Handler_method const method; - - Handler(HANDLER &object, Handler_method method) - : object(object), method(method) { } - - - /********************** - ** Timeout::Handler ** - **********************/ - - void handle_timeout(Microseconds curr_time) override { - (object.*method)(curr_time); } - - } _handler; - - public: - - Periodic_timeout(Timeout_scheduler &timeout_scheduler, - HANDLER &object, - Handler_method method, - Microseconds duration) - : - _timeout(timeout_scheduler), _handler(object, method) - { - _timeout.schedule_periodic(duration, _handler); - } -}; - - -/** - * One-shot timeout that is linked to a custom handler, started manually - */ -template -class Genode::One_shot_timeout : private Noncopyable -{ - private: - - using Microseconds = Timeout_scheduler::Microseconds; - - typedef void (HANDLER::*Handler_method)(Microseconds); - - Timeout _timeout; - - struct Handler : Timeout::Handler - { - HANDLER &object; - Handler_method const method; - - Handler(HANDLER &object, Handler_method method) - : object(object), method(method) { } - - - /********************** - ** Timeout::Handler ** - **********************/ - - void handle_timeout(Microseconds curr_time) override { - (object.*method)(curr_time); } - - } _handler; - - public: - - One_shot_timeout(Timeout_scheduler &timeout_scheduler, - HANDLER &object, - Handler_method method) - : _timeout(timeout_scheduler), _handler(object, method) { } - - void start(Microseconds duration) { - _timeout.schedule_one_shot(duration, _handler); } + bool scheduled() { return _alarm.handler != nullptr; } }; @@ -239,17 +194,22 @@ class Genode::Alarm_timeout_scheduler : private Noncopyable, public Timeout_scheduler, public Time_source::Timeout_handler { + friend class Timer::Connection; + friend class Timer::Root_component; + private: Time_source &_time_source; Alarm_scheduler _alarm_scheduler; + void _enable(); + /********************************** ** Time_source::Timeout_handler ** **********************************/ - void handle_timeout(Microseconds curr_time) override; + void handle_timeout(Duration curr_time) override; /*********************** @@ -271,8 +231,7 @@ class Genode::Alarm_timeout_scheduler : private Noncopyable, ** Timeout_scheduler ** ***********************/ - Microseconds curr_time() const override { - return _time_source.curr_time(); } + Duration curr_time() override { return _time_source.curr_time(); } }; -#endif /* _OS__TIMEOUT_H_ */ +#endif /* _TIMER__TIMEOUT_H_ */ diff --git a/repos/os/include/timer_session/connection.h b/repos/os/include/timer_session/connection.h index b0839f4e3..c2e4cd6a9 100644 --- a/repos/os/include/timer_session/connection.h +++ b/repos/os/include/timer_session/connection.h @@ -1,5 +1,5 @@ /* - * \brief Connection to timer service + * \brief Connection to timer service and timeout scheduler * \author Norman Feske * \date 2008-08-22 */ @@ -14,16 +14,158 @@ #ifndef _INCLUDE__TIMER_SESSION__CONNECTION_H_ #define _INCLUDE__TIMER_SESSION__CONNECTION_H_ +/* Genode includes */ #include #include +#include +#include +#include +#include -namespace Timer { class Connection; } +namespace Timer +{ + class Connection; + template class Periodic_timeout; + template class One_shot_timeout; +} -class Timer::Connection : public Genode::Connection, public Session_client +/** + * Periodic timeout that is linked to a custom handler, scheduled when constructed + */ +template +struct Timer::Periodic_timeout : private Genode::Noncopyable { private: + using Duration = Genode::Duration; + using Timeout = Genode::Timeout; + using Timeout_scheduler = Genode::Timeout_scheduler; + using Microseconds = Genode::Microseconds; + + typedef void (HANDLER::*Handler_method)(Duration); + + Timeout _timeout; + + struct Handler : Timeout::Handler + { + HANDLER &object; + Handler_method const method; + + Handler(HANDLER &object, Handler_method method) + : object(object), method(method) { } + + + /********************** + ** Timeout::Handler ** + **********************/ + + void handle_timeout(Duration curr_time) override { + (object.*method)(curr_time); } + + } _handler; + + public: + + Periodic_timeout(Timeout_scheduler &timeout_scheduler, + HANDLER &object, + Handler_method method, + Microseconds duration) + : + _timeout(timeout_scheduler), _handler(object, method) + { + _timeout.schedule_periodic(duration, _handler); + } +}; + + +/** + * One-shot timeout that is linked to a custom handler, scheduled manually + */ +template +class Timer::One_shot_timeout : private Genode::Noncopyable +{ + private: + + using Duration = Genode::Duration; + using Timeout = Genode::Timeout; + using Timeout_scheduler = Genode::Timeout_scheduler; + using Microseconds = Genode::Microseconds; + + typedef void (HANDLER::*Handler_method)(Duration); + + Timeout _timeout; + + struct Handler : Timeout::Handler + { + HANDLER &object; + Handler_method const method; + + Handler(HANDLER &object, Handler_method method) + : object(object), method(method) { } + + + /********************** + ** Timeout::Handler ** + **********************/ + + void handle_timeout(Duration curr_time) override { + (object.*method)(curr_time); } + + } _handler; + + public: + + One_shot_timeout(Timeout_scheduler &timeout_scheduler, + HANDLER &object, + Handler_method method) + : _timeout(timeout_scheduler), _handler(object, method) { } + + void schedule(Microseconds duration) { + _timeout.schedule_one_shot(duration, _handler); } + + void discard() { _timeout.discard(); } + + bool scheduled() { return _timeout.scheduled(); } +}; + + +/** + * Connection to timer service and timeout scheduler + * + * Multiplexes a timer session amongst different timeouts. + */ +class Timer::Connection : public Genode::Connection, + public Session_client, + private Genode::Time_source, + public Genode::Timeout_scheduler +{ + private: + + using Timeout = Genode::Timeout; + using Timeout_handler = Genode::Time_source::Timeout_handler; + using Timestamp = Genode::Trace::Timestamp; + using Duration = Genode::Duration; + using Lock = Genode::Lock; + using Microseconds = Genode::Microseconds; + using Milliseconds = Genode::Milliseconds; + using Entrypoint = Genode::Entrypoint; + + /* + * The mode determines which interface of the timer connection is + * enabled. Initially, a timer connection is in LEGACY mode. When in + * MODERN mode, a call to the LEGACY interface causes an exception. + * When in LEGACY mode, a call to the MODERN interface causes a switch + * to the MODERN mode. The LEGACY interface is deprecated. Please + * prefer using the MODERN interface. + * + * LEGACY = Timer Session interface, blocking calls usleep and msleep + * MODERN = more precise curr_time, non-blocking and multiplexed + * handling with Periodic_timeout resp. One_shot_timeout + */ + enum Mode { LEGACY, MODERN }; + + Mode _mode { LEGACY }; Genode::Lock _lock; Genode::Signal_receiver _sig_rec; Genode::Signal_context _default_sigh_ctx; @@ -33,21 +175,88 @@ class Timer::Connection : public Genode::Connection, public Session_cli Genode::Signal_context_capability _custom_sigh_cap; + void _enable_modern_mode(); + + void _sigh(Signal_context_capability sigh) + { + Session_client::sigh(sigh); + } + + + /************************* + ** Time_source helpers ** + *************************/ + + /* + * The higher the factor shift, the more precise is the time + * interpolation but the more likely it becomes that an overflow + * would occur during calculations. In this case, the timer + * down-scales the values live which is avoidable overhead. + */ + enum { TS_TO_US_RATIO_SHIFT = 4 }; + enum { MIN_TIMEOUT_US = 5000 }; + enum { REAL_TIME_UPDATE_PERIOD_US = 500000 }; + enum { MAX_TS = ~(Timestamp)0ULL >> TS_TO_US_RATIO_SHIFT }; + enum { MAX_INTERPOLATION_QUALITY = 3 }; + enum { MAX_REMOTE_TIME_LATENCY_US = 500 }; + enum { MAX_REMOTE_TIME_TRIALS = 5 }; + + Genode::Io_signal_handler _signal_handler; + + Timeout_handler *_handler { nullptr }; + Lock _real_time_lock { Lock::UNLOCKED }; + unsigned long _ms { elapsed_ms() }; + Timestamp _ts { _timestamp() }; + Duration _real_time { Milliseconds(_ms) }; + Duration _interpolated_time { _real_time }; + unsigned _interpolation_quality { 0 }; + unsigned long _us_to_ts_factor { 1UL << TS_TO_US_RATIO_SHIFT }; + + Timestamp _timestamp(); + + void _update_interpolation_quality(unsigned long min_factor, + unsigned long max_factor); + + unsigned long _ts_to_us_ratio(Timestamp ts, unsigned long us); + + void _update_real_time(); + + Duration _update_interpolated_time(Duration &interpolated_time); + + void _handle_timeout(); + + + /***************** + ** Time_source ** + *****************/ + + void schedule_timeout(Microseconds duration, Timeout_handler &handler) override; + Microseconds max_timeout() const override { return Microseconds(REAL_TIME_UPDATE_PERIOD_US); } + + + /******************************* + ** Timeout_scheduler helpers ** + *******************************/ + + Genode::Alarm_timeout_scheduler _scheduler { *this }; + + + /*********************** + ** Timeout_scheduler ** + ***********************/ + + void _schedule_one_shot(Timeout &timeout, Microseconds duration) override; + void _schedule_periodic(Timeout &timeout, Microseconds duration) override; + void _discard(Timeout &timeout) override; + public: + struct Cannot_use_both_legacy_and_modern_interface : Genode::Exception { }; + /** * Constructor */ - Connection(Genode::Env &env, char const *label = "") - : - Genode::Connection(env, session(env.parent(), - "ram_quota=10K, cap_quota=%u, label=\"%s\"", - CAP_QUOTA, label)), - Session_client(cap()) - { - /* register default signal handler */ - Session_client::sigh(_default_sigh_cap); - } + Connection(Genode::Env &env, char const *label = ""); /** * Constructor @@ -56,28 +265,36 @@ class Timer::Connection : public Genode::Connection, public Session_cli * \deprecated Use the constructor with 'Env &' as first * argument instead */ - Connection() __attribute__((deprecated)) - : - Genode::Connection(session("ram_quota=10K")), - Session_client(cap()) - { - /* register default signal handler */ - Session_client::sigh(_default_sigh_cap); - } + Connection() __attribute__((deprecated)); ~Connection() { _sig_rec.dissolve(&_default_sigh_ctx); } /* * Intercept 'sigh' to keep track of customized signal handlers + * + * \noapi + * \deprecated Use One_shot_timeout (or Periodic_timeout) instead */ - void sigh(Signal_context_capability sigh) + void sigh(Signal_context_capability sigh) override { + if (_mode == MODERN) { + throw Cannot_use_both_legacy_and_modern_interface(); + } _custom_sigh_cap = sigh; Session_client::sigh(_custom_sigh_cap); } + /* + * Block for a time span of 'us' microseconds + * + * \noapi + * \deprecated Use One_shot_timeout (or Periodic_timeout) instead + */ void usleep(unsigned us) { + if (_mode == MODERN) { + throw Cannot_use_both_legacy_and_modern_interface(); + } /* * Omit the interaction with the timer driver for the corner case * of not sleeping at all. This corner case may be triggered when @@ -105,10 +322,26 @@ class Timer::Connection : public Genode::Connection, public Session_cli Session_client::sigh(_custom_sigh_cap); } + /* + * Block for a time span of 'ms' milliseconds + * + * \noapi + * \deprecated Use One_shot_timeout (or Periodic_timeout) instead + */ void msleep(unsigned ms) { + if (_mode == MODERN) { + throw Cannot_use_both_legacy_and_modern_interface(); + } usleep(1000*ms); } + + + /*********************************** + ** Timeout_scheduler/Time_source ** + ***********************************/ + + Duration curr_time() override; }; #endif /* _INCLUDE__TIMER_SESSION__CONNECTION_H_ */ diff --git a/repos/os/lib/mk/timeout.mk b/repos/os/lib/mk/timeout.mk index 2c489a767..eaddf2777 100644 --- a/repos/os/lib/mk/timeout.mk +++ b/repos/os/lib/mk/timeout.mk @@ -1,5 +1,10 @@ SRC_CC += timeout.cc +SRC_CC += timer_connection.cc +SRC_CC += timer_connection_timestamp.cc +SRC_CC += duration.cc LIBS += alarm +INC_DIR += $(BASE_DIR)/src/include + vpath % $(REP_DIR)/src/lib/timeout diff --git a/repos/os/run/timeout.run b/repos/os/run/timeout.run index 671bcec62..8cf04ca95 100644 --- a/repos/os/run/timeout.run +++ b/repos/os/run/timeout.run @@ -2,7 +2,28 @@ # Build # -build "core init drivers/timer test/timeout" +# +# Do not run on QEMU as its time emulation is not precise enough +# +if {[get_cmd_switch --autopilot] && [have_include "power_on/qemu"]} { + puts "\nRunning timeout test in autopilot on Qemu is not recommended.\n" + exit 0 +} + +# +# Do not run on ARM with kernels other than HW +# +# The ARM performance counter has no reliable frequency as the ARM idle command +# (often called on idle) halts the counter. Only on the HW kernel we have a syscall +# that enables us to avoid the use of the performance counter by reading the kernel +# time instead. +# +if {[expr [have_spec arm] && ![have_spec hw]]} { + puts "\n Run script is not supported on this platform.\n"; + exit 0 +} + +build "core init drivers/platform drivers/timer test/timeout test/cpufreq" # # Boot image @@ -33,7 +54,7 @@ install_config { - + } @@ -44,30 +65,8 @@ build_boot_image "core ld.lib.so init timer test-timeout" # Execution # -append qemu_args "-nographic -m 64" +append qemu_args "-nographic -m 350" -# -# We check for each timeout that has a distance of at least 200ms to each -# other timeout: -# -# 0 ms -# 0 ms -# 700 ms -> check for 700 ms -# 1000 ms -> check for 1000 ms -# 1400 ms -> check for 700 ms -# 2000 ms -# 2100 ms -# 2800 ms -> check for 700 ms -# 3000 ms -> check for 1000 ms -# 3250 ms -> check for 3250 ms -# 3500 ms -> check for 700 ms -# 4000 ms -> check for 1000 ms -# 4200 ms -> check for 700 ms -# 4900 ms -# 5000 ms -# 5200 ms -> check for 5200 ms -# 5600 ms -> check for 700 ms -# 6000 ms -> check for 1000 ms -# - -run_genode_until ".*700ms timeout.*\n.*1000ms timeout.*\n.*700ms timeout.*\n.*700ms timeout.*\n.*1000ms timeout.*\n.*3250ms timeout.*\n.*700ms timeout.*\n.*1000ms timeout.*\n.*700ms timeout.*\n.*5200ms timeout.*\n.*700ms timeout.*\n.*1000ms timeout.*\n" 20 +run_genode_until "child \"test\" exited with exit value.*\n" 900 +grep_output {\[init\] child "test" exited with exit value} +compare_output_to {[init] child "test" exited with exit value 0} diff --git a/repos/os/src/drivers/timer/fiasco/time_source.cc b/repos/os/src/drivers/timer/fiasco/time_source.cc index 35bb2fc64..91d706675 100644 --- a/repos/os/src/drivers/timer/fiasco/time_source.cc +++ b/repos/os/src/drivers/timer/fiasco/time_source.cc @@ -39,7 +39,8 @@ namespace Fiasco { #include using namespace Fiasco; -using Microseconds = Genode::Time_source::Microseconds; +using Microseconds = Genode::Microseconds; +using Duration = Genode::Duration; static l4_timeout_s mus_to_timeout(unsigned long mus) @@ -71,14 +72,14 @@ Microseconds Timer::Time_source::max_timeout() const } -Microseconds Timer::Time_source::curr_time() const +Duration Timer::Time_source::curr_time() { Genode::Lock::Guard lock_guard(_lock); static Genode::Attached_rom_dataspace kip_ds(_env, "l4v2_kip"); static Fiasco::l4_kernel_info_t * const kip = kip_ds.local_addr(); - return Microseconds(kip->clock); + return Duration(Microseconds(kip->clock)); } diff --git a/repos/os/src/drivers/timer/hw/time_source.cc b/repos/os/src/drivers/timer/hw/time_source.cc index 96c979149..722a2f9dc 100644 --- a/repos/os/src/drivers/timer/hw/time_source.cc +++ b/repos/os/src/drivers/timer/hw/time_source.cc @@ -23,7 +23,6 @@ #include using namespace Genode; -using Microseconds = Genode::Time_source::Microseconds; enum { MIN_TIMEOUT_US = 1000 }; @@ -56,14 +55,14 @@ void Timer::Time_source::schedule_timeout(Microseconds duration, } -Microseconds Timer::Time_source::curr_time() const +Duration Timer::Time_source::curr_time() { unsigned long const timeout_age_us = Kernel::timeout_age_us(); if (timeout_age_us > _last_timeout_age_us) { /* increment time by the difference since the last update */ - _curr_time_us += timeout_age_us - _last_timeout_age_us; + _curr_time += Microseconds(timeout_age_us - _last_timeout_age_us); _last_timeout_age_us = timeout_age_us; } - return Microseconds(_curr_time_us); + return _curr_time; } diff --git a/repos/os/src/drivers/timer/hw/time_source.h b/repos/os/src/drivers/timer/hw/time_source.h index 0eb29f165..73e507746 100644 --- a/repos/os/src/drivers/timer/hw/time_source.h +++ b/repos/os/src/drivers/timer/hw/time_source.h @@ -14,17 +14,26 @@ #ifndef _TIME_SOURCE_H_ #define _TIME_SOURCE_H_ +/* Genode includes */ +#include + /* local includes */ #include -namespace Timer { class Time_source; } +namespace Timer { + + using Microseconds = Genode::Microseconds; + using Duration = Genode::Duration; + class Time_source; +} class Timer::Time_source : public Genode::Signalled_time_source { private: - unsigned long mutable _curr_time_us = 0; + + Duration mutable _curr_time { Microseconds(0) }; unsigned long mutable _last_timeout_age_us = 0; unsigned long const _max_timeout_us; @@ -37,7 +46,7 @@ class Timer::Time_source : public Genode::Signalled_time_source ** Genode::Time_source ** *************************/ - Microseconds curr_time() const override; + Duration curr_time() override; void schedule_timeout(Microseconds duration, Timeout_handler &handler) override; Microseconds max_timeout() const override { return Microseconds(_max_timeout_us); }; diff --git a/repos/os/src/drivers/timer/include/root_component.h b/repos/os/src/drivers/timer/include/root_component.h index b123898ed..41045b0b7 100644 --- a/repos/os/src/drivers/timer/include/root_component.h +++ b/repos/os/src/drivers/timer/include/root_component.h @@ -56,7 +56,9 @@ class Timer::Root_component : public Genode::Root_component : Genode::Root_component(&env.ep().rpc_ep(), &md_alloc), _time_source(env), _timeout_scheduler(_time_source) - { } + { + _timeout_scheduler._enable(); + } }; #endif /* _ROOT_COMPONENT_H_ */ diff --git a/repos/os/src/drivers/timer/include/session_component.h b/repos/os/src/drivers/timer/include/session_component.h index 61ec05f44..09f1e8e2d 100644 --- a/repos/os/src/drivers/timer/include/session_component.h +++ b/repos/os/src/drivers/timer/include/session_component.h @@ -21,9 +21,14 @@ #include #include #include -#include +#include -namespace Timer { class Session_component; } +namespace Timer { + + using Microseconds = Genode::Microseconds; + using Duration = Genode::Duration; + class Session_component; +} class Timer::Session_component : public Genode::Rpc_object, @@ -36,9 +41,10 @@ class Timer::Session_component : public Genode::Rpc_object, Genode::Timeout_scheduler &_timeout_scheduler; Genode::Signal_context_capability _sigh; - unsigned long const _init_time_us = _timeout_scheduler.curr_time().value; + unsigned long const _init_time_us = + _timeout_scheduler.curr_time().trunc_to_plain_us().value; - void handle_timeout(Microseconds) { + void handle_timeout(Duration) { Genode::Signal_transmitter(_sigh).submit(); } public: @@ -65,7 +71,8 @@ class Timer::Session_component : public Genode::Rpc_object, } unsigned long elapsed_ms() const override { - return (_timeout_scheduler.curr_time().value - _init_time_us) / 1000; } + return (_timeout_scheduler.curr_time().trunc_to_plain_us().value - + _init_time_us) / 1000; } void msleep(unsigned) override { /* never called at the server side */ } void usleep(unsigned) override { /* never called at the server side */ } diff --git a/repos/os/src/drivers/timer/include/signalled_time_source.h b/repos/os/src/drivers/timer/include/signalled_time_source.h index 9a9b18888..2e7c30d05 100644 --- a/repos/os/src/drivers/timer/include/signalled_time_source.h +++ b/repos/os/src/drivers/timer/include/signalled_time_source.h @@ -16,7 +16,7 @@ /* Genode includes */ #include -#include +#include #include namespace Genode { class Signalled_time_source; } diff --git a/repos/os/src/drivers/timer/include/threaded_time_source.h b/repos/os/src/drivers/timer/include/threaded_time_source.h index 1f21d5f3f..fa13aaeaf 100644 --- a/repos/os/src/drivers/timer/include/threaded_time_source.h +++ b/repos/os/src/drivers/timer/include/threaded_time_source.h @@ -18,10 +18,12 @@ /* Genode inludes */ #include #include -#include +#include namespace Timer { + using Genode::Microseconds; + using Genode::Duration; class Threaded_time_source; } @@ -33,7 +35,7 @@ class Timer::Threaded_time_source : public Genode::Time_source, struct Irq_dispatcher { - GENODE_RPC(Rpc_do_dispatch, void, do_dispatch, Microseconds); + GENODE_RPC(Rpc_do_dispatch, void, do_dispatch, Duration); GENODE_RPC_INTERFACE(Rpc_do_dispatch); }; @@ -47,10 +49,10 @@ class Timer::Threaded_time_source : public Genode::Time_source, ** Irq_dispatcher ** ********************/ - void do_dispatch(Microseconds duration) + void do_dispatch(Duration curr_time) { if (handler) { - handler->handle_timeout(Microseconds(duration)); } + handler->handle_timeout(Duration(curr_time)); } } } _irq_dispatcher_component; diff --git a/repos/os/src/drivers/timer/linux/time_source.cc b/repos/os/src/drivers/timer/linux/time_source.cc index 0d6937289..ddaaecfb7 100644 --- a/repos/os/src/drivers/timer/linux/time_source.cc +++ b/repos/os/src/drivers/timer/linux/time_source.cc @@ -20,7 +20,6 @@ #include using namespace Genode; -using Microseconds = Genode::Time_source::Microseconds; inline int lx_gettimeofday(struct timeval *tv, struct timeval *tz) { @@ -34,11 +33,11 @@ Microseconds Timer::Time_source::max_timeout() const } -Microseconds Timer::Time_source::curr_time() const +Duration Timer::Time_source::curr_time() { struct timeval tv; lx_gettimeofday(&tv, 0); - return Microseconds(tv.tv_sec * 1000 * 1000 + tv.tv_usec); + return Duration(Microseconds(tv.tv_sec * 1000 * 1000 + tv.tv_usec)); } diff --git a/repos/os/src/drivers/timer/nova/time_source.cc b/repos/os/src/drivers/timer/nova/time_source.cc index a17fa4f76..a92ba0da1 100644 --- a/repos/os/src/drivers/timer/nova/time_source.cc +++ b/repos/os/src/drivers/timer/nova/time_source.cc @@ -23,7 +23,6 @@ using namespace Genode; using namespace Nova; -using Microseconds = Genode::Time_source::Microseconds; Timer::Time_source::Time_source(Env &env) : Threaded_time_source(env) diff --git a/repos/os/src/drivers/timer/nova/time_source.h b/repos/os/src/drivers/timer/nova/time_source.h index c775708ba..ad7794bb4 100644 --- a/repos/os/src/drivers/timer/nova/time_source.h +++ b/repos/os/src/drivers/timer/nova/time_source.h @@ -64,8 +64,8 @@ class Timer::Time_source : public Threaded_time_source Microseconds max_timeout() const override { return _tsc_to_us(~0UL); } void schedule_timeout(Microseconds duration, Timeout_handler &handler) override; - Microseconds curr_time() const override { - return _tsc_to_us(Genode::Trace::timestamp()); } + Duration curr_time() override { + return Duration(_tsc_to_us(Genode::Trace::timestamp())); } }; #endif /* _TIME_SOURCE_H_ */ diff --git a/repos/os/src/drivers/timer/periodic/time_source.cc b/repos/os/src/drivers/timer/periodic/time_source.cc index c5e81ae2e..0a67971e2 100644 --- a/repos/os/src/drivers/timer/periodic/time_source.cc +++ b/repos/os/src/drivers/timer/periodic/time_source.cc @@ -30,7 +30,7 @@ void Timer::Time_source::schedule_timeout(Microseconds duration, void Timer::Time_source::_wait_for_irq() { enum { SLEEP_GRANULARITY_US = 1000UL }; - unsigned long last_time_us = curr_time().value; + unsigned long last_time_us = curr_time().trunc_to_plain_us().value; _lock.lock(); while (_next_timeout_us > 0) { _lock.unlock(); @@ -38,7 +38,7 @@ void Timer::Time_source::_wait_for_irq() try { _usleep(SLEEP_GRANULARITY_US); } catch (Blocking_canceled) { } - unsigned long curr_time_us = curr_time().value; + unsigned long curr_time_us = curr_time().trunc_to_plain_us().value; unsigned long sleep_duration_us = curr_time_us - last_time_us; last_time_us = curr_time_us; diff --git a/repos/os/src/drivers/timer/periodic/time_source.h b/repos/os/src/drivers/timer/periodic/time_source.h index 98519bd3c..d43a47b8f 100644 --- a/repos/os/src/drivers/timer/periodic/time_source.h +++ b/repos/os/src/drivers/timer/periodic/time_source.h @@ -50,7 +50,7 @@ class Timer::Time_source : public Threaded_time_source ** Genode::Time_source ** *************************/ - Microseconds curr_time() const override; + Duration curr_time() override; Microseconds max_timeout() const override; void schedule_timeout(Microseconds duration, Timeout_handler &handler) override; }; diff --git a/repos/os/src/drivers/timer/pit/time_source.cc b/repos/os/src/drivers/timer/pit/time_source.cc index 8c1752f01..737126d10 100644 --- a/repos/os/src/drivers/timer/pit/time_source.cc +++ b/repos/os/src/drivers/timer/pit/time_source.cc @@ -16,7 +16,6 @@ #include using namespace Genode; -using Microseconds = Genode::Time_source::Microseconds; void Timer::Time_source::_set_counter(uint16_t value) @@ -67,7 +66,7 @@ void Timer::Time_source::schedule_timeout(Microseconds duration, } -Microseconds Timer::Time_source::curr_time() const +Duration Timer::Time_source::curr_time() { uint32_t passed_ticks; @@ -100,7 +99,7 @@ Microseconds Timer::Time_source::curr_time() const /* use current counter as the reference for the next update */ _counter_init_value = curr_counter; - return Microseconds(_curr_time_us); + return Duration(Microseconds(_curr_time_us)); } diff --git a/repos/os/src/drivers/timer/pit/time_source.h b/repos/os/src/drivers/timer/pit/time_source.h index 62e26bdce..0d192162c 100644 --- a/repos/os/src/drivers/timer/pit/time_source.h +++ b/repos/os/src/drivers/timer/pit/time_source.h @@ -18,11 +18,17 @@ /* Genode includes */ #include #include +#include /* local includes */ #include -namespace Timer { class Time_source; } +namespace Timer { + + using Microseconds = Genode::Microseconds; + using Duration = Genode::Duration; + class Time_source; +} class Timer::Time_source : public Genode::Signalled_time_source @@ -81,7 +87,7 @@ class Timer::Time_source : public Genode::Signalled_time_source ** Genode::Time_source ** *************************/ - Microseconds curr_time() const override; + Duration curr_time() override; void schedule_timeout(Microseconds duration, Timeout_handler &handler) override; Microseconds max_timeout() const override { return Microseconds(PIT_MAX_USEC); } diff --git a/repos/os/src/drivers/timer/target.inc b/repos/os/src/drivers/timer/target.inc index 72b8f8e65..b110dc04b 100644 --- a/repos/os/src/drivers/timer/target.inc +++ b/repos/os/src/drivers/timer/target.inc @@ -1,5 +1,5 @@ SRC_CC += main.cc -LIBS += base timeout +LIBS += base INC_DIR += $(REP_DIR)/src/drivers/timer/include vpath %.cc $(REP_DIR)/src/drivers/timer diff --git a/repos/os/src/lib/timeout/duration.cc b/repos/os/src/lib/timeout/duration.cc new file mode 100644 index 000000000..41321ebf6 --- /dev/null +++ b/repos/os/src/lib/timeout/duration.cc @@ -0,0 +1,67 @@ +/* + * \brief A duration type for both highly precise and long durations + * \author Martin Stein + * \date 2017-03-21 + */ + +/* + * Copyright (C) 2016-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 + +using namespace Genode; + + +void Duration::_raise_hours(unsigned long hours) +{ + unsigned long const old_hours = _hours; + _hours += hours; + if (_hours < old_hours) { + throw Overflow(); } +} + + +void Duration::operator += (Microseconds us) +{ + _microseconds += us.value; + if (_microseconds > US_PER_HOUR) { + _microseconds -= US_PER_HOUR; + _raise_hours(1); + } +} + + +void Duration::operator += (Milliseconds ms) +{ + /* filter out hours if any */ + if (ms.value >= MS_PER_HOUR) { + unsigned long const hours = ms.value / MS_PER_HOUR; + _raise_hours(hours); + ms.value -= hours * MS_PER_HOUR; + } + /* add the rest as microseconds value */ + *this += Microseconds(ms.value * US_PER_MS); +} + + +bool Duration::operator < (Duration &other) const +{ + if (_hours != other._hours) { + return _hours < other._hours; } + + if (_microseconds != other._microseconds) { + return _microseconds < other._microseconds; } + + return false; +} + + +Microseconds Duration::trunc_to_plain_us() const +{ + return Microseconds(_microseconds + (_hours ? _hours * US_PER_HOUR : 0)); +} diff --git a/repos/os/src/lib/timeout/hw/timer_connection_timestamp.cc b/repos/os/src/lib/timeout/hw/timer_connection_timestamp.cc new file mode 100644 index 000000000..dc2f04b04 --- /dev/null +++ b/repos/os/src/lib/timeout/hw/timer_connection_timestamp.cc @@ -0,0 +1,24 @@ +/* + * \brief Timestamp implementation for the Genode Timer + * \author Martin Stein + * \date 2016-11-04 + */ + +/* + * Copyright (C) 2016-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 + +using namespace Genode; + + +Trace::Timestamp Timer::Connection::_timestamp() +{ + return Kernel::time(); +} diff --git a/repos/os/src/lib/timeout/timeout.cc b/repos/os/src/lib/timeout/timeout.cc index b5bfd532d..4846f7a0c 100644 --- a/repos/os/src/lib/timeout/timeout.cc +++ b/repos/os/src/lib/timeout/timeout.cc @@ -12,7 +12,7 @@ */ /* Genode includes */ -#include +#include using namespace Genode; @@ -28,6 +28,7 @@ void Timeout::schedule_periodic(Microseconds duration, Handler &handler) _alarm.timeout_scheduler._schedule_periodic(*this, duration); } + void Timeout::schedule_one_shot(Microseconds duration, Handler &handler) { _alarm.handler = &handler; @@ -35,6 +36,7 @@ void Timeout::schedule_one_shot(Microseconds duration, Handler &handler) _alarm.timeout_scheduler._schedule_one_shot(*this, duration); } + void Timeout::discard() { _alarm.timeout_scheduler._discard(*this); @@ -49,24 +51,29 @@ void Timeout::discard() bool Timeout::Alarm::on_alarm(unsigned) { if (handler) { - handler->handle_timeout(timeout_scheduler.curr_time()); } + Handler *current = handler; + if (!periodic) { + handler = nullptr; + } + current->handle_timeout(timeout_scheduler.curr_time()); + } return periodic; } - /***************************** ** Alarm_timeout_scheduler ** *****************************/ -void Alarm_timeout_scheduler::handle_timeout(Microseconds curr_time) +void Alarm_timeout_scheduler::handle_timeout(Duration curr_time) { - _alarm_scheduler.handle(curr_time.value); + unsigned long const curr_time_us = curr_time.trunc_to_plain_us().value; + _alarm_scheduler.handle(curr_time_us); unsigned long sleep_time_us; Alarm::Time deadline_us; if (_alarm_scheduler.next_deadline(&deadline_us)) { - sleep_time_us = deadline_us - curr_time.value; + sleep_time_us = deadline_us - curr_time_us; } else { sleep_time_us = _time_source.max_timeout().value; } @@ -83,17 +90,21 @@ void Alarm_timeout_scheduler::handle_timeout(Microseconds curr_time) Alarm_timeout_scheduler::Alarm_timeout_scheduler(Time_source &time_source) : _time_source(time_source) +{ } + + +void Alarm_timeout_scheduler::_enable() { - time_source.schedule_timeout(Microseconds(0), *this); + _time_source.schedule_timeout(Microseconds(0), *this); } void Alarm_timeout_scheduler::_schedule_one_shot(Timeout &timeout, Microseconds duration) { - _alarm_scheduler.schedule_absolute(&timeout._alarm, - _time_source.curr_time().value + - duration.value); + _alarm_scheduler.schedule_absolute( + &timeout._alarm, + _time_source.curr_time().trunc_to_plain_us().value + duration.value); if (_alarm_scheduler.head_timeout(&timeout._alarm)) { _time_source.schedule_timeout(Microseconds(0), *this); } @@ -103,7 +114,7 @@ void Alarm_timeout_scheduler::_schedule_one_shot(Timeout &timeout, void Alarm_timeout_scheduler::_schedule_periodic(Timeout &timeout, Microseconds duration) { - _alarm_scheduler.handle(_time_source.curr_time().value); + _alarm_scheduler.handle(_time_source.curr_time().trunc_to_plain_us().value); _alarm_scheduler.schedule(&timeout._alarm, duration.value); if (_alarm_scheduler.head_timeout(&timeout._alarm)) { _time_source.schedule_timeout(Microseconds(0), *this); } diff --git a/repos/os/src/lib/timeout/timer_connection.cc b/repos/os/src/lib/timeout/timer_connection.cc new file mode 100644 index 000000000..3a3f33a96 --- /dev/null +++ b/repos/os/src/lib/timeout/timer_connection.cc @@ -0,0 +1,268 @@ +/* + * \brief Connection to timer service and timeout scheduler + * \author Martin Stein + * \date 2016-11-04 + */ + +/* + * Copyright (C) 2016-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 + +using namespace Genode; +using namespace Genode::Trace; + + +void Timer::Connection::_update_interpolation_quality(unsigned long min_factor, + unsigned long max_factor) +{ + /* + * If the factor gets adapted less than 12.5%, we raise the + * interpolation-quality value. Otherwise, we reset it to zero. + * We can safely do the shift on the factor as it should be + * at least of value 1 << TS_TO_US_RATIO_SHIFT. + */ + if ((max_factor - min_factor) < (max_factor >> 3)) { + if (_interpolation_quality < MAX_INTERPOLATION_QUALITY) { + _interpolation_quality++; } + } else if (_interpolation_quality) { + _interpolation_quality = 0; + } +} + + +unsigned long Timer::Connection::_ts_to_us_ratio(Timestamp ts, + unsigned long us) +{ + /* + * If the timestamp difference is to big to do the following + * factor calculation without an overflow, scale both timestamp + * and time difference down equally. This should neither happen + * often nor have much effect on the resulting factor. + */ + while (ts > MAX_TS) { + warning("timestamp value too big"); + ts >>= 1; + us >>= 1; + } + if (!us) { us = 1; } + if (!ts) { ts = 1; } + + /* + * To make the result more precise, we scale up the numerator of + * the calculation. This upscaling must be considered when using + * the result. + */ + Timestamp const result = (ts << TS_TO_US_RATIO_SHIFT) / us; + unsigned long const result_ul = (unsigned long)result; + if (result == result_ul) { + return result_ul; } + + warning("Timestamp-to-time ratio too big"); + return ~0UL; +} + + +void Timer::Connection::_update_real_time() +{ + Lock_guard lock_guard(_real_time_lock); + + Timestamp ts = 0UL; + unsigned long ms = 0UL; + unsigned long us_diff = ~0UL; + + for (unsigned remote_time_trials = 0; + remote_time_trials < MAX_REMOTE_TIME_TRIALS; + remote_time_trials++) + { + /* determine time and timestamp difference since the last call */ + Timestamp volatile new_ts = _timestamp(); + unsigned long volatile new_ms = elapsed_ms(); + + if (_interpolation_quality < MAX_INTERPOLATION_QUALITY) { + ms = new_ms; + ts = new_ts; + break; + } + + Timestamp const ts_diff = _timestamp() - new_ts; + unsigned long const new_us_diff = _ts_to_us_ratio(ts_diff, + _us_to_ts_factor); + + /* remember results only if the latency was better than last time */ + if (new_us_diff < us_diff) { + ms = new_ms; + ts = new_ts; + + if (us_diff < MAX_REMOTE_TIME_LATENCY_US) { + break; + } + } + } + + unsigned long const ms_diff = ms - _ms; + Timestamp const ts_diff = ts - _ts; + + /* update real time and values for next difference calculation */ + _ms = ms; + _ts = ts; + _real_time += Milliseconds(ms_diff); + + unsigned long const new_factor = _ts_to_us_ratio(ts_diff, ms_diff * 1000UL); + unsigned long const old_factor = _us_to_ts_factor; + + /* update interpolation-quality value */ + if (old_factor > new_factor) { _update_interpolation_quality(new_factor, old_factor); } + else { _update_interpolation_quality(old_factor, new_factor); } + + _us_to_ts_factor = new_factor; +} + + +Duration Timer::Connection::_update_interpolated_time(Duration &interpolated_time) +{ + /* + * The new interpolated time value may be smaller than a + * previously interpolated time value (based on an older real time + * value and factor). In this case, we don't want the user time to + * jump back but to freeze at the higher value until the new + * interpolation has caught up. + */ + if (_interpolated_time < interpolated_time) { + _interpolated_time = interpolated_time; } + + return _interpolated_time; +} + + +void Timer::Connection::_handle_timeout() +{ + unsigned long const ms = elapsed_ms(); + if (ms - _ms > REAL_TIME_UPDATE_PERIOD_US / 1000UL) { + _update_real_time(); + } + if (_handler) { + _handler->handle_timeout(Duration(Milliseconds(ms))); + } +} + + +void Timer::Connection::schedule_timeout(Microseconds duration, + Timeout_handler &handler) +{ + if (duration.value < MIN_TIMEOUT_US) + duration.value = MIN_TIMEOUT_US; + + if (duration.value > REAL_TIME_UPDATE_PERIOD_US) + duration.value = REAL_TIME_UPDATE_PERIOD_US; + + _handler = &handler; + trigger_once(duration.value); +} + + +Duration Timer::Connection::curr_time() +{ + _enable_modern_mode(); + + Reconstructible > lock_guard(_real_time_lock); + Duration interpolated_time(_real_time); + + /* + * Interpolate with timestamps only if the factor value + * remained stable for some time. If we would interpolate with + * a yet unstable factor, there's an increased risk that the + * interpolated time falsely reaches an enourmous level. Then + * the value would stand still for quite some time because we + * can't let it jump back to a more realistic level. This + * would also eliminate updates of the real time as the + * timeout scheduler that manages our update timeout also + * uses this function. + */ + if (_interpolation_quality == MAX_INTERPOLATION_QUALITY) { + + /* locally buffer real-time related members */ + unsigned long const ts = _ts; + unsigned long const us_to_ts_factor = _us_to_ts_factor; + + lock_guard.destruct(); + + /* get time difference since the last real time update */ + Timestamp const ts_diff = _timestamp() - ts; + unsigned long const us_diff = _ts_to_us_ratio(ts_diff, us_to_ts_factor); + + interpolated_time += Microseconds(us_diff); + + } else { + + /* + * Use remote timer instead of timestamps + */ + interpolated_time += Milliseconds(elapsed_ms() - _ms); + + lock_guard.destruct(); + } + return _update_interpolated_time(interpolated_time); +} + + +void Timer::Connection::_enable_modern_mode() +{ + if (_mode == MODERN) { + return; + } + _mode = MODERN; + _sigh(_signal_handler); + _scheduler._enable(); +} + + +Timer::Connection::Connection(Genode::Env &env, char const *label) +: + Genode::Connection(env, session(env.parent(), + "ram_quota=10K, cap_quota=%u, label=\"%s\"", + CAP_QUOTA, label)), + Session_client(cap()), + _signal_handler(env.ep(), *this, &Connection::_handle_timeout) +{ + /* register default signal handler */ + Session_client::sigh(_default_sigh_cap); +} + + +Timer::Connection::Connection() +: + Genode::Connection(session("ram_quota=10K")), + Session_client(cap()), + _signal_handler(internal_env().ep(), *this, &Connection::_handle_timeout) +{ + /* register default signal handler */ + Session_client::sigh(_default_sigh_cap); +} + + +void Timer::Connection::_schedule_one_shot(Timeout &timeout, Microseconds duration) +{ + _enable_modern_mode(); + _scheduler._schedule_one_shot(timeout, duration); +}; + + +void Timer::Connection::_schedule_periodic(Timeout &timeout, Microseconds duration) +{ + _enable_modern_mode(); + _scheduler._schedule_periodic(timeout, duration); +}; + + +void Timer::Connection::_discard(Timeout &timeout) +{ + _enable_modern_mode(); + _scheduler._discard(timeout); +} diff --git a/repos/os/src/lib/timeout/timer_connection_timestamp.cc b/repos/os/src/lib/timeout/timer_connection_timestamp.cc new file mode 100644 index 000000000..8e7928235 --- /dev/null +++ b/repos/os/src/lib/timeout/timer_connection_timestamp.cc @@ -0,0 +1,24 @@ +/* + * \brief Timestamp implementation for the Genode Timer + * \author Martin Stein + * \date 2016-11-04 + */ + +/* + * Copyright (C) 2016-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 + +using namespace Genode; + + +Trace::Timestamp Timer::Connection::_timestamp() +{ + return Trace::timestamp(); +} diff --git a/repos/os/src/server/input_filter/chargen_source.h b/repos/os/src/server/input_filter/chargen_source.h index 670132583..a256e141c 100644 --- a/repos/os/src/server/input_filter/chargen_source.h +++ b/repos/os/src/server/input_filter/chargen_source.h @@ -333,28 +333,28 @@ class Input_filter::Chargen_source : public Source, Source::Sink */ struct Char_repeater { - Source::Sink &_destination; - Genode::Timer &_timer; + Source::Sink &_destination; + Timer::Connection &_timer; - Time_source::Microseconds const _delay; - Time_source::Microseconds const _rate; + Microseconds const _delay; + Microseconds const _rate; Input::Event::Utf8 _curr_character { 0 }; enum State { IDLE, REPEAT } _state; - void _handle_timeout(Time_source::Microseconds) + void _handle_timeout(Duration) { if (_state == REPEAT) { _destination.submit_event(Input::Event(_curr_character)); - _timeout.start(_rate); + _timeout.schedule(_rate); } } - One_shot_timeout _timeout { + Timer::One_shot_timeout _timeout { _timer, *this, &Char_repeater::_handle_timeout }; - Char_repeater(Source::Sink &destination, Genode::Timer &timer, + Char_repeater(Source::Sink &destination, Timer::Connection &timer, Xml_node node) : _destination(destination), _timer(timer), @@ -367,7 +367,7 @@ class Input_filter::Chargen_source : public Source, Source::Sink _curr_character = character; _state = REPEAT; - _timeout.start(_delay); + _timeout.schedule(_delay); } void cancel() diff --git a/repos/os/src/server/input_filter/main.cc b/repos/os/src/server/input_filter/main.cc index f42dc4105..0209f53c8 100644 --- a/repos/os/src/server/input_filter/main.cc +++ b/repos/os/src/server/input_filter/main.cc @@ -52,10 +52,9 @@ struct Input_filter::Main : Input_connection::Avail_handler, { struct Lazy { - Timer::Connection connection; - Genode::Timer timer; + Timer::Connection timer; - Lazy(Env &env) : connection(env), timer(connection, env.ep()) { } + Lazy(Env &env) : timer(env) { } }; Env &_env; @@ -67,7 +66,7 @@ struct Input_filter::Main : Input_connection::Avail_handler, /** * Timer_accessor interface */ - Genode::Timer &timer() override + Timer::Connection &timer() override { if (!lazy.constructed()) lazy.construct(_env); diff --git a/repos/os/src/server/input_filter/target.mk b/repos/os/src/server/input_filter/target.mk index af45ee015..dc5b5350d 100644 --- a/repos/os/src/server/input_filter/target.mk +++ b/repos/os/src/server/input_filter/target.mk @@ -1,4 +1,4 @@ TARGET = input_filter SRC_CC = main.cc -LIBS = base timeout +LIBS = base INC_DIR += $(PRG_DIR) diff --git a/repos/os/src/server/input_filter/timer_accessor.h b/repos/os/src/server/input_filter/timer_accessor.h index dfecc45a3..7bfe0bef3 100644 --- a/repos/os/src/server/input_filter/timer_accessor.h +++ b/repos/os/src/server/input_filter/timer_accessor.h @@ -15,10 +15,10 @@ #define _INPUT_FILTER__TIMER_ACCESSOR_H_ /* Genode includes */ -#include +#include namespace Input_filter { struct Timer_accessor; } -struct Input_filter::Timer_accessor { virtual Genode::Timer &timer() = 0; }; +struct Input_filter::Timer_accessor { virtual Timer::Connection &timer() = 0; }; #endif /* _INPUT_FILTER__TIMER_ACCESSOR_H_ */ diff --git a/repos/os/src/server/nic_dump/component.cc b/repos/os/src/server/nic_dump/component.cc index 4a6af43e2..fc67efce5 100644 --- a/repos/os/src/server/nic_dump/component.cc +++ b/repos/os/src/server/nic_dump/component.cc @@ -54,17 +54,17 @@ Session_component_base(Allocator &guarded_alloc_backing, ** Session_component ** ***********************/ -Net::Session_component::Session_component(Allocator &alloc, - size_t const amount, - Ram_session &buf_ram, - size_t const tx_buf_size, - size_t const rx_buf_size, - Region_map ®ion_map, - Uplink &uplink, - Xml_node config, - Genode::Timer &timer, - unsigned &curr_time, - Entrypoint &ep) +Net::Session_component::Session_component(Allocator &alloc, + size_t const amount, + Ram_session &buf_ram, + size_t const tx_buf_size, + size_t const rx_buf_size, + Region_map ®ion_map, + Uplink &uplink, + Xml_node config, + Timer::Connection &timer, + unsigned &curr_time, + Entrypoint &ep) : Session_component_base(alloc, amount, buf_ram, tx_buf_size, rx_buf_size), Session_rpc_object(region_map, _tx_buf, _rx_buf, &_range_alloc, ep.rpc_ep()), @@ -99,14 +99,14 @@ void Session_component::link_state_sigh(Signal_context_capability sigh) ** Root ** **********/ -Net::Root::Root(Entrypoint &ep, - Allocator &alloc, - Uplink &uplink, - Ram_session &buf_ram, - Xml_node config, - Genode::Timer &timer, - unsigned &curr_time, - Region_map ®ion_map) +Net::Root::Root(Entrypoint &ep, + Allocator &alloc, + Uplink &uplink, + Ram_session &buf_ram, + Xml_node config, + Timer::Connection &timer, + unsigned &curr_time, + Region_map ®ion_map) : Root_component(&ep.rpc_ep(), &alloc), diff --git a/repos/os/src/server/nic_dump/component.h b/repos/os/src/server/nic_dump/component.h index 9e08745c3..1ebbcab1e 100644 --- a/repos/os/src/server/nic_dump/component.h +++ b/repos/os/src/server/nic_dump/component.h @@ -93,7 +93,7 @@ class Net::Session_component : public Session_component_base, Genode::Region_map ®ion_map, Uplink &uplink, Genode::Xml_node config, - Genode::Timer &timer, + Timer::Connection &timer, unsigned &curr_time, Genode::Entrypoint &ep); @@ -118,7 +118,7 @@ class Net::Root : public Genode::Root_component #include -#include +#include namespace Net { @@ -49,7 +49,7 @@ class Net::Interface Genode::Allocator &_alloc; Pointer _remote; Interface_label _label; - Genode::Timer &_timer; + Timer::Connection &_timer; unsigned &_curr_time; bool _log_time; @@ -77,7 +77,7 @@ class Net::Interface Interface(Genode::Entrypoint &ep, Interface_label label, - Genode::Timer &timer, + Timer::Connection &timer, unsigned &curr_time, bool log_time, Genode::Allocator &alloc); diff --git a/repos/os/src/server/nic_dump/main.cc b/repos/os/src/server/nic_dump/main.cc index 193e25706..f225aa07b 100644 --- a/repos/os/src/server/nic_dump/main.cc +++ b/repos/os/src/server/nic_dump/main.cc @@ -30,8 +30,7 @@ class Main private: Genode::Attached_rom_dataspace _config; - Timer::Connection _timer_connection; - Genode::Timer _timer; + Timer::Connection _timer; unsigned _curr_time { 0 }; Genode::Heap _heap; Uplink _uplink; @@ -45,8 +44,7 @@ class Main Main::Main(Env &env) : - _config(env, "config"), _timer_connection(env), - _timer(_timer_connection, env.ep()), _heap(&env.ram(), &env.rm()), + _config(env, "config"), _timer(env), _heap(&env.ram(), &env.rm()), _uplink(env, _config.xml(), _timer, _curr_time, _heap), _root(env.ep(), _heap, _uplink, env.ram(), _config.xml(), _timer, _curr_time, env.rm()) diff --git a/repos/os/src/server/nic_dump/target.mk b/repos/os/src/server/nic_dump/target.mk index a8e7ae9f8..7737008c7 100644 --- a/repos/os/src/server/nic_dump/target.mk +++ b/repos/os/src/server/nic_dump/target.mk @@ -1,6 +1,6 @@ TARGET = nic_dump -LIBS += base net config timeout +LIBS += base net config SRC_CC += component.cc main.cc uplink.cc interface.cc diff --git a/repos/os/src/server/nic_dump/uplink.cc b/repos/os/src/server/nic_dump/uplink.cc index 2d92f669b..82f124c88 100644 --- a/repos/os/src/server/nic_dump/uplink.cc +++ b/repos/os/src/server/nic_dump/uplink.cc @@ -22,11 +22,11 @@ using namespace Net; using namespace Genode; -Net::Uplink::Uplink(Env &env, - Xml_node config, - Genode::Timer &timer, - unsigned &curr_time, - Allocator &alloc) +Net::Uplink::Uplink(Env &env, + Xml_node config, + Timer::Connection &timer, + unsigned &curr_time, + Allocator &alloc) : Nic::Packet_allocator(&alloc), Nic::Connection(env, this, BUF_SIZE, BUF_SIZE), diff --git a/repos/os/src/server/nic_dump/uplink.h b/repos/os/src/server/nic_dump/uplink.h index ec6687057..b81339e8f 100644 --- a/repos/os/src/server/nic_dump/uplink.h +++ b/repos/os/src/server/nic_dump/uplink.h @@ -46,7 +46,7 @@ class Net::Uplink : public Nic::Packet_allocator, Uplink(Genode::Env &env, Genode::Xml_node config, - Genode::Timer &timer, + Timer::Connection &timer, unsigned &curr_time, Genode::Allocator &alloc); }; diff --git a/repos/os/src/server/nic_router/component.cc b/repos/os/src/server/nic_router/component.cc index a8bd64c87..3ab9ec0ac 100644 --- a/repos/os/src/server/nic_router/component.cc +++ b/repos/os/src/server/nic_router/component.cc @@ -55,7 +55,7 @@ Session_component_base(Allocator &guarded_alloc_backing, ***********************/ Net::Session_component::Session_component(Allocator &alloc, - Genode::Timer &timer, + Timer::Connection &timer, size_t const amount, Ram_session &buf_ram, size_t const tx_buf_size, @@ -95,7 +95,7 @@ void Session_component::link_state_sigh(Signal_context_capability sigh) **********/ Net::Root::Root(Entrypoint &ep, - Genode::Timer &timer, + Timer::Connection &timer, Allocator &alloc, Mac_address const &router_mac, Configuration &config, diff --git a/repos/os/src/server/nic_router/component.h b/repos/os/src/server/nic_router/component.h index ba2c03117..f47dbfda3 100644 --- a/repos/os/src/server/nic_router/component.h +++ b/repos/os/src/server/nic_router/component.h @@ -84,7 +84,7 @@ class Net::Session_component : public Session_component_base, public: Session_component(Genode::Allocator &alloc, - Genode::Timer &timer, + Timer::Connection &timer, Genode::size_t const amount, Genode::Ram_session &buf_ram, Genode::size_t const tx_buf_size, @@ -110,7 +110,7 @@ class Net::Root : public Genode::Root_component { private: - Genode::Timer &_timer; + Timer::Connection &_timer; Mac_allocator _mac_alloc; Genode::Entrypoint &_ep; Mac_address const _router_mac; @@ -128,7 +128,7 @@ class Net::Root : public Genode::Root_component public: Root(Genode::Entrypoint &ep, - Genode::Timer &timer, + Timer::Connection &timer, Genode::Allocator &alloc, Mac_address const &router_mac, Configuration &config, diff --git a/repos/os/src/server/nic_router/interface.cc b/repos/os/src/server/nic_router/interface.cc index 16fb4446c..e45a81264 100644 --- a/repos/os/src/server/nic_router/interface.cc +++ b/repos/os/src/server/nic_router/interface.cc @@ -570,7 +570,7 @@ void Interface::_send(Ethernet_frame ð, Genode::size_t const size) Interface::Interface(Entrypoint &ep, - Genode::Timer &timer, + Timer::Connection &timer, Mac_address const router_mac, Genode::Allocator &alloc, Mac_address const mac, diff --git a/repos/os/src/server/nic_router/interface.h b/repos/os/src/server/nic_router/interface.h index 3898e9a2d..683416304 100644 --- a/repos/os/src/server/nic_router/interface.h +++ b/repos/os/src/server/nic_router/interface.h @@ -52,7 +52,7 @@ class Net::Interface private: - Genode::Timer &_timer; + Timer::Connection &_timer; Genode::Allocator &_alloc; Domain &_domain; Arp_cache _arp_cache; @@ -150,7 +150,7 @@ class Net::Interface struct Packet_postponed : Genode::Exception { }; Interface(Genode::Entrypoint &ep, - Genode::Timer &timer, + Timer::Connection &timer, Mac_address const router_mac, Genode::Allocator &alloc, Mac_address const mac, diff --git a/repos/os/src/server/nic_router/link.cc b/repos/os/src/server/nic_router/link.cc index f4cb023d4..a1ec89425 100644 --- a/repos/os/src/server/nic_router/link.cc +++ b/repos/os/src/server/nic_router/link.cc @@ -115,7 +115,7 @@ Link::Link(Interface &cln_interface, Pointer const srv_port_alloc, Interface &srv_interface, Link_side_id const &srv_id, - Genode::Timer &timer, + Timer::Connection &timer, Configuration &config, uint8_t const protocol) : @@ -127,11 +127,11 @@ Link::Link(Interface &cln_interface, _close_timeout_us(_config.rtt_sec() * 2 * 1000 * 1000), _protocol(protocol) { - _close_timeout.start(_close_timeout_us); + _close_timeout.schedule(_close_timeout_us); } -void Link::_handle_close_timeout(Genode::Timer::Microseconds) +void Link::_handle_close_timeout(Duration) { dissolve(); _client._interface.link_closed(*this, _protocol); @@ -167,7 +167,7 @@ Tcp_link::Tcp_link(Interface &cln_interface, Pointer const srv_port_alloc, Interface &srv_interface, Link_side_id const &srv_id, - Genode::Timer &timer, + Timer::Connection &timer, Configuration &config, uint8_t const protocol) : @@ -179,7 +179,7 @@ Tcp_link::Tcp_link(Interface &cln_interface, void Tcp_link::_fin_acked() { if (_server_fin_acked && _client_fin_acked) { - _close_timeout.start(_close_timeout_us); + _close_timeout.schedule(_close_timeout_us); _closed = true; } } @@ -228,7 +228,7 @@ Udp_link::Udp_link(Interface &cln_interface, Pointer const srv_port_alloc, Interface &srv_interface, Link_side_id const &srv_id, - Genode::Timer &timer, + Timer::Connection &timer, Configuration &config, uint8_t const protocol) : diff --git a/repos/os/src/server/nic_router/link.h b/repos/os/src/server/nic_router/link.h index 44492722f..56f5c5e2d 100644 --- a/repos/os/src/server/nic_router/link.h +++ b/repos/os/src/server/nic_router/link.h @@ -15,7 +15,7 @@ #define _LINK_H_ /* Genode includes */ -#include +#include #include #include #include @@ -129,13 +129,13 @@ class Net::Link : public Link_list::Element Link_side _client; Pointer const _server_port_alloc; Link_side _server; - Genode::One_shot_timeout _close_timeout; - Genode::Timer::Microseconds const _close_timeout_us; + Timer::One_shot_timeout _close_timeout; + Genode::Microseconds const _close_timeout_us; Genode::uint8_t const _protocol; - void _handle_close_timeout(Genode::Timer::Microseconds); + void _handle_close_timeout(Genode::Duration); - void _packet() { _close_timeout.start(_close_timeout_us); } + void _packet() { _close_timeout.schedule(_close_timeout_us); } public: @@ -146,7 +146,7 @@ class Net::Link : public Link_list::Element Pointer const srv_port_alloc, Interface &srv_interface, Link_side_id const &srv_id, - Genode::Timer &timer, + Timer::Connection &timer, Configuration &config, Genode::uint8_t const protocol); @@ -188,7 +188,7 @@ class Net::Tcp_link : public Link Pointer const srv_port_alloc, Interface &srv_interface, Link_side_id const &srv_id, - Genode::Timer &timer, + Timer::Connection &timer, Configuration &config, Genode::uint8_t const protocol); @@ -205,7 +205,7 @@ struct Net::Udp_link : Link Pointer const srv_port_alloc, Interface &srv_interface, Link_side_id const &srv_id, - Genode::Timer &timer, + Timer::Connection &timer, Configuration &config, Genode::uint8_t const protocol); diff --git a/repos/os/src/server/nic_router/main.cc b/repos/os/src/server/nic_router/main.cc index 3c2720434..76de05c23 100644 --- a/repos/os/src/server/nic_router/main.cc +++ b/repos/os/src/server/nic_router/main.cc @@ -15,7 +15,6 @@ #include #include #include -#include #include #include @@ -32,8 +31,7 @@ class Main { private: - Timer::Connection _timer_connection; - Genode::Timer _timer; + Timer::Connection _timer; Genode::Heap _heap; Genode::Attached_rom_dataspace _config_rom; Configuration _config; @@ -48,12 +46,10 @@ class Main Main::Main(Env &env) : - _timer_connection(env), _timer(_timer_connection, env.ep()), - _heap(&env.ram(), &env.rm()), _config_rom(env, "config"), - _config(_config_rom.xml(), _heap), - _uplink(env, _timer, _heap, _config), - _root(env.ep(), _timer, _heap, _uplink.router_mac(), _config, env.ram(), - env.rm()) + _timer(env), _heap(&env.ram(), &env.rm()), _config_rom(env, "config"), + _config(_config_rom.xml(), _heap), _uplink(env, _timer, _heap, _config), + _root(env.ep(), _timer, _heap, _uplink.router_mac(), _config, + env.ram(), env.rm()) { env.parent().announce(env.ep().manage(_root)); } diff --git a/repos/os/src/server/nic_router/target.mk b/repos/os/src/server/nic_router/target.mk index aa1e192c8..3404d8d9a 100644 --- a/repos/os/src/server/nic_router/target.mk +++ b/repos/os/src/server/nic_router/target.mk @@ -1,6 +1,6 @@ TARGET = nic_router -LIBS += base net config timeout +LIBS += base net config SRC_CC += arp_waiter.cc ip_rule.cc SRC_CC += component.cc port_allocator.cc forward_rule.cc diff --git a/repos/os/src/server/nic_router/uplink.cc b/repos/os/src/server/nic_router/uplink.cc index d2d5bdf99..61de2afc4 100644 --- a/repos/os/src/server/nic_router/uplink.cc +++ b/repos/os/src/server/nic_router/uplink.cc @@ -24,7 +24,7 @@ using namespace Genode; Net::Uplink::Uplink(Env &env, - Genode::Timer &timer, + Timer::Connection &timer, Genode::Allocator &alloc, Configuration &config) : diff --git a/repos/os/src/server/nic_router/uplink.h b/repos/os/src/server/nic_router/uplink.h index d6358c4a1..0d6165fdc 100644 --- a/repos/os/src/server/nic_router/uplink.h +++ b/repos/os/src/server/nic_router/uplink.h @@ -47,7 +47,7 @@ class Net::Uplink : public Nic::Packet_allocator, public: Uplink(Genode::Env &env, - Genode::Timer &timer, + Timer::Connection &timer, Genode::Allocator &alloc, Configuration &config); diff --git a/repos/os/src/test/timeout/main.cc b/repos/os/src/test/timeout/main.cc index 107f8d316..d9c79be82 100644 --- a/repos/os/src/test/timeout/main.cc +++ b/repos/os/src/test/timeout/main.cc @@ -13,42 +13,553 @@ /* Genode includes */ #include +#include #include -#include - +#include +#include using namespace Genode; -class Main +struct Test { - private: + Env &env; + unsigned &error_cnt; + Signal_transmitter done; + unsigned id; + Timer::Connection timer_connection { env }; + Timer::Connection timer { env }; - using Microseconds = Genode::Timer::Microseconds; + Test(Env &env, + unsigned &error_cnt, + Signal_context_capability done, + unsigned id, + char const *brief) + : + env(env), error_cnt(error_cnt), done(done), id(id) + { + /* + * FIXME Activate interpolation early to give it some time to + * calibrate. Otherwise, we may get non-representative + * results in at least the fast-polling test, which starts + * directly with the heaviest load. This is only necessary + * because Timer::Connection by now must be backwards compatible + * and therefore starts interpolation only on demand. + */ + timer.curr_time(); - void _handle(Microseconds now, Cstring name) { - log(now.value / 1000, " ms: ", name, " timeout triggered"); } + log("\nTEST ", id, ": ", brief, "\n"); + } - void _handle_pt1(Microseconds now) { _handle(now, "Periodic 700ms"); } - void _handle_pt2(Microseconds now) { _handle(now, "Periodic 1000ms"); } - void _handle_ot1(Microseconds now) { _handle(now, "One-shot 3250ms"); } - void _handle_ot2(Microseconds now) { _handle(now, "One-shot 5200ms"); } + float percentage(unsigned long value, unsigned long base) + { + /* + * FIXME When base = 0 and value != 0, we normally want to return + * FLT_MAX but this causes a compile error. Thus, we use a + * pretty high value instead. + */ + return base ? ((float)value / base * 100) : value ? 1000000 : 0; + } - Timer::Connection _timer_connection; - Genode::Timer _timer; - Periodic_timeout
_pt1 { _timer, *this, &Main::_handle_pt1, Microseconds(700000) }; - Periodic_timeout
_pt2 { _timer, *this, &Main::_handle_pt2, Microseconds(1000000) }; - One_shot_timeout
_ot1 { _timer, *this, &Main::_handle_ot1 }; - One_shot_timeout
_ot2 { _timer, *this, &Main::_handle_ot2 }; + ~Test() { log("\nTEST ", id, " finished\n"); } +}; - public: - Main(Env &env) : _timer_connection(env), - _timer(_timer_connection, env.ep()) - { - _ot1.start(Microseconds(3250000)); - _ot2.start(Microseconds(5300000)); +struct Mixed_timeouts : Test +{ + static constexpr char const *brief = "schedule multiple timeouts simultaneously"; + + enum { NR_OF_EVENTS = 20 }; + enum { NR_OF_TIMEOUTS = 4 }; + enum { MAX_ERROR_PC = 10 }; + + struct Timeout + { + char const *const name; + Microseconds const us; + }; + + struct Event + { + Timeout const *const timeout; + Duration const time; + }; + + Timeout const timeouts[NR_OF_TIMEOUTS] { + { "Periodic 700 ms", Microseconds( 700000) }, + { "Periodic 1000 ms", Microseconds(1000000) }, + { "One-shot 3250 ms", Microseconds(3250000) }, + { "One-shot 5200 ms", Microseconds(5200000) } + }; + + /* + * We want to check only timeouts that have a distance of at least + * 200ms to each other timeout. Thus, the items in this array that + * have an empty name are treated as wildcards and match any timeout. + */ + Event const events[NR_OF_EVENTS] { + { nullptr, Duration(Milliseconds(0)) }, + { nullptr, Duration(Milliseconds(0)) }, + { &timeouts[0], Duration(Milliseconds(700)) }, + { &timeouts[1], Duration(Milliseconds(1000)) }, + { &timeouts[0], Duration(Milliseconds(1400)) }, + { nullptr, Duration(Milliseconds(2000)) }, + { nullptr, Duration(Milliseconds(2100)) }, + { &timeouts[0], Duration(Milliseconds(2800)) }, + { &timeouts[1], Duration(Milliseconds(3000)) }, + { &timeouts[2], Duration(Milliseconds(3250)) }, + { &timeouts[0], Duration(Milliseconds(3500)) }, + { &timeouts[1], Duration(Milliseconds(4000)) }, + { &timeouts[0], Duration(Milliseconds(4200)) }, + { nullptr, Duration(Milliseconds(4900)) }, + { nullptr, Duration(Milliseconds(5000)) }, + { &timeouts[3], Duration(Milliseconds(5200)) }, + { &timeouts[0], Duration(Milliseconds(5600)) }, + { &timeouts[1], Duration(Milliseconds(6000)) }, + { &timeouts[0], Duration(Milliseconds(6300)) }, + { &timeouts[2], Duration(Milliseconds(6500)) } + }; + + Duration init_time { Microseconds(0) }; + unsigned event_id { 0 }; + + Timer::Periodic_timeout pt1 { timer, *this, &Mixed_timeouts::handle_pt1, timeouts[0].us }; + Timer::Periodic_timeout pt2 { timer, *this, &Mixed_timeouts::handle_pt2, timeouts[1].us }; + Timer::One_shot_timeout ot1 { timer, *this, &Mixed_timeouts::handle_ot1 }; + Timer::One_shot_timeout ot2 { timer, *this, &Mixed_timeouts::handle_ot2 }; + + void handle_pt1(Duration time) { handle(time, timeouts[0]); } + void handle_pt2(Duration time) { handle(time, timeouts[1]); } + void handle_ot1(Duration time) { handle(time, timeouts[2]); ot1.schedule(timeouts[2].us); } + void handle_ot2(Duration time) { handle(time, timeouts[3]); } + + void handle(Duration time, Timeout const &timeout) + { + if (event_id == NR_OF_EVENTS) { + return; } + + if (!event_id) { + init_time = time; } + + Event const &event = events[event_id++]; + unsigned long time_us = time.trunc_to_plain_us().value - + init_time.trunc_to_plain_us().value; + + unsigned long event_time_us = event.time.trunc_to_plain_us().value; + unsigned long error_us = max(time_us, event_time_us) - + min(time_us, event_time_us); + + float const error_pc = percentage(error_us, timeout.us.value); + + log(time_us / 1000UL, " ms: ", timeout.name, " timeout triggered," + " error ", error_us, " us (", error_pc, " %)"); + + if (error_pc > MAX_ERROR_PC) { + + error("absolute timeout error greater than ", (unsigned)MAX_ERROR_PC, " %"); + error_cnt++; } + if (event.timeout && event.timeout != &timeout) { + + error("expected timeout ", timeout.name); + error_cnt++; + } + if (event_id == NR_OF_EVENTS) { + done.submit(); } + } + + Mixed_timeouts(Env &env, + unsigned &error_cnt, + Signal_context_capability done, + unsigned id) + : + Test(env, error_cnt, done, id, brief) + { + ot1.schedule(timeouts[2].us); + ot2.schedule(timeouts[3].us); + } +}; + + +struct Fast_polling : Test +{ + static constexpr char const *brief = "poll time pretty fast"; + + enum { NR_OF_ROUNDS = 4 }; + enum { MIN_ROUND_DURATION_MS = 2000 }; + enum { MAX_NR_OF_POLLS = 10000000 }; + enum { MIN_NR_OF_POLLS = 1000 }; + enum { STACK_SIZE = 4 * 1024 * sizeof(addr_t) }; + enum { MIN_TIME_COMPARISONS = 100 }; + enum { MAX_TIME_ERR_US = 10000 }; + enum { MAX_AVG_TIME_ERR_US = 1000 }; + enum { MAX_DELAY_ERR_US = 2000 }; + enum { MAX_AVG_DELAY_ERR_US = 20 }; + enum { MAX_POLL_LATENCY_US = 1000 }; + enum { BUF_SIZE = MAX_NR_OF_POLLS * sizeof(unsigned long) }; + + Entrypoint main_ep; + Signal_handler main_handler; + + Attached_ram_dataspace local_us_buf_1 { env.ram(), env.rm(), BUF_SIZE }; + Attached_ram_dataspace local_us_buf_2 { env.ram(), env.rm(), BUF_SIZE }; + Attached_ram_dataspace remote_ms_buf { env.ram(), env.rm(), BUF_SIZE }; + unsigned long volatile *local_us_1 { local_us_buf_1.local_addr() }; + unsigned long volatile *local_us_2 { local_us_buf_2.local_addr() }; + unsigned long volatile *remote_ms { remote_ms_buf.local_addr() }; + + unsigned const delay_loops_per_poll[NR_OF_ROUNDS] { 1, + 1000, + 10000, + 100000 }; + + /* + * Accumulates great amounts of integer values to one average value + * + * Aims for best possible precision with a fixed amount of integer buffers + */ + struct Average_accumulator + { + private: + + unsigned long _avg { 0 }; + unsigned long _avg_cnt { 0 }; + unsigned long _acc { 0 }; + unsigned long _acc_cnt { 0 }; + + public: + + void flush() + { + unsigned long acc_avg = _acc / _acc_cnt; + if (!_avg_cnt) { _avg = _avg + acc_avg; } + else { + float acc_fac = (float)_acc_cnt / _avg_cnt; + _avg = (_avg + ((float)acc_fac * acc_avg)) / (1 + acc_fac); + } + _avg_cnt += _acc_cnt; + _acc = 0; + _acc_cnt = 0; + } + + void add(unsigned long add) + { + if (add > (~0UL - _acc)) { + flush(); } + _acc += add; + _acc_cnt++; + } + + unsigned long avg() + { + if (_acc_cnt) { + flush(); } + return _avg; + } + + unsigned long avg_cnt() + { + if (_acc_cnt) { + flush(); } + return _avg_cnt; + } + }; + + unsigned long delay_us(unsigned poll) + { + return local_us_1[poll - 1] > local_us_1[poll] ? + local_us_1[poll - 1] - local_us_1[poll] : + local_us_1[poll] - local_us_1[poll - 1]; + } + + unsigned long estimate_delay_loops_per_ms() + { + log("estimate CPU speed ..."); + for (unsigned long max_cnt = 1000UL * 1000UL; ; max_cnt *= 2) { + + /* measure consumed time of a limited busy loop */ + unsigned long volatile start_ms = timer_connection.elapsed_ms(); + for (unsigned long volatile cnt = 0; cnt < max_cnt; cnt++) { } + unsigned long volatile end_ms = timer_connection.elapsed_ms(); + + /* + * We only return the result if the loop was time intensive enough + * and therefore representative. Otherwise we raise the loop- + * counter limit and do a new estimation. + */ + unsigned long diff_ms = end_ms - start_ms; + if (diff_ms > 1000UL) { + return max_cnt / diff_ms; } + } + } + + void main() + { + /* + * Estimate CPU speed + * + * The test delays must be done through busy spinning. If we would + * use a timer session instead, we could not produce delays of only a + * few microseconds. Thus, to get similar delays on each platform we + * have to do this estimation. + */ + unsigned long volatile delay_loops_per_remote_poll = + estimate_delay_loops_per_ms() / 100; + + /* do several rounds of the test with different parameters each */ + for (unsigned round = 0; round < NR_OF_ROUNDS; round++) { + + /* print round parameters */ + log(""); + log("--- Round ", round + 1, + ": polling delay ", delay_loops_per_poll[round], " loop(s) ---"); + log(""); + + unsigned long volatile delay_loops = 0; + + unsigned long nr_of_polls = MAX_NR_OF_POLLS; + unsigned long delay_loops_per_poll_ = delay_loops_per_poll[round]; + unsigned long end_remote_ms = timer_connection.elapsed_ms() + + MIN_ROUND_DURATION_MS; + + /* limit polling to our buffer capacity */ + for (unsigned poll = 0; poll < nr_of_polls; poll++) { + + /* create delay between two polls */ + for (unsigned long volatile i = 0; i < delay_loops_per_poll_; i++) { } + + /* count delay loops to limit frequency of remote time reading */ + delay_loops += delay_loops_per_poll_; + + /* + * We buffer the results in local variables first so the RAM + * access wont raise the delay between the reading of the + * different time values. + */ + unsigned long volatile local_us_1_; + unsigned long volatile local_us_2_; + unsigned long volatile remote_ms_; + + /* read local time before the remote time reading */ + local_us_1_ = timer.curr_time().trunc_to_plain_us().value; + + /* + * Limit frequency of remote-time reading + * + * If we would stress the timer connection to much, the + * back-end functionality of the timeout framework would + * remarkably slow down which causes a phase of adaption with + * bigger errors. But the goal of the framework is to spare + * calls to timer connections anyway. So, its fine to limit + * the polling frequency here. + */ + if (delay_loops > delay_loops_per_remote_poll) { + + /* read remote time and second local time */ + remote_ms_ = timer_connection.elapsed_ms(); + local_us_2_ = timer.curr_time().trunc_to_plain_us().value; + + /* reset delay counter for remote-time reading */ + delay_loops = 0; + + } else { + + /* mark remote-time and second local-time value invalid */ + remote_ms_ = 0; + local_us_2_ = 0; + } + /* store results to the buffers */ + remote_ms[poll] = remote_ms_; + local_us_1[poll] = local_us_1_; + local_us_2[poll] = local_us_2_; + + /* if the minimum round duration is reached, end polling */ + if (remote_ms_ > end_remote_ms) { + nr_of_polls = poll + 1; + break; + } + } + + /* + * Mark results with a bad latency dismissed + * + * It might be, that we got scheduled away between reading out + * local and remote time. This would cause the test result to + * be much worse than the actual precision of the timeout + * framework. Thus, we ignore such results. + */ + unsigned nr_of_good_polls = 0; + unsigned nr_of_bad_polls = 0; + for (unsigned poll = 0; poll < nr_of_polls; poll++) { + + if (remote_ms[poll] && + local_us_2[poll] - local_us_1[poll] > MAX_POLL_LATENCY_US) + { + local_us_1[poll] = 0; + nr_of_bad_polls++; + + } else { + + nr_of_good_polls++; } + } + + /* + * Calculate the average delay between consecutive polls + * (using the local time). + */ + Average_accumulator avg_delay_us; + for (unsigned poll = 1; poll < nr_of_polls; poll++) { + + /* skip if this result was dismissed */ + if (!local_us_1[poll]) { + poll++; + continue; + } + /* check if local time is monotone */ + if (local_us_1[poll - 1] > local_us_1[poll]) { + + error("time is not monotone at poll #", poll); + error_cnt++; + } + /* check out delay between this poll and the last one */ + avg_delay_us.add(delay_us(poll)); + } + + /* + * Calculate the average and maximum error of the local time + * compared to the remote time as well as the duration of the + * whole test round. + */ + Average_accumulator avg_time_err_us; + unsigned long max_time_err_us = 0UL; + + for (unsigned poll = 0; poll < nr_of_polls; poll++) { + + /* skip if this result was dismissed */ + if (!local_us_1[poll]) { + continue; } + + /* skip if this poll contains no remote time */ + if (!remote_ms[poll]) { + continue; } + + /* scale-up remote time to microseconds and calculate error */ + if (remote_ms[poll] > ~0UL / 1000UL) { + error("can not translate remote time to microseconds"); + error_cnt++; + } + unsigned long const remote_us = remote_ms[poll] * 1000UL; + unsigned long const time_err_us = remote_us > local_us_1[poll] ? + remote_us - local_us_1[poll] : + local_us_1[poll] - remote_us; + + /* update max time error */ + if (time_err_us > max_time_err_us) { + max_time_err_us = time_err_us; } + + /* update average time error */ + avg_time_err_us.add(time_err_us); + } + Average_accumulator avg_delay_err_us; + unsigned long avg_delay_us_ = avg_delay_us.avg(); + + /* + * Calculate the average error of the delays compared to the + * average delay (in microseconds and percent of the average + * delay). + */ + unsigned long max_delay_err_us = 0; + for (unsigned poll = 1; poll < nr_of_polls; poll++) { + + /* skip if this result was dismissed */ + if (!local_us_1[poll]) { + poll++; + continue; + } + + unsigned long delay_us_ = delay_us(poll); + unsigned long delay_err_us = delay_us_ > avg_delay_us_ ? + delay_us_ - avg_delay_us_ : + avg_delay_us_ - delay_us_; + + if (delay_err_us > max_delay_err_us) { + max_delay_err_us = delay_err_us; } + + avg_delay_err_us.add(delay_err_us); + } + + unsigned long const max_avg_delay_err_us = (unsigned long)MAX_AVG_DELAY_ERR_US + + avg_delay_us_ / 20; + + bool const error_nr_of_good_polls = (nr_of_good_polls < MIN_NR_OF_POLLS); + bool const error_nr_of_time_cmprs = (avg_time_err_us.avg_cnt() < MIN_TIME_COMPARISONS); + bool const error_avg_time_err = (avg_time_err_us.avg() > MAX_AVG_TIME_ERR_US); + bool const error_max_time_err = (max_time_err_us > MAX_TIME_ERR_US); + bool const error_avg_delay_err = (avg_delay_err_us.avg() > max_avg_delay_err_us); + + error_cnt += error_nr_of_good_polls; + error_cnt += error_nr_of_time_cmprs; + error_cnt += error_avg_time_err; + error_cnt += error_max_time_err; + error_cnt += error_avg_delay_err; + + log(error_nr_of_good_polls ? "\033[31mbad: " : "good: ", "nr of good polls ", nr_of_good_polls, " (min ", (unsigned)MIN_NR_OF_POLLS, ")\033[0m"); + log( " ", "nr of bad polls ", nr_of_bad_polls ); + log(error_nr_of_time_cmprs ? "\033[31mbad: " : "good: ", "nr of time comparisons ", avg_time_err_us.avg_cnt(), " (min ", (unsigned)MIN_TIME_COMPARISONS, ")\033[0m"); + log(error_avg_time_err ? "\033[31mbad: " : "good: ", "average time error ", avg_time_err_us.avg(), " us (max ", (unsigned long)MAX_AVG_TIME_ERR_US, " us)\033[0m"); + log(error_max_time_err ? "\033[31mbad: " : "good: ", "maximum time error ", max_time_err_us, " us (max ", (unsigned long)MAX_TIME_ERR_US, " us)\033[0m"); + log( " ", "average delay ", avg_delay_us.avg(), " us" ); + log(error_avg_delay_err ? "\033[31mbad: " : "good: ", "average delay error ", avg_delay_err_us.avg(), " us (max ", max_avg_delay_err_us, " us)\033[0m"); + log( " ", "maximum delay error ", max_delay_err_us, " us" ); + + } + done.submit(); + } + + Fast_polling(Env &env, + unsigned &error_cnt, + Signal_context_capability done, + unsigned id) + : + Test(env, error_cnt, done, id, brief), + main_ep(env, STACK_SIZE, "fast_polling_ep"), + main_handler(main_ep, *this, &Fast_polling::main) + { + Signal_transmitter(main_handler).submit(); + } +}; + + +struct Main +{ + Env &env; + unsigned error_cnt { 0 }; + Constructible test_1; + Constructible test_2; + Signal_handler
test_1_done { env.ep(), *this, &Main::handle_test_1_done }; + Signal_handler
test_2_done { env.ep(), *this, &Main::handle_test_2_done }; + + Main(Env &env) : env(env) + { + test_1.construct(env, error_cnt, test_1_done, 1); + } + + void handle_test_1_done() + { + test_1.destruct(); + test_2.construct(env, error_cnt, test_2_done, 2); + } + + void handle_test_2_done() + { + test_2.destruct(); + if (error_cnt) { + error("test failed because of ", error_cnt, " error(s)"); + env.parent().exit(-1); + } else { + env.parent().exit(0); + } + } }; diff --git a/repos/os/src/test/timeout/target.mk b/repos/os/src/test/timeout/target.mk index 146f03935..d5e522101 100644 --- a/repos/os/src/test/timeout/target.mk +++ b/repos/os/src/test/timeout/target.mk @@ -1,4 +1,4 @@ TARGET = test-timeout SRC_CC += main.cc -LIBS += base timeout +LIBS += base INC_DIR += $(PRG_DIR)