fix(qemu): release the datapath after the process is gone

QEMU is the guest side of every adapter TAP, and uBridge's tap delete
refuses a device another process holds open ('Device or resource busy') —
a best-effort delete whose failure is suppressed. QEMU's stop did the
delete first and stopped the process after, so every node stop or project
delete leaked the persistent TAPs: found by inspecting the host after a
project delete (16 taps left behind, carrier down, no holder), and the
held-tap behaviour reproduced against uBridge directly.

Stop the QEMU process first, then remove the TAPs and the per-link kernel
bridges — process side first, then the devices, the same lesson as IOU's
reverse stop order. Verified live: node stop and project delete now leave
no taps and no processes behind, while a restart recreates the TAPs.

The adapter anchor naming also goes through the shared
utils.kernel_anchor helper, like the other node types.
This commit is contained in:
YueGuobin committed 2026-10-06 00:21:09 +08:00
1 parent c343ce0b8f
commit 0b78d313eb
3 files changed
+58 -12

No files matched your search

+13 -5
View File
@@ -83,11 +83,15 @@ datapaths (delete + re-create, or a project reopen).
* **Life (running)** — the TAP is never created or destroyed again. Link
create/delete/switch, suspend, capture and markers only change *what is
attached to it*.
* **Death (node stop)** — `_remove_taps` un-persists the TAPs (`tap delete`;
QEMU's open fd keeps each device alive until it exits) and deletes the
per-link kernel bridges the node still holds. Deleting a node never runs the
link-teardown path, so those bridges would otherwise stay behind as empty
orphans; a restart rebuilds them from the NIO.
* **Death (node stop)** — the QEMU process is stopped **first**, then
`_remove_taps` un-persists the TAPs (`tap delete`) and deletes the
per-link kernel bridges the node still holds. The order matters: uBridge's
`tap delete` refuses a device another process holds open ("Device or
resource busy") and that best-effort delete is suppressed, so deleting the
TAPs while QEMU still ran leaked every persistent TAP (process side first,
then the devices — the same lesson as IOU's reverse stop order). Deleting
a node never runs the link-teardown path, so those bridges would otherwise
stay behind as empty orphans; a restart rebuilds them from the NIO.
## Link operations
@@ -176,3 +180,7 @@ TAPs bound to the IOL fabric; serial links stay relay). **Dynamips has
landed** too — see `docs/features/dynamips-kernel-datapath.md` (the
hypervisor opens uBridge-created TAPs with `nio create_tap`: the same
external-fd-holder shape QEMU uses; serial/ATM/POS ports stay relay).
**The Ethernet switch has landed** — see
`docs/features/ethernet-switch-kernel-datapath.md` (the switch absorbs a
peer's anchor into its own kernel bridge; switch-to-switch cascades stay
relay for now).