Merge branch 'for-rmk/broadcast' of git://git.kernel.org/pub/scm/linux/kernel/git/will/linux into next/virt

* 'for-rmk/broadcast' of git://git.kernel.org/pub/scm/linux/kernel/git/will/linux:
  arm: Add generic timer broadcast support
  arm: Use generic timer broadcast receiver
  clockevents: Add generic timer broadcast function
  clockevents: Add generic timer broadcast receiver
This commit is contained in:
Olof Johansson 2013-02-11 09:03:05 -08:00
commit 3ddc0e1a7f
5 changed files with 48 additions and 9 deletions

View File

@ -4,6 +4,7 @@ config ARM
select ARCH_BINFMT_ELF_RANDOMIZE_PIE select ARCH_BINFMT_ELF_RANDOMIZE_PIE
select ARCH_HAS_ATOMIC64_DEC_IF_POSITIVE select ARCH_HAS_ATOMIC64_DEC_IF_POSITIVE
select ARCH_HAVE_CUSTOM_GPIO_H select ARCH_HAVE_CUSTOM_GPIO_H
select ARCH_HAS_TICK_BROADCAST if GENERIC_CLOCKEVENTS_BROADCAST
select ARCH_WANT_IPC_PARSE_VERSION select ARCH_WANT_IPC_PARSE_VERSION
select BUILDTIME_EXTABLE_SORT if MMU select BUILDTIME_EXTABLE_SORT if MMU
select CPU_PM if (SUSPEND || CPU_IDLE) select CPU_PM if (SUSPEND || CPU_IDLE)

View File

@ -475,14 +475,8 @@ u64 smp_irq_stat_cpu(unsigned int cpu)
*/ */
static DEFINE_PER_CPU(struct clock_event_device, percpu_clockevent); static DEFINE_PER_CPU(struct clock_event_device, percpu_clockevent);
static void ipi_timer(void)
{
struct clock_event_device *evt = &__get_cpu_var(percpu_clockevent);
evt->event_handler(evt);
}
#ifdef CONFIG_GENERIC_CLOCKEVENTS_BROADCAST #ifdef CONFIG_GENERIC_CLOCKEVENTS_BROADCAST
static void smp_timer_broadcast(const struct cpumask *mask) void tick_broadcast(const struct cpumask *mask)
{ {
smp_cross_call(mask, IPI_TIMER); smp_cross_call(mask, IPI_TIMER);
} }
@ -530,7 +524,6 @@ static void __cpuinit percpu_timer_setup(void)
struct clock_event_device *evt = &per_cpu(percpu_clockevent, cpu); struct clock_event_device *evt = &per_cpu(percpu_clockevent, cpu);
evt->cpumask = cpumask_of(cpu); evt->cpumask = cpumask_of(cpu);
evt->broadcast = smp_timer_broadcast;
if (!lt_ops || lt_ops->setup(evt)) if (!lt_ops || lt_ops->setup(evt))
broadcast_timer_setup(evt); broadcast_timer_setup(evt);
@ -596,11 +589,13 @@ void handle_IPI(int ipinr, struct pt_regs *regs)
case IPI_WAKEUP: case IPI_WAKEUP:
break; break;
#ifdef CONFIG_GENERIC_CLOCKEVENTS_BROADCAST
case IPI_TIMER: case IPI_TIMER:
irq_enter(); irq_enter();
ipi_timer(); tick_receive_broadcast();
irq_exit(); irq_exit();
break; break;
#endif
case IPI_RESCHEDULE: case IPI_RESCHEDULE:
scheduler_ipi(); scheduler_ipi();

View File

@ -161,6 +161,15 @@ clockevents_calc_mult_shift(struct clock_event_device *ce, u32 freq, u32 minsec)
extern void clockevents_suspend(void); extern void clockevents_suspend(void);
extern void clockevents_resume(void); extern void clockevents_resume(void);
#ifdef CONFIG_GENERIC_CLOCKEVENTS_BROADCAST
#ifdef CONFIG_ARCH_HAS_TICK_BROADCAST
extern void tick_broadcast(const struct cpumask *mask);
#else
#define tick_broadcast NULL
#endif
extern int tick_receive_broadcast(void);
#endif
#ifdef CONFIG_GENERIC_CLOCKEVENTS #ifdef CONFIG_GENERIC_CLOCKEVENTS
extern void clockevents_notify(unsigned long reason, void *arg); extern void clockevents_notify(unsigned long reason, void *arg);
#else #else

View File

@ -38,6 +38,10 @@ config GENERIC_CLOCKEVENTS_BUILD
default y default y
depends on GENERIC_CLOCKEVENTS depends on GENERIC_CLOCKEVENTS
# Architecture can handle broadcast in a driver-agnostic way
config ARCH_HAS_TICK_BROADCAST
bool
# Clockevents broadcasting infrastructure # Clockevents broadcasting infrastructure
config GENERIC_CLOCKEVENTS_BROADCAST config GENERIC_CLOCKEVENTS_BROADCAST
bool bool

View File

@ -18,6 +18,7 @@
#include <linux/percpu.h> #include <linux/percpu.h>
#include <linux/profile.h> #include <linux/profile.h>
#include <linux/sched.h> #include <linux/sched.h>
#include <linux/smp.h>
#include "tick-internal.h" #include "tick-internal.h"
@ -86,6 +87,11 @@ int tick_is_broadcast_device(struct clock_event_device *dev)
return (dev && tick_broadcast_device.evtdev == dev); return (dev && tick_broadcast_device.evtdev == dev);
} }
static void err_broadcast(const struct cpumask *mask)
{
pr_crit_once("Failed to broadcast timer tick. Some CPUs may be unresponsive.\n");
}
/* /*
* Check, if the device is disfunctional and a place holder, which * Check, if the device is disfunctional and a place holder, which
* needs to be handled by the broadcast device. * needs to be handled by the broadcast device.
@ -105,6 +111,13 @@ int tick_device_uses_broadcast(struct clock_event_device *dev, int cpu)
*/ */
if (!tick_device_is_functional(dev)) { if (!tick_device_is_functional(dev)) {
dev->event_handler = tick_handle_periodic; dev->event_handler = tick_handle_periodic;
if (!dev->broadcast)
dev->broadcast = tick_broadcast;
if (!dev->broadcast) {
pr_warn_once("%s depends on broadcast, but no broadcast function available\n",
dev->name);
dev->broadcast = err_broadcast;
}
cpumask_set_cpu(cpu, tick_get_broadcast_mask()); cpumask_set_cpu(cpu, tick_get_broadcast_mask());
tick_broadcast_start_periodic(tick_broadcast_device.evtdev); tick_broadcast_start_periodic(tick_broadcast_device.evtdev);
ret = 1; ret = 1;
@ -125,6 +138,23 @@ int tick_device_uses_broadcast(struct clock_event_device *dev, int cpu)
return ret; return ret;
} }
#ifdef CONFIG_GENERIC_CLOCKEVENTS_BROADCAST
int tick_receive_broadcast(void)
{
struct tick_device *td = this_cpu_ptr(&tick_cpu_device);
struct clock_event_device *evt = td->evtdev;
if (!evt)
return -ENODEV;
if (!evt->event_handler)
return -EINVAL;
evt->event_handler(evt);
return 0;
}
#endif
/* /*
* Broadcast the event to the cpus, which are set in the mask (mangled). * Broadcast the event to the cpus, which are set in the mask (mangled).
*/ */