linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [RFC] Imprecise timers.
@ 2008-07-22  3:02 David Woodhouse
  2008-07-22  3:05 ` [RFC] schedule_timeout_range() David Woodhouse
                   ` (2 more replies)
  0 siblings, 3 replies; 22+ messages in thread
From: David Woodhouse @ 2008-07-22  3:02 UTC (permalink / raw)
  To: linux-kernel; +Cc: Thomas Gleixner, Ingo Molnar, arjan

Many users of timers don't really care too much about exactly when their
timer fires -- and waking a CPU to satisfy such a timer is a waste of
power. This patch implements a 'range' timer which will fire at a 'convenient'
moment within given constraints.

It's implemented by a deferrable timer at the beginning of the range,
which will run some time later when the CPU happens to be awake. And a
non-deferrable timer at the hard deadline, to ensure it really does
happen by then.

Signed-off-by: David Woodhouse <David.Woodhouse@intel.com>
---
 include/linux/timer.h |  101 ++++++++++++++++++++++++++++++++++++++++++++++++-
 kernel/timer.c        |   18 +++++---
 2 files changed, 110 insertions(+), 9 deletions(-)

diff --git a/include/linux/timer.h b/include/linux/timer.h
index d4ba792..163137c 100644
--- a/include/linux/timer.h
+++ b/include/linux/timer.h
@@ -23,22 +23,60 @@ struct timer_list {
 #endif
 };
 
+/* This can probably be optimised somehow, but for now we do it the
+   simple way: two timers, one deferrable and one for the deadline. */
+struct range_timer {
+	struct timer_list early;
+	struct timer_list deadline;
+	void (*function)(unsigned long);
+	unsigned long data;
+};
+
 extern struct tvec_base boot_tvec_bases;
+/*
+ * Note that all tvec_bases are 2 byte aligned and lower bit of
+ * base in timer_list is guaranteed to be zero. Use the LSB for
+ * the new flag to indicate whether the timer is deferrable
+ */
+#define TBASE_DEFERRABLE_FLAG		(0x1)
 
-#define TIMER_INITIALIZER(_function, _expires, _data) {		\
+#define __TIMER_INITIALIZER(_function, _expires, _data, _base) {\
 		.entry = { .prev = TIMER_ENTRY_STATIC },	\
 		.function = (_function),			\
 		.expires = (_expires),				\
 		.data = (_data),				\
-		.base = &boot_tvec_bases,			\
+		.base = (struct tvec_base *)(_base),		\
 	}
+#define TIMER_INITIALIZER(_function, _expires, _data)		\
+	__TIMER_INITIALIZER(_function, _expires, _data, &boot_tvec_bases)
+
+#define TIMER_INITIALIZER_DEFERRABLE(_function, _expires, _data)	\
+	__TIMER_INITIALIZER(_function, _expires, _data,			\
+		(unsigned long)&boot_tvec_bases + TBASE_DEFERRABLE_FLAG)
+
+void range_timer_func(unsigned long timer);
 
 #define DEFINE_TIMER(_name, _function, _expires, _data)		\
 	struct timer_list _name =				\
 		TIMER_INITIALIZER(_function, _expires, _data)
 
+#define DEFINE_RANGE_TIMER(_name, _function, _expires, _deadline, _data)\
+	struct range_timer _name = {					\
+		.early = TIMER_INITIALIZER_DEFERRABLE(range_timer_func,	\
+			(_expires), (unsigned long)&(_name)),		\
+		.deadline = TIMER_INITIALIZER(range_timer_func,		\
+			(_deadline), (unsigned long)(&(_name))),	\
+		.function = (_function),				\
+		.data = (_data),					\
+	}
+
 void init_timer(struct timer_list *timer);
 void init_timer_deferrable(struct timer_list *timer);
+static inline void init_range_timer(struct range_timer *timer)
+{
+	init_timer_deferrable(&timer->early);
+	init_timer(&timer->deadline);
+}
 
 #ifdef CONFIG_DEBUG_OBJECTS_TIMERS
 extern void init_timer_on_stack(struct timer_list *timer);
@@ -51,6 +89,19 @@ static inline void init_timer_on_stack(struct timer_list *timer)
 }
 #endif
 
+static inline void setup_range_timer(struct range_timer *timer,
+				     void (*function)(unsigned long),
+				     unsigned long data)
+{
+	timer->early.function = range_timer_func;
+	timer->early.data = (unsigned long)timer;
+	timer->deadline.function = range_timer_func;
+	timer->deadline.data = (unsigned long)timer;
+	timer->function = function;
+	timer->data = data;
+
+	init_range_timer(timer);
+}
 static inline void setup_timer(struct timer_list * timer,
 				void (*function)(unsigned long),
 				unsigned long data)
@@ -83,12 +134,54 @@ static inline int timer_pending(const struct timer_list * timer)
 {
 	return timer->entry.next != NULL;
 }
