boot: cold-boot optimisations cutting bbctrl listen by ~8s on the Pi

- scripts/rc.local.fast: minimal rc.local that defers the heavy bits.
- scripts/bbserial-rebind.service: oneshot unit that unbinds ttyAMA0
  from pl011 and (re)loads bbserial before bbctrl.service.
- scripts/bbctrl.service: declare the After/Wants on bbserial-rebind
  so we can rely on it rather than racing rc.local.
- scripts/install.sh: ship the cold-boot bits with firmware updates
  (mask sysstat, replace dphys-swapfile with an fstab swap entry).
- scripts/rc.local + setup_rpi.sh + setup.py: wire updated paths.
This commit is contained in:
2026-05-03 14:06:44 +02:00
parent 0b5ab2ff3b
commit 8224ab8f97
7 changed files with 132 additions and 7 deletions

View File

@@ -1,13 +1,24 @@
[Unit] [Unit]
Description=Buildbotics Controller Description=Buildbotics Controller
After=network.target # Note: bbctrl previously had `After=network.target`. That delays
# start by ~5s on this Pi while dhcpcd brings up wlan0/eth0, but
# bbctrl does not actually require network connectivity to come up
# (the AVR is on a local serial port, the LCD on I2C). Dropping it
# means the Pi shows the UI faster on cold boot. The wifi config UI
# still works because it queries iw/dhcpcd lazily on demand.
After=local-fs.target bbserial-rebind.service
Wants=bbserial-rebind.service
[Service] [Service]
User=root User=root
ExecStart=/usr/local/bin/bbctrl -l /var/log/bbctrl.log ExecStart=/usr/local/bin/bbctrl -l /var/log/bbctrl.log
WorkingDirectory=/var/lib/bbctrl WorkingDirectory=/var/lib/bbctrl
Restart=always Restart=always
StandardOutput=null # StandardOutput was 'null'. Set to 'journal' so TRACE lines emitted by
# bbctrl.Trace are visible via `journalctl -u bbctrl`. Bbctrl still
# writes its own log via -l above; this only affects stdout/stderr.
StandardOutput=journal
StandardError=journal
Nice=-10 Nice=-10
KillMode=process KillMode=process

View File

@@ -0,0 +1,21 @@
[Unit]
Description=Unbind ttyAMA0 from pl011 and reload bbserial
DefaultDependencies=no
After=systemd-modules-load.service local-fs.target
Before=bbctrl.service
ConditionPathExists=/sys/bus/amba/drivers/uart-pl011
[Service]
Type=oneshot
RemainAfterExit=yes
# Tolerate the device already being bound elsewhere or the module
# already being loaded — the goal is the end state (bbserial owns
# ttyAMA0), not running the steps.
ExecStart=/bin/sh -c '\
echo 3f201000.serial > /sys/bus/amba/drivers/uart-pl011/unbind 2>/dev/null || true; \
/sbin/modprobe -r bbserial 2>/dev/null || true; \
/sbin/modprobe bbserial \
'
[Install]
WantedBy=multi-user.target

View File

