Module: xenomai-3
Branch: next
Commit: d01ed47a31bad462210facbc3b45a0adf91318f1
URL:    
http://git.xenomai.org/?p=xenomai-3.git;a=commit;h=d01ed47a31bad462210facbc3b45a0adf91318f1

Author: Philippe Gerum <r...@xenomai.org>
Date:   Fri Dec  4 12:07:41 2015 +0100

cobalt/clock: introduce CPU affinity for clock event sources

Xenomai clocks may be backed by devices issuing event notifications on
a restricted set of CPUs only. Typically, the interrupt distributor of
GIC-equiped ARM processors is programmed to route shared processor
interrupts to CPU0 by default. For instance, a Xenomai clock paced by
a GPIO input signal would tick exclusively on CPU0 in that
configuration.

We have to make sure that timers are always attached to per-CPU queues
which are expected to receive event notifications from the underlying
clock device, otherwise threads may wait for events indefinitely on
those timers.

Previously, a restrictive assumption was made that all CPUs from the
real-time set should be able to receive events, indirectly assuming
that each ticking device is a per-CPU device. Although this is correct
for the core Xenomai clock based on local timers for SMP, this turns
out to be wrong quite frequently with external Xenomai clocks backed
by common devices issuing a periodic master beat.

To fix this, we require the set of CPUs the event source may tick on
to be declared as part of the Xenomai clock properties. This
information is then used to migrate timers to an appropriate CPU if
need be, and to detect mismatching CPU affinities between timers and
clock devices when DEBUG_COBALT is enabled.

---

 include/cobalt/kernel/clock.h |   23 +++++++++++++++---
 kernel/cobalt/clock.c         |   47 ++++++++++++++++++++++++++++++++---
 kernel/cobalt/posix/clock.c   |    5 ++--
 kernel/cobalt/posix/clock.h   |    2 ++
 kernel/cobalt/thread.c        |   37 ++++++++++++++++++++++------
 kernel/cobalt/timer.c         |   54 ++++++++++++++++++++++++++++++++---------
 6 files changed, 140 insertions(+), 28 deletions(-)

