diff options
| author | Ingo Molnar <mingo@kernel.org> | 2026-05-29 09:51:00 +0200 |
|---|---|---|
| committer | Ingo Molnar <mingo@kernel.org> | 2026-05-29 09:51:01 +0200 |
| commit | 83358dec92a4c05c1bbdefa29d677db8a2a2799f (patch) | |
| tree | 4b59627eaa77b70bd42903886274e5e9afd40a68 /fs | |
| parent | ee5ce6e9ef0dff9f58c182509212f26dd634b3f7 (diff) | |
| parent | a05ca50fc0b832753669ab68fbbb6dc352cf6699 (diff) | |
| download | linux-next-history-83358dec92a4c05c1bbdefa29d677db8a2a2799f.tar.gz | |
Merge branch into tip/master: 'timers/merge'
# New commits in timers/merge:
3eb4923e6851 ("clocksource: Add devm_clocksource_register_*() helpers")
c8d32a0389fb ("timers: Fix flseep() typo in kernel-doc comment")
5d330d652d7a ("hrtimer: Fix the bogus return type of __hrtimer_start_range_ns()")
3af1f49f415d ("hrtimer: Return ktime_t from hrtimer_get_next_event()/hrtimer_next_event_without()")
33d4bfc49613 ("clocksource: Clean up clocksource_update_freq() functions")
ed3b3c497668 ("alarmtimer: Remove stale return description from alarm_handle_timer()")
b00385b8d081 ("selftests/posix_timers: Use CLOCK_THREAD_CPUTIME_ID for ITIMER_PROF measurements")
cab0cd0130eb ("scripts/timers: Add timer_migration_tree.py")
5a7dfbcbbdb6 ("timers/migration: Handle capacity in connect tracepoints")
098cbaad8e57 ("timers/migration: Split per-capacity hierarchies")
3ba25488380f ("timers/migration: Track CPUs in a hierarchy")
ff65875f80d1 ("timers/migration: Abstract out hierarchy to prepare for CPU capacity awareness")
ed78a7019419 ("alarmtimer: Remove unused interfaces")
12e4311aa5b2 ("netfilter: xt_IDLETIMER: Switch to alarm_start_timer()")
9fa2e38ab749 ("power: supply: charger-manager: Switch to alarm_start_timer()")
7dda99952ced ("fs/timerfd: Use the new alarm/hrtimer functions")
f4b58f61da79 ("alarmtimer: Convert posix timer functions to alarm_start_timer()")
183d00b72713 ("alarmtimer: Provide alarm_start_timer()")
acc071343d29 ("posix-timers: Switch to hrtimer_start_expires_user()")
cfb7fe3fdd4c ("posix-timers: Handle the timer_[re]arm() return value")
6fdb2677a594 ("posix-timers: Expand timer_[re]arm() callbacks with a boolean return value")
b40c927345a9 ("hrtimer: Use hrtimer_start_expires_user() for hrtimer sleepers")
bd5956166d20 ("hrtimer: Provide hrtimer_start_range_ns_user()")
68ed094971b0 ("clocksource/drivers/timer-of: Make the code compatible with modules")
2423405880c2 ("clocksource/drivers/mmio: Make the code compatible with modules")
fed9f727cc3f ("clocksource/drivers/sun5i: Handle error returns from devm_reset_control_get_optional_exclusive()")
045a9dac7eb7 ("clocksource/drivers/timer-rtl-otto: Make rttm_cs variable static")
b385caf91868 ("dt-bindings: timer: fsl,imxgpt: add compatible string fsl,imx25-epit")
Signed-off-by: Ingo Molnar <mingo@kernel.org>
Diffstat (limited to 'fs')
| -rw-r--r-- | fs/timerfd.c | 117 |
1 files changed, 68 insertions, 49 deletions
diff --git a/fs/timerfd.c b/fs/timerfd.c index 73104f36bcae0..fe845af0b74e6 100644 --- a/fs/timerfd.c +++ b/fs/timerfd.c @@ -55,6 +55,15 @@ static inline bool isalarm(struct timerfd_ctx *ctx) ctx->clockid == CLOCK_BOOTTIME_ALARM; } +static void __timerfd_triggered(struct timerfd_ctx *ctx) +{ + lockdep_assert_held(&ctx->wqh.lock); + + ctx->expired = 1; + ctx->ticks++; + wake_up_locked_poll(&ctx->wqh, EPOLLIN); +} + /* * 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, @@ -62,13 +71,8 @@ static inline bool isalarm(struct timerfd_ctx *ctx) */ static void timerfd_triggered(struct timerfd_ctx *ctx) { - unsigned long flags; - - spin_lock_irqsave(&ctx->wqh.lock, flags); - ctx->expired = 1; - ctx->ticks++; - wake_up_locked_poll(&ctx->wqh, EPOLLIN); - spin_unlock_irqrestore(&ctx->wqh.lock, flags); + guard(spinlock_irqsave)(&ctx->wqh.lock); + __timerfd_triggered(ctx); } static enum hrtimer_restart timerfd_tmrproc(struct hrtimer *htmr) @@ -184,15 +188,54 @@ static ktime_t timerfd_get_remaining(struct timerfd_ctx *ctx) return remaining < 0 ? 0: remaining; } +static void timerfd_alarm_start(struct timerfd_ctx *ctx, ktime_t exp, bool relative) +{ + /* Start the timer. If it's expired already, handle the callback. */ + if (!alarm_start_timer(&ctx->t.alarm, exp, relative)) + __timerfd_triggered(ctx); +} + +static u64 timerfd_alarm_restart(struct timerfd_ctx *ctx) +{ + /* -1 to account for ctx->ticks++ in __timerfd_triggered() */ + u64 ticks = alarm_forward_now(&ctx->t.alarm, ctx->tintv) - 1; + + timerfd_alarm_start(ctx, alarm_get_expires(&ctx->t.alarm), false); + return ticks; +} + +static void timerfd_hrtimer_start(struct timerfd_ctx *ctx, ktime_t exp, + const enum hrtimer_mode mode) +{ + /* Start the timer. If it's expired already, handle the callback. */ + if (!hrtimer_start_range_ns_user(&ctx->t.tmr, exp, 0, mode)) + __timerfd_triggered(ctx); +} + +static u64 timerfd_hrtimer_restart(struct timerfd_ctx *ctx) +{ + /* -1 to account for ctx->ticks++ in __timerfd_triggered() */ + u64 ticks = hrtimer_forward_now(&ctx->t.tmr, ctx->tintv) - 1; + + timerfd_hrtimer_start(ctx, hrtimer_get_expires(&ctx->t.tmr), HRTIMER_MODE_ABS); + return ticks; +} + +static u64 timerfd_restart(struct timerfd_ctx *ctx) +{ + if (isalarm(ctx)) + return timerfd_alarm_restart(ctx); + return timerfd_hrtimer_restart(ctx); +} + static int timerfd_setup(struct timerfd_ctx *ctx, int flags, const struct itimerspec64 *ktmr) { + int clockid = ctx->clockid; enum hrtimer_mode htmode; ktime_t texp; - int clockid = ctx->clockid; - htmode = (flags & TFD_TIMER_ABSTIME) ? - HRTIMER_MODE_ABS: HRTIMER_MODE_REL; + htmode = (flags & TFD_TIMER_ABSTIME) ? HRTIMER_MODE_ABS: HRTIMER_MODE_REL; texp = timespec64_to_ktime(ktmr->it_value); ctx->expired = 0; @@ -206,20 +249,15 @@ static int timerfd_setup(struct timerfd_ctx *ctx, int flags, timerfd_alarmproc); } else { hrtimer_setup(&ctx->t.tmr, timerfd_tmrproc, clockid, htmode); - hrtimer_set_expires(&ctx->t.tmr, texp); } if (texp != 0) { if (flags & TFD_TIMER_ABSTIME) texp = timens_ktime_to_host(clockid, texp); - if (isalarm(ctx)) { - if (flags & TFD_TIMER_ABSTIME) - alarm_start(&ctx->t.alarm, texp); - else - alarm_start_relative(&ctx->t.alarm, texp); - } else { - hrtimer_start(&ctx->t.tmr, texp, htmode); - } + if (isalarm(ctx)) + timerfd_alarm_start(ctx, texp, !(flags & TFD_TIMER_ABSTIME)); + else + timerfd_hrtimer_start(ctx, texp, htmode); if (timerfd_canceled(ctx)) return -ECANCELED; @@ -287,27 +325,19 @@ static ssize_t timerfd_read_iter(struct kiocb *iocb, struct iov_iter *to) } if (ctx->ticks) { - ticks = ctx->ticks; + unsigned int expired = ctx->expired; - 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); - } - } + ticks = ctx->ticks; ctx->expired = 0; ctx->ticks = 0; + + /* + * 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 (expired && ctx->tintv) + ticks += timerfd_restart(ctx); } spin_unlock_irq(&ctx->wqh.lock); if (ticks) { @@ -526,18 +556,7 @@ static int do_timerfd_gettime(int ufd, struct itimerspec64 *t) spin_lock_irq(&ctx->wqh.lock); if (ctx->expired && ctx->tintv) { ctx->expired = 0; - - if (isalarm(ctx)) { - ctx->ticks += - alarm_forward_now( - &ctx->t.alarm, ctx->tintv) - 1; - alarm_restart(&ctx->t.alarm); - } else { - ctx->ticks += - hrtimer_forward_now(&ctx->t.tmr, ctx->tintv) - - 1; - hrtimer_restart(&ctx->t.tmr); - } + ctx->ticks += timerfd_restart(ctx); } t->it_value = ktime_to_timespec64(timerfd_get_remaining(ctx)); t->it_interval = ktime_to_timespec64(ctx->tintv); |