+static inline int range_timer_pending(const struct range_timer *timer)
+{
+	return timer_pending(&timer->early) || timer_pending(&timer->deadline);
+}
 
 extern void add_timer_on(struct timer_list *timer, int cpu);
 extern int del_timer(struct timer_list * timer);
 extern int __mod_timer(struct timer_list *timer, unsigned long expires);
 extern int mod_timer(struct timer_list *timer, unsigned long expires);
 
+static inline void add_range_timer_on(struct range_timer *timer, int cpu)
+{
+	add_timer_on(&timer->early, cpu);
+	add_timer_on(&timer->deadline, cpu);
+}
+static inline int del_range_timer(struct range_timer *timer)
+{
+	return del_timer(&timer->early) | del_timer(&timer->deadline);
+}
+static inline int __mod_range_timer(struct range_timer *timer,
+				    unsigned long expires,
+				    unsigned long deadline)
+{
+	int ret;
+	WARN_ON(deadline < expires);
+	/* We need them on the same CPU */
+	preempt_disable();
+	ret = __mod_timer(&timer->early, expires);
+	ret |= __mod_timer(&timer->deadline, deadline);
+	preempt_enable();
+
+	return ret;
+}
+
+static inline int mod_range_timer(struct range_timer *timer,
+				  unsigned long expires,
+				  unsigned long deadline)
+{
+	int ret;
+	WARN_ON(deadline < expires);
+	/* We need them on the same CPU */
+	preempt_disable();
+	ret = mod_timer(&timer->early, expires);
+	ret |= mod_timer(&timer->deadline, deadline);
+	preempt_enable();
+
+	return ret;
+}
 /*
  * The jiffies value which is added to now, when there is no timer
  * in the timer wheel:
@@ -174,6 +267,10 @@ static inline void add_timer(struct timer_list *timer)
 # define del_timer_sync(t)		del_timer(t)
 #endif
 
+static inline int del_range_timer_sync(struct range_timer *timer)
+{
+	return del_timer_sync(&timer->early) | del_timer_sync(&timer->deadline);
+}
 #define del_singleshot_timer_sync(t) del_timer_sync(t)
 
 extern void init_timers(void);
diff --git a/kernel/timer.c b/kernel/timer.c
index 03bc7f1..e114f08 100644
--- a/kernel/timer.c
+++ b/kernel/timer.c
@@ -81,13 +81,6 @@ struct tvec_base boot_tvec_bases;
 EXPORT_SYMBOL(boot_tvec_bases);
 static DEFINE_PER_CPU(struct tvec_base *, tvec_bases) = &boot_tvec_bases;
 
-/*
- * Note that all tvec_bases are 2 byte aligned and lower bit of
- * base in timer_list is guaranteed to be zero. Use the LSB for
- * the new flag to indicate whether the timer is deferrable
- */
-#define TBASE_DEFERRABLE_FLAG		(0x1)
-
 /* Functions below help us manage 'deferrable' flag */
 static inline unsigned int tbase_get_deferrable(struct tvec_base *base)
 {
@@ -1525,3 +1518,14 @@ unsigned long msleep_interruptible(unsigned int msecs)
 }
 
 EXPORT_SYMBOL(msleep_interruptible);
+
+void range_timer_func(unsigned long t)
+{
+	struct range_timer *timer = (void *)t;
+
+	del_timer(&timer->early);
+	del_timer(&timer->deadline);
+
+	timer->function(timer->data);
+}
+EXPORT_SYMBOL_GPL(range_timer_func);
-- 
1.5.5.1


-- 
David Woodhouse                            Open Source Technology Centre
David.Woodhouse@intel.com                              Intel Corporation



^ permalink raw reply related	[flat|nested] 22+ messages in thread

end of thread, other threads:[~2008-08-12 22:13 UTC | newest]

Thread overview: 22+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2008-07-22  3:02 [RFC] Imprecise timers David Woodhouse
2008-07-22  3:05 ` [RFC] schedule_timeout_range() David Woodhouse
2008-07-22  3:56   ` Nick Piggin
2008-07-22  4:12     ` David Woodhouse
2008-07-22  4:26       ` Arjan van de Ven
2008-07-22  4:34         ` David Woodhouse
2008-07-22  4:33       ` Nick Piggin
2008-07-22  4:45         ` David Woodhouse
2008-07-22  4:50           ` Nick Piggin
2008-07-22  4:58             ` David Woodhouse
2008-07-22  5:35               ` Jan Engelhardt
2008-07-22  4:33     ` Arjan van de Ven
2008-07-22  7:19 ` [RFC] Imprecise timers Rene Herman
2008-07-22 12:54   ` Arjan van de Ven
2008-07-22 14:04     ` Rene Herman
2008-07-29  0:36 ` Pallipadi, Venkatesh
2008-08-09 12:54   ` Pavel Machek
2008-08-11 17:35     ` Venki Pallipadi
2008-08-12 12:00       ` Pavel Machek
2008-08-12 18:11         ` Venki Pallipadi
2008-08-12 21:55           ` Alan Cox
2008-08-12 21:58           ` Pavel Machek

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).