2007-02-16 17:28:03 +08:00
|
|
|
/*
|
|
|
|
* linux/kernel/time/tick-oneshot.c
|
|
|
|
*
|
|
|
|
* This file contains functions which manage high resolution tick
|
|
|
|
* related events.
|
|
|
|
*
|
|
|
|
* Copyright(C) 2005-2006, Thomas Gleixner <tglx@linutronix.de>
|
|
|
|
* Copyright(C) 2005-2007, Red Hat, Inc., Ingo Molnar
|
|
|
|
* Copyright(C) 2006-2007, Timesys Corp., Thomas Gleixner
|
|
|
|
*
|
|
|
|
* This code is licenced under the GPL version 2. For details see
|
|
|
|
* kernel-base/COPYING.
|
|
|
|
*/
|
|
|
|
#include <linux/cpu.h>
|
|
|
|
#include <linux/err.h>
|
|
|
|
#include <linux/hrtimer.h>
|
[S390] genirq/clockevents: move irq affinity prototypes/inlines to interrupt.h
> Generic code is not supposed to include irq.h. Replace this include
> by linux/hardirq.h instead and add/replace an include of linux/irq.h
> in asm header files where necessary.
> This change should only matter for architectures that make use of
> GENERIC_CLOCKEVENTS.
> Architectures in question are mips, x86, arm, sh, powerpc, uml and sparc64.
>
> I did some cross compile tests for mips, x86_64, arm, powerpc and sparc64.
> This patch fixes also build breakages caused by the include replacement in
> tick-common.h.
I generally dislike adding optional linux/* includes in asm/* includes -
I'm nervous about this causing include loops.
However, there's a separate point to be discussed here.
That is, what interfaces are expected of every architecture in the kernel.
If generic code wants to be able to set the affinity of interrupts, then
that needs to become part of the interfaces listed in linux/interrupt.h
rather than linux/irq.h.
So what I suggest is this approach instead (against Linus' tree of a
couple of days ago) - we move irq_set_affinity() and irq_can_set_affinity()
to linux/interrupt.h, change the linux/irq.h includes to linux/interrupt.h
and include asm/irq_regs.h where needed (asm/irq_regs.h is supposed to be
rarely used include since not much touches the stacked parent context
registers.)
Build tested on ARM PXA family kernels and ARM's Realview platform
kernels which both use genirq.
[ tglx@linutronix.de: add GENERIC_HARDIRQ dependencies ]
Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
Signed-off-by: Heiko Carstens <heiko.carstens@de.ibm.com>
2008-04-17 13:46:24 +08:00
|
|
|
#include <linux/interrupt.h>
|
2007-02-16 17:28:03 +08:00
|
|
|
#include <linux/percpu.h>
|
|
|
|
#include <linux/profile.h>
|
|
|
|
#include <linux/sched.h>
|
|
|
|
#include <linux/tick.h>
|
|
|
|
|
|
|
|
#include "tick-internal.h"
|
|
|
|
|
2010-03-13 00:34:14 +08:00
|
|
|
/* Limit min_delta to a jiffie */
|
|
|
|
#define MIN_DELTA_LIMIT (NSEC_PER_SEC / HZ)
|
|
|
|
|
|
|
|
static int tick_increase_min_delta(struct clock_event_device *dev)
|
|
|
|
{
|
|
|
|
/* Nothing to do if we already reached the limit */
|
|
|
|
if (dev->min_delta_ns >= MIN_DELTA_LIMIT)
|
|
|
|
return -ETIME;
|
|
|
|
|
|
|
|
if (dev->min_delta_ns < 5000)
|
|
|
|
dev->min_delta_ns = 5000;
|
|
|
|
else
|
|
|
|
dev->min_delta_ns += dev->min_delta_ns >> 1;
|
|
|
|
|
|
|
|
if (dev->min_delta_ns > MIN_DELTA_LIMIT)
|
|
|
|
dev->min_delta_ns = MIN_DELTA_LIMIT;
|
|
|
|
|
|
|
|
printk(KERN_WARNING "CE: %s increased min_delta_ns to %llu nsec\n",
|
|
|
|
dev->name ? dev->name : "?",
|
|
|
|
(unsigned long long) dev->min_delta_ns);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2007-02-16 17:28:03 +08:00
|
|
|
/**
|
2008-09-04 05:37:03 +08:00
|
|
|
* tick_program_event internal worker function
|
2007-02-16 17:28:03 +08:00
|
|
|
*/
|
2008-09-04 05:37:14 +08:00
|
|
|
int tick_dev_program_event(struct clock_event_device *dev, ktime_t expires,
|
|
|
|
int force)
|
2007-02-16 17:28:03 +08:00
|
|
|
{
|
|
|
|
ktime_t now = ktime_get();
|
2008-09-04 05:37:14 +08:00
|
|
|
int i;
|
2007-02-16 17:28:03 +08:00
|
|
|
|
2008-09-04 05:37:14 +08:00
|
|
|
for (i = 0;;) {
|
2007-02-16 17:28:03 +08:00
|
|
|
int ret = clockevents_program_event(dev, expires, now);
|
|
|
|
|
|
|
|
if (!ret || !force)
|
|
|
|
return ret;
|
2008-09-04 05:37:14 +08:00
|
|
|
|
2010-03-13 00:34:14 +08:00
|
|
|
dev->retries++;
|
2008-09-04 05:37:14 +08:00
|
|
|
/*
|
2010-03-13 00:34:14 +08:00
|
|
|
* We tried 3 times to program the device with the given
|
|
|
|
* min_delta_ns. If that's not working then we increase it
|
2008-09-04 05:37:14 +08:00
|
|
|
* and emit a warning.
|
|
|
|
*/
|
|
|
|
if (++i > 2) {
|
2008-09-10 03:38:57 +08:00
|
|
|
/* Increase the min. delta and try again */
|
2010-03-13 00:34:14 +08:00
|
|
|
if (tick_increase_min_delta(dev)) {
|
|
|
|
/*
|
|
|
|
* Get out of the loop if min_delta_ns
|
|
|
|
* hit the limit already. That's
|
|
|
|
* better than staying here forever.
|
|
|
|
*
|
|
|
|
* We clear next_event so we have a
|
|
|
|
* chance that the box survives.
|
|
|
|
*/
|
|
|
|
printk(KERN_WARNING
|
|
|
|
"CE: Reprogramming failure. Giving up\n");
|
|
|
|
dev->next_event.tv64 = KTIME_MAX;
|
|
|
|
return -ETIME;
|
|
|
|
}
|
2008-09-04 05:37:14 +08:00
|
|
|
i = 0;
|
|
|
|
}
|
|
|
|
|
2007-02-16 17:28:03 +08:00
|
|
|
now = ktime_get();
|
2008-09-04 05:37:14 +08:00
|
|
|
expires = ktime_add_ns(now, dev->min_delta_ns);
|
2007-02-16 17:28:03 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-09-04 05:37:03 +08:00
|
|
|
/**
|
|
|
|
* tick_program_event
|
|
|
|
*/
|
|
|
|
int tick_program_event(ktime_t expires, int force)
|
|
|
|
{
|
|
|
|
struct clock_event_device *dev = __get_cpu_var(tick_cpu_device).evtdev;
|
|
|
|
|
2008-09-04 05:37:14 +08:00
|
|
|
return tick_dev_program_event(dev, expires, force);
|
2008-09-04 05:37:03 +08:00
|
|
|
}
|
|
|
|
|
2007-03-17 07:25:52 +08:00
|
|
|
/**
|
|
|
|
* tick_resume_onshot - resume oneshot mode
|
|
|
|
*/
|
|
|
|
void tick_resume_oneshot(void)
|
|
|
|
{
|
|
|
|
struct tick_device *td = &__get_cpu_var(tick_cpu_device);
|
|
|
|
struct clock_event_device *dev = td->evtdev;
|
|
|
|
|
|
|
|
clockevents_set_mode(dev, CLOCK_EVT_MODE_ONESHOT);
|
|
|
|
tick_program_event(ktime_get(), 1);
|
|
|
|
}
|
|
|
|
|
2007-02-16 17:28:03 +08:00
|
|
|
/**
|
|
|
|
* tick_setup_oneshot - setup the event device for oneshot mode (hres or nohz)
|
|
|
|
*/
|
|
|
|
void tick_setup_oneshot(struct clock_event_device *newdev,
|
|
|
|
void (*handler)(struct clock_event_device *),
|
|
|
|
ktime_t next_event)
|
|
|
|
{
|
|
|
|
newdev->event_handler = handler;
|
|
|
|
clockevents_set_mode(newdev, CLOCK_EVT_MODE_ONESHOT);
|
2008-09-04 05:37:14 +08:00
|
|
|
tick_dev_program_event(newdev, next_event, 1);
|
2007-02-16 17:28:03 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* tick_switch_to_oneshot - switch to oneshot mode
|
|
|
|
*/
|
|
|
|
int tick_switch_to_oneshot(void (*handler)(struct clock_event_device *))
|
|
|
|
{
|
|
|
|
struct tick_device *td = &__get_cpu_var(tick_cpu_device);
|
|
|
|
struct clock_event_device *dev = td->evtdev;
|
|
|
|
|
|
|
|
if (!dev || !(dev->features & CLOCK_EVT_FEAT_ONESHOT) ||
|
2007-07-21 19:37:36 +08:00
|
|
|
!tick_device_is_functional(dev)) {
|
|
|
|
|
|
|
|
printk(KERN_INFO "Clockevents: "
|
|
|
|
"could not switch to one-shot mode:");
|
|
|
|
if (!dev) {
|
|
|
|
printk(" no tick device\n");
|
|
|
|
} else {
|
|
|
|
if (!tick_device_is_functional(dev))
|
|
|
|
printk(" %s is not functional.\n", dev->name);
|
|
|
|
else
|
|
|
|
printk(" %s does not support one-shot mode.\n",
|
|
|
|
dev->name);
|
|
|
|
}
|
2007-02-16 17:28:03 +08:00
|
|
|
return -EINVAL;
|
2007-07-21 19:37:36 +08:00
|
|
|
}
|
2007-02-16 17:28:03 +08:00
|
|
|
|
|
|
|
td->mode = TICKDEV_MODE_ONESHOT;
|
|
|
|
dev->event_handler = handler;
|
|
|
|
clockevents_set_mode(dev, CLOCK_EVT_MODE_ONESHOT);
|
|
|
|
tick_broadcast_switch_to_oneshot();
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2009-06-12 17:29:27 +08:00
|
|
|
/**
|
|
|
|
* tick_check_oneshot_mode - check whether the system is in oneshot mode
|
|
|
|
*
|
|
|
|
* returns 1 when either nohz or highres are enabled. otherwise 0.
|
|
|
|
*/
|
|
|
|
int tick_oneshot_mode_active(void)
|
|
|
|
{
|
|
|
|
unsigned long flags;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
local_irq_save(flags);
|
|
|
|
ret = __get_cpu_var(tick_cpu_device).mode == TICKDEV_MODE_ONESHOT;
|
|
|
|
local_irq_restore(flags);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2007-02-16 17:28:03 +08:00
|
|
|
#ifdef CONFIG_HIGH_RES_TIMERS
|
|
|
|
/**
|
|
|
|
* tick_init_highres - switch to high resolution mode
|
|
|
|
*
|
|
|
|
* Called with interrupts disabled.
|
|
|
|
*/
|
|
|
|
int tick_init_highres(void)
|
|
|
|
{
|
|
|
|
return tick_switch_to_oneshot(hrtimer_interrupt);
|
|
|
|
}
|
|
|
|
#endif
|