From 7213e5613110a82aa8edead76d6d8d3f13418a0e Mon Sep 17 00:00:00 2001 From: Manuel de Brito Fontes Date: Fri, 13 Nov 2026 00:54:51 -0300 Subject: [PATCH 1/2] boot: bench row, the serial getty Type=simple (experiment) Co-Authored-By: Claude Opus 5.5 --- boot/bench_test.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/boot/bench_test.go b/boot/bench_test.go index 67a8230..64aa4f1 100644 --- a/boot/bench_test.go +++ b/boot/bench_test.go @@ -104,6 +104,11 @@ var variants = []variant{ // changes nothing (1229 against 1232, 12 boots, 2026-09-26). {label: "as shipped", cpus: "2", memory: "2048"}, {label: "baseline", cpus: "2", memory: "2048", files: gettyDropin(gettyEcho)}, + // Experiment, 2026-11-13: since spin lingers, user@1000.service and its chain are jobs of the + // boot transaction, and the getty, Type=idle, waits for the queue to drain before it execs - + // 91.8 ms of boot:waits's median boot on v20261113.01, against 3-5 before. This row is the + // getty Type=simple. Drop the row once it has answered. + {label: "getty simple", cpus: "2", memory: "2048", files: gettyDropin(gettyEcho + "Type=simple\n")}, } // kernelB is the kernel SPIN_KERNEL_B names, resolved, or "" when it names none. A test From 131378e8c2cf27c9fd2693eeeb1c37e9c6e90dfd Mon Sep 17 00:00:00 2001 From: Manuel de Brito Fontes Date: Fri, 13 Nov 2026 00:58:26 -0300 Subject: [PATCH 2/2] image: the serial getty is Type=simple v20261113.01's console prompt came 77 ms later than v20261112.01's: the getty is Type=idle, systemd holds it until no job is left running, and since spin lingers logind starts user@1000.service and its chain inside the boot transaction. boot:waits on the release: 91.8 ms between the getty's fork and its exec in the median boot, against 3-5 before. A drop-in makes it Type=simple: 346/358 against 423/441 ms p50/p95 to a login prompt, 20 boots interleaved on the bench fleet (lab run 43362067503) - back to where v20261112.01 was (344/353), with the first SSH login 494/506 against 570/626 and the kernel 89.1 against 95.1 ms kept. Type=idle keeps a prompt clear of the boot's status lines, and this machine's console prints none. build.sh asserts the drop-in. Co-Authored-By: Claude Opus 5.5 --- boot/bench_test.go | 8 +++----- image/build.sh | 7 +++++++ .../system/serial-getty@.service.d/type.conf | 14 ++++++++++++++ 3 files changed, 24 insertions(+), 5 deletions(-) create mode 100644 image/mkosi.extra/usr/local/lib/spin-base/rootfs/etc/systemd/system/serial-getty@.service.d/type.conf diff --git a/boot/bench_test.go b/boot/bench_test.go index 64aa4f1..2d0c091 100644 --- a/boot/bench_test.go +++ b/boot/bench_test.go @@ -87,6 +87,9 @@ func without(units ...string) variant { // - tmpfs /tmp against /tmp on the disk: 3 ms, within noise (220/232 against 223/230, 20 // boots, 2026-09-26); image/build.sh forbids the disk. // - serial-getty Type=simple instead of Type=idle: within noise of the echo (2026-09-10). +// And again once spin lingered and user@1000.service was in the boot transaction: 346/358 +// against 423/441, 20 boots, lab run 43362067503 (2026-11-13) - now the image's +// (serial-getty@.service.d/type.conf). // - loop.max_loop=0 8250.nr_uarts=1, and CONFIG_VT=n: no change to a login prompt // (2026-09-26). udev's coldplug overlaps the boot rather than delaying it. // - 19 udev rule files for hardware the machine cannot have, masked: no change, same day. @@ -104,11 +107,6 @@ var variants = []variant{ // changes nothing (1229 against 1232, 12 boots, 2026-09-26). {label: "as shipped", cpus: "2", memory: "2048"}, {label: "baseline", cpus: "2", memory: "2048", files: gettyDropin(gettyEcho)}, - // Experiment, 2026-11-13: since spin lingers, user@1000.service and its chain are jobs of the - // boot transaction, and the getty, Type=idle, waits for the queue to drain before it execs - - // 91.8 ms of boot:waits's median boot on v20261113.01, against 3-5 before. This row is the - // getty Type=simple. Drop the row once it has answered. - {label: "getty simple", cpus: "2", memory: "2048", files: gettyDropin(gettyEcho + "Type=simple\n")}, } // kernelB is the kernel SPIN_KERNEL_B names, resolved, or "" when it names none. A test diff --git a/image/build.sh b/image/build.sh index daae0e8..40e282a 100755 --- a/image/build.sh +++ b/image/build.sh @@ -299,6 +299,13 @@ for f in /etc/systemd/system/serial-getty@.service.d/term.conf \ in_image "$f" || { echo "ERROR: the image has no $f; a login would inherit TERM=dumb" >&2; exit 1; } done +# The serial getty is started Type=simple: Type=idle holds it until the boot's job queue +# drains, which with spin lingering is 77 ms of a login prompt (serial-getty@.service.d/type.conf). +"$bin/debugfs" -R "cat /etc/systemd/system/serial-getty@.service.d/type.conf" /work/base.raw 2>/dev/null | + grep -qx 'Type=simple' || { + echo "ERROR: the serial getty is not Type=simple; its prompt waits for the job queue to drain" >&2 + exit 1; } + # A boot optimization that is invisible in a file listing and expensive when it regresses: # a masked unit is a symlink to /dev/null, and a package upgrade replacing one turns it # back on. One of the ones optimize-systemd.sh masked for measured reasons. diff --git a/image/mkosi.extra/usr/local/lib/spin-base/rootfs/etc/systemd/system/serial-getty@.service.d/type.conf b/image/mkosi.extra/usr/local/lib/spin-base/rootfs/etc/systemd/system/serial-getty@.service.d/type.conf new file mode 100644 index 0000000..ffbdb7d --- /dev/null +++ b/image/mkosi.extra/usr/local/lib/spin-base/rootfs/etc/systemd/system/serial-getty@.service.d/type.conf @@ -0,0 +1,14 @@ +# The serial login prompt as soon as its getty is started, not once the boot's job queue drains. +# +# The template is Type=idle: systemd forks the getty and holds it until no job is left running. +# That cost nothing while the queue was empty by then, and Type=simple measured within noise of +# it (2026-09-10). Since spin lingers, logind starts user@1000.service and its chain inside the +# boot transaction, and the getty waited for them: 91.8 ms between its fork and its exec in the +# median boot of v20261113.01 (task boot:waits, lab run 43361486792), against 3-5 before. +# Type=simple gives the prompt 77 ms back - 346/358 against 423/441 ms p50/p95 to a login +# prompt, 20 boots interleaved on the bench fleet (task boot:bench, lab run 43362067503). +# +# What Type=idle is for is a prompt not overwritten by the boot's own status lines, and this +# machine's console prints none: the kernel is quiet and systemd shows no status. +[Service] +Type=simple