Compare commits
2
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6d73221123 | ||
|
|
cd259556d4 |
@@ -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
|
||||||
|
}
|
||||||
@@ -268,7 +268,10 @@ func openFramebuffer() (*framebuffer, error) {
|
|||||||
return nil, fmt.Errorf("set crtc: %w", err)
|
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.
|
// Nothing has been flipped yet, so the first frame is owed its turn.
|
||||||
fb.flips <- struct{}{}
|
fb.flips <- struct{}{}
|
||||||
return fb, nil
|
return fb, nil
|
||||||
|
|||||||
@@ -113,6 +113,7 @@ func watchTouch(w, h int) (*touchState, error) {
|
|||||||
|
|
||||||
state := &touchState{}
|
state := &touchState{}
|
||||||
go func() {
|
go func() {
|
||||||
|
defer holdPanic()
|
||||||
defer f.Close()
|
defer f.Close()
|
||||||
buf := make([]byte, sizeofInputEvent*32)
|
buf := make([]byte, sizeofInputEvent*32)
|
||||||
var rawX, rawY int32
|
var rawX, rawY int32
|
||||||
|
|||||||
@@ -457,6 +457,7 @@ func (d *direction) start(wg *sync.WaitGroup, done *atomic.Bool, rxReady *sync.W
|
|||||||
wg.Add(1)
|
wg.Add(1)
|
||||||
go func() {
|
go func() {
|
||||||
defer wg.Done()
|
defer wg.Done()
|
||||||
|
defer holdPanic()
|
||||||
w.run(done)
|
w.run(done)
|
||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
@@ -473,6 +474,7 @@ func (d *direction) start(wg *sync.WaitGroup, done *atomic.Bool, rxReady *sync.W
|
|||||||
wg.Add(1)
|
wg.Add(1)
|
||||||
go func() {
|
go func() {
|
||||||
defer wg.Done()
|
defer wg.Done()
|
||||||
|
defer holdPanic()
|
||||||
w.run(done)
|
w.run(done)
|
||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
@@ -481,6 +483,7 @@ func (d *direction) start(wg *sync.WaitGroup, done *atomic.Bool, rxReady *sync.W
|
|||||||
wg.Add(1)
|
wg.Add(1)
|
||||||
go func() {
|
go func() {
|
||||||
defer wg.Done()
|
defer wg.Done()
|
||||||
|
defer holdPanic()
|
||||||
sender.run(done, startTx)
|
sender.run(done, startTx)
|
||||||
}()
|
}()
|
||||||
|
|
||||||
@@ -488,12 +491,14 @@ func (d *direction) start(wg *sync.WaitGroup, done *atomic.Bool, rxReady *sync.W
|
|||||||
wg.Add(1)
|
wg.Add(1)
|
||||||
go func() {
|
go func() {
|
||||||
defer wg.Done()
|
defer wg.Done()
|
||||||
|
defer holdPanic()
|
||||||
receiver.run(done)
|
receiver.run(done)
|
||||||
}()
|
}()
|
||||||
|
|
||||||
wg.Add(1)
|
wg.Add(1)
|
||||||
go func() {
|
go func() {
|
||||||
defer wg.Done()
|
defer wg.Done()
|
||||||
|
defer holdPanic()
|
||||||
d.poller.run(done, startTx)
|
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")
|
nsPerM := flag.Float64("ns-per-m", 4.85, "mean of both directions, per metre of cable")
|
||||||
flag.Parse()
|
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 {
|
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
|
// 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
|
// 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 {
|
if err := reportChecks("BOOT", bootstrap()); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -670,6 +682,7 @@ func run(aName, bName string, nsPerM float64) error {
|
|||||||
wg.Add(1)
|
wg.Add(1)
|
||||||
go func() {
|
go func() {
|
||||||
defer wg.Done()
|
defer wg.Done()
|
||||||
|
defer holdPanic()
|
||||||
samp.run(&done, startTx)
|
samp.run(&done, startTx)
|
||||||
}()
|
}()
|
||||||
// Not gated on startTx: the cycle and the connected verdict are wanted the
|
// 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)
|
wg.Add(1)
|
||||||
go func() {
|
go func() {
|
||||||
defer wg.Done()
|
defer wg.Done()
|
||||||
|
defer holdPanic()
|
||||||
noise.run(&done)
|
noise.run(&done)
|
||||||
}()
|
}()
|
||||||
// Every return from here on stops the workers before the deferred closes
|
// 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()
|
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)
|
sig := make(chan os.Signal, 1)
|
||||||
signal.Notify(sig, syscall.SIGINT, syscall.SIGTERM)
|
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}
|
stats := &streamTable{cols: intervalCols, headerEvery: 20}
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
|
case p := <-fatalCh:
|
||||||
|
return fmt.Errorf("%v", p)
|
||||||
case <-sig:
|
case <-sig:
|
||||||
return nil
|
return nil
|
||||||
case <-space:
|
case <-space:
|
||||||
|
|||||||
@@ -38,13 +38,13 @@ cmdline="drm.edid_firmware=HDMI-A-1:$edid video=HDMI-A-1:e"
|
|||||||
wanty=(
|
wanty=(
|
||||||
EFI_STUB BLK_DEV_INITRD INITRAMFS_COMPRESSION_NONE CMDLINE_BOOL
|
EFI_STUB BLK_DEV_INITRD INITRAMFS_COMPRESSION_NONE CMDLINE_BOOL
|
||||||
PACKET INET DEVTMPFS PROC_FS SYSFS TMPFS
|
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
|
DRM_I915 DRM_FBDEV_EMULATION DRM_LOAD_EDID_FIRMWARE DRM_PANIC
|
||||||
FB_DEVICE FRAMEBUFFER_CONSOLE VT_CONSOLE
|
FB_DEVICE FRAMEBUFFER_CONSOLE VT_CONSOLE
|
||||||
USB_XHCI_PCI USB_HID HID_MULTITOUCH INPUT_EVDEV
|
USB_XHCI_PCI USB_HID HID_MULTITOUCH INPUT_EVDEV
|
||||||
CPU_FREQ_GOV_PERFORMANCE X86_INTEL_PSTATE
|
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' "$*"; }
|
say() { printf '\n\033[36m== %s\033[0m\n' "$*"; }
|
||||||
die() { printf '\033[31merror: %s\033[0m\n' "$*" >&2; exit 1; }
|
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 \
|
( cd "$src" && scripts/config \
|
||||||
--disable MODULES \
|
--disable MODULES \
|
||||||
--enable EXPERT --disable BLOCK \
|
--enable EXPERT --disable BLOCK \
|
||||||
--disable I40E --disable IGC \
|
--enable I40E --disable IGC \
|
||||||
--enable DRM_LOAD_EDID_FIRMWARE \
|
--enable DRM_LOAD_EDID_FIRMWARE \
|
||||||
--set-str EXTRA_FIRMWARE "$edid ${distrofw[*]}" \
|
--set-str EXTRA_FIRMWARE "$edid ${distrofw[*]}" \
|
||||||
--set-str EXTRA_FIRMWARE_DIR "$fwdir" \
|
--set-str EXTRA_FIRMWARE_DIR "$fwdir" \
|
||||||
|
|||||||
Reference in New Issue
Block a user