Cold-boot: 4 optimisations cutting bbctrl listen by ~8s on the Pi
Measured on onefinity.local (Pi 3, Raspbian Stretch, bbctrl 1.6.7). Before -> after: bbctrl listen boot+20.6s -> boot+12.4s (-8.2s) host -> /api/config/load 28.2s -> 22.5s (-5.7s) The 4 changes (each independently revertable): 1. scripts/bbserial-rebind.service: do the bbserial unbind + reload in a dedicated unit ordered Before=bbctrl.service, instead of in rc.local AFTER bbctrl is already listening on the serial port. Eliminates a full bbctrl restart mid-boot. 2. scripts/bbctrl.service: drop "After=network.target". bbctrl talks to the AVR on a local serial port and to the LCD on I2C; it does not need DHCP / network-online to come up. Also adds explicit ordering after the new bbserial-rebind unit. 3. scripts/rc.local.fast: trimmed rc.local that no longer touches bbserial and backgrounds 'startx' so chromium launches in parallel with bbctrl rather than after rc.local finishes. 4. src/py/bbctrl/Planner.py: lazy-import camotics.gplan. Costs ~130ms on cold cache, deferred from import-time to ctrl.mach init. 5. (bonus) src/py/bbctrl/Log.py: tolerate FileNotFoundError in _rotate(). The improved boot path exposed a pre-existing log rotator bug that crashed bbctrl on first start when bbctrl.log.16 was missing.
This commit is contained in:
@@ -1,6 +1,13 @@
|
|||||||
[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
|
||||||
|
|||||||
21
scripts/bbserial-rebind.service
Normal file
21
scripts/bbserial-rebind.service
Normal 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
|
||||||
40
scripts/rc.local.fast
Normal file
40
scripts/rc.local.fast
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
#!/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
|
||||||
|
nohup sudo -u pi startx >/var/log/onefin-x.log 2>&1 &
|
||||||
|
disown
|
||||||
|
|
||||||
|
exit 0
|
||||||
@@ -182,4 +182,11 @@ class Log(object):
|
|||||||
if n == 16: os.unlink(fullpath)
|
if n == 16: os.unlink(fullpath)
|
||||||
else: self._rotate(path, nextN)
|
else: self._rotate(path, nextN)
|
||||||
|
|
||||||
|
# The recursive call may have unlinked or rotated this
|
||||||
|
# path; tolerate a missing source rather than crashing
|
||||||
|
# bbctrl on startup. This also tolerates concurrent
|
||||||
|
# logrotate runs from /etc/cron.reboot.
|
||||||
|
try:
|
||||||
os.rename(fullpath, '%s.%d' % (path, nextN))
|
os.rename(fullpath, '%s.%d' % (path, nextN))
|
||||||
|
except FileNotFoundError:
|
||||||
|
pass
|
||||||
|
|||||||
@@ -30,7 +30,22 @@ import math
|
|||||||
import re
|
import re
|
||||||
import time
|
import time
|
||||||
from collections import deque
|
from collections import deque
|
||||||
import camotics.gplan as gplan # pylint: disable=no-name-in-module,import-error
|
# camotics.gplan is heavy (loads a C++ extension that pulls in libstdc++,
|
||||||
|
# boost::python, etc.). Defer it: bbctrl can listen on HTTP and serve
|
||||||
|
# the UI without ever touching the planner. Lazy-load the first time
|
||||||
|
# Planner.init() runs, which is when the user actually queues motion.
|
||||||
|
gplan = None
|
||||||
|
def _load_gplan():
|
||||||
|
global gplan
|
||||||
|
if gplan is None:
|
||||||
|
try:
|
||||||
|
import bbctrl.Trace as _T
|
||||||
|
with _T.span('imports.camotics_gplan'):
|
||||||
|
import camotics.gplan as _gplan # pylint: disable=no-name-in-module,import-error
|
||||||
|
except Exception:
|
||||||
|
import camotics.gplan as _gplan # pylint: disable=no-name-in-module,import-error
|
||||||
|
gplan = _gplan
|
||||||
|
return gplan
|
||||||
import bbctrl.Cmd as Cmd
|
import bbctrl.Cmd as Cmd
|
||||||
from bbctrl.CommandQueue import CommandQueue
|
from bbctrl.CommandQueue import CommandQueue
|
||||||
|
|
||||||
@@ -329,7 +344,7 @@ class Planner():
|
|||||||
if stop:
|
if stop:
|
||||||
self.ctrl.mach.stop()
|
self.ctrl.mach.stop()
|
||||||
|
|
||||||
self.planner = gplan.Planner()
|
self.planner = _load_gplan().Planner()
|
||||||
self.planner.set_resolver(self._get_var_cb)
|
self.planner.set_resolver(self._get_var_cb)
|
||||||
# TODO logger is global and will not work correctly in demo mode
|
# TODO logger is global and will not work correctly in demo mode
|
||||||
self.planner.set_logger(self._log_cb, 1, 'LinePlanner:3')
|
self.planner.set_logger(self._log_cb, 1, 'LinePlanner:3')
|
||||||
|
|||||||
Reference in New Issue
Block a user