Commit 54cdfdb4 authored by Thomas Gleixner's avatar Thomas Gleixner Committed by Linus Torvalds

[PATCH] hrtimers: add high resolution timer support

Implement high resolution timers on top of the hrtimers infrastructure and the
clockevents / tick-management framework.  This provides accurate timers for
all hrtimer subsystem users.
Signed-off-by: default avatarThomas Gleixner <tglx@linutronix.de>
Signed-off-by: default avatarIngo Molnar <mingo@elte.hu>
Cc: john stultz <johnstul@us.ibm.com>
Cc: Roman Zippel <zippel@linux-m68k.org>
Signed-off-by: default avatarAndrew Morton <akpm@linux-foundation.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@linux-foundation.org>
parent d40891e7
...@@ -609,6 +609,10 @@ and is between 256 and 4096 characters. It is defined in the file ...@@ -609,6 +609,10 @@ and is between 256 and 4096 characters. It is defined in the file
highmem otherwise. This also works to reduce highmem highmem otherwise. This also works to reduce highmem
size on bigger boxes. size on bigger boxes.
highres= [KNL] Enable/disable high resolution timer mode.
Valid parameters: "on", "off"
Default: "on"
hisax= [HW,ISDN] hisax= [HW,ISDN]
See Documentation/isdn/README.HiSax. See Documentation/isdn/README.HiSax.
......
...@@ -41,16 +41,35 @@ enum hrtimer_restart { ...@@ -41,16 +41,35 @@ enum hrtimer_restart {
}; };
/* /*
* Bit values to track state of the timer * hrtimer callback modes:
*
* HRTIMER_CB_SOFTIRQ: Callback must run in softirq context
* HRTIMER_CB_IRQSAFE: Callback may run in hardirq context
* HRTIMER_CB_IRQSAFE_NO_RESTART: Callback may run in hardirq context and
* does not restart the timer
* HRTIMER_CB_IRQSAFE_NO_SOFTIRQ: Callback must run in softirq context
* Special mode for tick emultation
*/
enum hrtimer_cb_mode {
HRTIMER_CB_SOFTIRQ,
HRTIMER_CB_IRQSAFE,
HRTIMER_CB_IRQSAFE_NO_RESTART,
HRTIMER_CB_IRQSAFE_NO_SOFTIRQ,
};
/*
* Values to track state of the timer
* *
* Possible states: * Possible states:
* *
* 0x00 inactive * 0x00 inactive
* 0x01 enqueued into rbtree * 0x01 enqueued into rbtree
* 0x02 callback function running * 0x02 callback function running
* 0x04 callback pending (high resolution mode)
*
* Special case:
* 0x03 callback function running and enqueued * 0x03 callback function running and enqueued
* (was requeued on another CPU) * (was requeued on another CPU)
*
* The "callback function running and enqueued" status is only possible on * The "callback function running and enqueued" status is only possible on
* SMP. It happens for example when a posix timer expired and the callback * SMP. It happens for example when a posix timer expired and the callback
* queued a signal. Between dropping the lock which protects the posix timer * queued a signal. Between dropping the lock which protects the posix timer
...@@ -67,6 +86,7 @@ enum hrtimer_restart { ...@@ -67,6 +86,7 @@ enum hrtimer_restart {
#define HRTIMER_STATE_INACTIVE 0x00 #define HRTIMER_STATE_INACTIVE 0x00
#define HRTIMER_STATE_ENQUEUED 0x01 #define HRTIMER_STATE_ENQUEUED 0x01
#define HRTIMER_STATE_CALLBACK 0x02 #define HRTIMER_STATE_CALLBACK 0x02
#define HRTIMER_STATE_PENDING 0x04
/** /**
* struct hrtimer - the basic hrtimer structure * struct hrtimer - the basic hrtimer structure
...@@ -77,8 +97,17 @@ enum hrtimer_restart { ...@@ -77,8 +97,17 @@ enum hrtimer_restart {
* @function: timer expiry callback function * @function: timer expiry callback function
* @base: pointer to the timer base (per cpu and per clock) * @base: pointer to the timer base (per cpu and per clock)
* @state: state information (See bit values above) * @state: state information (See bit values above)
* @cb_mode: high resolution timer feature to select the callback execution
* mode
* @cb_entry: list head to enqueue an expired timer into the callback list
* @start_site: timer statistics field to store the site where the timer
* was started
* @start_comm: timer statistics field to store the name of the process which
* started the timer
* @start_pid: timer statistics field to store the pid of the task which
* started the timer
* *
* The hrtimer structure must be initialized by init_hrtimer_#CLOCKTYPE() * The hrtimer structure must be initialized by hrtimer_init()
*/ */
struct hrtimer { struct hrtimer {
struct rb_node node; struct rb_node node;
...@@ -86,6 +115,10 @@ struct hrtimer { ...@@ -86,6 +115,10 @@ struct hrtimer {
enum hrtimer_restart (*function)(struct hrtimer *); enum hrtimer_restart (*function)(struct hrtimer *);
struct hrtimer_clock_base *base; struct hrtimer_clock_base *base;
unsigned long state; unsigned long state;
#ifdef CONFIG_HIGH_RES_TIMERS
enum hrtimer_cb_mode cb_mode;
struct list_head cb_entry;
#endif
}; };
/** /**
...@@ -110,6 +143,9 @@ struct hrtimer_sleeper { ...@@ -110,6 +143,9 @@ struct hrtimer_sleeper {
* @get_time: function to retrieve the current time of the clock * @get_time: function to retrieve the current time of the clock
* @get_softirq_time: function to retrieve the current time from the softirq * @get_softirq_time: function to retrieve the current time from the softirq
* @softirq_time: the time when running the hrtimer queue in the softirq * @softirq_time: the time when running the hrtimer queue in the softirq
* @cb_pending: list of timers where the callback is pending
* @offset: offset of this clock to the monotonic base
* @reprogram: function to reprogram the timer event
*/ */
struct hrtimer_clock_base { struct hrtimer_clock_base {
struct hrtimer_cpu_base *cpu_base; struct hrtimer_cpu_base *cpu_base;
...@@ -120,6 +156,12 @@ struct hrtimer_clock_base { ...@@ -120,6 +156,12 @@ struct hrtimer_clock_base {
ktime_t (*get_time)(void); ktime_t (*get_time)(void);
ktime_t (*get_softirq_time)(void); ktime_t (*get_softirq_time)(void);
ktime_t softirq_time; ktime_t softirq_time;
#ifdef CONFIG_HIGH_RES_TIMERS
ktime_t offset;
int (*reprogram)(struct hrtimer *t,
struct hrtimer_clock_base *b,
ktime_t n);
#endif
}; };
#define HRTIMER_MAX_CLOCK_BASES 2 #define HRTIMER_MAX_CLOCK_BASES 2
...@@ -131,19 +173,74 @@ struct hrtimer_clock_base { ...@@ -131,19 +173,74 @@ struct hrtimer_clock_base {
* @lock_key: the lock_class_key for use with lockdep * @lock_key: the lock_class_key for use with lockdep
* @clock_base: array of clock bases for this cpu * @clock_base: array of clock bases for this cpu
* @curr_timer: the timer which is executing a callback right now * @curr_timer: the timer which is executing a callback right now
* @expires_next: absolute time of the next event which was scheduled
* via clock_set_next_event()
* @hres_active: State of high resolution mode
* @check_clocks: Indictator, when set evaluate time source and clock
* event devices whether high resolution mode can be
* activated.
* @cb_pending: Expired timers are moved from the rbtree to this
* list in the timer interrupt. The list is processed
* in the softirq.
* @nr_events: Total number of timer interrupt events
*/ */
struct hrtimer_cpu_base { struct hrtimer_cpu_base {
spinlock_t lock; spinlock_t lock;
struct lock_class_key lock_key; struct lock_class_key lock_key;
struct hrtimer_clock_base clock_base[HRTIMER_MAX_CLOCK_BASES]; struct hrtimer_clock_base clock_base[HRTIMER_MAX_CLOCK_BASES];
#ifdef CONFIG_HIGH_RES_TIMERS
ktime_t expires_next;
int hres_active;
struct list_head cb_pending;
unsigned long nr_events;
#endif
}; };
#ifdef CONFIG_HIGH_RES_TIMERS
struct clock_event_device;
extern void clock_was_set(void);
extern void hrtimer_interrupt(struct clock_event_device *dev);
/*
* In high resolution mode the time reference must be read accurate
*/
static inline ktime_t hrtimer_cb_get_time(struct hrtimer *timer)
{
return timer->base->get_time();
}
/*
* The resolution of the clocks. The resolution value is returned in
* the clock_getres() system call to give application programmers an
* idea of the (in)accuracy of timers. Timer values are rounded up to
* this resolution values.
*/
# define KTIME_HIGH_RES (ktime_t) { .tv64 = 1 }
# define KTIME_MONOTONIC_RES KTIME_HIGH_RES
#else
# define KTIME_MONOTONIC_RES KTIME_LOW_RES
/* /*
* clock_was_set() is a NOP for non- high-resolution systems. The * clock_was_set() is a NOP for non- high-resolution systems. The
* time-sorted order guarantees that a timer does not expire early and * time-sorted order guarantees that a timer does not expire early and
* is expired in the next softirq when the clock was advanced. * is expired in the next softirq when the clock was advanced.
*/ */
#define clock_was_set() do { } while (0) static inline void clock_was_set(void) { }
/*
* In non high resolution mode the time reference is taken from
* the base softirq time variable.
*/
static inline ktime_t hrtimer_cb_get_time(struct hrtimer *timer)
{
return timer->base->softirq_time;
}
#endif
extern ktime_t ktime_get(void); extern ktime_t ktime_get(void);
extern ktime_t ktime_get_real(void); extern ktime_t ktime_get_real(void);
...@@ -168,9 +265,7 @@ static inline int hrtimer_restart(struct hrtimer *timer) ...@@ -168,9 +265,7 @@ static inline int hrtimer_restart(struct hrtimer *timer)
extern ktime_t hrtimer_get_remaining(const struct hrtimer *timer); extern ktime_t hrtimer_get_remaining(const struct hrtimer *timer);
extern int hrtimer_get_res(const clockid_t which_clock, struct timespec *tp); extern int hrtimer_get_res(const clockid_t which_clock, struct timespec *tp);
#ifdef CONFIG_NO_IDLE_HZ
extern ktime_t hrtimer_get_next_event(void); extern ktime_t hrtimer_get_next_event(void);
#endif
/* /*
* A timer is active, when it is enqueued into the rbtree or the callback * A timer is active, when it is enqueued into the rbtree or the callback
...@@ -181,6 +276,15 @@ static inline int hrtimer_active(const struct hrtimer *timer) ...@@ -181,6 +276,15 @@ static inline int hrtimer_active(const struct hrtimer *timer)
return timer->state != HRTIMER_STATE_INACTIVE; return timer->state != HRTIMER_STATE_INACTIVE;
} }
/*
* Helper function to check, whether the timer is on one of the queues
*/
static inline int hrtimer_is_queued(struct hrtimer *timer)
{
return timer->state &
(HRTIMER_STATE_ENQUEUED | HRTIMER_STATE_PENDING);
}
/* Forward a hrtimer so it expires after now: */ /* Forward a hrtimer so it expires after now: */
extern unsigned long extern unsigned long
hrtimer_forward(struct hrtimer *timer, ktime_t now, ktime_t interval); hrtimer_forward(struct hrtimer *timer, ktime_t now, ktime_t interval);
......
...@@ -242,6 +242,9 @@ enum ...@@ -242,6 +242,9 @@ enum
BLOCK_SOFTIRQ, BLOCK_SOFTIRQ,
TASKLET_SOFTIRQ, TASKLET_SOFTIRQ,
SCHED_SOFTIRQ, SCHED_SOFTIRQ,
#ifdef CONFIG_HIGH_RES_TIMERS
HRTIMER_SOFTIRQ,
#endif
}; };
/* softirq mask and active fields moved to irq_cpustat_t in /* softirq mask and active fields moved to irq_cpustat_t in
......
...@@ -261,8 +261,7 @@ static inline s64 ktime_to_ns(const ktime_t kt) ...@@ -261,8 +261,7 @@ static inline s64 ktime_to_ns(const ktime_t kt)
* idea of the (in)accuracy of timers. Timer values are rounded up to * idea of the (in)accuracy of timers. Timer values are rounded up to
* this resolution values. * this resolution values.
*/ */
#define KTIME_REALTIME_RES (ktime_t){ .tv64 = TICK_NSEC } #define KTIME_LOW_RES (ktime_t){ .tv64 = TICK_NSEC }
#define KTIME_MONOTONIC_RES (ktime_t){ .tv64 = TICK_NSEC }
/* Get the monotonic time in timespec format: */ /* Get the monotonic time in timespec format: */
extern void ktime_get_ts(struct timespec *ts); extern void ktime_get_ts(struct timespec *ts);
......
This diff is collapsed.
...@@ -136,7 +136,7 @@ enum hrtimer_restart it_real_fn(struct hrtimer *timer) ...@@ -136,7 +136,7 @@ enum hrtimer_restart it_real_fn(struct hrtimer *timer)
send_group_sig_info(SIGALRM, SEND_SIG_PRIV, sig->tsk); send_group_sig_info(SIGALRM, SEND_SIG_PRIV, sig->tsk);
if (sig->it_real_incr.tv64 != 0) { if (sig->it_real_incr.tv64 != 0) {
hrtimer_forward(timer, timer->base->softirq_time, hrtimer_forward(timer, hrtimer_cb_get_time(timer),
sig->it_real_incr); sig->it_real_incr);
return HRTIMER_RESTART; return HRTIMER_RESTART;
} }
......
...@@ -356,7 +356,7 @@ static enum hrtimer_restart posix_timer_fn(struct hrtimer *timer) ...@@ -356,7 +356,7 @@ static enum hrtimer_restart posix_timer_fn(struct hrtimer *timer)
if (timr->it.real.interval.tv64 != 0) { if (timr->it.real.interval.tv64 != 0) {
timr->it_overrun += timr->it_overrun +=
hrtimer_forward(timer, hrtimer_forward(timer,
timer->base->softirq_time, hrtimer_cb_get_time(timer),
timr->it.real.interval); timr->it.real.interval);
ret = HRTIMER_RESTART; ret = HRTIMER_RESTART;
++timr->it_requeue_pending; ++timr->it_requeue_pending;
......
...@@ -13,3 +13,13 @@ config NO_HZ ...@@ -13,3 +13,13 @@ config NO_HZ
This option enables a tickless system: timer interrupts will This option enables a tickless system: timer interrupts will
only trigger on an as-needed basis both when the system is only trigger on an as-needed basis both when the system is
busy and when the system is idle. busy and when the system is idle.
config HIGH_RES_TIMERS
bool "High Resolution Timer Support"
depends on GENERIC_TIME && GENERIC_CLOCKEVENTS
select TICK_ONESHOT
help
This option enables high resolution timer support. If your
hardware is not capable then this option only increases
the size of the kernel image.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment