Skip to contentSkip to conversation

new human-time soft-timer subsystem

RFC PATCH In review Nishanth Aravamudan 4 participants 12 messages Jul 14, 2005 – Jul 18, 2005

Subsystems (3)
lore
Share & tools
lore
#0 lore raw

cover new human-time soft-timer subsystem

On 14.07.2005 [12:18:41 -0700], john stultz wrote:

<snip>

Quoted 5 lines

Nish has some code, which I hope he'll be sending out shortly that does just this, converting the soft-timer subsystem to use absolute time instead of ticks for expiration. I feel it both simplifies the code and makes it easier to changing the timer interrupt frequency while the system is running.

Here's the set of patches John promised :)

1/4: add jiffies conversion helper functions

2/4: core human-time modifications to soft-timer subsystem

3/4: add new human-time schedule_timeout() functions

4/4: rework sys_nanosleep() to use schedule_timeout_nsecs()

The individual patches have more details, but the gist is this:

We no longer use jiffies (the variable) as the basis for determining what "time" a timer should expire or when it should be added. Instead, we use a new function, do_monotonic_clock(), which is simply a wrapper for getnstimeofday(). That is to say, we use uptime in nanoseconds. But, to avoid modifying the existing soft-timer algorithm, we convert the 64-bit nanosecond value to "timerinterval" units. These units are simply 2^TIMEINTERVAL_BITS nanoseconds in length (thus determined at compile time).

To sum up, soft-timers now use time (as defined by the timeofday-subsystem) not ticks. Hopefully, the individual e-mails/patches make this change clear.

Thanks,
Nish

#1 lore raw

RFC PATCH 1/4 add jiffies_to_nsecs() helper and fix up size of usecs

From: Nishanth Aravamudan <[email protected]>

Description: Add a jiffies_to_nsecs() helper function. Make consistent the size of microseconds (unsigned long) throughout the conversion functions.

---

 jiffies.h |   15 +++++++++++++--
 1 files changed, 13 insertions(+), 2 deletions(-)
include/linux/jiffies.h +13 -2
diff -urpN 2.6.13-rc3-base/include/linux/jiffies.h 2.6.13-rc3-dev/include/linux/jiffies.h
@@ -263,7 +263,7 @@ static inline unsigned int jiffies_to_ms
 #endif
 }
 
-static inline unsigned int jiffies_to_usecs(const unsigned long j)
+static inline unsigned long jiffies_to_usecs(const unsigned long j)
 {
 #if HZ <= 1000000 && !(1000000 % HZ)
 	return (1000000 / HZ) * j;
@@ -274,6 +274,17 @@ static inline unsigned int jiffies_to_us
 #endif
 }
 
+static inline u64 jiffies_to_nsecs(const unsigned long j)
+{
+#if HZ <= NSEC_PER_SEC && !(NSEC_PER_SEC % HZ)
+	return (NSEC_PER_SEC / HZ) * (u64)j;
+#elif HZ > NSEC_PER_SEC && !(HZ % NSEC_PER_SEC)
+	return ((u64)j + (HZ / NSEC_PER_SEC) - 1)/(HZ / NSEC_PER_SEC);
+#else
+	return ((u64)j * NSEC_PER_SEC) / HZ;
+#endif
+}
+
 static inline unsigned long msecs_to_jiffies(const unsigned int m)
 {
 	if (m > jiffies_to_msecs(MAX_JIFFY_OFFSET))
@@ -287,7 +298,7 @@ static inline unsigned long msecs_to_jif
 #endif
 }
 
-static inline unsigned long usecs_to_jiffies(const unsigned int u)
+static inline unsigned long usecs_to_jiffies(const unsigned long u)
 {
 	if (u > jiffies_to_usecs(MAX_JIFFY_OFFSET))
 		return MAX_JIFFY_OFFSET;
#2 lore raw

Re: [RFC][PATCH 1/4] add jiffies_to_nsecs() helper and fix up size of usecs

include/linux/jiffies.h static inline unsigned int jiffies_to_us · line 286

Quoted 10 lines · Nishanth Aravamudan
include/linux/jiffies.h +10 -0
+static inline u64 jiffies_to_nsecs(const unsigned long j)
+{
+#if HZ <= NSEC_PER_SEC && !(NSEC_PER_SEC % HZ)
+	return (NSEC_PER_SEC / HZ) * (u64)j;
+#elif HZ > NSEC_PER_SEC && !(HZ % NSEC_PER_SEC)
+	return ((u64)j + (HZ / NSEC_PER_SEC) - 1)/(HZ / NSEC_PER_SEC);
+#else
+	return ((u64)j * NSEC_PER_SEC) / HZ;
+#endif
+}

That might look a little better something like:

static inline u64 jiffies_to_nsecs(const unsigned long __j)

{
	u64 j = __j;

	if (HZ <= NSEC_PER_SEC && !(NSEC_PER_SEC % HZ))
		return (NSEC_PER_SEC / HZ) * j;
	else if (HZ > NSEC_PER_SEC && !(HZ % NSEC_PER_SEC))
		return (j + (HZ / NSEC_PER_SEC) - 1)/(HZ / NSEC_PER_SEC);
	else
		return (j * NSEC_PER_SEC) / HZ;
}

Compilers are smart :)