diff --git a/include/cobalt/kernel/clock.h b/include/cobalt/kernel/clock.h
index 70e1c15..6ad8719 100644
--- a/include/cobalt/kernel/clock.h
+++ b/include/cobalt/kernel/clock.h
@@ -39,12 +39,13 @@ struct xnclock_gravity {
 };
 
 struct xnclock {
-       /** ns */
+       /** (ns) */
        xnticks_t wallclock_offset;
-       /** ns */
+       /** (ns) */
        xnticks_t resolution;
-       /** raw clock ticks. */
+       /** (raw clock ticks). */
        struct xnclock_gravity gravity;
+       /** Clock name. */
        const char *name;
        struct {
 #ifdef CONFIG_XENO_OPT_EXTCLOCK
@@ -74,6 +75,10 @@ struct xnclock {
        /* Private section. */
        struct xntimerdata *timerdata;
        int id;
+#ifdef CONFIG_SMP
+       /** Possible CPU affinity of clock beat. */
+       cpumask_t affinity;
+#endif
 #ifdef CONFIG_XENO_OPT_STATS
        struct xnvfile_snapshot timer_vfile;
        struct xnvfile_rev_tag timer_revtag;
@@ -91,7 +96,8 @@ extern unsigned long nktimerlat;
 
 extern unsigned int nkclock_lock;
 
-int xnclock_register(struct xnclock *clock);
+int xnclock_register(struct xnclock *clock,
+                    const cpumask_t *affinity);
 
 void xnclock_deregister(struct xnclock *clock);
 
@@ -248,6 +254,15 @@ static inline int xnclock_set_time(struct xnclock *clock,
 
 #endif /* !CONFIG_XENO_OPT_EXTCLOCK */
 
+#ifdef CONFIG_SMP
+int xnclock_get_default_cpu(struct xnclock *clock, int cpu);
+#else
+static inline int xnclock_get_default_cpu(struct xnclock *clock, int cpu)
+{
+       return cpu;
+}
+#endif
+
 static inline xnticks_t xnclock_get_offset(struct xnclock *clock)
 {
        return clock->wallclock_offset;
diff --git a/kernel/cobalt/clock.c b/kernel/cobalt/clock.c
index 5ee3eeb..783ac7d 100644
--- a/kernel/cobalt/clock.c
+++ b/kernel/cobalt/clock.c
@@ -322,6 +322,28 @@ xnticks_t xnclock_core_read_monotonic(void)
 }
 EXPORT_SYMBOL_GPL(xnclock_core_read_monotonic);
 
+#ifdef CONFIG_SMP
+
+int xnclock_get_default_cpu(struct xnclock *clock, int cpu)
+{
+       cpumask_t set;
+       /*
+        * Check a CPU number against the possible set of CPUs
+        * receiving events from the underlying clock device. If the
+        * suggested CPU does not receive events from this device,
+        * return the first one which does.  We also account for the
+        * dynamic set of real-time CPUs.
+        */
+       cpumask_and(&set, &clock->affinity, &cobalt_cpu_affinity);
+       if (!cpumask_empty(&set) && !cpumask_test_cpu(cpu, &set))
+               cpu = cpumask_first(&set);
+
+       return cpu;
+}
+EXPORT_SYMBOL_GPL(xnclock_get_default_cpu);
+
+#endif /* !CONFIG_SMP */
+
 #ifdef CONFIG_XENO_OPT_STATS
 
 static struct xnvfile_directory timerlist_vfroot;
@@ -587,7 +609,6 @@ static inline void cleanup_clock_proc(struct xnclock 
*clock) { }
 #endif /* !CONFIG_XENO_OPT_VFILE */
 
 /**
- * @fn void xnclock_register(struct xnclock *clock)
  * @brief Register a Xenomai clock.
  *
  * This service installs a new clock which may be used to drive
@@ -595,20 +616,40 @@ static inline void cleanup_clock_proc(struct xnclock 
*clock) { }
  *
  * @param clock The new clock to register.
  *
+ * @param affinity The set of CPUs we may expect the backing clock
+ * device to tick on.
+ *
  * @coretags{secondary-only}
  */
-int xnclock_register(struct xnclock *clock)
+int xnclock_register(struct xnclock *clock, const cpumask_t *affinity)
 {
        struct xntimerdata *tmd;
        int cpu;
 
        secondary_mode_only();
 
+#ifdef CONFIG_SMP
+       /*
+        * A CPU affinity set is defined for each clock, enumerating
+        * the CPUs which can receive ticks from the backing clock
+        * device.  This set must be a subset of the real-time CPU
+        * set.
+        */
+       cpumask_and(&clock->affinity, affinity, &xnsched_realtime_cpus);
+       if (cpumask_empty(&clock->affinity))
+               return -EINVAL;
+#endif
+
        /* Allocate the percpu timer queue slot. */
        clock->timerdata = alloc_percpu(struct xntimerdata);
        if (clock->timerdata == NULL)
                return -ENOMEM;
 
+       /*
+        * POLA: init all timer slots for the new clock, although some
+        * of them might remain unused depending on the CPU affinity
+        * of the event source(s).
+        */
        for_each_online_cpu(cpu) {
                tmd = xnclock_percpu_timerdata(clock, cpu);
                xntimerq_init(&tmd->q);
@@ -829,7 +870,7 @@ int __init xnclock_init(unsigned long long freq)
 #endif
        nktimerlat = xnarch_timer_calibrate();
        xnclock_reset_gravity(&nkclock);
-       xnclock_register(&nkclock);
+       xnclock_register(&nkclock, &xnsched_realtime_cpus);
 
        return 0;
 }
diff --git a/kernel/cobalt/posix/clock.c b/kernel/cobalt/posix/clock.c
index 7fd148a..b51cb4c 100644
--- a/kernel/cobalt/posix/clock.c
+++ b/kernel/cobalt/posix/clock.c
@@ -303,7 +303,8 @@ COBALT_SYSCALL(clock_nanosleep, nonrestartable,
        return ret;
 }
 
-int cobalt_clock_register(struct xnclock *clock, clockid_t *clk_id)
+int cobalt_clock_register(struct xnclock *clock, const cpumask_t *affinity,
+                         clockid_t *clk_id)
 {
        int ret, nr;
        spl_t s;
@@ -326,7 +327,7 @@ int cobalt_clock_register(struct xnclock *clock, clockid_t 
*clk_id)
 
        xnlock_put_irqrestore(&nklock, s);
 
-       ret = xnclock_register(clock);
+       ret = xnclock_register(clock, affinity);
        if (ret)
                return ret;
 
diff --git a/kernel/cobalt/posix/clock.h b/kernel/cobalt/posix/clock.h
index c810c1e..82cb0b6 100644
--- a/kernel/cobalt/posix/clock.h
+++ b/kernel/cobalt/posix/clock.h
@@ -20,6 +20,7 @@
 
 #include <linux/types.h>
 #include <linux/time.h>
+#include <linux/cpumask.h>
 #include <cobalt/uapi/time.h>
 #include <xenomai/posix/syscall.h>
 
@@ -112,6 +113,7 @@ COBALT_SYSCALL_DECL(clock_nanosleep,
                     struct timespec __user *u_rmt));
 
 int cobalt_clock_register(struct xnclock *clock,
+                         const cpumask_t *affinity,
                          clockid_t *clk_id);
 
 void cobalt_clock_deregister(struct xnclock *clock);
diff --git a/kernel/cobalt/thread.c b/kernel/cobalt/thread.c
index f3d57a8..435e902 100644
--- a/kernel/cobalt/thread.c
+++ b/kernel/cobalt/thread.c
@@ -58,6 +58,23 @@ static void timeout_handler(struct xntimer *timer)
        xnthread_resume(thread, XNDELAY);
 }
 
+static inline void fixup_ptimer_affinity(struct xnthread *thread)
+{
+#ifdef CONFIG_SMP
+       struct xntimer *timer = &thread->ptimer;
+       int cpu;
+       /*
+        * The thread a periodic timer is affine to might have been
+        * migrated to another CPU while passive. Fix this up.
+        */
+       if (thread->sched != timer->sched) {
+               cpu = xnclock_get_default_cpu(xntimer_clock(timer),
+                                             xnsched_cpu(thread->sched));
+               xntimer_set_sched(timer, xnsched_struct(cpu));
+       }
+#endif
+}
+
 static void periodic_handler(struct xntimer *timer)
 {
        struct xnthread *thread = container_of(timer, struct xnthread, ptimer);
@@ -67,11 +84,8 @@ static void periodic_handler(struct xntimer *timer)
         */
        if (xnthread_test_state(thread, XNDELAY|XNPEND) == XNDELAY)
                xnthread_resume(thread, XNDELAY);
-       /*
-        * The thread a periodic timer is affine to might have been
-        * migrated to another CPU while passive. Fix this up.
-        */
-       xntimer_set_sched(timer, thread->sched);
+
+       fixup_ptimer_affinity(thread);
 }
 
 struct kthread_arg {
@@ -1284,7 +1298,7 @@ EXPORT_SYMBOL_GPL(xnthread_unblock);
 int xnthread_set_periodic(struct xnthread *thread, xnticks_t idate,
                          xntmode_t timeout_mode, xnticks_t period)
 {
-       int ret = 0;
+       int ret = 0, cpu;
        spl_t s;
 
        if (thread == NULL) {
@@ -1315,7 +1329,16 @@ int xnthread_set_periodic(struct xnthread *thread, 
xnticks_t idate,
                goto unlock_and_exit;
        }
 
-       xntimer_set_sched(&thread->ptimer, thread->sched);
+       /*
+        * Pin the periodic timer to a proper CPU, by order of
+        * preference: the CPU the timed thread runs on if possible,
+        * or the first CPU by logical number which can receive events
+        * from the clock device backing the timer, among the dynamic
+        * set of real-time CPUs currently enabled.
+        */
+       cpu = xnclock_get_default_cpu(xntimer_clock(&thread->ptimer),
+                                     xnsched_cpu(thread->sched));
+       xntimer_set_sched(&thread->ptimer, xnsched_struct(cpu));
 
        if (idate == XN_INFINITE)
                xntimer_start(&thread->ptimer, period, period, XN_RELATIVE);
diff --git a/kernel/cobalt/timer.c b/kernel/cobalt/timer.c
index 526c615..ee0bb87 100644
--- a/kernel/cobalt/timer.c
+++ b/kernel/cobalt/timer.c
@@ -348,19 +348,24 @@ void __xntimer_init(struct xntimer *timer,
        timer->handler = handler;
        timer->interval_ns = 0;
        /*
-        * Timers are affine to a scheduler slot, which is in turn
-        * bound to a real-time CPU. If no scheduler affinity was
-        * given, assign the timer to the scheduler slot of the
-        * current CPU if real-time, otherwise default to the
-        * scheduler slot of the first real-time CPU.
+        * Timers are affine to a real-time CPU. If no affinity was
+        * specified, assign the timer to the first possible CPU which
+        * can receive interrupt events from the clock device attached
+        * to the reference clock for this timer.
         */
-       if (sched)
+       if (sched) {
+               /*
+                * Complain loudly if no tick is expected from the
+                * clock device on the CPU served by the specified
+                * scheduler slot. This reveals a CPU affinity
+                * mismatch between the clock hardware and the client
+                * code initializing the timer.
+                */
+               XENO_WARN_ON_SMP(COBALT, !cpumask_test_cpu(xnsched_cpu(sched),
+                                              &clock->affinity));
                timer->sched = sched;
-       else {
-               cpu = ipipe_processor_id();
-               if (!xnsched_supported_cpu(cpu))
-                       cpu = cpumask_first(&xnsched_realtime_cpus);
-
+       } else {
+               cpu = xnclock_get_default_cpu(clock, 0);
                timer->sched = xnsched_struct(cpu);
        }
 
@@ -429,6 +434,21 @@ void __xntimer_switch_tracking(struct xntimer *timer,
 
 #endif /* CONFIG_XENO_OPT_STATS */
 
+static inline void __xntimer_set_clock(struct xntimer *timer,
+                                      struct xnclock *newclock)
+{
+#ifdef CONFIG_SMP
+       int cpu;
+       /*
+        * Make sure the timer lives on a CPU the backing clock device
+        * ticks on.
+        */
+       cpu = xnclock_get_default_cpu(newclock, xnsched_cpu(timer->sched));
+       xntimer_migrate(timer, xnsched_struct(cpu));
+#endif
+       __xntimer_switch_tracking(timer, newclock);
+}
+
 /**
  * @brief Set the reference clock of a timer.
  *
@@ -446,7 +466,7 @@ void xntimer_set_clock(struct xntimer *timer,
 {
        xntimer_stop(timer);
        timer->clock = newclock;
-       __xntimer_switch_tracking(timer, newclock);
+       __xntimer_set_clock(timer, newclock);
 }
 
 #endif /* CONFIG_XENO_OPT_EXTCLOCK */
@@ -508,6 +528,16 @@ void __xntimer_migrate(struct xntimer *timer, struct 
xnsched *sched)
 
        trace_cobalt_timer_migrate(timer, xnsched_cpu(sched));
 
+       /*
+        * This assertion triggers when the timer is migrated to a CPU
+        * for which we do not expect any clock events/IRQs from the
+        * associated clock device. If so, the timer would never fire
+        * since clock ticks would never happen on that CPU (timer
+        * queues are per-CPU constructs).
+        */
+       XENO_WARN_ON_SMP(COBALT, !cpumask_test_cpu(xnsched_cpu(sched),
+                      &xntimer_clock(timer)->affinity));
+
        if (timer->status & XNTIMER_RUNNING) {
                xntimer_stop(timer);
                timer->sched = sched;


_______________________________________________
Xenomai-git mailing list
Xenomai-git@xenomai.org
http://xenomai.org/mailman/listinfo/xenomai-git

Reply via email to