You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
feat(rootfs): native armhf on aarch64 host via runtime-disable of qemu-arm
When building an armhf image on an aarch64 host that supports 32-bit ARM
execution (kernel CONFIG_COMPAT, CPU has 32-bit user mode), disable the
qemu-arm handler in /proc/sys/fs/binfmt_misc/ for the duration of the
build. The kernel's own binfmt_elf then handles 32-bit ARM ELFs natively
via CONFIG_COMPAT — typically ~12x faster than qemu-arm-static emulation
on Cortex-A53/A55/A72/A73/A76.
Continues #9284 (arm64-compat-vdso extension + custom_kernel_make_params
hook), which enabled COMPAT_VDSO in arm64 kernels.
Activation point is delayed to AFTER mmdebstrap. Its cross-arch path
extracts base packages and runs their postinst hooks, which require a
working qemu-arm registration to populate the chroot before
libc/ld-linux-armhf.so.3 exist for binfmt_elf to fall through to.
The speedup applies to all subsequent chroot operations
(install_distribution_specific, customize_image, update-initramfs).
Killswitch: NATIVE_ARMHF_ON_ARM64=no (synonyms: never, disabled).
Concurrent armbian builds on the same host kernel are coordinated purely
via kernel BSD flock on the binfmt entry itself — no userspace state, no
per-builder lockfiles, no /run/lock directory tree:
- Each builder holds LOCK_SH on /proc/sys/fs/binfmt_misc/qemu-arm via a
long-lived fd. Kernel BSD-flock counter is the refcount; the kernel
releases the fd on process exit (crash-safe).
- First-arrival idempotently `echo 0` to disable. Subsequent arrivals
observe 0 and proceed without writing.
- On exit, release LOCK_SH; last-out detects via LOCK_EX-LOCK_NB on a
fresh fd, succeeds iff zero other LOCK_SH holders. Last-out re-enables
qemu-arm.
Trade-offs (documented):
- Prior qemu-arm state is not recorded across independent builds. Last-
out unconditionally re-enables. Admin's pre-existing `disabled` policy
is not preserved.
- No defense against an external agent toggling qemu-arm mid-build.
Documented; either don't, or set NATIVE_ARMHF_ON_ARM64=no on builds
that should not interfere.
- Cleanup ordering invariant: BSD flock is per-OFD, so a forked subshell
inheriting our SH-fd shares the same lock entry. add_cleanup_handler
runs in registration order; we register after the umount/SDCARD/MOUNT
teardown handlers, so by the time we run, the docker container is
killed and its child-tree (with our inherited fd) gone. Documented in
the trap handler's docstring; if a future refactor breaks ordering,
POSIX F_SETLK or explicit descendant-kill are the documented escape
hatches.
Empirical (helios4 mvebu/armhf, BUILD_MINIMAL=yes BRANCH=edge RELEASE=noble):
- baseline (rootfs cache-miss): 60:35
- this PR (rootfs cache-miss): 19:27 (3.12x)
- this PR (rootfs cache-hit): 6:38 (~9x over expected baseline cache-hit)
Concurrency verified on Hetzner CAX21 (kernel 6.8.0-90-generic): two
independent armbian worktrees concurrent on same host, both enter native
path, kernel SH-counter holds qemu-arm disabled across both, first-
finished suppresses restore (LOCK_EX-NB blocked by other's SH), last-
finished restores qemu-arm to enabled. SIGINT mid-chroot also cleans up
correctly.
Reviewed iteratively with codex (gpt-5.5) and coderabbitai. The earlier
revision of this work coordinated via a userspace owner-flock directory
(~250 lines, depended on /run/lock being host-bind-mounted into build
containers, which Armbian docker does not do); replaced with the kernel-
flock-on-binfmt-entry approach above.
Assisted-by: Claude:claude-opus-4.7
# Post-disable check is authoritative: arch-test now faces what the
233
+
# chroot exec will face. False-positive if host kernel lacks COMPAT_VDSO
234
+
# (see extensions/arm64-compat-vdso, PR #9284).
235
+
if! arch-test armhf > /dev/null 2>&1;then
236
+
display_alert "Native armhf via binfmt_elf""post-disable verification failed (host kernel lacks COMPAT_VDSO — see extensions/arm64-compat-vdso); restoring and falling back to emulation""wrn"
237
+
trap_handler_native_armhf_restore_qemu_arm
238
+
return 1
239
+
fi
240
+
241
+
display_alert "Native armhf via binfmt_elf""kernel $(uname -r), aarch64 host with COMPAT_VDSO; qemu-arm disabled, kernel binfmt_elf takes over""info"
display_alert "binfmt qemu-arm""registered but observably disabled — another concurrent build likely holds native-armhf; refusing to clobber""err"
375
+
exit_with_error "qemu-arm globally disabled by another concurrent build; cannot safely re-enable. Wait for it to finish or run on a separate host."
376
+
fi
377
+
fi
378
+
196
379
display_alert "Trying to update binfmts - aarch64 mostly does 32-bit sans emulation, but Apple said no""update-binfmts --enable qemu-${wanted_arch}""debug"
197
380
run_host_command_logged update-binfmts --enable "qemu-${wanted_arch}""&>""/dev/null""||""true"# don't fail nor produce output, which can be misleading.
198
381
@@ -201,12 +384,13 @@ function prepare_host_binfmt_qemu_cross_arm64_host_armhf_target() {
201
384
run_host_command_logged arch-test "||"true
202
385
fi
203
386
204
-
# to check, we use arch-test; if will return 0 if _either_ the host can natively run armhf, or if qemu-arm is correctly working.
205
-
if arch-test arm;then
387
+
# to check, we use arch-test; will return 0 if _either_ the host can natively run armhf, or if qemu-arm is correctly working.
388
+
# Use armhf (Debian-arch) rather than arm to match the build target and the post-disable check in _native_armhf_setup_binfmt_elf.
389
+
if arch-test armhf;then
206
390
display_alert "Host can run armhf natively or emulation is correctly setup already""no need to enable qemu-arm""debug"
207
391
else
208
392
display_alert "arm64 host can't run armhf natively""importing enabling qemu-arm""debug"
0 commit comments