/* * This gets called when the timer event triggers. We set the "expired" * flag, but we do not re-arm the timer (in case it's necessary, * tintv != 0) until the timer is accessed.
*/ staticvoid timerfd_triggered(struct timerfd_ctx *ctx)
{ unsignedlong flags;
/* * Called when the clock was set to cancel the timers in the cancel * list. This will wake up processes waiting on these timers. The * wake-up requires ctx->ticks to be non zero, therefore we increment * it before calling wake_up_locked().
*/ void timerfd_clock_was_set(void)
{
ktime_t moffs = ktime_mono_to_real(0); struct timerfd_ctx *ctx; unsignedlong flags;
/* * Invoked from timekeeping_resume(). Defer the actual update to work so * timerfd_clock_was_set() runs in task context.
*/ void timerfd_resume(void)
{
schedule_work(&timerfd_work);
}
if (iov_iter_count(to) < sizeof(ticks)) return -EINVAL;
spin_lock_irq(&ctx->wqh.lock); if (file->f_flags & O_NONBLOCK || iocb->ki_flags & IOCB_NOWAIT)
res = -EAGAIN; else
res = wait_event_interruptible_locked_irq(ctx->wqh, ctx->ticks);
/* * If clock has changed, we do not care about the * ticks and we do not rearm the timer. Userspace must * reevaluate anyway.
*/ if (timerfd_canceled(ctx)) {
ctx->ticks = 0;
ctx->expired = 0;
res = -ECANCELED;
}
if (ctx->ticks) {
ticks = ctx->ticks;
if (ctx->expired && ctx->tintv) { /* * If tintv != 0, this is a periodic timer that * needs to be re-armed. We avoid doing it in the timer * callback to avoid DoS attacks specifying a very * short timer period.
*/ if (isalarm(ctx)) {
ticks += alarm_forward_now(
&ctx->t.alarm, ctx->tintv) - 1;
alarm_restart(&ctx->t.alarm);
} else {
ticks += hrtimer_forward_now(&ctx->t.tmr,
ctx->tintv) - 1;
hrtimer_restart(&ctx->t.tmr);
}
}
ctx->expired = 0;
ctx->ticks = 0;
}
spin_unlock_irq(&ctx->wqh.lock); if (ticks) {
res = copy_to_iter(&ticks, sizeof(ticks), to); if (!res)
res = -EFAULT;
} return res;
}
staticint do_timerfd_settime(int ufd, int flags, conststruct itimerspec64 *new, struct itimerspec64 *old)
{ struct timerfd_ctx *ctx; int ret;
if ((flags & ~TFD_SETTIME_FLAGS) ||
!itimerspec64_valid(new)) return -EINVAL;
CLASS(fd, f)(ufd); if (fd_empty(f)) return -EBADF;
if (fd_file(f)->f_op != &timerfd_fops) return -EINVAL;
ctx = fd_file(f)->private_data;
if (isalarm(ctx) && !capable(CAP_WAKE_ALARM)) return -EPERM;
timerfd_setup_cancel(ctx, flags);
/* * We need to stop the existing timer before reprogramming * it to the new values.
*/ for (;;) {
spin_lock_irq(&ctx->wqh.lock);
if (isalarm(ctx)) { if (alarm_try_to_cancel(&ctx->t.alarm) >= 0) break;
} else { if (hrtimer_try_to_cancel(&ctx->t.tmr) >= 0) break;
}
spin_unlock_irq(&ctx->wqh.lock);
if (isalarm(ctx))
hrtimer_cancel_wait_running(&ctx->t.alarm.timer); else
hrtimer_cancel_wait_running(&ctx->t.tmr);
}
/* * If the timer is expired and it's periodic, we need to advance it * because the caller may want to know the previous expiration time. * We do not update "ticks" and "expired" since the timer will be * re-programmed again in the following timerfd_setup() call.
*/ if (ctx->expired && ctx->tintv) { if (isalarm(ctx))
alarm_forward_now(&ctx->t.alarm, ctx->tintv); else
hrtimer_forward_now(&ctx->t.tmr, ctx->tintv);
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung ist noch experimentell.