/* Delete a POSIX.1b interval timer. */ asmlinkage long sys_timer_delete(timer_t timer_id) { struct k_itimer *timer; long flags; retry_delete: timer = lock_timer(timer_id, &flags); if (!timer) return -EINVAL; if (timer_delete_hook(timer) == TIMER_RETRY) { unlock_timer(timer, flags); goto retry_delete; } spin_lock(¤t->sighand->siglock); list_del(&timer->list); spin_unlock(¤t->sighand->siglock); /* * This keeps any tasks waiting on the spin lock from thinking * they got something (see the lock code above). */ if (timer->it_process) { if (timer->it_sigev_notify == (SIGEV_SIGNAL|SIGEV_THREAD_ID)) put_task_struct(timer->it_process); timer->it_process = NULL; } unlock_timer(timer, flags); release_posix_timer(timer, IT_ID_SET); return 0; }
/* Delete a POSIX.1b interval timer. */ SYSCALL_DEFINE1(timer_delete, timer_t, timer_id) { struct k_itimer *timer; unsigned long flags; retry_delete: timer = lock_timer(timer_id, &flags); if (!timer) return -EINVAL; if (timer_delete_hook(timer) == TIMER_RETRY) { unlock_timer(timer, flags); goto retry_delete; } spin_lock(¤t->sighand->siglock); list_del(&timer->list); spin_unlock(¤t->sighand->siglock); /* * This keeps any tasks waiting on the spin lock from thinking * they got something (see the lock code above). */ timer->it_signal = NULL; unlock_timer(timer, flags); release_posix_timer(timer, IT_ID_SET); return 0; }
/* Get the time remaining on a POSIX.1b interval timer. */ SYSCALL_DEFINE2(timer_gettime, timer_t, timer_id, struct itimerspec __user *, setting) { struct itimerspec cur_setting; struct k_itimer *timr; struct k_clock *kc; unsigned long flags; int ret = 0; timr = lock_timer(timer_id, &flags); if (!timr) return -EINVAL; kc = clockid_to_kclock(timr->it_clock); if (WARN_ON_ONCE(!kc || !kc->timer_get)) ret = -EINVAL; else kc->timer_get(timr, &cur_setting); unlock_timer(timr, flags); if (!ret && copy_to_user(setting, &cur_setting, sizeof (cur_setting))) return -EFAULT; return ret; }
/***************************************************************************** * remove_timer * ****************************************************************************/ static void remove_timer(int id) { /* removes a timer from the timer list */ struct ddekit_timer_s *l,*m; lock_timer(); for (l = &list; l && l->next && l->next->id!=id; l = l->next ) ; if (l && l->next) { m = l->next; DDEBUG_MSG_VERBOSE( "deleting timer at for tick: %d fn: %p, (now: %d)\n", m->exp, m->fn, jiffies); l->next = m->next; DDEBUG_MSG_TIMER(m); ddekit_simple_free(m); } unlock_timer(); }
/***************************************************************************** * _ddekit_timer_update * ****************************************************************************/ void _ddekit_timer_update() { lock_timer(); static myclock_t next_timout; if(list.next) { if(!_ddekit_timer_pending || list.next->exp < next_timout) { unsigned to = list.next->exp - jiffies; _ddekit_timer_pending = 1; if (list.next->exp <= jiffies) { DDEBUG_MSG_WARN("Timeout lies in past to %d, now: %d", list.next->exp, jiffies); to = 1; } sys_setalarm(to, 0 /* REL */); DDEBUG_MSG_VERBOSE("requesting alarm for clock tick %d , now %d", list.next->exp, jiffies); } next_timout = list.next->exp; } unlock_timer(); }
/***************************************************************************** * insert_timer * ****************************************************************************/ static int insert_timer(struct ddekit_timer_s *t) { /* inserts a timer to the timer list */ int ret; lock_timer(); struct ddekit_timer_s *l; for (l = &list; l->next && l->next->exp <= t->exp; l = l->next) { } t->next = l->next; l->next = t; t->id = ret = _id; _id++; if (_id==0) { DDEBUG_MSG_WARN("Timer ID overflow..."); } DDEBUG_MSG_TIMER(t); unlock_timer(); return ret; }
/***************************************************************************** * ddekit_timer_pending * ****************************************************************************/ int ddekit_timer_pending(int timer) { int ret=0; struct ddekit_timer_s *t; lock_timer(); for (t=list.next; t; t = t->next) { if (t->id==timer) { ret = 1; } } unlock_timer(); return ret; }
/* * Get the number of overruns of a POSIX.1b interval timer. This is to * be the overrun of the timer last delivered. At the same time we are * accumulating overruns on the next timer. The overrun is frozen when * the signal is delivered, either at the notify time (if the info block * is not queued) or at the actual delivery time (as we are informed by * the call back to do_schedule_next_timer(). So all we need to do is * to pick up the frozen overrun. */ SYSCALL_DEFINE1(timer_getoverrun, timer_t, timer_id) { struct k_itimer *timr; int overrun; unsigned long flags; timr = lock_timer(timer_id, &flags); if (!timr) return -EINVAL; overrun = timr->it_overrun_last; unlock_timer(timr, flags); return overrun; }
/* * Get the number of overruns of a POSIX.1b interval timer. This is to * be the overrun of the timer last delivered. At the same time we are * accumulating overruns on the next timer. The overrun is frozen when * the signal is delivered, either at the notify time (if the info block * is not queued) or at the actual delivery time (as we are informed by * the call back to do_schedule_next_timer(). So all we need to do is * to pick up the frozen overrun. */ asmlinkage long sys_timer_getoverrun(timer_t timer_id) { struct k_itimer *timr; int overrun; long flags; timr = lock_timer(timer_id, &flags); if (!timr) return -EINVAL; overrun = timr->it_overrun_last; unlock_timer(timr, flags); return overrun; }
/* 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; struct k_clock *kc; 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; rcu_read_lock(); kc = clockid_to_kclock(timr->it_clock); if (WARN_ON_ONCE(!kc || !kc->timer_set)) error = -EINVAL; else error = kc->timer_set(timr, flags, &new_spec, rtn); unlock_timer(timr, flag); if (error == TIMER_RETRY) { timer_wait_for_callback(kc, timr); rtn = NULL; // We already got the old time... rcu_read_unlock(); goto retry; } rcu_read_unlock(); if (old_setting && !error && copy_to_user(old_setting, &old_spec, sizeof (old_spec))) error = -EFAULT; return error; }
/* * This function is exported for use by the signal deliver code. It is * called just prior to the info block being released and passes that * block to us. It's function is to update the overrun entry AND to * restart the timer. It should only be called if the timer is to be * restarted (i.e. we have flagged this in the sys_private entry of the * info block). * * To protect aginst the timer going away while the interrupt is queued, * we require that the it_requeue_pending flag be set. */ void do_schedule_next_timer(struct siginfo *info) { struct k_itimer *timr; unsigned long flags; timr = lock_timer(info->si_tid, &flags); if (timr && timr->it_requeue_pending == info->si_sys_private) { if (timr->it_clock < 0) posix_cpu_timer_schedule(timr); else schedule_next_timer(timr); info->si_overrun = timr->it_overrun_last; } unlock_timer(timr, flags); }
/***************************************************************************** * get_next * ****************************************************************************/ static struct ddekit_timer_s * get_next( myclock_t exp ) { /* * this one get the next timer, which's timeout expired, * returns NULL if no timer is pending */ struct ddekit_timer_s * ret = 0; lock_timer(); if (list.next) { if (list.next->exp <= exp) { ret = list.next; list.next = ret->next; } } unlock_timer(); return ret; }
/* 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; }
SYSCALL_DEFINE1(timer_delete, timer_t, timer_id) { struct k_itimer *timer; unsigned long flags; retry_delete: timer = lock_timer(timer_id, &flags); if (!timer) return -EINVAL; if (timer_delete_hook(timer) == TIMER_RETRY) { unlock_timer(timer, flags); goto retry_delete; } spin_lock(¤t->sighand->siglock); list_del(&timer->list); spin_unlock(¤t->sighand->siglock); timer->it_signal = NULL; unlock_timer(timer, flags); release_posix_timer(timer, IT_ID_SET); return 0; }