new human-time soft-timer subsystem
Subsystems (3)
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
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
@@ -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 ;
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
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
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.
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
RFC PATCH 2/4 human-time soft-timer core changes
In reply to Nishanth AravamudanFrom: 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
@@ -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
@@ -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 stextern 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
@@ -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
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()(viado_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. Addset_timer_nsecs()andset_timer_nsecs_on()functions to directly request nanosecond delays. These functions replaceadd_timer(),mod_timer()andadd_timer_on().
Quoted 1 line
Signed-off-by Nishanth Aravamudan <[email protected]>
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
@@ -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
@@ -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 stextern 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
@@ -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);
}
/*
RFC PATCH 3/4 new human-time schedule_timeout() functions
In reply to Nishanth AravamudanFrom: 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
@@ -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
@@ -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
RFC PATCH 4/4 convert sys_nanosleep() to use set_timer_nsecs()
In reply to Nishanth AravamudanFrom: 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
@@ -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
@@ -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 &©_to_user (rmtp, &t,sizeof (t)))
@@ -1508,7 +1508,7 @@ static long __sched nanosleep_restart(stasmlinkage 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 timif ((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 &©_to_user (rmtp, &t,sizeof (t)))return -EFAULT ; restart = ¤t_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(infor (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
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 forgetnstimeofday().
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
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 forgetnstimeofday().
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