-- Dave

#3 lore raw

Re: [RFC][PATCH 1/4] add jiffies_to_nsecs() helper and fix up size of usecs

Quoted 26 lines · Dave Hansen

include/linux/jiffies.h static inline unsigned int jiffies_to_us · line 286

Quoted 10 lines · Nishanth Aravamudan
include/linux/jiffies.h +10 -0
+static inline u64 jiffies_to_nsecs(const unsigned long j)
+{
+#if HZ <= NSEC_PER_SEC && !(NSEC_PER_SEC % HZ)
+	return (NSEC_PER_SEC / HZ) * (u64)j;
+#elif HZ > NSEC_PER_SEC && !(HZ % NSEC_PER_SEC)
+	return ((u64)j + (HZ / NSEC_PER_SEC) - 1)/(HZ / NSEC_PER_SEC);
+#else
+	return ((u64)j * NSEC_PER_SEC) / HZ;
+#endif
+}

That might look a little better something like:

static inline u64 jiffies_to_nsecs(const unsigned long __j)

{
	u64 j = __j;

	if (HZ <= NSEC_PER_SEC && !(NSEC_PER_SEC % HZ))
		return (NSEC_PER_SEC / HZ) * j;
	else if (HZ > NSEC_PER_SEC && !(HZ % NSEC_PER_SEC))
		return (j + (HZ / NSEC_PER_SEC) - 1)/(HZ / NSEC_PER_SEC);
	else
		return (j * NSEC_PER_SEC) / HZ;
}
Quoted 1 line

Compilers are smart :)

Well, I was trying to keep it similar to the other conversion functions. I guess the compiler can evaluate the conditional full of constants at compile-time regardless of whether it is #if or if ().

I can make these changes if others would like them as well.

Thanks,
Nish

#4 lore raw

Re: [RFC][PATCH 1/4] add jiffies_to_nsecs() helper and fix up size of usecs

Hi!

Quoted 25 lines
Quoted 25 lines

include/linux/jiffies.h static inline unsigned int jiffies_to_us · line 286

Quoted 10 lines
include/linux/jiffies.h +10 -0
+static inline u64 jiffies_to_nsecs(const unsigned long j)
+{
+#if HZ <= NSEC_PER_SEC && !(NSEC_PER_SEC % HZ)
+	return (NSEC_PER_SEC / HZ) * (u64)j;
+#elif HZ > NSEC_PER_SEC && !(HZ % NSEC_PER_SEC)
+	return ((u64)j + (HZ / NSEC_PER_SEC) - 1)/(HZ / NSEC_PER_SEC);
+#else
+	return ((u64)j * NSEC_PER_SEC) / HZ;
+#endif
+}

That might look a little better something like:

static inline u64 jiffies_to_nsecs(const unsigned long __j)

{
	u64 j = __j;

	if (HZ <= NSEC_PER_SEC && !(NSEC_PER_SEC % HZ))
		return (NSEC_PER_SEC / HZ) * j;
	else if (HZ > NSEC_PER_SEC && !(HZ % NSEC_PER_SEC))
		return (j + (HZ / NSEC_PER_SEC) - 1)/(HZ / NSEC_PER_SEC);
	else
		return (j * NSEC_PER_SEC) / HZ;
}
Quoted 7 lines
Quoted 1 line

Compilers are smart :)

Well, I was trying to keep it similar to the other conversion functions. I guess the compiler can evaluate the conditional full of constants at compile-time regardless of whether it is #if or if ().

I can make these changes if others would like them as well.

Yes, please. And feel free to convert nearby functions, too ;-).
Pavel

