Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

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

[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
Loading
Loading
Loading
Loading
+4 −0
Original line number Original line Diff line number Diff line
@@ -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.


+110 −6
Original line number Original line Diff line number Diff line
@@ -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 {
#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 {
 * @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 {
	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 {
 * @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 {
	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 {
 * @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)
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)
	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);
+3 −0
Original line number Original line Diff line number Diff line
@@ -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
+1 −2
Original line number Original line Diff line number Diff line
@@ -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);
+520 −48

File changed.

Preview size limit exceeded, changes collapsed.

Loading