Summary
Since #183 the VMM is spawned via CLONE_INTO_CGROUP directly into cocoon.slice/vm-<id>.scope, so everything the VMM does from its first instruction runs under the VM's Guaranteed-at-N policy (quota = N×period, burst 0) and inside the #185 fence. That includes the restore/clone-time memory loading:
- copy mode: the eager multi-core memory copy executes inside the child scope's N-core quota;
- mmap mode (default): the clone window itself is thin, but the deferred first-touch page faults pay inside the same quota after claim.
Restore/clone execution is operator/control-plane work — the per-VM policy is meant to bound the VM's workload, not the act of provisioning it. The docs already state this philosophy explicitly (docs/vm.md): "the fence bounds the VMs; the caller's own work (clone, restore, the API consumer) is deliberately not cocoon's to manage" — and warn that AllowedCPUs=15 "would confine clone's multi-core memory restore to one core". The implementation currently does exactly that confinement for the VMM-side half of restore, via the child's own scope.
Measured
16-core bare-metal host (AMD 9700X), CH lane, 1-vCPU/512M guests, 20-VM CPU-burn storm (each guest spinning its vCPU), sequential timed clones from one snapshot, n=12 per cell, quiet controls taken. A/B/C = pre-cgroup master (07f1d69) / 3542c8a scopes only / 3542c8a + cgroup_cpus=0-14:
| clone p50/P90/max (ms) |
07f1d69 |
scopes only |
scopes + fence |
--restore-mode copy |
390 / 530 / 547 |
330 / 609 / 717 |
516 / 1032 / 1348 |
| mmap (default) |
98 / 169 / 216 |
155 / 334 / 463 |
114 / 198 / 230 |
Copy-mode P90 roughly doubles once the child scope + fence apply — the eager copy that wants N cores gets exactly 1 core of quota with burst 0, placed inside the saturated fenced set. Quiet-host clones are unaffected (quota not binding). For contrast, the same round measured the fence's intended win on control-plane ops: vm list under the same storm went 62/133/147 → 5/5/5 p50/P90/max — the mechanism works as designed for steady-state isolation; it is specifically the provisioning window that lands on the wrong side of the boundary.
Options
- Spawn the VMM outside
vm-<id>.scope (parent slice or a transient provisioning scope) and migrate it into its scope once restore completes / the VM reports ready — restore then runs as caller-side work, matching the documented contract. One controlled migration per boot; keeps CLONE_INTO_CGROUP semantics for the steady state.
- Keep CLONE_INTO_CGROUP but hold
cpu.max=max during the restore window and arm Guaranteed-at-N at ready. Simpler; restore still respects the fence's placement, only the quota is deferred.
- At minimum, document the copy-mode × Guaranteed-at-N interaction as a known caveat for dense hosts.
Option 1 also covers mmap's deferred first-touch cost only if migration happens after warm-up, which it can't reasonably — so for mmap the residual is inherent and worth a doc note either way.
Summary
Since #183 the VMM is spawned via CLONE_INTO_CGROUP directly into
cocoon.slice/vm-<id>.scope, so everything the VMM does from its first instruction runs under the VM's Guaranteed-at-N policy (quota = N×period, burst 0) and inside the #185 fence. That includes the restore/clone-time memory loading:Restore/clone execution is operator/control-plane work — the per-VM policy is meant to bound the VM's workload, not the act of provisioning it. The docs already state this philosophy explicitly (docs/vm.md): "the fence bounds the VMs; the caller's own work (clone, restore, the API consumer) is deliberately not cocoon's to manage" — and warn that
AllowedCPUs=15"would confine clone's multi-core memory restore to one core". The implementation currently does exactly that confinement for the VMM-side half of restore, via the child's own scope.Measured
16-core bare-metal host (AMD 9700X), CH lane, 1-vCPU/512M guests, 20-VM CPU-burn storm (each guest spinning its vCPU), sequential timed clones from one snapshot, n=12 per cell, quiet controls taken. A/B/C = pre-cgroup master (07f1d69) / 3542c8a scopes only / 3542c8a +
cgroup_cpus=0-14:--restore-mode copyCopy-mode P90 roughly doubles once the child scope + fence apply — the eager copy that wants N cores gets exactly 1 core of quota with burst 0, placed inside the saturated fenced set. Quiet-host clones are unaffected (quota not binding). For contrast, the same round measured the fence's intended win on control-plane ops:
vm listunder the same storm went 62/133/147 → 5/5/5 p50/P90/max — the mechanism works as designed for steady-state isolation; it is specifically the provisioning window that lands on the wrong side of the boundary.Options
vm-<id>.scope(parent slice or a transient provisioning scope) and migrate it into its scope once restore completes / the VM reports ready — restore then runs as caller-side work, matching the documented contract. One controlled migration per boot; keeps CLONE_INTO_CGROUP semantics for the steady state.cpu.max=maxduring the restore window and arm Guaranteed-at-N at ready. Simpler; restore still respects the fence's placement, only the quota is deferred.Option 1 also covers mmap's deferred first-touch cost only if migration happens after warm-up, which it can't reasonably — so for mmap the residual is inherent and worth a doc note either way.