[Xenomai-git] Philippe Gerum : cobalt/clock: introduce CPU affinity for clock event sources
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 GerumDate: 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
Module: xenomai-3 Branch: next Commit: d01ed47a31bad462210facbc3b45a0adf91318f1 URL: http://git.xenomai.org/?p=xenomai-3.git;a=commit;h=d01ed47a31bad462210facbc3b45a0adf91318f1 Author: Philippe GerumDate: 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