Module: xenomai-forge Branch: next Commit: c20fceb147e337e2379b29ecd81b943318c7fbc7 URL: http://git.xenomai.org/?p=xenomai-forge.git;a=commit;h=c20fceb147e337e2379b29ecd81b943318c7fbc7
Author: Gilles Chanteperdrix <gilles.chanteperd...@xenomai.org> Date: Sun Jun 1 16:51:59 2014 +0200 cobalt/timer: avoid periodic timer drift Because timer period is converted to clock ticks when starting a periodic timer, round-off errors may accumulate at each timer tick causing the timer to drift. Fix this by keeping the timer period as a count of nanoseconds and convert to clock ticks at each period. This adds some overhead, but is more correct. --- include/cobalt/kernel/timer.h | 60 ++++++++++++++++++++++++++++++----------- kernel/cobalt/clock.c | 40 ++++++++++++++++----------- kernel/cobalt/posix/timer.c | 6 ++--- kernel/cobalt/posix/timerfd.c | 2 +- kernel/cobalt/thread.c | 2 +- kernel/cobalt/timer.c | 44 ++++++++++-------------------- 6 files changed, 88 insertions(+), 66 deletions(-) diff --git a/include/cobalt/kernel/timer.h b/include/cobalt/kernel/timer.h index 3c04cd0..d7f6867 100644 --- a/include/cobalt/kernel/timer.h +++ b/include/cobalt/kernel/timer.h @@ -188,10 +188,16 @@ struct xntimer { struct list_head adjlink; /** Timer status. */ unsigned long status; - /** Periodic interval (raw ticks, 0 == one shot). */ + /** Periodic interval (clock ticks, 0 == one shot). */ xnticks_t interval; - /** Date of next periodic release point (raw ticks). */ - xnticks_t pexpect; + /** Periodic interval (nanoseconds, 0 == one shot). */ + xnticks_t interval_ns; + /** Count of timer ticks in periodic mode. */ + xnticks_t periodic_ticks; + /** First tick date in periodic mode. */ + xnticks_t start_date; + /** Date of next periodic release point (timer ticks). */ + xnticks_t pexpect_ticks; /** Sched structure to which the timer is attached. */ struct xnsched *sched; /** Timeout handler. */ @@ -255,20 +261,19 @@ static inline xntimerq_t *xntimer_this_queue(struct xntimer *timer) return &tmd->q; } -static inline xnticks_t xntimer_interval(struct xntimer *timer) +static inline void xntimer_update_date(struct xntimer *timer) { - return timer->interval; -} + xntimerh_date(&timer->aplink) = timer->start_date + + xnclock_ns_to_ticks(xntimer_clock(timer), + timer->periodic_ticks * timer->interval_ns); -static inline xnticks_t xntimer_pexpect(struct xntimer *timer) -{ - return timer->pexpect; } -static inline xnticks_t xntimer_pexpect_forward(struct xntimer *timer, - xnticks_t delta) +static inline xnticks_t xntimer_pexpect(struct xntimer *timer) { - return timer->pexpect += delta; + return timer->start_date + + xnclock_ns_to_ticks(xntimer_clock(timer), + timer->pexpect_ticks * timer->interval_ns); } static inline void xntimer_set_priority(struct xntimer *timer, @@ -292,6 +297,11 @@ static inline int xntimer_fired_p(struct xntimer *timer) return (timer->status & XNTIMER_FIRED) != 0; } +static inline int xntimer_periodic_p(struct xntimer *timer) +{ + return (timer->status & XNTIMER_PERIODIC) != 0; +} + static inline int xntimer_reload_p(struct xntimer *timer) { return (timer->status & @@ -368,10 +378,30 @@ void xntimer_destroy(struct xntimer *timer); * \addtogroup timer *@{ */ +/** + * @fn xnticks_t xntimer_interval(struct xntimer *timer) + * + * @brief Return the timer interval value. + * + * Return the timer interval value in nanoseconds. + * + * @param timer The address of a valid timer descriptor. + * + * @return The duration of a period in nanoseconds. The special value + * XN_INFINITE is returned if @a timer is currently disabled or + * one shot. + * + * @remark Tags: isr-allowed. + */ +static inline xnticks_t xntimer_interval(struct xntimer *timer) +{ + return timer->interval_ns; +} + int xntimer_start(struct xntimer *timer, - xnticks_t value, - xnticks_t interval, - xntmode_t mode); + xnticks_t value, + xnticks_t interval, + xntmode_t mode); void __xntimer_stop(struct xntimer *timer); diff --git a/kernel/cobalt/clock.c b/kernel/cobalt/clock.c index 33469e8..2fc8aef 100644 --- a/kernel/cobalt/clock.c +++ b/kernel/cobalt/clock.c @@ -201,17 +201,19 @@ static void adjust_timer(struct xntimer *timer, xntimerq_t *q, xnsticks_t delta) { struct xnclock *clock = xntimer_clock(timer); - xnticks_t period, mod; + xnticks_t period, div; xnsticks_t diff; xntimerh_date(&timer->aplink) -= delta; - if ((timer->status & XNTIMER_PERIODIC) == 0) + if (xntimer_periodic_p(timer) == 0) goto enqueue; + timer->start_date -= delta; period = xntimer_interval(timer); - timer->pexpect -= delta; - diff = xnclock_read_raw(clock) - xntimerh_date(&timer->aplink); + diff = xnclock_ticks_to_ns(clock, + xnclock_read_raw(clock) - + xntimerh_date(&timer->aplink)); if ((xnsticks_t) (diff - period) >= 0) { /* @@ -221,8 +223,9 @@ static void adjust_timer(struct xntimer *timer, xntimerq_t *q, * that timer will tick only once and the lost ticks * will be counted as overruns. */ - mod = xnarch_mod64(diff, period); - xntimerh_date(&timer->aplink) += diff - mod; + div = xnarch_div64(diff, period); + timer->periodic_ticks += div; + xntimer_update_date(timer); } else if (delta < 0 && (timer->status & XNTIMER_FIRED) && (xnsticks_t) (diff + period) <= 0) { @@ -233,9 +236,10 @@ static void adjust_timer(struct xntimer *timer, xntimerq_t *q, * time to a sooner date, real-time periodic timers do * not tick until the original date has passed. */ - mod = xnarch_mod64(-diff, period); - xntimerh_date(&timer->aplink) += diff + mod; - timer->pexpect += diff + mod; + div = xnarch_div64(-diff, period); + timer->periodic_ticks -= div; + timer->pexpect_ticks -= div; + xntimer_update_date(timer); } enqueue: @@ -384,7 +388,7 @@ static int clock_vfile_next(struct xnvfile_snapshot_iterator *it, void *data) p->scheduled = xnstat_counter_get(&timer->scheduled); p->fired = xnstat_counter_get(&timer->fired); p->timeout = xntimer_get_timeout(timer); - p->interval = xntimer_get_interval(timer); + p->interval = xntimer_interval(timer); p->status = timer->status; knamecpy(p->handler, timer->handler_name); knamecpy(p->name, timer->name); @@ -552,7 +556,7 @@ void xnclock_tick(struct xnclock *clock) { xntimerq_t *timerq = &xnclock_this_timerdata(clock)->q; struct xnsched *sched = xnsched_current(); - xnticks_t now, interval; + xnticks_t now, interval_ticks; struct xntimer *timer; xnsticks_t delta; xntimerh_t *h; @@ -618,7 +622,10 @@ void xnclock_tick(struct xnclock *clock) * wait for 250 ms for the user to continue * program execution. */ - interval = xnclock_ns_to_ticks(clock, 250000000ULL); + if (timer->interval_ns > 250000000) + goto advance; + interval_ticks = 250000000 / + (unsigned)timer->interval_ns; goto requeue; } fire: @@ -633,11 +640,12 @@ void xnclock_tick(struct xnclock *clock) if (!xntimer_reload_p(timer)) continue; advance: - interval = timer->interval; + interval_ticks = 1; requeue: - do - xntimerh_date(&timer->aplink) += interval; - while (xntimerh_date(&timer->aplink) < now + clock->gravity); + do { + timer->periodic_ticks += interval_ticks; + xntimer_update_date(timer); + } while (xntimerh_date(&timer->aplink) < now + clock->gravity); #ifdef CONFIG_SMP /* * Make sure to pick the right percpu queue, in case diff --git a/kernel/cobalt/posix/timer.c b/kernel/cobalt/posix/timer.c index 476ea77..aa335ab 100644 --- a/kernel/cobalt/posix/timer.c +++ b/kernel/cobalt/posix/timer.c @@ -121,7 +121,7 @@ cobalt_timer_by_id(struct cobalt_process *cc, timer_t timer_id) if (test_bit(timer_id, cc->timers_map)) return NULL; - + return cc->timers[timer_id]; } @@ -185,7 +185,7 @@ static inline int timer_create(clockid_t clockid, timer = xnmalloc(sizeof(*timer)); if (timer == NULL) return -ENOMEM; - + timer->sigp.si.si_errno = 0; timer->sigp.si.si_code = SI_TIMER; timer->sigp.si.si_overrun = 0; @@ -667,7 +667,7 @@ int cobalt_timer_deliver(timer_t timerid) /* nklocked, IRQs off. */ /* Killed before ultimate delivery, who cares then? */ return 0; - if (!xntimer_interval(&timer->timerbase)) + if (!xntimer_periodic_p(&timer->timerbase)) timer->overruns = 0; else { now = xnclock_read_raw(xntimer_clock(&timer->timerbase)); diff --git a/kernel/cobalt/posix/timerfd.c b/kernel/cobalt/posix/timerfd.c index 3ffd8de..ebc554c 100644 --- a/kernel/cobalt/posix/timerfd.c +++ b/kernel/cobalt/posix/timerfd.c @@ -77,7 +77,7 @@ static ssize_t timerfd_read(struct rtdm_fd *fd, void __user *buf, size_t size) if (err == 0) { xnticks_t now; - if (xntimer_interval(&tfd->timer)) { + if (xntimer_periodic_p(&tfd->timer)) { now = xnclock_read_raw(xntimer_clock(&tfd->timer)); ticks = 1 + xntimer_get_overruns(&tfd->timer, now); } else diff --git a/kernel/cobalt/thread.c b/kernel/cobalt/thread.c index 5887be9..e3e9f44 100644 --- a/kernel/cobalt/thread.c +++ b/kernel/cobalt/thread.c @@ -340,7 +340,7 @@ xnticks_t xnthread_get_period(struct xnthread *thread) * - or zero, meaning "no periodic activity". */ if (xntimer_running_p(&thread->ptimer)) - period = xntimer_get_interval(&thread->ptimer); + period = xntimer_interval(&thread->ptimer); else if (xnthread_test_state(thread,XNRRB)) period = xnthread_time_slice(thread); diff --git a/kernel/cobalt/timer.c b/kernel/cobalt/timer.c index ac3aa9f..bd22eae 100644 --- a/kernel/cobalt/timer.c +++ b/kernel/cobalt/timer.c @@ -137,10 +137,14 @@ int xntimer_start(struct xntimer *timer, xntimerh_date(&timer->aplink) = date; + timer->interval_ns = XN_INFINITE; timer->interval = XN_INFINITE; if (interval != XN_INFINITE) { + timer->interval_ns = interval; timer->interval = xnclock_ns_to_ticks(clock, interval); - timer->pexpect = date; + timer->periodic_ticks = 0; + timer->start_date = date; + timer->pexpect_ticks = 0; timer->status |= XNTIMER_PERIODIC; } @@ -256,29 +260,6 @@ xnticks_t xntimer_get_timeout(struct xntimer *timer) EXPORT_SYMBOL_GPL(xntimer_get_timeout); /** - * @fn xnticks_t xntimer_get_interval(struct xntimer *timer) - * - * @brief Return the timer interval value. - * - * Return the timer interval value in nanoseconds. - * - * @param timer The address of a valid timer descriptor. - * - * @return The duration of a period in nanoseconds. The special value - * XN_INFINITE is returned if @a timer is currently disabled or - * one shot. - * - * @remark Tags: isr-allowed. - */ -xnticks_t xntimer_get_interval(struct xntimer *timer) -{ - struct xnclock *clock = xntimer_clock(timer); - - return xnclock_ticks_to_ns_rounded(clock, timer->interval); -} -EXPORT_SYMBOL_GPL(xntimer_get_interval); - -/** * @fn void xntimer_init(struct xntimer *timer,struct xnclock *clock,void (*handler)(struct xntimer *timer), struct xnthread *thread) * @brief Initialize a timer object. * @@ -329,7 +310,7 @@ void __xntimer_init(struct xntimer *timer, xntimer_set_priority(timer, XNTIMER_STDPRIO); timer->status = XNTIMER_DEQUEUED; timer->handler = handler; - timer->interval = 0; + timer->interval_ns = 0; /* * Timers have to run on a real-time CPU, i.e. a member of the * xnsched_realtime_cpus mask. If the new timer is affine to a @@ -366,7 +347,7 @@ void xntimer_switch_tracking(struct xntimer *timer, { struct xnclock *oldclock = timer->tracker; spl_t s; - + xnlock_get_irqsave(&nklock, s); list_del(&timer->next_stat); oldclock->nrtimers--; @@ -486,16 +467,19 @@ void xntimer_release_ipi(void) */ unsigned long long xntimer_get_overruns(struct xntimer *timer, xnticks_t now) { - xnticks_t period = xntimer_interval(timer); - xnsticks_t delta = now - timer->pexpect; + xnticks_t period = timer->interval; + xnsticks_t delta; unsigned long long overruns = 0; + delta = now - xntimer_pexpect(timer); if (unlikely(delta >= (xnsticks_t) period)) { + period = timer->interval_ns; + delta = xnclock_ticks_to_ns(xntimer_clock(timer), delta); overruns = xnarch_div64(delta, period); - timer->pexpect += period * overruns; + timer->pexpect_ticks += overruns; } - timer->pexpect += period; + timer->pexpect_ticks++; return overruns; } EXPORT_SYMBOL_GPL(xntimer_get_overruns); _______________________________________________ Xenomai-git mailing list Xenomai-git@xenomai.org http://www.xenomai.org/mailman/listinfo/xenomai-git