diff options
author | Thomas Gleixner <tglx@linutronix.de> | 2007-02-16 01:28:02 -0800 |
---|---|---|
committer | Linus Torvalds <torvalds@woody.linux-foundation.org> | 2007-02-16 08:13:59 -0800 |
commit | f8381cba04ba8173fd5a2b8e5cd8b3290ee13a98 (patch) | |
tree | ad8c9f91ce031a04c62ff75fcd3237fc666f1c2e /kernel/time/tick-common.c | |
parent | 906568c9c668ff994f4078932ec6ae1e3950d1af (diff) |
[PATCH] tick-management: broadcast functionality
With Ingo Molnar <mingo@elte.hu>
Add broadcast functionality, so per cpu clock event devices can be registered
as dummy devices or switched from/to broadcast on demand. The broadcast
function distributes the events via the broadcast function of the clock event
device. This is primarily designed to replace the switch apic timer to / from
IPI in power states, where the apic stops.
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Signed-off-by: Ingo Molnar <mingo@elte.hu>
Cc: john stultz <johnstul@us.ibm.com>
Cc: Roman Zippel <zippel@linux-m68k.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'kernel/time/tick-common.c')
-rw-r--r-- | kernel/time/tick-common.c | 53 |
1 files changed, 44 insertions, 9 deletions
diff --git a/kernel/time/tick-common.c b/kernel/time/tick-common.c index 46e4381c26e..48167a6ae55 100644 --- a/kernel/time/tick-common.c +++ b/kernel/time/tick-common.c @@ -20,17 +20,19 @@ #include <linux/sched.h> #include <linux/tick.h> +#include "tick-internal.h" + /* * Tick devices */ -static DEFINE_PER_CPU(struct tick_device, tick_cpu_device); +DEFINE_PER_CPU(struct tick_device, tick_cpu_device); /* * Tick next event: keeps track of the tick time */ -static ktime_t tick_next_period; -static ktime_t tick_period; +ktime_t tick_next_period; +ktime_t tick_period; static int tick_do_timer_cpu = -1; -static DEFINE_SPINLOCK(tick_device_lock); +DEFINE_SPINLOCK(tick_device_lock); /* * Periodic tick @@ -78,9 +80,13 @@ void tick_handle_periodic(struct clock_event_device *dev) /* * Setup the device for a periodic tick */ -void tick_setup_periodic(struct clock_event_device *dev) +void tick_setup_periodic(struct clock_event_device *dev, int broadcast) { - dev->event_handler = tick_handle_periodic; + tick_set_periodic_handler(dev, broadcast); + + /* Broadcast setup ? */ + if (!tick_device_is_functional(dev)) + return; if (dev->features & CLOCK_EVT_FEAT_PERIODIC) { clockevents_set_mode(dev, CLOCK_EVT_MODE_PERIODIC); @@ -145,6 +151,15 @@ static void tick_setup_device(struct tick_device *td, if (!cpus_equal(newdev->cpumask, cpumask)) irq_set_affinity(newdev->irq, cpumask); + /* + * When global broadcasting is active, check if the current + * device is registered as a placeholder for broadcast mode. + * This allows us to handle this x86 misfeature in a generic + * way. + */ + if (tick_device_uses_broadcast(newdev, cpu)) + return; + if (td->mode == TICKDEV_MODE_PERIODIC) tick_setup_periodic(newdev, 0); } @@ -197,19 +212,33 @@ static int tick_check_new_device(struct clock_event_device *newdev) * Check the rating */ if (curdev->rating >= newdev->rating) - goto out; + goto out_bc; } /* * Replace the eventually existing device by the new - * device. + * device. If the current device is the broadcast device, do + * not give it back to the clockevents layer ! */ + if (tick_is_broadcast_device(curdev)) { + clockevents_set_mode(curdev, CLOCK_EVT_MODE_SHUTDOWN); + curdev = NULL; + } clockevents_exchange_device(curdev, newdev); tick_setup_device(td, newdev, cpu, cpumask); - ret = NOTIFY_STOP; + spin_unlock_irqrestore(&tick_device_lock, flags); + return NOTIFY_STOP; + +out_bc: + /* + * Can the new device be used as a broadcast device ? + */ + if (tick_check_broadcast_device(newdev)) + ret = NOTIFY_STOP; out: spin_unlock_irqrestore(&tick_device_lock, flags); + return ret; } @@ -251,7 +280,13 @@ static int tick_notify(struct notifier_block *nb, unsigned long reason, case CLOCK_EVT_NOTIFY_ADD: return tick_check_new_device(dev); + case CLOCK_EVT_NOTIFY_BROADCAST_ON: + case CLOCK_EVT_NOTIFY_BROADCAST_OFF: + tick_broadcast_on_off(reason, dev); + break; + case CLOCK_EVT_NOTIFY_CPU_DEAD: + tick_shutdown_broadcast(dev); tick_shutdown(dev); break; |