[PATCH] posix-timers: Fix clock_nanosleep() doesn't return the remaining time in compatibility mode
The clock_nanosleep() function does not return the time remaining when the sleep is interrupted by a signal. This patch creates a new call out, compat_clock_nanosleep_restart(), which handles returning the remaining time after a sleep is interrupted. This patch revives clock_nanosleep_restart(). It is now accessed via the new call out. The compat_clock_nanosleep_restart() is used for compatibility access. Since this is implemented in compatibility mode the normal path is virtually unaffected - no real performance impact. Signed-off-by: Toyo 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: Andrew Morton <akpm@osdl.org> Signed-off-by: Linus Torvalds <torvalds@osdl.org>
This commit is contained in:
parent
9c4751fd0e
commit
1711ef3866
|
@ -138,6 +138,7 @@ extern long hrtimer_nanosleep(struct timespec *rqtp,
|
||||||
struct timespec __user *rmtp,
|
struct timespec __user *rmtp,
|
||||||
const enum hrtimer_mode mode,
|
const enum hrtimer_mode mode,
|
||||||
const clockid_t clockid);
|
const clockid_t clockid);
|
||||||
|
extern long hrtimer_nanosleep_restart(struct restart_block *restart_block);
|
||||||
|
|
||||||
extern void hrtimer_init_sleeper(struct hrtimer_sleeper *sl,
|
extern void hrtimer_init_sleeper(struct hrtimer_sleeper *sl,
|
||||||
struct task_struct *tsk);
|
struct task_struct *tsk);
|
||||||
|
|
|
@ -72,6 +72,7 @@ struct k_clock {
|
||||||
int (*timer_create) (struct k_itimer *timer);
|
int (*timer_create) (struct k_itimer *timer);
|
||||||
int (*nsleep) (const clockid_t which_clock, int flags,
|
int (*nsleep) (const clockid_t which_clock, int flags,
|
||||||
struct timespec *, struct timespec __user *);
|
struct timespec *, struct timespec __user *);
|
||||||
|
long (*nsleep_restart) (struct restart_block *restart_block);
|
||||||
int (*timer_set) (struct k_itimer * timr, int flags,
|
int (*timer_set) (struct k_itimer * timr, int flags,
|
||||||
struct itimerspec * new_setting,
|
struct itimerspec * new_setting,
|
||||||
struct itimerspec * old_setting);
|
struct itimerspec * old_setting);
|
||||||
|
@ -97,6 +98,7 @@ int posix_cpu_clock_set(const clockid_t which_clock, const struct timespec *ts);
|
||||||
int posix_cpu_timer_create(struct k_itimer *timer);
|
int posix_cpu_timer_create(struct k_itimer *timer);
|
||||||
int posix_cpu_nsleep(const clockid_t which_clock, int flags,
|
int posix_cpu_nsleep(const clockid_t which_clock, int flags,
|
||||||
struct timespec *rqtp, struct timespec __user *rmtp);
|
struct timespec *rqtp, struct timespec __user *rmtp);
|
||||||
|
long posix_cpu_nsleep_restart(struct restart_block *restart_block);
|
||||||
int posix_cpu_timer_set(struct k_itimer *timer, int flags,
|
int posix_cpu_timer_set(struct k_itimer *timer, int flags,
|
||||||
struct itimerspec *new, struct itimerspec *old);
|
struct itimerspec *new, struct itimerspec *old);
|
||||||
int posix_cpu_timer_del(struct k_itimer *timer);
|
int posix_cpu_timer_del(struct k_itimer *timer);
|
||||||
|
@ -111,4 +113,6 @@ void posix_cpu_timers_exit_group(struct task_struct *task);
|
||||||
void set_process_cpu_timer(struct task_struct *task, unsigned int clock_idx,
|
void set_process_cpu_timer(struct task_struct *task, unsigned int clock_idx,
|
||||||
cputime_t *newval, cputime_t *oldval);
|
cputime_t *newval, cputime_t *oldval);
|
||||||
|
|
||||||
|
long clock_nanosleep_restart(struct restart_block *restart_block);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -22,6 +22,7 @@
|
||||||
#include <linux/security.h>
|
#include <linux/security.h>
|
||||||
#include <linux/timex.h>
|
#include <linux/timex.h>
|
||||||
#include <linux/migrate.h>
|
#include <linux/migrate.h>
|
||||||
|
#include <linux/posix-timers.h>
|
||||||
|
|
||||||
#include <asm/uaccess.h>
|
#include <asm/uaccess.h>
|
||||||
|
|
||||||
|
@ -601,6 +602,30 @@ long compat_sys_clock_getres(clockid_t which_clock,
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static long compat_clock_nanosleep_restart(struct restart_block *restart)
|
||||||
|
{
|
||||||
|
long err;
|
||||||
|
mm_segment_t oldfs;
|
||||||
|
struct timespec tu;
|
||||||
|
struct compat_timespec *rmtp = (struct compat_timespec *)(restart->arg1);
|
||||||
|
|
||||||
|
restart->arg1 = (unsigned long) &tu;
|
||||||
|
oldfs = get_fs();
|
||||||
|
set_fs(KERNEL_DS);
|
||||||
|
err = clock_nanosleep_restart(restart);
|
||||||
|
set_fs(oldfs);
|
||||||
|
|
||||||
|
if ((err == -ERESTART_RESTARTBLOCK) && rmtp &&
|
||||||
|
put_compat_timespec(&tu, rmtp))
|
||||||
|
return -EFAULT;
|
||||||
|
|
||||||
|
if (err == -ERESTART_RESTARTBLOCK) {
|
||||||
|
restart->fn = compat_clock_nanosleep_restart;
|
||||||
|
restart->arg1 = (unsigned long) rmtp;
|
||||||
|
}
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
long compat_sys_clock_nanosleep(clockid_t which_clock, int flags,
|
long compat_sys_clock_nanosleep(clockid_t which_clock, int flags,
|
||||||
struct compat_timespec __user *rqtp,
|
struct compat_timespec __user *rqtp,
|
||||||
struct compat_timespec __user *rmtp)
|
struct compat_timespec __user *rmtp)
|
||||||
|
@ -608,6 +633,7 @@ long compat_sys_clock_nanosleep(clockid_t which_clock, int flags,
|
||||||
long err;
|
long err;
|
||||||
mm_segment_t oldfs;
|
mm_segment_t oldfs;
|
||||||
struct timespec in, out;
|
struct timespec in, out;
|
||||||
|
struct restart_block *restart;
|
||||||
|
|
||||||
if (get_compat_timespec(&in, rqtp))
|
if (get_compat_timespec(&in, rqtp))
|
||||||
return -EFAULT;
|
return -EFAULT;
|
||||||
|
@ -618,9 +644,16 @@ long compat_sys_clock_nanosleep(clockid_t which_clock, int flags,
|
||||||
(struct timespec __user *) &in,
|
(struct timespec __user *) &in,
|
||||||
(struct timespec __user *) &out);
|
(struct timespec __user *) &out);
|
||||||
set_fs(oldfs);
|
set_fs(oldfs);
|
||||||
|
|
||||||
if ((err == -ERESTART_RESTARTBLOCK) && rmtp &&
|
if ((err == -ERESTART_RESTARTBLOCK) && rmtp &&
|
||||||
put_compat_timespec(&out, rmtp))
|
put_compat_timespec(&out, rmtp))
|
||||||
return -EFAULT;
|
return -EFAULT;
|
||||||
|
|
||||||
|
if (err == -ERESTART_RESTARTBLOCK) {
|
||||||
|
restart = ¤t_thread_info()->restart_block;
|
||||||
|
restart->fn = compat_clock_nanosleep_restart;
|
||||||
|
restart->arg1 = (unsigned long) rmtp;
|
||||||
|
}
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -693,7 +693,7 @@ static int __sched do_nanosleep(struct hrtimer_sleeper *t, enum hrtimer_mode mod
|
||||||
return t->task == NULL;
|
return t->task == NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
static long __sched nanosleep_restart(struct restart_block *restart)
|
long __sched hrtimer_nanosleep_restart(struct restart_block *restart)
|
||||||
{
|
{
|
||||||
struct hrtimer_sleeper t;
|
struct hrtimer_sleeper t;
|
||||||
struct timespec __user *rmtp;
|
struct timespec __user *rmtp;
|
||||||
|
@ -702,13 +702,13 @@ static long __sched nanosleep_restart(struct restart_block *restart)
|
||||||
|
|
||||||
restart->fn = do_no_restart_syscall;
|
restart->fn = do_no_restart_syscall;
|
||||||
|
|
||||||
hrtimer_init(&t.timer, restart->arg3, HRTIMER_ABS);
|
hrtimer_init(&t.timer, restart->arg0, HRTIMER_ABS);
|
||||||
t.timer.expires.tv64 = ((u64)restart->arg1 << 32) | (u64) restart->arg0;
|
t.timer.expires.tv64 = ((u64)restart->arg3 << 32) | (u64) restart->arg2;
|
||||||
|
|
||||||
if (do_nanosleep(&t, HRTIMER_ABS))
|
if (do_nanosleep(&t, HRTIMER_ABS))
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
rmtp = (struct timespec __user *) restart->arg2;
|
rmtp = (struct timespec __user *) restart->arg1;
|
||||||
if (rmtp) {
|
if (rmtp) {
|
||||||
time = ktime_sub(t.timer.expires, t.timer.base->get_time());
|
time = ktime_sub(t.timer.expires, t.timer.base->get_time());
|
||||||
if (time.tv64 <= 0)
|
if (time.tv64 <= 0)
|
||||||
|
@ -718,7 +718,7 @@ static long __sched nanosleep_restart(struct restart_block *restart)
|
||||||
return -EFAULT;
|
return -EFAULT;
|
||||||
}
|
}
|
||||||
|
|
||||||
restart->fn = nanosleep_restart;
|
restart->fn = hrtimer_nanosleep_restart;
|
||||||
|
|
||||||
/* The other values in restart are already filled in */
|
/* The other values in restart are already filled in */
|
||||||
return -ERESTART_RESTARTBLOCK;
|
return -ERESTART_RESTARTBLOCK;
|
||||||
|
@ -751,11 +751,11 @@ long hrtimer_nanosleep(struct timespec *rqtp, struct timespec __user *rmtp,
|
||||||
}
|
}
|
||||||
|
|
||||||
restart = ¤t_thread_info()->restart_block;
|
restart = ¤t_thread_info()->restart_block;
|
||||||
restart->fn = nanosleep_restart;
|
restart->fn = hrtimer_nanosleep_restart;
|
||||||
restart->arg0 = t.timer.expires.tv64 & 0xFFFFFFFF;
|
restart->arg0 = (unsigned long) t.timer.base->index;
|
||||||
restart->arg1 = t.timer.expires.tv64 >> 32;
|
restart->arg1 = (unsigned long) rmtp;
|
||||||
restart->arg2 = (unsigned long) rmtp;
|
restart->arg2 = t.timer.expires.tv64 & 0xFFFFFFFF;
|
||||||
restart->arg3 = (unsigned long) t.timer.base->index;
|
restart->arg3 = t.timer.expires.tv64 >> 32;
|
||||||
|
|
||||||
return -ERESTART_RESTARTBLOCK;
|
return -ERESTART_RESTARTBLOCK;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1393,8 +1393,6 @@ void set_process_cpu_timer(struct task_struct *tsk, unsigned int clock_idx,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static long posix_cpu_clock_nanosleep_restart(struct restart_block *);
|
|
||||||
|
|
||||||
int posix_cpu_nsleep(const clockid_t which_clock, int flags,
|
int posix_cpu_nsleep(const clockid_t which_clock, int flags,
|
||||||
struct timespec *rqtp, struct timespec __user *rmtp)
|
struct timespec *rqtp, struct timespec __user *rmtp)
|
||||||
{
|
{
|
||||||
|
@ -1471,7 +1469,7 @@ int posix_cpu_nsleep(const clockid_t which_clock, int flags,
|
||||||
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_clock_nanosleep_restart;
|
restart_block->fn = posix_cpu_nsleep_restart;
|
||||||
/* Caller already set restart_block->arg1 */
|
/* 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;
|
||||||
|
@ -1484,8 +1482,7 @@ int posix_cpu_nsleep(const clockid_t which_clock, int flags,
|
||||||
return error;
|
return error;
|
||||||
}
|
}
|
||||||
|
|
||||||
static long
|
long posix_cpu_nsleep_restart(struct restart_block *restart_block)
|
||||||
posix_cpu_clock_nanosleep_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;
|
||||||
|
@ -1524,6 +1521,10 @@ static int process_cpu_nsleep(const clockid_t which_clock, int flags,
|
||||||
{
|
{
|
||||||
return posix_cpu_nsleep(PROCESS_CLOCK, flags, rqtp, rmtp);
|
return posix_cpu_nsleep(PROCESS_CLOCK, flags, rqtp, rmtp);
|
||||||
}
|
}
|
||||||
|
static long process_cpu_nsleep_restart(struct restart_block *restart_block)
|
||||||
|
{
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
static int thread_cpu_clock_getres(const clockid_t which_clock,
|
static int thread_cpu_clock_getres(const clockid_t which_clock,
|
||||||
struct timespec *tp)
|
struct timespec *tp)
|
||||||
{
|
{
|
||||||
|
@ -1544,6 +1545,10 @@ static int thread_cpu_nsleep(const clockid_t which_clock, int flags,
|
||||||
{
|
{
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
static long thread_cpu_nsleep_restart(struct restart_block *restart_block)
|
||||||
|
{
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
static __init int init_posix_cpu_timers(void)
|
static __init int init_posix_cpu_timers(void)
|
||||||
{
|
{
|
||||||
|
@ -1553,6 +1558,7 @@ static __init int init_posix_cpu_timers(void)
|
||||||
.clock_set = do_posix_clock_nosettime,
|
.clock_set = do_posix_clock_nosettime,
|
||||||
.timer_create = process_cpu_timer_create,
|
.timer_create = process_cpu_timer_create,
|
||||||
.nsleep = process_cpu_nsleep,
|
.nsleep = process_cpu_nsleep,
|
||||||
|
.nsleep_restart = process_cpu_nsleep_restart,
|
||||||
};
|
};
|
||||||
struct k_clock thread = {
|
struct k_clock thread = {
|
||||||
.clock_getres = thread_cpu_clock_getres,
|
.clock_getres = thread_cpu_clock_getres,
|
||||||
|
@ -1560,6 +1566,7 @@ static __init int init_posix_cpu_timers(void)
|
||||||
.clock_set = do_posix_clock_nosettime,
|
.clock_set = do_posix_clock_nosettime,
|
||||||
.timer_create = thread_cpu_timer_create,
|
.timer_create = thread_cpu_timer_create,
|
||||||
.nsleep = thread_cpu_nsleep,
|
.nsleep = thread_cpu_nsleep,
|
||||||
|
.nsleep_restart = thread_cpu_nsleep_restart,
|
||||||
};
|
};
|
||||||
|
|
||||||
register_posix_clock(CLOCK_PROCESS_CPUTIME_ID, &process);
|
register_posix_clock(CLOCK_PROCESS_CPUTIME_ID, &process);
|
||||||
|
|
|
@ -973,3 +973,24 @@ sys_clock_nanosleep(const clockid_t which_clock, int flags,
|
||||||
return CLOCK_DISPATCH(which_clock, nsleep,
|
return CLOCK_DISPATCH(which_clock, nsleep,
|
||||||
(which_clock, flags, &t, rmtp));
|
(which_clock, flags, &t, rmtp));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* nanosleep_restart for monotonic and realtime clocks
|
||||||
|
*/
|
||||||
|
static int common_nsleep_restart(struct restart_block *restart_block)
|
||||||
|
{
|
||||||
|
return hrtimer_nanosleep_restart(restart_block);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This will restart clock_nanosleep. This is required only by
|
||||||
|
* compat_clock_nanosleep_restart for now.
|
||||||
|
*/
|
||||||
|
long
|
||||||
|
clock_nanosleep_restart(struct restart_block *restart_block)
|
||||||
|
{
|
||||||
|
clockid_t which_clock = restart_block->arg0;
|
||||||
|
|
||||||
|
return CLOCK_DISPATCH(which_clock, nsleep_restart,
|
||||||
|
(restart_block));
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue