Commit e4b76555 authored by Toyo Abe's avatar Toyo Abe Committed by Linus Torvalds

[PATCH] posix-timers: Fix the flags handling in posix_cpu_nsleep()

When a posix_cpu_nsleep() sleep is interrupted by a signal more than twice, it
incorrectly reports the sleep time remaining to the user.  Because
posix_cpu_nsleep() doesn't report back to the user when it's called from
restart function due to the wrong flags handling.

This patch, which applies after previous one, moves the nanosleep() function
from posix_cpu_nsleep() to do_cpu_nanosleep() and cleans up the flags handling
appropriately.
Signed-off-by: default avatarToyo Abe <toyoa@mvista.com>
Cc: Thomas Gleixner <tglx@linutronix.de>
Cc: Ingo Molnar <mingo@elte.hu>
Cc: Roland McGrath <roland@redhat.com>
Signed-off-by: default avatarAndrew Morton <akpm@osdl.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@osdl.org>
parent 1711ef38
...@@ -1393,22 +1393,12 @@ void set_process_cpu_timer(struct task_struct *tsk, unsigned int clock_idx, ...@@ -1393,22 +1393,12 @@ void set_process_cpu_timer(struct task_struct *tsk, unsigned int clock_idx,
} }
} }
int posix_cpu_nsleep(const clockid_t which_clock, int flags, static int do_cpu_nanosleep(const clockid_t which_clock, int flags,
struct timespec *rqtp, struct timespec __user *rmtp) struct timespec *rqtp, struct itimerspec *it)
{ {
struct restart_block *restart_block =
&current_thread_info()->restart_block;
struct k_itimer timer; struct k_itimer timer;
int error; int error;
/*
* Diagnose required errors first.
*/
if (CPUCLOCK_PERTHREAD(which_clock) &&
(CPUCLOCK_PID(which_clock) == 0 ||
CPUCLOCK_PID(which_clock) == current->pid))
return -EINVAL;
/* /*
* Set up a temporary timer and then wait for it to go off. * Set up a temporary timer and then wait for it to go off.
*/ */
...@@ -1420,11 +1410,12 @@ int posix_cpu_nsleep(const clockid_t which_clock, int flags, ...@@ -1420,11 +1410,12 @@ int posix_cpu_nsleep(const clockid_t which_clock, int flags,
timer.it_process = current; timer.it_process = current;
if (!error) { if (!error) {
static struct itimerspec zero_it; static struct itimerspec zero_it;
struct itimerspec it = { .it_value = *rqtp,
.it_interval = {} }; memset(it, 0, sizeof *it);
it->it_value = *rqtp;
spin_lock_irq(&timer.it_lock); spin_lock_irq(&timer.it_lock);
error = posix_cpu_timer_set(&timer, flags, &it, NULL); error = posix_cpu_timer_set(&timer, flags, it, NULL);
if (error) { if (error) {
spin_unlock_irq(&timer.it_lock); spin_unlock_irq(&timer.it_lock);
return error; return error;
...@@ -1452,33 +1443,56 @@ int posix_cpu_nsleep(const clockid_t which_clock, int flags, ...@@ -1452,33 +1443,56 @@ int posix_cpu_nsleep(const clockid_t which_clock, int flags,
* We were interrupted by a signal. * We were interrupted by a signal.
*/ */
sample_to_timespec(which_clock, timer.it.cpu.expires, rqtp); sample_to_timespec(which_clock, timer.it.cpu.expires, rqtp);
posix_cpu_timer_set(&timer, 0, &zero_it, &it); posix_cpu_timer_set(&timer, 0, &zero_it, it);
spin_unlock_irq(&timer.it_lock); spin_unlock_irq(&timer.it_lock);
if ((it.it_value.tv_sec | it.it_value.tv_nsec) == 0) { if ((it->it_value.tv_sec | it->it_value.tv_nsec) == 0) {
/* /*
* It actually did fire already. * It actually did fire already.
*/ */
return 0; return 0;
} }
error = -ERESTART_RESTARTBLOCK;
}
return error;
}
int posix_cpu_nsleep(const clockid_t which_clock, int flags,
struct timespec *rqtp, struct timespec __user *rmtp)
{
struct restart_block *restart_block =
&current_thread_info()->restart_block;
struct itimerspec it;
int error;
/*
* Diagnose required errors first.
*/
if (CPUCLOCK_PERTHREAD(which_clock) &&
(CPUCLOCK_PID(which_clock) == 0 ||
CPUCLOCK_PID(which_clock) == current->pid))
return -EINVAL;
error = do_cpu_nanosleep(which_clock, flags, rqtp, &it);
if (error == -ERESTART_RESTARTBLOCK) {
if (flags & TIMER_ABSTIME)
return -ERESTARTNOHAND;
/* /*
* Report back to the user the time still remaining. * Report back to the user the time still remaining.
*/ */
if (rmtp != NULL && !(flags & TIMER_ABSTIME) && if (rmtp != NULL && copy_to_user(rmtp, &it.it_value, sizeof *rmtp))
copy_to_user(rmtp, &it.it_value, sizeof *rmtp))
return -EFAULT; return -EFAULT;
restart_block->fn = posix_cpu_nsleep_restart; restart_block->fn = posix_cpu_nsleep_restart;
/* Caller already set restart_block->arg1 */
restart_block->arg0 = which_clock; restart_block->arg0 = which_clock;
restart_block->arg1 = (unsigned long) rmtp; restart_block->arg1 = (unsigned long) rmtp;
restart_block->arg2 = rqtp->tv_sec; restart_block->arg2 = rqtp->tv_sec;
restart_block->arg3 = rqtp->tv_nsec; restart_block->arg3 = rqtp->tv_nsec;
error = -ERESTART_RESTARTBLOCK;
} }
return error; return error;
} }
...@@ -1487,13 +1501,31 @@ long posix_cpu_nsleep_restart(struct restart_block *restart_block) ...@@ -1487,13 +1501,31 @@ long posix_cpu_nsleep_restart(struct restart_block *restart_block)
clockid_t which_clock = restart_block->arg0; clockid_t which_clock = restart_block->arg0;
struct timespec __user *rmtp; struct timespec __user *rmtp;
struct timespec t; struct timespec t;
struct itimerspec it;
int error;
rmtp = (struct timespec __user *) restart_block->arg1; rmtp = (struct timespec __user *) restart_block->arg1;
t.tv_sec = restart_block->arg2; t.tv_sec = restart_block->arg2;
t.tv_nsec = restart_block->arg3; t.tv_nsec = restart_block->arg3;
restart_block->fn = do_no_restart_syscall; restart_block->fn = do_no_restart_syscall;
return posix_cpu_nsleep(which_clock, TIMER_ABSTIME, &t, rmtp); error = do_cpu_nanosleep(which_clock, TIMER_ABSTIME, &t, &it);
if (error == -ERESTART_RESTARTBLOCK) {
/*
* Report back to the user the time still remaining.
*/
if (rmtp != NULL && copy_to_user(rmtp, &it.it_value, sizeof *rmtp))
return -EFAULT;
restart_block->fn = posix_cpu_nsleep_restart;
restart_block->arg0 = which_clock;
restart_block->arg1 = (unsigned long) rmtp;
restart_block->arg2 = t.tv_sec;
restart_block->arg3 = t.tv_nsec;
}
return error;
} }
......
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