Commit 0a20887d authored by Andrew Morton's avatar Andrew Morton Committed by Linus Torvalds

[PATCH] sys_nanosleep() fix

The current nanosleep implementation has a signedness problem which can cause
it to sleep for 0x7ffffffe jiffies if a clock interrupt happens at the wrong
time.

The patch fixes that up, and also fixes an wrapping-unsafe 64-bit time
comparison.  Also uninline tstojiffie(), which has three call sites.
parent a20b40ff
...@@ -182,8 +182,7 @@ init_posix_timers(void) ...@@ -182,8 +182,7 @@ init_posix_timers(void)
__initcall(init_posix_timers); __initcall(init_posix_timers);
static inline int static void tstojiffie(struct timespec *tp, int res, u64 *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;
...@@ -212,17 +211,14 @@ tstojiffie(struct timespec *tp, int res, u64 *jiff) ...@@ -212,17 +211,14 @@ tstojiffie(struct timespec *tp, int res, u64 *jiff)
* Split to jiffie and sub jiffie * Split to jiffie and sub jiffie
*/ */
*jiff += nsec / (NSEC_PER_SEC / HZ); *jiff += nsec / (NSEC_PER_SEC / HZ);
/*
* We trust that the optimizer will use the remainder from the
* above div in the following operation as long as they are close.
*/
return 0;
} }
static void static void
tstotimer(struct itimerspec *time, struct k_itimer *timer) tstotimer(struct itimerspec *time, struct k_itimer *timer)
{ {
u64 result; u64 result;
int res = posix_clocks[timer->it_clock].res; int res = posix_clocks[timer->it_clock].res;
tstojiffie(&time->it_value, res, &result); tstojiffie(&time->it_value, res, &result);
timer->it_timer.expires = (unsigned long)result; timer->it_timer.expires = (unsigned long)result;
tstojiffie(&time->it_interval, res, &result); tstojiffie(&time->it_interval, res, &result);
...@@ -1195,6 +1191,7 @@ sys_clock_nanosleep(clockid_t which_clock, int flags, ...@@ -1195,6 +1191,7 @@ 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)
{ {
...@@ -1225,41 +1222,40 @@ do_clock_nanosleep(clockid_t which_clock, int flags, struct timespec *tsave) ...@@ -1225,41 +1222,40 @@ do_clock_nanosleep(clockid_t which_clock, int flags, struct timespec *tsave)
rq_time = (rq_time << 32) + restart_block->arg2; rq_time = (rq_time << 32) + restart_block->arg2;
if (!rq_time) if (!rq_time)
return -EINTR; return -EINTR;
if (rq_time <= get_jiffies_64()) left = rq_time - get_jiffies_64();
return 0; if (left <= 0LL)
return 0; /* Already passed */
} }
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)) {
add_wait_queue(&nanosleep_abs_wqueue, &abs_wqueue); add_wait_queue(&nanosleep_abs_wqueue, &abs_wqueue);
} }
do { do {
t = *tsave; t = *tsave;
if (abs || !rq_time){ if (abs || !rq_time) {
adjust_abs_time(&posix_clocks[which_clock], &t, abs); adjust_abs_time(&posix_clocks[which_clock], &t, abs);
tstojiffie(&t, posix_clocks[which_clock].res, &rq_time); tstojiffie(&t, posix_clocks[which_clock].res, &rq_time);
} }
#if (BITS_PER_LONG < 64)
if ((rq_time - get_jiffies_64()) > MAX_JIFFY_OFFSET){ left = rq_time - get_jiffies_64();
new_timer.expires = MAX_JIFFY_OFFSET; if (left >= MAX_JIFFY_OFFSET)
}else left = MAX_JIFFY_OFFSET;
#endif if (left < 0)
{ break;
new_timer.expires = (long)rq_time;
} new_timer.expires = jiffies + left;
current->state = TASK_INTERRUPTIBLE; __set_current_state(TASK_INTERRUPTIBLE);
add_timer(&new_timer); add_timer(&new_timer);
schedule(); schedule();
del_timer_sync(&new_timer); del_timer_sync(&new_timer);
left = rq_time - get_jiffies_64(); left = rq_time - get_jiffies_64();
} } while (left > 0 && !test_thread_flag(TIF_SIGPENDING));
while ( (left > 0) &&
!test_thread_flag(TIF_SIGPENDING));
if( abs_wqueue.task_list.next) if (abs_wqueue.task_list.next)
finish_wait(&nanosleep_abs_wqueue, &abs_wqueue); finish_wait(&nanosleep_abs_wqueue, &abs_wqueue);
if (left > 0) { if (left > 0) {
......
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