NetBSD-Bugs archive
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]
Re: kern/59339: heartbeat watchdog fires since 10.99.14
New patch! More diagnostics, more potential problems avoided.
diff -r 55a6ded9e19e -r a27cadc62a5e sys/kern/kern_time.c
--- a/sys/kern/kern_time.c Thu May 08 20:51:40 2025 +0000
+++ b/sys/kern/kern_time.c Sat May 10 08:36:45 2025 +0000
@@ -837,6 +837,105 @@ itimer_arm_real(struct itimer * const it
}
/*
+ * itimer_rearm_real:
+ *
+ * Re-arm a non-virtual timer, given the current clock readout.
+ */
+static void
+itimer_rearm_real(struct itimer * const it, const struct timespec * const now,
+ const struct timespec * const last)
+{
+ const struct timespec * const interval = &it->it_time.it_interval;
+ const struct timespec * const next = &it->it_time.it_value;
+ struct timespec delta;
+ int ticks;
+
+ KASSERT(!it->it_dying);
+ KASSERT(!CLOCK_VIRTUAL_P(it->it_clockid));
+ KASSERT(!callout_pending(&it->it_ch));
+
+ if (__predict_true(timespecisset(next))) {
+ struct timespec now1;
+
+ if (it->it_clockid == CLOCK_MONOTONIC) {
+ getnanouptime(&now1);
+ } else {
+ getnanotime(&now1);
+ }
+ if (timespeccmp(next, &now1, <=)) {
+ printf("PR kern/59339 (heartbeat watchdog fires"
+ " since 10.99.14) would fire:"
+ " [clock %u]"
+ " [now=%lld.%09ld -> %lld.%09ld],"
+ " last=%lld.%09ld + interval=%lld.%09ld"
+ " -> next=%lld.%09ld\n",
+ it->it_clockid,
+ (long long)now->tv_sec, (long)now->tv_nsec,
+ (long long)now1.tv_sec, (long)now1.tv_nsec,
+ (long long)last->tv_sec, (long)last->tv_nsec,
+ (long long)interval->tv_sec,
+ (long)interval->tv_nsec,
+ (long long)next->tv_sec, (long)next->tv_nsec);
+ }
+
+ KASSERTMSG(timespeccmp(now, next, <),
+ "[clock %u]"
+ " now=%lld.%09ld,"
+ " last=%lld.%09ld + interval=%lld.%09ld"
+ " -> next=%lld.%09ld",
+ it->it_clockid,
+ (long long)now->tv_sec, (long)now->tv_nsec,
+ (long long)last->tv_sec, (long)last->tv_nsec,
+ (long long)interval->tv_sec, (long)interval->tv_nsec,
+ (long long)next->tv_sec, (long)next->tv_nsec);
+ timespecsub(next, now, &delta);
+ } else {
+ /*
+ * If the arithmetic overflowed, just take the interval
+ * as the time to wait. This is unlikely to happen
+ * unless you are messing with your clock to set it
+ * ahead by hundreds of years.
+ */
+ printf("[clock %u]"
+ " itimer arithmetic overflowed:"
+ " now=%lld.%09ld, last=%lld.%09ld + interval=%lld.%09ld\n",
+ it->it_clockid,
+ (long long)now->tv_sec, (long)now->tv_nsec,
+ (long long)last->tv_sec, (long)last->tv_nsec,
+ (long long)interval->tv_sec, (long)interval->tv_nsec);
+ delta = it->it_time.it_interval; /* overflow */
+ }
+ ticks = tstohz(&delta);
+ if (delta.tv_sec == 0 && delta.tv_nsec < 1000) {
+ printf("[clock %u]"
+ " now=%lld.%09ld,"
+ " last=%lld.%09ld + interval=%lld.%09ld"
+ " -> next=%lld.%09ld;"
+ " delta=%lld.%09ld ticks rounded to %d",
+ it->it_clockid,
+ (long long)now->tv_sec, (long)now->tv_nsec,
+ (long long)last->tv_sec, (long)last->tv_nsec,
+ (long long)interval->tv_sec, (long)interval->tv_nsec,
+ (long long)next->tv_sec, (long)next->tv_nsec,
+ (long long)delta.tv_sec, (long)delta.tv_nsec,
+ ticks);
+ }
+ KASSERTMSG(ticks > 0,
+ "[clock %u]"
+ " now=%lld.%09ld,"
+ " last=%lld.%09ld + interval=%lld.%09ld -> next=%lld.%09ld;"
+ " delta=%lld.%09ld ticks=%d",
+ it->it_clockid,
+ (long long)now->tv_sec, (long)now->tv_nsec,
+ (long long)last->tv_sec, (long)last->tv_nsec,
+ (long long)interval->tv_sec, (long)interval->tv_nsec,
+ (long long)next->tv_sec, (long)next->tv_nsec,
+ (long long)delta.tv_sec, (long)delta.tv_nsec,
+ ticks);
+ callout_schedule(&it->it_ch, ticks);
+}
+
+/*
* itimer_callout:
*
* Callout to expire a non-virtual timer. Queue it up for processing,
@@ -848,7 +947,7 @@ itimer_arm_real(struct itimer * const it
static void
itimer_callout(void *arg)
{
- struct timespec now, next;
+ struct timespec last, now, next;
struct itimer * const it = arg;
int overruns;
@@ -871,7 +970,20 @@ itimer_callout(void *arg)
* Given the current itimer value and interval and the time
* now, compute the next itimer value and count overruns.
*/
+ last = it->it_time.it_value;
itimer_transition(&it->it_time, &now, &next, &overruns);
+ KASSERTMSG(timespeccmp(&now, &next, <),
+ "[clock %u]"
+ " it->it_time.it_value=%lld.%09ld"
+ " it->it_time.it_interval=%lld.%09ld"
+ " now=%lld.%09ld next=%lld.%09ld",
+ it->it_clockid,
+ (long long)it->it_time.it_value.tv_sec,
+ (long)it->it_time.it_value.tv_nsec,
+ (long long)it->it_time.it_interval.tv_sec,
+ (long)it->it_time.it_interval.tv_nsec,
+ (long long)now.tv_sec, (long)now.tv_nsec,
+ (long long)next.tv_sec, (long)next.tv_nsec);
it->it_time.it_value = next;
it->it_overruns += overruns;
@@ -879,7 +991,7 @@ itimer_callout(void *arg)
* Reset the callout, if it's not going away.
*/
if (!it->it_dying)
- itimer_arm_real(it);
+ itimer_rearm_real(it, &now, &last);
itimer_unlock();
}
diff -r 55a6ded9e19e -r a27cadc62a5e sys/kern/subr_time.c
--- a/sys/kern/subr_time.c Thu May 08 20:51:40 2025 +0000
+++ b/sys/kern/subr_time.c Sat May 10 08:36:45 2025 +0000
@@ -86,17 +86,28 @@ tshztoup(const struct timespec *tsp)
/*
* Compute number of ticks in the specified amount of time.
+ *
+ * Round up. Return 0 iff ts <= 0.
*/
int
tstohz(const struct timespec *ts)
{
struct timeval tv;
+ KASSERT(ts->tv_sec >= 0);
+ KASSERT(ts->tv_nsec >= 0);
+ KASSERT(ts->tv_nsec < 1000000000);
+
/*
* usec has great enough resolution for hz, so convert to a
* timeval and use tvtohz() above.
*/
- TIMESPEC_TO_TIMEVAL(&tv, ts);
+ tv.tv_sec = ts->tv_sec;
+ tv.tv_usec = (ts->tv_nsec + 999)/1000;
+ if (tv.tv_usec >= 1000000) {
+ tv.tv_sec++;
+ tv.tv_usec -= 1000000;
+ }
return tvtohz(&tv);
}
Home |
Main Index |
Thread Index |
Old Index