[Xenomai-git] Philippe Gerum : cobalt/clock: introduce CPU affinity for clock event sources

2015-12-04 Thread git repository hosting
Module: xenomai-3
Branch: stable-3.0.x
Commit: a929d1963b7f8b7378b12119e7e5d4b90449e53a
URL:
http://git.xenomai.org/?p=xenomai-3.git;a=commit;h=a929d1963b7f8b7378b12119e7e5d4b90449e53a

Author: Philippe Gerum 
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(, >affinity, _cpu_affinity);
+   if (!cpumask_empty() && !cpumask_test_cpu(cpu, ))
+   cpu = cpumask_first();
+
+   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 

[Xenomai-git] Philippe Gerum : cobalt/clock: introduce CPU affinity for clock event sources

2015-12-04 Thread git repository hosting
Module: xenomai-3
Branch: next
Commit: d01ed47a31bad462210facbc3b45a0adf91318f1
URL:
http://git.xenomai.org/?p=xenomai-3.git;a=commit;h=d01ed47a31bad462210facbc3b45a0adf91318f1

Author: Philippe Gerum 
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(, >affinity, _cpu_affinity);
+   if (!cpumask_empty() && !cpumask_test_cpu(cpu, ))
+   cpu = cpumask_first();
+
+   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