diff options
| author | Linus Torvalds <torvalds@home.transmeta.com> | 2002-12-05 19:10:36 -0800 |
|---|---|---|
| committer | Linus Torvalds <torvalds@home.transmeta.com> | 2002-12-05 19:10:36 -0800 |
| commit | 80ca75c4023da493b4300ee16e758c2c1900f6a7 (patch) | |
| tree | 7b8d50f49559bd981120db2e5c4f07bc294cb522 /kernel | |
| parent | 66d77a121414f655fa37479981d660d78edb0d46 (diff) | |
Implement system call restarting for the "nanosleep()" system call
using the new system call restart infrastructure.
This breaks the compat layer - it really needs to do its own version
of restarting, since the restarting depends on the types.
Diffstat (limited to 'kernel')
| -rw-r--r-- | kernel/timer.c | 48 |
1 files changed, 36 insertions, 12 deletions
diff --git a/kernel/timer.c b/kernel/timer.c index fe16e7a87afc..efa02b1c512c 100644 --- a/kernel/timer.c +++ b/kernel/timer.c @@ -25,6 +25,7 @@ #include <linux/init.h> #include <linux/mm.h> #include <linux/notifier.h> +#include <linux/thread_info.h> #include <asm/uaccess.h> @@ -1020,37 +1021,60 @@ asmlinkage long sys_gettid(void) return current->pid; } -long do_nanosleep(struct timespec *t) +static long nanosleep_restart(struct restart_block *restart) { - unsigned long expire; - - if ((t->tv_nsec >= 1000000000L) || (t->tv_nsec < 0) || (t->tv_sec < 0)) - return -EINVAL; + unsigned long expire = restart->arg0, now = jiffies; + struct timespec *rmtp = (struct timespec *) restart->arg1; + long ret; - expire = timespec_to_jiffies(t) + (t->tv_sec || t->tv_nsec); + /* Did it expire while we handled signals? */ + if (!time_after(expire, now)) + return 0; current->state = TASK_INTERRUPTIBLE; - expire = schedule_timeout(expire); + expire = schedule_timeout(expire - now); + ret = 0; if (expire) { - jiffies_to_timespec(expire, t); - return -EINTR; + struct timespec t; + jiffies_to_timespec(expire, &t); + + ret = -ERESTART_RESTARTBLOCK; + if (copy_to_user(rmtp, &t, sizeof(t))) + ret = -EFAULT; + /* The 'restart' block is already filled in */ } - return 0; + return ret; } asmlinkage long sys_nanosleep(struct timespec *rqtp, struct timespec *rmtp) { struct timespec t; + unsigned long expire; long ret; if (copy_from_user(&t, rqtp, sizeof(t))) return -EFAULT; - ret = do_nanosleep(&t); - if (rmtp && (ret == -EINTR)) { + if ((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); + + ret = 0; + if (expire) { + struct restart_block *restart; + jiffies_to_timespec(expire, &t); if (copy_to_user(rmtp, &t, sizeof(t))) return -EFAULT; + + restart = ¤t_thread_info()->restart_block; + restart->fn = nanosleep_restart; + restart->arg0 = jiffies + expire; + restart->arg1 = (unsigned long) rmtp; + ret = -ERESTART_RESTARTBLOCK; } return ret; } |