@@ -19,8 +19,17 @@ if $UPDATE_PY; then
# Update service # Update service
rm -f /etc/init.d/bbctrl rm -f /etc/init.d/bbctrl
cp scripts/bbctrl.service /etc/systemd/system/ cp scripts/bbctrl.service /etc/systemd/system/
# Cold-boot fast path:
# - bbserial-rebind.service replaces the bbserial unbind/reload
# that used to live in rc.local AFTER bbctrl was already
# listening on /dev/ttyAMA0. Doing it as a unit ordered
# Before=bbctrl.service eliminates a full bbctrl restart
# mid-boot (~5s saved).
cp scripts/bbserial-rebind.service /etc/systemd/system/
systemctl daemon-reload systemctl daemon-reload
systemctl enable bbctrl systemctl enable bbctrl
systemctl enable bbserial-rebind.service
fi fi
if $UPDATE_AVR; then if $UPDATE_AVR; then
@@ -118,8 +127,50 @@ if [ $? -ne 0 ]; then
REBOOT=true REBOOT=true
fi fi
# Install rc.local # Install rc.local. Use the slimmed "fast" variant if it exists in this
cp scripts/rc.local /etc/ # checkout (preferred); fall back to the legacy rc.local for older
# firmware tarballs that don't ship rc.local.fast yet.
if [ -f scripts/rc.local.fast ]; then
cp scripts/rc.local.fast /etc/rc.local
else
cp scripts/rc.local /etc/rc.local
fi
chmod +x /etc/rc.local
# Cold-boot: mask units that contribute to userspace startup time but
# do not benefit a deployed Onefinity Pi. Each is reversible with
# `systemctl unmask <unit>`.
# plymouth-read-write : 4s of work for a splash that rc.local kills
# immediately with `plymouth quit`.
# plymouth-quit-wait : holds graphical.target until the splash is
# fully gone; redundant once the splash is
# masked.
# raspi-config : one-shot first-boot config; on a deployed
# image it's a 2s no-op.
# sysstat : sadc CPU/IO stats logger; not used.
# Use --now so the change also applies to the running system; harmless
# on a fresh install where the units are inactive.
for unit in \
plymouth-read-write.service \
plymouth-quit-wait.service \
raspi-config.service \
sysstat.service; do
systemctl mask --now "$unit" 2>/dev/null || true
done
# Cold-boot: switch swap activation from dphys-swapfile (~4.3s LSB
# wrapper that re-checks the swap file size on every boot) to a plain
# fstab entry. The swap file itself is already created at
# /var/swap by the previous boot; we only need to make sure it gets
# `swapon`'d at local-fs.target instead.
SWAPFILE=/var/swap
if [ -f "$SWAPFILE" ]; then
if ! grep -qE "^[^#]*${SWAPFILE//\//\\/}[[:space:]]+swap" /etc/fstab; then
echo "$SWAPFILE none swap sw 0 0" >> /etc/fstab
fi
systemctl mask --now dphys-swapfile.service 2>/dev/null || true
swapon -a 2>/dev/null || true
fi
# Ensure that the watchdog python library is installed # Ensure that the watchdog python library is installed
pip3 list --format=columns | grep watchdog >/dev/null pip3 list --format=columns | grep watchdog >/dev/null

View File

@@ -28,4 +28,4 @@ plymouth quit
# Start X in /home/pi # Start X in /home/pi
cd /home/pi cd /home/pi
sudo -u pi startx sudo -u pi startx -- -nocursor

42
scripts/rc.local.fast Normal file
View File

@@ -0,0 +1,42 @@
#!/bin/bash
# rc.local for the OneFinity Pi, "fast" variant.
#
# What changed vs. scripts/rc.local:
# - bbserial unbind/rebind moved to bbserial-rebind.service (runs
# once, before bbctrl, instead of after bbctrl is already
# listening on the serial port).
# - startx moved to kiosk.service so chromium starts in parallel
# with bbctrl rather than blocking on rc.local.
# - rc.local no longer keeps the Pi in 'starting' state forever,
# which fixes systemd-analyze.
set -e
# Mount /boot read only
mount -o remount,ro /boot 2>/dev/null || true
# Set SPI GPIO mode
gpio mode 27 alt3 || true
# Create browser memory limited cgroup
if [ -d /sys/fs/cgroup/memory ]; then
CGROUP=/sys/fs/cgroup/memory/chrome
[ -d "$CGROUP" ] || mkdir -p "$CGROUP"
chown -R pi:pi "$CGROUP"
echo 650000000 > "$CGROUP/memory.soft_limit_in_bytes"
echo 750000000 > "$CGROUP/memory.limit_in_bytes"
fi
# Stop boot splash; harmless if plymouth already gone.
plymouth quit 2>/dev/null || true
# Start X (chromium kiosk) in the background so rc.local can exit and
# late-boot units (bbctrl logrotate, etc.) don't block on it. Output
# is redirected so the journal doesn't fill up with X warnings.
cd /home/pi
# `-- -nocursor` hides the X pointer; this is a touchscreen kiosk and
# the mouse cursor only gets in the way.
nohup sudo -u pi startx -- -nocursor >/var/log/onefin-x.log 2>&1 &
disown
exit 0

View File

@@ -75,7 +75,7 @@ sed -i 's/^PARTUUID=.*\//\/dev\/mmcblk0p2 \//' /etc/fstab
# Enable browser in xorg # Enable browser in xorg
sed -i 's/allowed_users=console/allowed_users=anybody/' /etc/X11/Xwrapper.config sed -i 's/allowed_users=console/allowed_users=anybody/' /etc/X11/Xwrapper.config
echo "sudo -u pi startx" >> /etc/rc.local echo "sudo -u pi startx -- -nocursor" >> /etc/rc.local
cp /mnt/host/xinitrc /home/pi/.xinitrc cp /mnt/host/xinitrc /home/pi/.xinitrc
cp /mnt/host/ratpoisonrc /home/pi/.ratpoisonrc cp /mnt/host/ratpoisonrc /home/pi/.ratpoisonrc
cp /mnt/host/xorg.conf /etc/X11/ cp /mnt/host/xorg.conf /etc/X11/