/* * \brief Signal context for timer events * \author Sebastian Sumpf * \author Emery Hemingway * \author Christian Helmuth * \date 2012-05-23 */ /* * Copyright (C) 2012-2017 Genode Labs GmbH * * This file is distributed under the terms of the GNU General Public License * version 2. */ /* Genode includes */ #include #include #include #include #include /* Linux kit includes */ #include /* local includes */ #include #include /********************* ** linux/jiffies.h ** *********************/ unsigned long jiffies; /** * Lx::Timer */ namespace Lx { class Timer; } class Lx::Timer { public: /** * Context encapsulates a regular linux timer_list */ struct Context : public Lx_kit::List::Element { enum { INVALID_TIMEOUT = ~0UL }; enum Type { LIST }; Type type; void *timer; bool pending { false }; unsigned long timeout { INVALID_TIMEOUT }; /* absolute in jiffies */ Context(struct timer_list *timer) : type(LIST), timer(timer) { } void expires(unsigned long e) { if (type == LIST) static_cast(timer)->expires = e; } void function() { switch (type) { case LIST: { timer_list *t = static_cast(timer); if (t->function) t->function(t->data); } break; } } }; private: Genode::Entrypoint &_ep; Genode::Timeout_scheduler &_scheduler; /* One-shot timeout for timer list */ ::Timer::One_shot_timeout _timers_one_shot { _scheduler, *this, &Lx::Timer::_handle_timers }; /* One-shot timeout for 'wait' */ ::Timer::One_shot_timeout _wait_one_shot { _scheduler, *this, &Lx::Timer::_handle_wait }; Lx_kit::List _list; Genode::Tslab _timer_alloc; void (*_tick)(); public: bool ready { true }; private: /** * Lookup local timer */ Context *_find_context(void const *timer) { for (Context *c = _list.first(); c; c = c->next()) if (c->timer == timer) return c; return 0; } /** * Program the first timer in the list */ void _program_first_timer() { Context *ctx = _list.first(); if (!ctx) return; /* calculate relative microseconds for trigger */ Genode::uint64_t us = ctx->timeout > jiffies ? (Genode::uint64_t)jiffies_to_msecs(ctx->timeout - jiffies) * 1000 : 0; _timers_one_shot.schedule(Genode::Microseconds{us}); } /** * Schedule timer * * Add the context to the scheduling list depending on its timeout * and reprogram the first timer. */ void _schedule_timer(Context *ctx, unsigned long expires) { _list.remove(ctx); ctx->timeout = expires; ctx->pending = true; /* * Also write the timeout value to the expires field in * struct timer_list because some code the checks * it directly. */ ctx->expires(expires); Context *c; for (c = _list.first(); c; c = c->next()) if (ctx->timeout <= c->timeout) break; _list.insert_before(ctx, c); _program_first_timer(); } inline void _upate_jiffies(Genode::Duration dur) { auto new_jiffies = usecs_to_jiffies(dur.trunc_to_plain_us().value); if (new_jiffies < jiffies) jiffies = usecs_to_jiffies(_scheduler.curr_time().trunc_to_plain_us().value); else jiffies = new_jiffies; } /** * Check timers and wake application */ void _handle_timers(Genode::Duration dur) { _upate_jiffies(dur); while (Lx::Timer::Context *ctx = _list.first()) { if (ctx->timeout > jiffies) break; ctx->pending = false; ctx->function(); if (!ctx->pending) del(ctx->timer); } /* tick the higher layer of the component */ _tick(); } void _handle_wait(Genode::Duration dur) { _upate_jiffies(dur); } public: /** * Constructor */ Timer(Genode::Entrypoint &ep, Genode::Timeout_scheduler &scheduler, Genode::Allocator &alloc, void (*tick)()) : _ep(ep), _scheduler(scheduler), _timer_alloc(&alloc), _tick(tick) { update_jiffies(); } /** * Add new linux timer */ template void add(TIMER *timer) { Context *t = new (&_timer_alloc) Context(timer); _list.append(t); } /** * Delete linux timer */ int del(void *timer) { Context *ctx = _find_context(timer); /** * If the timer expired it was already cleaned up after its * execution. */ if (!ctx) return 0; int rv = ctx->pending ? 1 : 0; _list.remove(ctx); destroy(&_timer_alloc, ctx); return rv; } /** * Initial scheduling of linux timer */ int schedule(void *timer, unsigned long expires) { Context *ctx = _find_context(timer); if (!ctx) { Genode::error("schedule unknown timer ", timer); return -1; /* XXX better use 0 as rv? */ } /* * If timer was already active return 1, otherwise 0. The return * value is needed by mod_timer(). */ int rv = ctx->pending ? 1 : 0; _schedule_timer(ctx, expires); return rv; } /** * Schedule next linux timer */ void schedule_next() { _program_first_timer(); } /** * Check if the timer is currently pending */ bool pending(void const *timer) { Context *ctx = _find_context(timer); if (!ctx) { return false; } return ctx->pending; } Context *find(struct timer_list const *timer) { return _find_context(timer); } /** * Update jiffie counter */ void update_jiffies() { /* * Do not use lx_emul usecs_to_jiffies(unsigned int) because * of implicit truncation! */ jiffies = _scheduler.curr_time().trunc_to_plain_ms().value / JIFFIES_TICK_MS; } /** * Get first timer context */ Context* first() { return _list.first(); } void wait(unsigned long timeo = 0) { if (timeo > 0) _wait_one_shot.schedule(Genode::Microseconds(jiffies_to_usecs(timeo))); _ep.wait_and_dispatch_one_io_signal(); } void wait_uninterruptible(unsigned long timeo) { if (timeo > 0) { _wait_one_shot.schedule(Genode::Microseconds(jiffies_to_usecs(timeo))); while (_wait_one_shot.scheduled()) _ep.wait_and_dispatch_one_io_signal(); } } }; static Lx::Timer *_timer; void Lx::timer_init(Genode::Entrypoint &ep, Genode::Timeout_scheduler &scheduler, Genode::Allocator &alloc, void (*tick)()) { static Lx::Timer inst(ep, scheduler, alloc, tick); _timer = &inst; } void update_jiffies() { _timer->update_jiffies(); } void Lx::timer_update_jiffies() { update_jiffies(); } /******************* ** linux/timer.h ** *******************/ void init_timer(struct timer_list *timer) { } void add_timer(struct timer_list *timer) { BUG_ON(timer_pending(timer)); mod_timer(timer, timer->expires); } int mod_timer(struct timer_list *timer, unsigned long expires) { update_jiffies(); if (!_timer->find(timer)) _timer->add(timer); return _timer->schedule(timer, expires); } void setup_timer(struct timer_list *timer,void (*function)(unsigned long), unsigned long data) { timer->function = function; timer->data = data; } int timer_pending(const struct timer_list * timer) { bool pending = _timer->pending(timer); lx_log(DEBUG_TIMER, "Pending %p %u", timer, pending); return pending; } int del_timer(struct timer_list *timer) { update_jiffies(); lx_log(DEBUG_TIMER, "Delete timer %p", timer); int rv = _timer->del(timer); _timer->schedule_next(); return rv; } /******************* ** linux/sched.h ** *******************/ signed long schedule_timeout(signed long timeout) { unsigned long expire = timeout + jiffies; long start = jiffies; _timer->wait(timeout); timeout -= jiffies - start; return timeout < 0 ? 0 : timeout; } long schedule_timeout_uninterruptible(signed long timeout) { _timer->wait_uninterruptible(timeout); return 0; } void poll_wait(struct file * filp, wait_queue_head_t * wait_address, poll_table *p) { _timer->wait(); } bool poll_does_not_wait(const poll_table *p) { return p == nullptr; } /****************** ** linux/time.h ** ******************/ unsigned long get_seconds(void) { return jiffies / HZ; } /******************* ** linux/timer.h ** *******************/ static unsigned long round_jiffies(unsigned long j, bool force_up) { unsigned remainder = j % HZ; /* * from timer.c * * If the target jiffie is just after a whole second (which can happen * due to delays of the timer irq, long irq off times etc etc) then * we should round down to the whole second, not up. Use 1/4th second * as cutoff for this rounding as an extreme upper bound for this. * But never round down if @force_up is set. */ /* per default round down */ j = j - remainder; /* round up if remainder more than 1/4 second (or if we're forced to) */ if (remainder >= HZ/4 || force_up) j += HZ; return j; } unsigned long round_jiffies(unsigned long j) { return round_jiffies(j, false); } unsigned long round_jiffies_up(unsigned long j) { return round_jiffies(j, true); } unsigned long round_jiffies_relative(unsigned long j) { return round_jiffies(j + jiffies, false) - jiffies; }