Signature · 1 line
teflon -- maybe it is a trademark, but it should not be.
#5 lore raw

Re: [RFC][PATCH 1/4] add jiffies_to_nsecs() helper and fix up size of usecs

Quoted 29 lines · Pavel Machek

Hi!

Quoted 27 lines
Quoted 26 lines

include/linux/jiffies.h static inline unsigned int jiffies_to_us · line 286

Quoted 10 lines
include/linux/jiffies.h +10 -0
+static inline u64 jiffies_to_nsecs(const unsigned long j)
+{
+#if HZ <= NSEC_PER_SEC && !(NSEC_PER_SEC % HZ)
+	return (NSEC_PER_SEC / HZ) * (u64)j;
+#elif HZ > NSEC_PER_SEC && !(HZ % NSEC_PER_SEC)
+	return ((u64)j + (HZ / NSEC_PER_SEC) - 1)/(HZ / NSEC_PER_SEC);
+#else
+	return ((u64)j * NSEC_PER_SEC) / HZ;
+#endif
+}

That might look a little better something like:

static inline u64 jiffies_to_nsecs(const unsigned long __j)

{
	u64 j = __j;

	if (HZ <= NSEC_PER_SEC && !(NSEC_PER_SEC % HZ))
		return (NSEC_PER_SEC / HZ) * j;
	else if (HZ > NSEC_PER_SEC && !(HZ % NSEC_PER_SEC))
		return (j + (HZ / NSEC_PER_SEC) - 1)/(HZ / NSEC_PER_SEC);
	else
		return (j * NSEC_PER_SEC) / HZ;
}

Compilers are smart :)

Quoted 7 lines
Quoted 5 lines

Well, I was trying to keep it similar to the other conversion functions. I guess the compiler can evaluate the conditional full of constants at compile-time regardless of whether it is #if or if ().

I can make these changes if others would like them as well.

Yes, please. And feel free to convert nearby functions, too ;-).

