Commit 9c58c3bd authored by Andrew Morton's avatar Andrew Morton Committed by Dave Jones

[PATCH] posix timers update

Patch from george anzinger <george@mvista.com>

Fix the "large sleep returns EINVAL" problem, and clean a few things up.
parent 58a90ba8
...@@ -12,7 +12,7 @@ ...@@ -12,7 +12,7 @@
*/ */
struct restart_block { struct restart_block {
long (*fn)(struct restart_block *); long (*fn)(struct restart_block *);
unsigned long arg0, arg1, arg2; unsigned long arg0, arg1, arg2, arg3;
}; };
extern long do_no_restart_syscall(struct restart_block *parm); extern long do_no_restart_syscall(struct restart_block *parm);
......
...@@ -9,7 +9,6 @@ ...@@ -9,7 +9,6 @@
/* These are all the functions necessary to implement /* These are all the functions necessary to implement
* POSIX clocks & timers * POSIX clocks & timers
*/ */
#include <linux/mm.h> #include <linux/mm.h>
#include <linux/smp_lock.h> #include <linux/smp_lock.h>
#include <linux/interrupt.h> #include <linux/interrupt.h>
...@@ -23,6 +22,7 @@ ...@@ -23,6 +22,7 @@
#include <linux/compiler.h> #include <linux/compiler.h>
#include <linux/idr.h> #include <linux/idr.h>
#include <linux/posix-timers.h> #include <linux/posix-timers.h>
#include <linux/wait.h>
#ifndef div_long_long_rem #ifndef div_long_long_rem
#include <asm/div64.h> #include <asm/div64.h>
...@@ -56,8 +56,8 @@ ...@@ -56,8 +56,8 @@
* Lets keep our timers in a slab cache :-) * Lets keep our timers in a slab cache :-)
*/ */
static kmem_cache_t *posix_timers_cache; static kmem_cache_t *posix_timers_cache;
struct idr posix_timers_id; static struct idr posix_timers_id;
spinlock_t idr_lock = SPIN_LOCK_UNLOCKED; static spinlock_t idr_lock = SPIN_LOCK_UNLOCKED;
/* /*
* Just because the timer is not in the timer list does NOT mean it is * Just because the timer is not in the timer list does NOT mean it is
...@@ -130,7 +130,7 @@ spinlock_t idr_lock = SPIN_LOCK_UNLOCKED; ...@@ -130,7 +130,7 @@ spinlock_t idr_lock = SPIN_LOCK_UNLOCKED;
* which we beg off on and pass to do_sys_settimeofday(). * which we beg off on and pass to do_sys_settimeofday().
*/ */
struct k_clock posix_clocks[MAX_CLOCKS]; static struct k_clock posix_clocks[MAX_CLOCKS];
#define if_clock_do(clock_fun, alt_fun,parms) (! clock_fun)? alt_fun parms :\ #define if_clock_do(clock_fun, alt_fun,parms) (! clock_fun)? alt_fun parms :\
clock_fun parms clock_fun parms
...@@ -183,7 +183,7 @@ init_posix_timers(void) ...@@ -183,7 +183,7 @@ init_posix_timers(void)
__initcall(init_posix_timers); __initcall(init_posix_timers);
static inline int static inline int
tstojiffie(struct timespec *tp, int res, unsigned long *jiff) tstojiffie(struct timespec *tp, int res, u64 *jiff)
{ {
unsigned long sec = tp->tv_sec; unsigned long sec = tp->tv_sec;
long nsec = tp->tv_nsec + res - 1; long nsec = tp->tv_nsec + res - 1;
...@@ -203,7 +203,7 @@ tstojiffie(struct timespec *tp, int res, unsigned long *jiff) ...@@ -203,7 +203,7 @@ tstojiffie(struct timespec *tp, int res, unsigned long *jiff)
* below. Here it is enough to just discard the high order * below. Here it is enough to just discard the high order
* bits. * bits.
*/ */
*jiff = HZ * sec; *jiff = (u64)sec * HZ;
/* /*
* Do the res thing. (Don't forget the add in the declaration of nsec) * Do the res thing. (Don't forget the add in the declaration of nsec)
*/ */
...@@ -221,9 +221,12 @@ tstojiffie(struct timespec *tp, int res, unsigned long *jiff) ...@@ -221,9 +221,12 @@ tstojiffie(struct timespec *tp, int res, unsigned long *jiff)
static void static void
tstotimer(struct itimerspec *time, struct k_itimer *timer) tstotimer(struct itimerspec *time, struct k_itimer *timer)
{ {
u64 result;
int res = posix_clocks[timer->it_clock].res; int res = posix_clocks[timer->it_clock].res;
tstojiffie(&time->it_value, res, &timer->it_timer.expires); tstojiffie(&time->it_value, res, &result);
tstojiffie(&time->it_interval, res, &timer->it_incr); timer->it_timer.expires = (unsigned long)result;
tstojiffie(&time->it_interval, res, &result);
timer->it_incr = (unsigned long)result;
} }
static void static void
...@@ -1020,6 +1023,9 @@ do_posix_gettime(struct k_clock *clock, struct timespec *tp) ...@@ -1020,6 +1023,9 @@ do_posix_gettime(struct k_clock *clock, struct timespec *tp)
* Note also that the while loop assures that the sub_jiff_offset * Note also that the while loop assures that the sub_jiff_offset
* will be less than a jiffie, thus no need to normalize the result. * will be less than a jiffie, thus no need to normalize the result.
* Well, not really, if called with ints off :( * Well, not really, if called with ints off :(
* HELP, this code should make an attempt at resolution beyond the
* jiffie. Trouble is this is "arch" dependent...
*/ */
int int
...@@ -1127,26 +1133,14 @@ nanosleep_wake_up(unsigned long __data) ...@@ -1127,26 +1133,14 @@ nanosleep_wake_up(unsigned long __data)
* holds (or has held for it) a write_lock_irq( xtime_lock) and is * holds (or has held for it) a write_lock_irq( xtime_lock) and is
* called from the timer bh code. Thus we need the irq save locks. * called from the timer bh code. Thus we need the irq save locks.
*/ */
spinlock_t nanosleep_abs_list_lock = SPIN_LOCK_UNLOCKED;
struct list_head nanosleep_abs_list = LIST_HEAD_INIT(nanosleep_abs_list); static DECLARE_WAIT_QUEUE_HEAD(nanosleep_abs_wqueue);
struct abs_struct {
struct list_head list;
struct task_struct *t;
};
void void
clock_was_set(void) clock_was_set(void)
{ {
struct list_head *pos; wake_up_all(&nanosleep_abs_wqueue);
unsigned long flags;
spin_lock_irqsave(&nanosleep_abs_list_lock, flags);
list_for_each(pos, &nanosleep_abs_list) {
wake_up_process(list_entry(pos, struct abs_struct, list)->t);
}
spin_unlock_irqrestore(&nanosleep_abs_list_lock, flags);
} }
long clock_nanosleep_restart(struct restart_block *restart_block); long clock_nanosleep_restart(struct restart_block *restart_block);
...@@ -1201,19 +1195,19 @@ sys_clock_nanosleep(clockid_t which_clock, int flags, ...@@ -1201,19 +1195,19 @@ sys_clock_nanosleep(clockid_t which_clock, int flags,
return ret; return ret;
} }
long long
do_clock_nanosleep(clockid_t which_clock, int flags, struct timespec *tsave) do_clock_nanosleep(clockid_t which_clock, int flags, struct timespec *tsave)
{ {
struct timespec t; struct timespec t;
struct timer_list new_timer; struct timer_list new_timer;
struct abs_struct abs_struct = { .list = { .next = 0 } }; DECLARE_WAITQUEUE(abs_wqueue, current);
u64 rq_time = 0;
s64 left;
int abs; int abs;
int rtn = 0;
int active;
struct restart_block *restart_block = struct restart_block *restart_block =
&current_thread_info()->restart_block; &current_thread_info()->restart_block;
abs_wqueue.flags = 0;
init_timer(&new_timer); init_timer(&new_timer);
new_timer.expires = 0; new_timer.expires = 0;
new_timer.data = (unsigned long) current; new_timer.data = (unsigned long) current;
...@@ -1226,54 +1220,50 @@ do_clock_nanosleep(clockid_t which_clock, int flags, struct timespec *tsave) ...@@ -1226,54 +1220,50 @@ do_clock_nanosleep(clockid_t which_clock, int flags, struct timespec *tsave)
* time and continue. * time and continue.
*/ */
restart_block->fn = do_no_restart_syscall; restart_block->fn = do_no_restart_syscall;
if (!restart_block->arg2)
return -EINTR;
new_timer.expires = restart_block->arg2; rq_time = restart_block->arg3;
if (time_before(new_timer.expires, jiffies)) rq_time = (rq_time << 32) + restart_block->arg2;
if (!rq_time)
return -EINTR;
if (rq_time <= get_jiffies_64())
return 0; return 0;
} }
if (abs && (posix_clocks[which_clock].clock_get != if (abs && (posix_clocks[which_clock].clock_get !=
posix_clocks[CLOCK_MONOTONIC].clock_get)) { posix_clocks[CLOCK_MONOTONIC].clock_get)) {
spin_lock_irq(&nanosleep_abs_list_lock); add_wait_queue(&nanosleep_abs_wqueue, &abs_wqueue);
list_add(&abs_struct.list, &nanosleep_abs_list);
abs_struct.t = current;
spin_unlock_irq(&nanosleep_abs_list_lock);
} }
do { do {
t = *tsave; t = *tsave;
if ((abs || !new_timer.expires) && if (abs || !rq_time){
!(rtn = adjust_abs_time(&posix_clocks[which_clock], adjust_abs_time(&posix_clocks[which_clock], &t, abs);
&t, abs))) {
/*
* On error, we don't set up the timer so
* we don't arm the timer so
* del_timer_sync() will return 0, thus
* active is zero... and so it goes.
*/
tstojiffie(&t, tstojiffie(&t, posix_clocks[which_clock].res, &rq_time);
posix_clocks[which_clock].res,
&new_timer.expires);
} }
if (new_timer.expires) { #if (BITS_PER_LONG < 64)
current->state = TASK_INTERRUPTIBLE; if ((rq_time - get_jiffies_64()) > MAX_JIFFY_OFFSET){
add_timer(&new_timer); new_timer.expires = MAX_JIFFY_OFFSET;
}else
schedule(); #endif
{
new_timer.expires = (long)rq_time;
} }
} current->state = TASK_INTERRUPTIBLE;
while ((active = del_timer_sync(&new_timer)) && add_timer(&new_timer);
!test_thread_flag(TIF_SIGPENDING));
schedule();
if (abs_struct.list.next) { del_timer_sync(&new_timer);
spin_lock_irq(&nanosleep_abs_list_lock); left = rq_time - get_jiffies_64();
list_del(&abs_struct.list);
spin_unlock_irq(&nanosleep_abs_list_lock);
} }
if (active) { while ( (left > 0) &&
long jiffies_left; !test_thread_flag(TIF_SIGPENDING));
if( abs_wqueue.task_list.next)
finish_wait(&nanosleep_abs_wqueue, &abs_wqueue);
if (left > 0) {
unsigned long rmd;
/* /*
* Always restart abs calls from scratch to pick up any * Always restart abs calls from scratch to pick up any
...@@ -1282,29 +1272,19 @@ do_clock_nanosleep(clockid_t which_clock, int flags, struct timespec *tsave) ...@@ -1282,29 +1272,19 @@ do_clock_nanosleep(clockid_t which_clock, int flags, struct timespec *tsave)
if (abs) if (abs)
return -ERESTARTNOHAND; return -ERESTARTNOHAND;
jiffies_left = new_timer.expires - jiffies; tsave->tv_sec = div_long_long_rem(left, HZ, &rmd);
tsave->tv_nsec = rmd * (NSEC_PER_SEC / HZ);
if (jiffies_left < 0)
return 0;
jiffies_to_timespec(jiffies_left, tsave);
while (tsave->tv_nsec < 0) {
tsave->tv_nsec += NSEC_PER_SEC;
tsave->tv_sec--;
}
if (tsave->tv_sec < 0) {
tsave->tv_sec = 0;
tsave->tv_nsec = 1;
}
restart_block->fn = clock_nanosleep_restart; restart_block->fn = clock_nanosleep_restart;
restart_block->arg0 = which_clock; restart_block->arg0 = which_clock;
restart_block->arg1 = (unsigned long)tsave; restart_block->arg1 = (unsigned long)tsave;
restart_block->arg2 = new_timer.expires; restart_block->arg2 = rq_time & 0xffffffffLL;
restart_block->arg3 = rq_time >> 32;
return -ERESTART_RESTARTBLOCK; return -ERESTART_RESTARTBLOCK;
} }
return rtn; return 0;
} }
/* /*
* This will restart either clock_nanosleep or clock_nanosleep * This will restart either clock_nanosleep or clock_nanosleep
......
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