runtime: detect netbsd netpoll overrun in sysmon

The netbsd kernel has a bug [1] that occassionally prevents netpoll from
waking with netpollBreak, which could result in missing timers for an
unbounded amount of time, as netpoll can't restart with a shorter delay
when an earlier timer is added.

Prior to CL 232298, sysmon could detect these overrun timers and
manually start an M to run them. With this fallback gone, the bug
actually prevents timer execution indefinitely.

As a workaround, we add back sysmon detection only for netbsd.

[1] https://gnats.netbsd.org/cgi-bin/query-pr-single.pl?number=50094

Updates #42515

Change-Id: I8391f5b9dabef03dd1d94c50b3b4b3bd4f889e66
Reviewed-on: https://go-review.googlesource.com/c/go/+/277332
Run-TryBot: Michael Pratt <mpratt@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Michael Knyszek <mknyszek@google.com>
Reviewed-by: Austin Clements <austin@google.com>
Trust: Michael Pratt <mpratt@google.com>
diff --git a/src/runtime/proc.go b/src/runtime/proc.go
index 418e069..5adcbf0 100644
--- a/src/runtime/proc.go
+++ b/src/runtime/proc.go
@@ -5130,6 +5130,26 @@
 			}
 		}
 		mDoFixup()
+		if GOOS == "netbsd" {
+			// netpoll is responsible for waiting for timer
+			// expiration, so we typically don't have to worry
+			// about starting an M to service timers. (Note that
+			// sleep for timeSleepUntil above simply ensures sysmon
+			// starts running again when that timer expiration may
+			// cause Go code to run again).
+			//
+			// However, netbsd has a kernel bug that sometimes
+			// misses netpollBreak wake-ups, which can lead to
+			// unbounded delays servicing timers. If we detect this
+			// overrun, then startm to get something to handle the
+			// timer.
+			//
+			// See issue 42515 and
+			// https://gnats.netbsd.org/cgi-bin/query-pr-single.pl?number=50094.
+			if next, _ := timeSleepUntil(); next < now {
+				startm(nil, false)
+			}
+		}
 		if atomic.Load(&scavenge.sysmonWake) != 0 {
 			// Kick the scavenger awake if someone requested it.
 			wakeScavenger()