I have a patch to make this change for all the jiffies <--> human-time functions, but have a problem. I noticed that these functions, in the if/else form (as opposed to #if/#else) will warn about division-by-zero problems, as (HZ / MSEC_PER_SEC), (HZ / USEC_PER_SEC) & (HZ / NSEC_PER_SEC) are all 0 if HZ < 1000 (which, of course, is the default now :) ). Any suggestions? Just leave the functions as is? Even then, I'm going to update this patch to use USEC_PER_SEC and MSEC_PER_SEC in the other conversion functions like I use NSEC_PER_SEC in the first version.

Thanks,
Nish

#6 lore raw

RFC PATCH 2/4 human-time soft-timer core changes

In reply to Nishanth Aravamudan

From: Nishanth Aravamudan <[email protected]>

Description: The core revision to the soft-timer subsystem to divorce it from the timer interrupt in software, i.e. jiffies. Instead, use getnstimeofday() (via do_monotonic_clock()) as the basis for addition and expiration of timers. Add a new unit, the timerinterval, which is a 2^TIMERINTERVAL_BITS nanoseconds in length. The converted value in timerintervals is used where we would have used the timer's expires member before. Add set_timer_nsecs() and set_timer_nsecs_on() functions to directly request nanosecond delays. These functions replace add_timer(), mod_timer() and add_timer_on().

---

 include/linux/time.h  |    1 
 include/linux/timer.h |   27 +-----
 kernel/time.c         |   18 ++++
 kernel/timer.c        |  215 +++++++++++++++++++++++++++++++++++++++++++++-----
 4 files changed, 220 insertions(+), 41 deletions(-)
include/linux/time.h +1 -0
diff -urpN 2.6.13-rc3-base/include/linux/time.h 2.6.13-rc3-dev/include/linux/time.h
@@ -103,6 +103,7 @@ struct itimerval;
 extern int do_setitimer(int which, struct itimerval *value, struct itimerval *ovalue);
 extern int do_getitimer(int which, struct itimerval *value);
 extern void getnstimeofday (struct timespec *tv);
+extern u64 do_monotonic_clock(void);
 
 extern struct timespec timespec_trunc(struct timespec t, unsigned gran);
 
include/linux/timer.h +7 -20
diff -urpN 2.6.13-rc3-base/include/linux/timer.h 2.6.13-rc3-dev/include/linux/timer.h
@@ -11,6 +11,7 @@ struct timer_base_s;
 struct timer_list {
 	struct list_head entry;
 	unsigned long expires;
+	u64 expires_nsecs;
 
 	unsigned long magic;
 
@@ -27,6 +28,7 @@ extern struct timer_base_s __init_timer_
 #define TIMER_INITIALIZER(_function, _expires, _data) {		\
 		.function = (_function),			\
 		.expires = (_expires),				\
+		.expires_nsecs = 0,				\
 		.data = (_data),				\
 		.base = &__init_timer_base,			\
 		.magic = TIMER_MAGIC,				\
@@ -51,30 +53,15 @@ static inline int timer_pending(const st
 
 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);
 extern int mod_timer(struct timer_list *timer, unsigned long expires);
+extern void add_timer(struct timer_list *timer);
+extern int set_timer_nsecs(struct timer_list *timer, u64 expires_nsecs);
+extern void set_timer_on_nsecs(struct timer_list *timer, u64 expires_nsecs,
+								int cpu);
 
 extern unsigned long next_timer_interrupt(void);
 
-/***
- * add_timer - start a timer
- * @timer: the timer to be added
- *
- * The kernel will do a ->function(->data) callback from the
- * timer interrupt at the ->expired point in the future. The
- * current time is 'jiffies'.
- *
- * The timer's ->expired, ->function (and if the handler uses it, ->data)
- * fields must be set prior calling this function.
- *
- * Timers with an ->expired field in the past will be executed in the next
- * timer tick.
- */
-static inline void add_timer(struct timer_list * timer)
-{
-	__mod_timer(timer, timer->expires);
-}
-
 #ifdef CONFIG_SMP
   extern int try_to_del_timer_sync(struct timer_list *timer);
   extern int del_timer_sync(struct timer_list *timer);
kernel/time.c +18 -0
diff -urpN 2.6.13-rc3-base/kernel/time.c 2.6.13-rc3-dev/kernel/time.c
@@ -589,3 +589,21 @@ EXPORT_SYMBOL(get_jiffies_64);
 #endif
 
 EXPORT_SYMBOL(jiffies);
+
+u64 do_monotonic_clock(void)
+{
+	struct timespec now, now_w2m;
+	unsigned long seq;
+
+	getnstimeofday(&now);
+
+	do {
+		seq = read_seqbegin(&xtime_lock);
+		now_w2m = wall_to_monotonic;
+	} while (read_seqretry(&xtime_lock, seq));
+
+	return (u64)(now.tv_sec + now_w2m.tv_sec) * NSEC_PER_SEC +
+				(now.tv_nsec + now_w2m.tv_nsec);
+}
+
+EXPORT_SYMBOL_GPL(do_monotonic_clock);
kernel/timer.c +194 -21

343 lines not on this page · raw message

#7 lore raw

RFC PATCH 2/4 human-time soft-timer core changes

Quoted 12 lines · Nishanth Aravamudan

From: Nishanth Aravamudan <[email protected]>

Description: The core revision to the soft-timer subsystem to divorce it from the timer interrupt in software, i.e. jiffies. Instead, use getnstimeofday() (via do_monotonic_clock()) as the basis for addition and expiration of timers. Add a new unit, the timerinterval, which is a 2^TIMERINTERVAL_BITS nanoseconds in length. The converted value in timerintervals is used where we would have used the timer's expires member before. Add set_timer_nsecs() and set_timer_nsecs_on() functions to directly request nanosecond delays. These functions replace add_timer(), mod_timer() and add_timer_on().

Quoted 1 line

Sigh, one version of my development patches removed the export of mod_timer(). Of course, I forgot to revert that hunk before sending it out. If anyone (maybe not likely) is testing out these patches, please use this version.

Thanks,
Nish

---

 include/linux/time.h  |    1 
 include/linux/timer.h |   27 +-----
 kernel/time.c         |   18 ++++
 kernel/timer.c        |  215 +++++++++++++++++++++++++++++++++++++++++++++-----
 4 files changed, 221 insertions(+), 40 deletions(-)
include/linux/time.h +1 -0
diff -urpN 2.6.13-rc3-base/include/linux/time.h 2.6.13-rc3-dev/include/linux/time.h
@@ -103,6 +103,7 @@ struct itimerval;
 extern int do_setitimer(int which, struct itimerval *value, struct itimerval *ovalue);
 extern int do_getitimer(int which, struct itimerval *value);
 extern void getnstimeofday (struct timespec *tv);
+extern u64 do_monotonic_clock(void);
 
 extern struct timespec timespec_trunc(struct timespec t, unsigned gran);
 
include/linux/timer.h +7 -20
diff -urpN 2.6.13-rc3-base/include/linux/timer.h 2.6.13-rc3-dev/include/linux/timer.h
@@ -11,6 +11,7 @@ struct timer_base_s;
 struct timer_list {
 	struct list_head entry;
 	unsigned long expires;
+	u64 expires_nsecs;
 
 	unsigned long magic;
 
@@ -27,6 +28,7 @@ extern struct timer_base_s __init_timer_
 #define TIMER_INITIALIZER(_function, _expires, _data) {		\
 		.function = (_function),			\
 		.expires = (_expires),				\
+		.expires_nsecs = 0,				\
 		.data = (_data),				\
 		.base = &__init_timer_base,			\
 		.magic = TIMER_MAGIC,				\
@@ -51,30 +53,15 @@ static inline int timer_pending(const st
 
 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);
 extern int mod_timer(struct timer_list *timer, unsigned long expires);
+extern void add_timer(struct timer_list *timer);
+extern int set_timer_nsecs(struct timer_list *timer, u64 expires_nsecs);
+extern void set_timer_on_nsecs(struct timer_list *timer, u64 expires_nsecs,
+								int cpu);
 
 extern unsigned long next_timer_interrupt(void);
 
-/***
- * add_timer - start a timer
- * @timer: the timer to be added
- *
- * The kernel will do a ->function(->data) callback from the
- * timer interrupt at the ->expired point in the future. The
- * current time is 'jiffies'.
- *
- * The timer's ->expired, ->function (and if the handler uses it, ->data)
- * fields must be set prior calling this function.
- *
- * Timers with an ->expired field in the past will be executed in the next
- * timer tick.
- */
-static inline void add_timer(struct timer_list * timer)
-{
-	__mod_timer(timer, timer->expires);
-}
-
 #ifdef CONFIG_SMP
   extern int try_to_del_timer_sync(struct timer_list *timer);
   extern int del_timer_sync(struct timer_list *timer);
kernel/time.c +18 -0
diff -urpN 2.6.13-rc3-base/kernel/time.c 2.6.13-rc3-dev/kernel/time.c
@@ -589,3 +589,21 @@ EXPORT_SYMBOL(get_jiffies_64);
 #endif
 
 EXPORT_SYMBOL(jiffies);
+
+u64 do_monotonic_clock(void)
+{
+	struct timespec now, now_w2m;
+	unsigned long seq;
+
+	getnstimeofday(&now);
+
+	do {
+		seq = read_seqbegin(&xtime_lock);
+		now_w2m = wall_to_monotonic;
+	} while (read_seqretry(&xtime_lock, seq));
+
+	return (u64)(now.tv_sec + now_w2m.tv_sec) * NSEC_PER_SEC +
+				(now.tv_nsec + now_w2m.tv_nsec);
+}
+
+EXPORT_SYMBOL_GPL(do_monotonic_clock);
kernel/timer.c +131 -9

222 lines not on this page · raw message

EXPORT_SYMBOL(mod_timer);
+
+/*
+ * set_timer_nsecs - modify a timer's timeout in nsecs
+ * @timer: the timer to be modified
+ *
+ * set_timer_nsecs replaces both add_timer and mod_timer. The caller
+ * should call do_monotonic_clock() to determine the absolute timeout
+ * necessary.
+ */
+int set_timer_nsecs(struct timer_list *timer, u64 expires_nsecs) +{
+ BUG_ON(!timer->function);
+
+ check_timer(timer);
+
+ if (timer_pending(timer) && timer->expires_nsecs == expires_nsecs)
+ return 1;
+
+ timer->expires_nsecs = expires_nsecs;
+
+ return __mod_timer(timer);
+}
+
+EXPORT_SYMBOL_GPL(set_timer_nsecs);
+
+/***
+ * set_timer_on_nsecs - start a timer on a particular CPU
+ * @timer: the timer to be added
+ * @expires_nsecs: absolute time in nsecs when timer should expire
+ * @cpu: the CPU to start it on
+ *
+ * This is not very scalable on SMP. Double adds are not possible.
+ */
+void set_timer_on_nsecs(struct timer_list *timer, u64 expires_nsecs, int cpu) +{
+ tvec_base_t *base = &per_cpu(tvec_bases, cpu);
+ unsigned long flags;
+
+ BUG_ON(timer_pending(timer) || !timer->function);
+
+ check_timer(timer);
+
+ spin_lock_irqsave(&base->t_base.lock, flags);
+ timer->expires_nsecs = expires_nsecs;
+ timer->base = &base->t_base;
+ internal_add_timer(base, timer);
+ spin_unlock_irqrestore(&base->t_base.lock, flags);
+}

/***
* del_timer - deactive a timer.
@@ -455,17 +623,17 @@ static int cascade(tvec_base_t *base, tv
* This function cascades all vectors and executes all expired timer
* vectors.
*/
-#define INDEX(N) (base->timer_jiffies >> (TVR_BITS + N * TVN_BITS)) & TVN_MASK +#define INDEX(N) (base->last_timer_time >> (TVR_BITS + N * TVN_BITS)) & TVN_MASK

-static inline void __run_timers(tvec_base_t *base)
+static inline void __run_timers(tvec_base_t *base, unsigned long current_timer_time)
{
struct timer_list *timer;

spin_lock_irq(&base->t_base.lock);
- while (time_after_eq(jiffies, base->timer_jiffies)) {
+ while (time_after_eq(current_timer_time, base->last_timer_time)) {
struct list_head work_list = LIST_HEAD_INIT(work_list); struct list_head *head = &work_list;
- int index = base->timer_jiffies & TVR_MASK;
+ int index = base->last_timer_time & TVR_MASK;

/*
* Cascade timers:
@@ -475,7 +643,7 @@ static inline void __run_timers(tvec_bas
(!cascade(base, &base->tv3, INDEX(1))) &&
!cascade(base, &base->tv4, INDEX(2)))
cascade(base, &base->tv5, INDEX(3));
- ++base->timer_jiffies;
+ ++base->last_timer_time;
list_splice_init(base->tv1.vec + index, &work_list); while (!list_empty(head)) {
void (*fn)(unsigned long);
@@ -524,20 +692,20 @@ unsigned long next_timer_interrupt(void)

base = &__get_cpu_var(tvec_bases);
spin_lock(&base->t_base.lock);
- expires = base->timer_jiffies + (LONG_MAX >> 1);
+ expires = base->last_timer_time + (LONG_MAX >> 1);
list = 0;

/* Look for timer events in tv1. */
- j = base->timer_jiffies & TVR_MASK;
+ j = base->last_timer_time & TVR_MASK;
do {
list_for_each_entry(nte, base->tv1.vec + j, entry) {
expires = nte->expires;
- if (j < (base->timer_jiffies & TVR_MASK))
+ if (j < (base->last_timer_time & TVR_MASK))
list = base->tv2.vec + (INDEX(0));
goto found;
}
j = (j + 1) & TVR_MASK;
- } while (j != (base->timer_jiffies & TVR_MASK));
+ } while (j != (base->last_timer_time & TVR_MASK));

/* Check tv2-tv5. */
varray[0] = &base->tv2;
@@ -910,10 +1078,15 @@ EXPORT_SYMBOL(xtime_lock);
*/
static void run_timer_softirq(struct softirq_action *h) {
+ unsigned long current_timer_time;
tvec_base_t *base = &__get_cpu_var(tvec_bases);

- if (time_after_eq(jiffies, base->timer_jiffies))
- __run_timers(base);
+ /* cache the converted current time, rounding down */
+ current_timer_time =
+ nsecs_to_timerintervals_floor(do_monotonic_clock());
+
+ if (time_after_eq(current_timer_time, base->last_timer_time))
+ __run_timers(base, current_timer_time);
}

/*

#8 lore raw

RFC PATCH 3/4 new human-time schedule_timeout() functions

In reply to Nishanth Aravamudan

From: Nishanth Aravamudan <[email protected]>

Description: Add new human-time schedule_timeout() style functions, along with the appropriate constants/prototypes.

---

 include/linux/sched.h |    7 ++
 include/linux/time.h  |    4 +
 kernel/timer.c        |  147 ++++++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 158 insertions(+)
include/linux/sched.h +7 -0
diff -urpN 2.6.13-rc3-base/include/linux/sched.h 2.6.13-rc3-dev/include/linux/sched.h
@@ -182,7 +182,14 @@ extern void scheduler_tick(void);
 extern int in_sched_functions(unsigned long addr);
 
 #define	MAX_SCHEDULE_TIMEOUT	LONG_MAX
+#define	MAX_SCHEDULE_TIMEOUT_NSECS	((u64)(-1))
+#define	MAX_SCHEDULE_TIMEOUT_USECS	ULONG_MAX
+#define	MAX_SCHEDULE_TIMEOUT_MSECS	UINT_MAX
+
 extern signed long FASTCALL(schedule_timeout(signed long timeout));
+extern u64 FASTCALL(schedule_timeout_nsecs(u64 timeout_nsecs));
+extern unsigned long FASTCALL(schedule_timeout_usecs(unsigned long timeout_usecs));
+extern unsigned int FASTCALL(schedule_timeout_msecs(unsigned int timeout_msecs));
 asmlinkage void schedule(void);
 
 struct namespace;
include/linux/time.h +4 -0
diff -urpN 2.6.13-rc3-base/include/linux/time.h 2.6.13-rc3-dev/include/linux/time.h
@@ -36,6 +36,10 @@ struct timezone {
 #define NSEC_PER_SEC (1000000000L)
 #endif
 
+#ifndef NSEC_PER_MSEC
+#define NSEC_PER_MSEC (1000000L)
+#endif
+
 #ifndef NSEC_PER_USEC
 #define NSEC_PER_USEC (1000L)
 #endif
kernel/timer.c +147 -0

159 lines not on this page · raw message

#9 lore raw

RFC PATCH 4/4 convert sys_nanosleep() to use set_timer_nsecs()

In reply to Nishanth Aravamudan

From: Nishanth Aravamudan <[email protected]>

Description: Add timespec and timeval conversion functions for nanoseconds. Convert sys_nanosleep() to use schedule_timeout_nsecs().

---

 include/linux/time.h |   33 +++++++++++++++++++++++++++++++++
 kernel/timer.c       |   24 ++++++++++++------------
 2 files changed, 45 insertions(+), 12 deletions(-)
include/linux/time.h +33 -0
diff -urpN 2.6.13-rc3-base/include/linux/time.h 2.6.13-rc3-dev/include/linux/time.h
@@ -2,6 +2,7 @@
 #define _LINUX_TIME_H
 
 #include <linux/types.h>
+#include <asm/div64.h>
 
 #ifdef __KERNEL__
 #include <linux/seqlock.h>
@@ -126,6 +127,38 @@ set_normalized_timespec (struct timespec
 	ts->tv_nsec = nsec;
 }
 
+/* Inline helper functions */
+static inline struct timeval nsecs_to_timeval(u64 ns)
+{
+	struct timeval tv;
+	tv.tv_sec = div_long_long_rem(ns, NSEC_PER_SEC, &tv.tv_usec);
+	tv.tv_usec = (tv.tv_usec + NSEC_PER_USEC/2) / NSEC_PER_USEC;
+	return tv;
+}
+
+static inline struct timespec nsecs_to_timespec(u64 ns)
+{
+	struct timespec ts;
+	ts.tv_sec = div_long_long_rem(ns, NSEC_PER_SEC, &ts.tv_nsec);
+	return ts;
+}
+
+static inline u64 timespec_to_nsecs(struct timespec* ts)
+{
+	u64 ret;
+	ret = ((u64)ts->tv_sec) * NSEC_PER_SEC;
+	ret += (u64)ts->tv_nsec;
+	return ret;
+}
+
+static inline u64 timeval_to_nsecs(struct timeval* tv)
+{
+	u64 ret;
+	ret = ((u64)tv->tv_sec) * NSEC_PER_SEC;
+	ret += ((u64)tv->tv_usec) * NSEC_PER_USEC;
+	return ret;
+}
+
 #endif /* __KERNEL__ */
 
 #define NFDBITS			__NFDBITS
kernel/timer.c +12 -12
diff -urpN 2.6.13-rc3-base/kernel/timer.c 2.6.13-rc3-dev/kernel/timer.c
@@ -1481,21 +1481,21 @@ asmlinkage long sys_gettid(void)
 
 static long __sched nanosleep_restart(struct restart_block *restart)
 {
-	unsigned long expire = restart->arg0, now = jiffies;
+	u64 expire = restart->arg0, now = do_monotonic_clock();
 	struct timespec __user *rmtp = (struct timespec __user *) restart->arg1;
 	long ret;
 
 	/* Did it expire while we handled signals? */
-	if (!time_after(expire, now))
+	if (now > expire)
 		return 0;
 
-	current->state = TASK_INTERRUPTIBLE;
-	expire = schedule_timeout(expire - now);
+	set_current_state(TASK_INTERRUPTIBLE);
+	expire = schedule_timeout_nsecs(expire - now);
 
 	ret = 0;
 	if (expire) {
 		struct timespec t;
-		jiffies_to_timespec(expire, &t);
+		t = nsecs_to_timespec(expire);
 
 		ret = -ERESTART_RESTARTBLOCK;
 		if (rmtp && copy_to_user(rmtp, &t, sizeof(t)))
@@ -1508,7 +1508,7 @@ static long __sched nanosleep_restart(st
 asmlinkage long sys_nanosleep(struct timespec __user *rqtp, struct timespec __user *rmtp)
 {
 	struct timespec t;
-	unsigned long expire;
+	u64 expire;
 	long ret;
 
 	if (copy_from_user(&t, rqtp, sizeof(t)))
@@ -1517,20 +1517,20 @@ asmlinkage long sys_nanosleep(struct tim
 	if ((t.tv_nsec >= 1000000000L) || (t.tv_nsec < 0) || (t.tv_sec < 0))
 		return -EINVAL;
 
-	expire = timespec_to_jiffies(&t) + (t.tv_sec || t.tv_nsec);
-	current->state = TASK_INTERRUPTIBLE;
-	expire = schedule_timeout(expire);
+	expire = timespec_to_nsecs(&t);
+	set_current_state(TASK_INTERRUPTIBLE);
+	expire = schedule_timeout_nsecs(expire);
 
 	ret = 0;
 	if (expire) {
 		struct restart_block *restart;
-		jiffies_to_timespec(expire, &t);
+		t = nsecs_to_timespec(expire);
 		if (rmtp && copy_to_user(rmtp, &t, sizeof(t)))
 			return -EFAULT;
 
 		restart = &current_thread_info()->restart_block;
 		restart->fn = nanosleep_restart;
-		restart->arg0 = jiffies + expire;
+		restart->arg0 = do_monotonic_clock() + expire;
 		restart->arg1 = (unsigned long) rmtp;
 		ret = -ERESTART_RESTARTBLOCK;
 	}
@@ -1642,7 +1642,7 @@ static void __devinit init_timers_cpu(in
 	for (j = 0; j < TVR_SIZE; j++)
 		INIT_LIST_HEAD(base->tv1.vec + j);
 
-	base->timer_jiffies = jiffies;
+	base->last_timer_time = 0UL;
 }
 
 #ifdef CONFIG_HOTPLUG_CPU
#10 lore raw
In reply to Nishanth Aravamudan

Hi,

Quoted 4 lines · Nishanth Aravamudan

We no longer use jiffies (the variable) as the basis for determining what "time" a timer should expire or when it should be added. Instead, we use a new function, do_monotonic_clock(), which is simply a wrapper for getnstimeofday().

And suddenly a simple 32bit integer becomes a complex 64bit integer, which requires hardware access to read a timer and additional conversion into ns. Why is suddenly everyone so obsessed with molesting something simple and cute as jiffies?

bye, Roman

#11 lore raw
Quoted 9 lines · Roman Zippel

Hi,

Quoted 4 lines · Nishanth Aravamudan

We no longer use jiffies (the variable) as the basis for determining what "time" a timer should expire or when it should be added. Instead, we use a new function, do_monotonic_clock(), which is simply a wrapper for getnstimeofday().

Quoted 4 lines

And suddenly a simple 32bit integer becomes a complex 64bit integer, which requires hardware access to read a timer and additional conversion into ns. Why is suddenly everyone so obsessed with molesting something simple and cute as jiffies?

Thanks for the feedback, Roman. I know the 64-bit operations are critical from a performance perspective and may be excessive from a pragmatic perspective. Maybe an alternative would be to only provide *microsecond* resolution in the software, which I currently assume is storable in an unsigned long (a little over an hour?). We could then provide a supplemental interface for those sleeps which would exceed this time, either via looping or a 64-bit parameter for this special interface.

Would that perhaps be a better alternative from the 64-bit perspective?

We could do this one better, perhaps, by basically doing exactly what jiffies does now, but storing a time value (in microseconds) instead of a count of the number of ticks (jiffies' current interpretation). This would perhaps be a 64-bit op, but that is the case current with jiffies_64++ (or jiffies_64 += jiffies_increment). I will work on some patches to do something to this effect and will bring it up during the time/timer talk (Saturday at 13h30).

Thanks again,
Nish