Module: xenomai-gch
Branch: for-forge
Commit: a88858a4565e92e6084592c64bfc1ac218cc3c5a
URL:    
http://git.xenomai.org/?p=xenomai-gch.git;a=commit;h=a88858a4565e92e6084592c64bfc1ac218cc3c5a

Author: Gilles Chanteperdrix <gilles.chanteperd...@xenomai.org>
Date:   Sun Jun  1 16:51:59 2014 +0200

cobalt/timers: 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         |   37 ++++++++++++++-----------
 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, 82 insertions(+), 69 deletions(-)

diff --git a/include/cobalt/kernel/timer.h b/include/cobalt/kernel/timer.h
index 3c04cd0..7b4f36e 100644
--- a/include/cobalt/kernel/timer.h
+++ b/include/cobalt/kernel/timer.h
@@ -188,10 +188,14 @@ struct xntimer {
        struct list_head adjlink;
        /** Timer status. */
        unsigned long status;
-       /** Periodic interval (raw 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 +259,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 +295,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 +376,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 a2553d1..81b73a4 100644
--- a/kernel/cobalt/clock.c
+++ b/kernel/cobalt/clock.c
@@ -200,17 +200,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) {
                /*
@@ -220,8 +222,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) {
@@ -232,9 +235,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:
@@ -383,7 +387,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);
@@ -551,7 +555,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_ns;
        struct xntimer *timer;
        xnsticks_t delta;
        xntimerh_t *h;
@@ -617,7 +621,7 @@ void xnclock_tick(struct xnclock *clock)
                         * wait for 250 ms for the user to continue
                         * program execution.
                         */
-                       interval = xnclock_ns_to_ticks(clock, 250000000ULL);
+                       interval_ns = 250000000ULL;
                        goto requeue;
                }
        fire:
@@ -632,11 +636,12 @@ void xnclock_tick(struct xnclock *clock)
                if (!xntimer_reload_p(timer))
                        continue;
        advance:
-               interval = timer->interval;
+               interval_ns = timer->interval_ns;
        requeue:
-               do
-                       xntimerh_date(&timer->aplink) += interval;
-               while (xntimerh_date(&timer->aplink) < now + clock->gravity);
+               do {
+                       ++timer->periodic_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..6bdc426 100644
--- a/kernel/cobalt/timer.c
+++ b/kernel/cobalt/timer.c
@@ -137,10 +137,12 @@ int xntimer_start(struct xntimer *timer,
 
        xntimerh_date(&timer->aplink) = date;
 
-       timer->interval = XN_INFINITE;
+       timer->interval_ns = XN_INFINITE;
        if (interval != XN_INFINITE) {
-               timer->interval = xnclock_ns_to_ticks(clock, interval);
-               timer->pexpect = date;
+               timer->interval_ns = interval;
+               timer->periodic_ticks = 0;
+               timer->start_date = date;
+               timer->pexpect_ticks = 0;
                timer->status |= XNTIMER_PERIODIC;
        }
 
@@ -256,29 +258,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 +308,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 +345,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 +465,17 @@ 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_ns;
+       xnsticks_t delta;
        unsigned long long overruns = 0;
 
+       delta = now - xntimer_pexpect(timer);
        if (unlikely(delta >= (xnsticks_t) period)) {
                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

Reply via email to