Repository navigation
Recurring multi-hour VM hangs on arm64: do_seccomp → kick_all_cpus_sync IPI never returns, RCU sched starves, vmgr pinned at ~200% CPU #2471
Description
Recurring multi-hour VM hangs on arm64: do_seccomp → kick_all_cpus_sync IPI never returns, RCU sched starves, vmgr pinned at ~200% CPU
Describe the bug
The OrbStack guest Linux VM enters a partial-freeze state from which it never self-recovers. Once it happens, orb status reports Starting and the docker socket is unresponsive — but the helper process (vmgr) is alive and consuming ~200% CPU indefinitely. The only recovery is killing OrbStack and restarting it. This has happened multiple times per day for the past few weeks.
The OrbStack watchdog autonomously samples the process when this state is detected and writes ~/.orbstack/log/vmgr.sample.log — that file (and the kernel printk traces in vmgr.log) point to the same root cause every time: the guest kernel's BPF JIT compiler is installed by a seccomp filter, the JIT issues a cross-CPU IPI via __text_poke → kick_all_cpus_sync, and the IPI never completes. smp_call_function_many_cond spins forever, the RCU grace-period kthread (rcu_sched) starves, and everything downstream stalls. The host vmgr keeps spinning trying to drive the wedged guest.
Canonical guest-kernel stack (identical across 189 separate captures in a single session)
do_seccomp.cold+135
__arm64_sys_seccomp+35
→ bpf_prog_create_from_user.cold+219
→ bpf_prepare_filter+775
→ bpf_prog_select_runtime+83
→ bpf_int_jit_compile+1247
→ bpf_jit_binary_pack_finalize+39
→ bpf_arch_text_copy+27
→ aarch64_insn_copy+55
→ __text_poke+251
→ kick_all_cpus_sync+71
→ smp_call_function_many_cond+916 ← spins here, never returns
The same vCPU (in our case vCPU 9) is always the victim. RCU stall traces are emitted continuously, e.g.:
rcu: rcu_sched kthread starved for 98415 jiffies! g448205 f0x0 RCU_GP_WAIT_FQS(5) ->state=0x402 ->cpu=9
rcu: Unless rcu_sched kthread gets sufficient CPU time, OOM is now expected behavior.
The starvation counter climbs continuously (98k → 7.3M jiffies over 2 hours in today's incident). At one point OrbStack's VirtualClock thread rebases the guest clock — the jiffy counter rolls back partway, then the same vCPU re-wedges on the same BPF JIT path within seconds. The hypervisor's recovery attempt does not break the deadlock.
Downstream cascade once vCPU is dead
brnet::bridge::macos: failed to write packet to guest [BufferFull]— thousands of dropped packets to the guest (2,789 in 2h).vmgr: failed to send fsnotify events ... endpoint is closed for send— the host→guest fsnotify TCP channel (control port) tears down (8,337 errors in 2h). After this, file-change notifications to bind mounts are dead even if some IO still moves.dockerCLI commands hang indefinitely.orb statusreturnsStarting(exit 2) although the VM has been booted for hours.- vmgr (
OrbStack Helper vmgr) is permanently at 199% CPU (2 cores pinned) and grows its physical footprint to >20 GB.
To Reproduce
Exact deterministic reproduction has not been isolated, but the trigger profile is consistent:
- Apple Silicon Mac, macOS 26.5, OrbStack 2.1.3.
- Allocate OrbStack the default resources (no custom CPU/memory cap).
- Bring up multiple Docker Compose projects simultaneously — in our case 3 separate compose stacks at boot, each containing a long-running database service plus several application containers (~15–20 containers total across all stacks).
- Let the VM run for several hours while containers perform routine work (no churn required, no rebuilds, no
docker pull). Periodic health-checkexecs and any new container starts trigger fresh seccomp BPF JIT compiles. - Some time between 3 and 4 hours after VM boot, the RCU stall fires and the VM never recovers. In today's incident, VM was booted at 07:49 and the first
rcu_schedstarvation message appeared at 11:24 (printk timestamp ~12,724 s after boot). The watchdog logged its firststacks sampledat 11:51:40.
Frequency: 1–3 times per day in our environment. Always the same guest-kernel call path.
Expected behavior
Either:
- The BPF JIT path should not deadlock on
kick_all_cpus_sync— i.e. the cross-CPU IPI broadcast should always complete (this is a guest-kernel / hypervisor-IPI-delivery interaction issue specific to OrbStack'sHypervisor.framework/ Virtualization-based vCPU scheduling on aarch64); or - If the IPI is genuinely lost, the hypervisor should be able to break the deadlock without an external restart, and
orb status/ docker should not block on it.
Diagnostic report (REQUIRED)
Not attached. When OrbStack enters this state, the daemon is wedged: orb feedback panics with timed out waiting for services to start and orb report cannot complete. Generating a fresh orb report would require killing OrbStack and starting a new VM, which would destroy the in-memory state of the actual incident.
The OrbStack watchdog has, however, already auto-captured a 165 KB stack-sample log of every host-side vmgr thread at the time of detection. The relevant excerpts are inline above. I'm happy to upload the full vmgr.sample.log and a filtered vmgr.log (RCU stall traces + BPF JIT stack samples only) privately if useful — please advise how to share these; we are willing to share them only with OrbStack staff via a private channel since they contain hypervisor symbol offsets and some host process metadata.
If desired I can run orb report after recovering from the next hang (will be a "post-recovery" report on a clean VM, not the wedged state).
Additional context
Host environment
| Field | Value |
|---|---|
| OrbStack | 2.1.3 (build 2010300), commit 7a3258b7336a8a47b75771e87ef7b74ba4bba8eb |
| macOS | 26.5 (build 25F71) |
| Chip | Apple Silicon, arm64, 14 cores |
| Host RAM | 36 GB |
| Host swap at hang time | 4.45 / 5.12 GB used (~87%), ~6 k free pages |
| OrbStack VM physical footprint at hang | 21.0 GB (peak 23.8 GB) |
OrbStack ~/.orbstack/config/docker.json |
empty {} (defaults only) |
The host swap saturation is a consequence of the hang (vmgr's footprint grows while spinning), not the trigger — the first RCU stall fires before host swap is full. We also see prior occurrences in rotated logs where the host had abundant free memory at the time of the stall.
Recurrence evidence
~/.orbstack/log/vmgr.log(current session): 189 distinct stack samples, all on the samedo_seccomp→kick_all_cpus_syncpath; 27 distinctrcu_sched kthread starvedprintk progress messages.~/.orbstack/log/vmgr.1.log(rotated, prior session): also contains astacks sampledwatchdog event minutes before rotation — confirms this is not a one-off and recurs across reboots.
Workload profile (sanitized)
The workload is a small set of docker-compose projects running a long-running columnar database service alongside several supporting service containers. Container churn is low (no rebuilds, no docker run loops) — only routine compose up at the start of the day plus container health-check execs. Crucially, no --privileged, no VPN/gluetun, no unusual ulimit nofile, no oversized bind-mount sets. The compose stacks use plain bridge networking and named volumes.
This means the trigger is not "heavy I/O", not "too many interfaces", not "container thrash" — it's specifically that the kernel's seccomp BPF JIT path on arm64 occasionally loses an IPI ack and deadlocks.
Suspected upstream area
This call path — bpf_jit_binary_pack_finalize → bpf_arch_text_copy → aarch64_insn_copy → __text_poke → kick_all_cpus_sync — is what aarch64 added in Linux ~6.x to flush I-caches after JITting executable BPF text into the BPF JIT region. On bare metal this is unconditionally safe; under a virtualized arm64 environment where IPI delivery depends on the hypervisor scheduling target vCPU threads in time, a missed IPI can leave the caller blocked on smp_call_function_many_cond forever.
I suspect either:
- a subtle race between vCPU thread preemption on the host and IPI completion, where the target vCPU is descheduled before it can ack; or
- a Hypervisor.framework / Virtualization.framework issue where an IPI raised while a vCPU is in WFE/WFI is occasionally not delivered when the vCPU wakes.
Either way, it's reachable from inside a normal container starting up — which means any user who starts enough containers will eventually hit it.
Workaround attempted
Adding security_opt: ["seccomp=unconfined"] to compose services prevents the kernel from installing a BPF program for seccomp, which avoids the BPF JIT path entirely. This is what we are using locally to keep dev environments usable, but it's not acceptable as a long-term fix.
What would help us help you
- A way to selectively disable BPF JIT (e.g.
net.core.bpf_jit_enable=0) inside the OrbStack docker VM as a documented mitigation. - Confirmation whether a kernel patch for
kick_all_cpus_syncaarch64 robustness is on the roadmap, or whether this needs to go upstream first. - Whether a host-side workaround (different IPI delivery / different vCPU pinning strategy) is being considered for 2.x.
Happy to test patched builds, run with extra instrumentation, or attach more logs.
Activity
kdrag0n commentedon May 21, 2026
Please attach
orb reportafter restarting OrbStack. This analysis is inaccurate and doesn't have enough info for debugging.alex-fedotyev commentedon May 22, 2026
Additional info from a fresh occurrence today (2026-05-22):
Diagnostic bundle
orb report(taken after restart, as requested): attachingorbstack-diagreport.zipin a follow-up (docker-compose project names redacted; nothing else altered).OrbStack info:
Captured stuck state (before restart)
OrbStack's own hang detector fired and auto-sampled stacks — 40 separate "sampling stacks due to VM hang" events between 14:40:36 and 15:24:52 (~45 min sustained wedged state, not transient). Every event shows the same kernel stack on every vCPU:
Classic deadlock pattern: a guest userspace process called
seccomp(), the kernel started JIT-compiling its BPF filter, which needs an all-CPU IPI to patch kernel text — but every vCPU is itself stuck insidesmp_call_function_many_condwaiting on that same IPI. Nothing makes progress.Host-side fallout was consistent with the guest being wedged:
failed to send fsnotify events: endpoint is closed for send,host-tcp forward: context deadline exceeded,brnet::bridge::macos: failed to write packet to guest error=BufferFull,health check failed: Post "http://vcontrol/disk/report_stats": context deadline exceeded.Process state right before restart:
OrbStack Helper vmgrrunning since May 18, ~22 GB RSS, pinned at ~298% CPU, ~969 min CPU time accumulated.Attaching
vm-hang-first-event.log(single representative hang event — vCPU register dumps + stacks) in the follow-up. The full OrbStack-captured sample is included in the diag bundle asvmgr_logs/vmgr.sample.log.Restart behavior
Quitting OrbStack typically hangs in this state — the GUI app can't respond to the Quit Apple Event because vmgr is wedged.
kill -TERMon the helper also doesn't work;kill -KILLon theOrbStack Helper vmgrPID is required. After force-kill, relaunch is clean (vmgr healthy in ~2s).Trigger speculation
Common to the workloads I run are docker-compose stacks with multiple containers that apply seccomp profiles on startup (default docker behavior). The hang seems to develop over multi-day uptime — this VM had been up since May 18.
alex-fedotyev commentedon May 22, 2026
orbstack-diagreport-REDACTED.zip
vm-hang-first-event.log
alex-fedotyev commentedon May 22, 2026
@kdrag0n - hope this help, appreciate your help here, it is a very annoying issue
kdrag0n commentedon May 22, 2026
Already fixed for the next version.
kdrag0n commentedon Jun 4, 2026
Released in v2.2.0.