Description
__gf_tw_add_timer() (contrib/timer-wheel/timer-wheel.c:59) classifies a timer by
idx = expires - base->timer_sec, where idx is unsigned long. The branch meant to
catch an expiry already in the past tests idx < 0 — always false for an unsigned value:
contrib/timer-wheel/timer-wheel.c:59:24: warning: comparison of unsigned expression
in '< 0' is always false [-Wtype-limits]
59 | } else if (idx < 0) {
| ^
Two consequences:
- The warning, on every build under
-Wextra. - A dead branch. An already-past /
expires == nowtimer hasidxwrapped near
ULONG_MAX, so it misses all fouridx < SIZEbuckets and falls through to the final
else→tv5, where it is bucketed byexpiresbits — firing up to a full wheel
period late instead of fromtv1on the next tick.
The wheel is a port of the pre-4.8 Linux cascading timer wheel (kernel/time/timer.c,
__internal_add_timer), which guards the same branch with (signed long) idx < 0 to
detect the unsigned wrap. The cast was dropped in the port.
Impact: latent, not a live regression. Every insert path converts a relative timeout
to absolute before bucketing — timer->expires += base->timer_sec in gf_tw_add_timer(),
expires + base->timer_sec in gf_tw_mod_timer() / gf_tw_mod_timer_pending() — so
idx >= 0 for every current caller (posix janitor, bit-rot, leases, nl-cache). The value
here is silencing the warning and restoring the intended past-expiry safety net.
Suggested fix (one line) — restore the signed cast:
- } else if (idx < 0) {
+ } else if ((long)idx < 0) {Distinct from #4740 (the fls() / BITS_PER_LONG leftovers of 1626dee); this
idx < 0 predates that commit.
Activity