/* * 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)); }
SYSCALL_DEFINE2(clock_settime, const clockid_t, which_clock, const struct timespec __user *, tp) { struct timespec new_tp; if (invalid_clockid(which_clock)) return -EINVAL; if (copy_from_user(&new_tp, tp, sizeof (*tp))) return -EFAULT; return CLOCK_DISPATCH(which_clock, clock_set, (which_clock, &new_tp)); }
asmlinkage long sys_clock_settime(const clockid_t which_clock, const struct timespec __user *tp) { struct timespec new_tp; if (invalid_clockid(which_clock)) return -EINVAL; if (copy_from_user(&new_tp, tp, sizeof (*tp))) return -EFAULT; return CLOCK_DISPATCH(which_clock, clock_set, (which_clock, &new_tp)); }
SYSCALL_DEFINE2(clock_gettime, const clockid_t, which_clock, struct timespec __user *,tp) { struct timespec kernel_tp; int error; if (invalid_clockid(which_clock)) return -EINVAL; error = CLOCK_DISPATCH(which_clock, clock_get, (which_clock, &kernel_tp)); if (!error && copy_to_user(tp, &kernel_tp, sizeof (kernel_tp))) error = -EFAULT; return error; }
asmlinkage long sys_clock_gettime(const clockid_t which_clock, struct timespec __user *tp) { struct timespec kernel_tp; int error; if (invalid_clockid(which_clock)) return -EINVAL; error = CLOCK_DISPATCH(which_clock, clock_get, (which_clock, &kernel_tp)); if (!error && copy_to_user(tp, &kernel_tp, sizeof (kernel_tp))) error = -EFAULT; return error; }
SYSCALL_DEFINE4(clock_nanosleep, const clockid_t, which_clock, int, flags, const struct timespec __user *, rqtp, struct timespec __user *, rmtp) { struct timespec t; if (invalid_clockid(which_clock)) return -EINVAL; if (copy_from_user(&t, rqtp, sizeof (struct timespec))) return -EFAULT; if (!timespec_valid(&t)) return -EINVAL; return CLOCK_DISPATCH(which_clock, nsleep, (which_clock, flags, &t, rmtp)); }
SYSCALL_DEFINE2(clock_getres, const clockid_t, which_clock, struct timespec __user *, tp) { struct timespec rtn_tp; int error; if (invalid_clockid(which_clock)) return -EINVAL; error = CLOCK_DISPATCH(which_clock, clock_getres, (which_clock, &rtn_tp)); if (!error && tp && copy_to_user(tp, &rtn_tp, sizeof (rtn_tp))) { error = -EFAULT; } return error; }
asmlinkage long sys_clock_getres(const clockid_t which_clock, struct timespec __user *tp) { struct timespec rtn_tp; int error; if (invalid_clockid(which_clock)) return -EINVAL; error = CLOCK_DISPATCH(which_clock, clock_getres, (which_clock, &rtn_tp)); if (!error && tp && copy_to_user(tp, &rtn_tp, sizeof (rtn_tp))) { error = -EFAULT; } return error; }
asmlinkage long sys_clock_nanosleep(const clockid_t which_clock, int flags, const struct timespec __user *rqtp, struct timespec __user *rmtp) { struct timespec t; if (invalid_clockid(which_clock)) return -EINVAL; if (copy_from_user(&t, rqtp, sizeof (struct timespec))) return -EFAULT; if (!timespec_valid(&t)) return -EINVAL; return CLOCK_DISPATCH(which_clock, nsleep, (which_clock, flags, &t, rmtp)); }
/* Get the time remaining on a POSIX.1b interval timer. */ SYSCALL_DEFINE2(timer_gettime, timer_t, timer_id, struct itimerspec __user *, setting) { struct k_itimer *timr; struct itimerspec cur_setting; unsigned long flags; timr = lock_timer(timer_id, &flags); if (!timr) return -EINVAL; CLOCK_DISPATCH(timr->it_clock, timer_get, (timr, &cur_setting)); unlock_timer(timr, flags); if (copy_to_user(setting, &cur_setting, sizeof (cur_setting))) return -EFAULT; return 0; }
/* Set a POSIX.1b interval timer */ SYSCALL_DEFINE4(timer_settime, timer_t, timer_id, int, flags, const struct itimerspec __user *, new_setting, struct itimerspec __user *, old_setting) { struct k_itimer *timr; struct itimerspec new_spec, old_spec; int error = 0; unsigned long flag; struct itimerspec *rtn = old_setting ? &old_spec : NULL; if (!new_setting) return -EINVAL; if (copy_from_user(&new_spec, new_setting, sizeof (new_spec))) return -EFAULT; if (!timespec_valid(&new_spec.it_interval) || !timespec_valid(&new_spec.it_value)) return -EINVAL; retry: timr = lock_timer(timer_id, &flag); if (!timr) return -EINVAL; error = CLOCK_DISPATCH(timr->it_clock, timer_set, (timr, flags, &new_spec, rtn)); unlock_timer(timr, flag); if (error == TIMER_RETRY) { hrtimer_wait_for_timer(&timr->it.real.timer); rtn = NULL; // We already got the old time... goto retry; } if (old_setting && !error && copy_to_user(old_setting, &old_spec, sizeof (old_spec))) error = -EFAULT; return error; }
static inline int timer_delete_hook(struct k_itimer *timer) { return CLOCK_DISPATCH(timer->it_clock, timer_del, (timer)); }
SYSCALL_DEFINE3(timer_create, const clockid_t, which_clock, struct sigevent __user *, timer_event_spec, timer_t __user *, created_timer_id) { struct k_itimer *new_timer; int error, new_timer_id; sigevent_t event; int it_id_set = IT_ID_NOT_SET; if (invalid_clockid(which_clock)) return -EINVAL; new_timer = alloc_posix_timer(); if (unlikely(!new_timer)) return -EAGAIN; spin_lock_init(&new_timer->it_lock); retry: if (unlikely(!idr_pre_get(&posix_timers_id, GFP_KERNEL))) { error = -EAGAIN; goto out; } spin_lock_irq(&idr_lock); error = idr_get_new(&posix_timers_id, new_timer, &new_timer_id); spin_unlock_irq(&idr_lock); if (error) { if (error == -EAGAIN) goto retry; /* * Weird looking, but we return EAGAIN if the IDR is * full (proper POSIX return value for this) */ error = -EAGAIN; goto out; } it_id_set = IT_ID_SET; new_timer->it_id = (timer_t) new_timer_id; new_timer->it_clock = which_clock; new_timer->it_overrun = -1; error = CLOCK_DISPATCH(which_clock, timer_create, (new_timer)); if (error) goto out; /* * return the timer_id now. The next step is hard to * back out if there is an error. */ if (copy_to_user(created_timer_id, &new_timer_id, sizeof (new_timer_id))) { error = -EFAULT; goto out; } if (timer_event_spec) { if (copy_from_user(&event, timer_event_spec, sizeof (event))) { error = -EFAULT; goto out; } rcu_read_lock(); new_timer->it_pid = get_pid(good_sigevent(&event)); rcu_read_unlock(); if (!new_timer->it_pid) { error = -EINVAL; goto out; } } else { event.sigev_notify = SIGEV_SIGNAL; event.sigev_signo = SIGALRM; event.sigev_value.sival_int = new_timer->it_id; new_timer->it_pid = get_pid(task_tgid(current)); } new_timer->it_sigev_notify = event.sigev_notify; new_timer->sigq->info.si_signo = event.sigev_signo; new_timer->sigq->info.si_value = event.sigev_value; new_timer->sigq->info.si_tid = new_timer->it_id; new_timer->sigq->info.si_code = SI_TIMER; spin_lock_irq(¤t->sighand->siglock); new_timer->it_signal = current->signal; list_add(&new_timer->list, ¤t->signal->posix_timers); spin_unlock_irq(¤t->sighand->siglock); return 0; /* * In the case of the timer belonging to another task, after * the task is unlocked, the timer is owned by the other task * and may cease to exist at any time. Don't use or modify * new_timer after the unlock call. */ out: release_posix_timer(new_timer, it_id_set); return error; }
asmlinkage long sys_timer_create(const clockid_t which_clock, struct sigevent __user *timer_event_spec, timer_t __user * created_timer_id) { int error = 0; struct k_itimer *new_timer = NULL; int new_timer_id; struct task_struct *process = NULL; unsigned long flags; sigevent_t event; int it_id_set = IT_ID_NOT_SET; if (invalid_clockid(which_clock)) return -EINVAL; new_timer = alloc_posix_timer(); if (unlikely(!new_timer)) return -EAGAIN; spin_lock_init(&new_timer->it_lock); retry: if (unlikely(!idr_pre_get(&posix_timers_id, GFP_KERNEL))) { error = -EAGAIN; goto out; } spin_lock_irq(&idr_lock); error = idr_get_new(&posix_timers_id, (void *) new_timer, &new_timer_id); spin_unlock_irq(&idr_lock); if (error == -EAGAIN) goto retry; else if (error) { /* * Wierd looking, but we return EAGAIN if the IDR is * full (proper POSIX return value for this) */ error = -EAGAIN; goto out; } it_id_set = IT_ID_SET; new_timer->it_id = (timer_t) new_timer_id; new_timer->it_clock = which_clock; new_timer->it_overrun = -1; error = CLOCK_DISPATCH(which_clock, timer_create, (new_timer)); if (error) goto out; /* * return the timer_id now. The next step is hard to * back out if there is an error. */ if (copy_to_user(created_timer_id, &new_timer_id, sizeof (new_timer_id))) { error = -EFAULT; goto out; } if (timer_event_spec) { if (copy_from_user(&event, timer_event_spec, sizeof (event))) { error = -EFAULT; goto out; } new_timer->it_sigev_notify = event.sigev_notify; new_timer->it_sigev_signo = event.sigev_signo; new_timer->it_sigev_value = event.sigev_value; read_lock(&tasklist_lock); if ((process = good_sigevent(&event))) { /* * We may be setting up this process for another * thread. It may be exiting. To catch this * case the we check the PF_EXITING flag. If * the flag is not set, the siglock will catch * him before it is too late (in exit_itimers). * * The exec case is a bit more invloved but easy * to code. If the process is in our thread * group (and it must be or we would not allow * it here) and is doing an exec, it will cause * us to be killed. In this case it will wait * for us to die which means we can finish this * linkage with our last gasp. I.e. no code :) */ spin_lock_irqsave(&process->sighand->siglock, flags); if (!(process->flags & PF_EXITING)) { new_timer->it_process = process; list_add(&new_timer->list, &process->signal->posix_timers); spin_unlock_irqrestore(&process->sighand->siglock, flags); if (new_timer->it_sigev_notify == (SIGEV_SIGNAL|SIGEV_THREAD_ID)) get_task_struct(process); } else { spin_unlock_irqrestore(&process->sighand->siglock, flags); process = NULL; } } read_unlock(&tasklist_lock); if (!process) { error = -EINVAL; goto out; } } else { new_timer->it_sigev_notify = SIGEV_SIGNAL; new_timer->it_sigev_signo = SIGALRM; new_timer->it_sigev_value.sival_int = new_timer->it_id; process = current->group_leader; spin_lock_irqsave(&process->sighand->siglock, flags); new_timer->it_process = process; list_add(&new_timer->list, &process->signal->posix_timers); spin_unlock_irqrestore(&process->sighand->siglock, flags); } /* * In the case of the timer belonging to another task, after * the task is unlocked, the timer is owned by the other task * and may cease to exist at any time. Don't use or modify * new_timer after the unlock call. */ out: if (error) release_posix_timer(new_timer, it_id_set); return error; }