Compare commits

..
2 Commits
5 changed files with 151 additions and 9 deletions
+112
View File
@@ -0,0 +1,112 @@
package main
import (
"fmt"
"os"
"os/signal"
"strings"
"syscall"
"time"
"golang.org/x/sys/unix"
)
const fatalRebootDelay = 5 * time.Minute
var fatalCh = make(chan any, 1)
// Deferred in every goroutine that can panic: as PID 1 the message is shown by
// the run loop instead of killing init; anywhere else the panic keeps its stack.
func holdPanic() {
p := recover()
if p == nil {
return
}
if os.Getpid() != 1 {
panic(p)
}
select {
case fatalCh <- p:
default:
}
}
// Exiting would take init with it and the kernel's panic screen would cover
// the cause, so it is drawn and held instead, with a reboot to retry.
func fatal(cause error) {
if os.Getpid() != 1 {
panic(cause)
}
if err := drawFatal(cause); err != nil {
panic(fmt.Sprintf("%v; drawing it: %v", cause, err))
}
sig := make(chan os.Signal, 1)
signal.Notify(sig, syscall.SIGINT, syscall.SIGTERM)
select {
case <-sig:
case <-time.After(fatalRebootDelay):
}
if err := unix.Reboot(unix.LINUX_REBOOT_CMD_RESTART); err != nil {
panic(err)
}
}
func drawFatal(cause error) error {
fb, err := openFramebuffer()
if err != nil {
return err
}
title, err := loadFace(true, 40)
if err != nil {
return err
}
body, err := loadFace(false, 34)
if err != nil {
return err
}
fb.fill(uiBg)
noteY := fb.h - spaceRow - body.lineH
y := spaceRow
title.draw(fb, spaceRow, y-title.capTop, "FATAL", uiRed)
y += title.lineH + spaceRow
for _, line := range wrapWords(cause.Error(), (fb.w-2*spaceRow)/body.cellW) {
if y+body.lineH > noteY-spaceRow {
body.draw(fb, spaceRow, y-body.capTop, "...", uiFg)
break
}
body.draw(fb, spaceRow, y-body.capTop, line, uiFg)
y += body.lineH + spaceTight
}
note := fmt.Sprintf("rebooting in %d minutes", int(fatalRebootDelay.Minutes()))
body.draw(fb, spaceRow, noteY-body.capTop, note, uiDim)
return fb.flush()
}
func wrapWords(s string, cols int) []string {
var lines []string
line := ""
for _, w := range strings.Fields(s) {
for len(w) > cols {
if line != "" {
lines = append(lines, line)
line = ""
}
lines = append(lines, w[:cols])
w = w[cols:]
}
switch {
case line == "":
line = w
case len(line)+1+len(w) <= cols:
line += " " + w
default:
lines = append(lines, line)
line = w
}
}
if line != "" {
lines = append(lines, line)
}
return lines
}
+4 -1
View File
@@ -268,7 +268,10 @@ func openFramebuffer() (*framebuffer, error) {
return nil, fmt.Errorf("set crtc: %w", err)
}
go fb.readEvents()
go func() {
defer holdPanic()
fb.readEvents()
}()
// Nothing has been flipped yet, so the first frame is owed its turn.
fb.flips <- struct{}{}
return fb, nil
+1
View File
@@ -113,6 +113,7 @@ func watchTouch(w, h int) (*touchState, error) {
state := &touchState{}
go func() {
defer holdPanic()
defer f.Close()
buf := make([]byte, sizeofInputEvent*32)
var rawX, rawY int32
+31 -5
View File
@@ -457,6 +457,7 @@ func (d *direction) start(wg *sync.WaitGroup, done *atomic.Bool, rxReady *sync.W
wg.Add(1)
go func() {
defer wg.Done()
defer holdPanic()
w.run(done)
}()
}
@@ -473,6 +474,7 @@ func (d *direction) start(wg *sync.WaitGroup, done *atomic.Bool, rxReady *sync.W
wg.Add(1)
go func() {
defer wg.Done()
defer holdPanic()
w.run(done)
}()
}
@@ -481,6 +483,7 @@ func (d *direction) start(wg *sync.WaitGroup, done *atomic.Bool, rxReady *sync.W
wg.Add(1)
go func() {
defer wg.Done()
defer holdPanic()
sender.run(done, startTx)
}()
@@ -488,12 +491,14 @@ func (d *direction) start(wg *sync.WaitGroup, done *atomic.Bool, rxReady *sync.W
wg.Add(1)
go func() {
defer wg.Done()
defer holdPanic()
receiver.run(done)
}()
wg.Add(1)
go func() {
defer wg.Done()
defer holdPanic()
d.poller.run(done, startTx)
}()
}
@@ -536,10 +541,8 @@ func main() {
nsPerM := flag.Float64("ns-per-m", 4.85, "mean of both directions, per metre of cable")
flag.Parse()
// Nothing here is recoverable by the time it reaches this point, and as PID 1
// a plain exit would panic the kernel anyway with less to show for it.
if err := run(*aName, *bName, *nsPerM); err != nil {
panic(err)
fatal(err)
}
// A clean return is ctrl-alt-delete, which the kernel hands PID 1 as a
// SIGINT. Exiting on it would panic the kernel over the reboot it was asking
@@ -581,7 +584,16 @@ func (s *sampler) run(done *atomic.Bool, startTx <-chan struct{}) {
}
}
func run(aName, bName string, nsPerM float64) error {
func run(aName, bName string, nsPerM float64) (err error) {
defer func() {
if p := recover(); p != nil {
if os.Getpid() != 1 {
panic(p)
}
err = fmt.Errorf("%v", p)
}
}()
if err := reportChecks("BOOT", bootstrap()); err != nil {
return err
}
@@ -670,6 +682,7 @@ func run(aName, bName string, nsPerM float64) error {
wg.Add(1)
go func() {
defer wg.Done()
defer holdPanic()
samp.run(&done, startTx)
}()
// Not gated on startTx: the cycle and the connected verdict are wanted the
@@ -677,6 +690,7 @@ func run(aName, bName string, nsPerM float64) error {
wg.Add(1)
go func() {
defer wg.Done()
defer holdPanic()
noise.run(&done)
}()
// Every return from here on stops the workers before the deferred closes
@@ -693,7 +707,17 @@ func run(aName, bName string, nsPerM float64) error {
}
wg.Wait()
}()
rxReady.Wait()
// A worker that panics before signalling ready would hang a bare Wait.
ready := make(chan struct{})
go func() {
rxReady.Wait()
close(ready)
}()
select {
case <-ready:
case p := <-fatalCh:
return fmt.Errorf("%v", p)
}
sig := make(chan os.Signal, 1)
signal.Notify(sig, syscall.SIGINT, syscall.SIGTERM)
@@ -726,6 +750,8 @@ func run(aName, bName string, nsPerM float64) error {
stats := &streamTable{cols: intervalCols, headerEvery: 20}
for {
select {
case p := <-fatalCh:
return fmt.Errorf("%v", p)
case <-sig:
return nil
case <-space:
+3 -3
View File
@@ -38,13 +38,13 @@ cmdline="drm.edid_firmware=HDMI-A-1:$edid video=HDMI-A-1:e"
wanty=(
EFI_STUB BLK_DEV_INITRD INITRAMFS_COMPRESSION_NONE CMDLINE_BOOL
PACKET INET DEVTMPFS PROC_FS SYSFS TMPFS
ICE ICE_HWTS PTP_1588_CLOCK
ICE ICE_HWTS I40E PTP_1588_CLOCK
DRM_I915 DRM_FBDEV_EMULATION DRM_LOAD_EDID_FIRMWARE DRM_PANIC
FB_DEVICE FRAMEBUFFER_CONSOLE VT_CONSOLE
USB_XHCI_PCI USB_HID HID_MULTITOUCH INPUT_EVDEV
CPU_FREQ_GOV_PERFORMANCE X86_INTEL_PSTATE
)
wantn=(MODULES BLOCK I40E IGC)
wantn=(MODULES BLOCK IGC)
say() { printf '\n\033[36m== %s\033[0m\n' "$*"; }
die() { printf '\033[31merror: %s\033[0m\n' "$*" >&2; exit 1; }
@@ -111,7 +111,7 @@ cp "$repo/kernel/config" "$src/.config"
( cd "$src" && scripts/config \
--disable MODULES \
--enable EXPERT --disable BLOCK \
--disable I40E --disable IGC \
--enable I40E --disable IGC \
--enable DRM_LOAD_EDID_FIRMWARE \
--set-str EXTRA_FIRMWARE "$edid ${distrofw[*]}" \
--set-str EXTRA_FIRMWARE_DIR "$fwdir" \