refactor(compute): share the kernel-datapath machinery across node types

Move the datapath-agnostic half of DockerKernelDatapathMixin into a new
KernelDatapathMixin (compute/kernel_datapath.py) so a second node type can
reuse it verbatim: the three marker primitives, the tc netem / cls_bpf / eBPF
appliers, the uBridge tc-capability probe, kernel marker teardown, link
detach, the per-link bridge cleanup and the adapter carrier -- plus
_kernel_attach(), lifted from DockerVM._connect_nio's kernel branch.

The mixin talks about "the anchor" (the host-side interface an adapter owns in
the root namespace) instead of "the veth host end": a node type provides
_kernel_host_ifc(), _kernel_anchors() and, when needed, _relay_carrier_bridge()
and _kernel_error(). Docker keeps only the veth lifecycle and those hooks;
behaviour is unchanged.

The C901 baseline entry moves with _netem_command_parts (the docker module is
no longer over the limit) and the bpf-compile-failure test patches the logger
of the module that now owns the code -- net zero new ignores.
This commit is contained in:
YueGuobin committed 2026-10-06 00:21:08 +08:00
1 parent 0b507773b9
commit 12d8abce45
5 files changed
+679 -507

No files matched your search

+4 -25
View File
@@ -1821,31 +1821,10 @@ class DockerVM(DockerKernelDatapathMixin, BaseNode):
adapter_number=adapter_number, port_number=port_number, name=self.name
)
)
# Both endpoints run this concurrently and either may create the
# bridge first: treat an existing bridge as success instead of a
# create race failure.
try:
await self._ubridge_send(f'brctl create "{nio.bridge}"')
except UbridgeError:
# Raises again if the bridge genuinely does not exist.
await self._ubridge_send(f'brctl show "{nio.bridge}"')
await self._ubridge_send(f'link set "{nio.bridge}" up')
await self._ubridge_send(f'brctl addif "{nio.bridge}" "{host_ifc}"')
if nio.capturing:
# Restore a capture that was active before a node restart
# (mirrors the relay path's start_capture in _connect_nio).
await self._ubridge_send(f'capture start_kernel {host_ifc} "{nio.pcap_output_file}"')
# Markers carried by the NIO attach to the veth host end (AF_PACKET
# taps) — the anchor is the interface, not a relay bridge.
await self._ubridge_apply_markers(host_ifc, nio)
# Impairment filters become one tc netem qdisc on the veth host
# end (restored here on node restart, like the capture above);
# bpf expressions become cls_bpf match-drop classifiers and
# frequency_drop/quota/window_drop become the eBPF stateful
# classifier.
await self._ubridge_apply_netem(host_ifc, nio.filters)
await self._ubridge_apply_bpf_drops(host_ifc, nio.filters)
await self._ubridge_apply_ebpf_drops(host_ifc, nio.filters)
# The mechanics (bridge create/addif, capture, markers, filters)
# live in KernelDatapathMixin, shared with every other node type
# whose adapters own a host-side anchor.
await self._kernel_attach(host_ifc, nio)
return
# Relay NIO. On a veth-backed adapter (the unified Docker interface)