Skip to content

Commit ad61f9b

Browse files
committed
docs: document virtio-net data plane tuning
1 parent 5029e0c commit ad61f9b

2 files changed

Lines changed: 52 additions & 19 deletions

File tree

docs/bridge-networking.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -210,6 +210,6 @@ vmm-cli.py deploy --name my-vm --image dstack-0.5.6 --compose app.yaml --net use
210210

211211
### vhost-net and multiqueue
212212

213-
Bridge NICs use the host kernel's vhost-net data plane by default, and can expose several virtio-net queue pairs. Both are configurable per node and per VM — see [network-data-plane.md](network-data-plane.md) for the knobs, the mode support matrix, and how to pick a queue count.
213+
Bridge NICs can run on the host kernel's vhost-net data plane and expose several virtio-net queue pairs. Both are off by default and enabled per node or per VM — see [network-data-plane.md](network-data-plane.md) for the knobs, the enablement checklist, the mode support matrix, and how to pick a queue count.
214214

215215
vhost-net works in a TDX guest: the virtio rings and buffers live in shared, unencrypted memory so that a host-side backend can reach them, which is the same mechanism `vhost-vsock-pci` has always relied on.

docs/network-data-plane.md

Lines changed: 51 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -30,9 +30,14 @@ cost twice over.
3030
it relocates the ceiling rather than removing
3131
it: packets now arrive faster than a single guest receive queue can drain, and
3232
the drops reappear at a higher rate. More queue pairs is what removes them,
33-
which is why both are defaults: vhost everywhere, and a queue count that follows
34-
the VM's vCPU count. The knob you are more likely to reach for is the other
35-
direction — see [Choosing a queue count](#choosing-a-queue-count).
33+
which is why enabling vhost also enables a queue count that follows the VM's
34+
vCPU count — the two travel together.
35+
36+
vhost is **off by default** and enabled per node (or per VM). Two things make
37+
it an opt-in rather than a default: turning it on changes the virtio-net device
38+
of every bridge/macvtap VM on its next boot, and it requires `/dev/vhost-net`
39+
to be accessible to the account QEMU runs under, which the VMM cannot verify on
40+
the operator's behalf — see [Enabling vhost on a node](#enabling-vhost-on-a-node).
3641

3742
## Configuration
3843

@@ -47,22 +52,24 @@ bridge = "dstack-br0"
4752
vhost = true
4853
```
4954

50-
Queue pairs are not a node setting. They default to the VM's vCPU count, capped
51-
at 16, because the useful number follows the VM rather than the host — the guest
52-
driver uses at most one queue pair per vCPU. A deployment overrides that per VM,
53-
up to `max_net_queues`.
55+
Queue pairs are not a node setting. With vhost on they default to the VM's vCPU
56+
count, capped at 16, because the useful number follows the VM rather than the
57+
host — the guest driver uses at most one queue pair per vCPU. A deployment
58+
overrides that per VM, up to `max_net_queues`.
5459

5560
Raising `max_net_queues` above 16 widens what a deployment may ask for without
5661
moving the default's cap, so a larger VM never silently acquires a worse
5762
default. Lowering it below 16 does lower the default too, because a node that
5863
refuses a request for four queue pairs should not hand out sixteen by itself.
5964
The hard ceiling from any source is 64.
6065

61-
Turning vhost off also turns the multiqueue default off. Without vhost the QEMU
62-
main loop drains every queue on one thread, so extra queues buy little while
63-
still costing a netd interface, more MSI-X vectors, and a changed guest device.
64-
An explicit queue count is still honoured without vhost, since that combination
65-
is a deliberate request rather than a default.
66+
Without vhost the default is a single queue pair. The QEMU main loop drains
67+
every queue on one thread, so extra queues buy little while still costing a
68+
netd interface, more MSI-X vectors, and a changed guest device. An explicit
69+
queue count is still honoured without vhost, since that combination is a
70+
deliberate request rather than a default. The two defaults travelling together
71+
also means a node that never sets `vhost` keeps building the device its VMs
72+
have always had.
6673

6774
A VM overrides either value at deploy time, and `UpdateVm` changes them
6875
afterwards — the new values apply from the VM's next boot:
@@ -128,6 +135,31 @@ vhost state are not part of it, so retuning a NIC does not change app identity
128135
or require an on-chain update. Adding or removing a NIC does: the NIC count
129136
changes the guest's ACPI tables and therefore RTMR0.
130137

138+
## Enabling vhost on a node
139+
140+
Setting `vhost = true` in `[cvm.networking]` is a node-wide behaviour change:
141+
every bridge or macvtap VM that has not pinned its own data plane gets a
142+
different virtio-net device on its next boot — `vhost=on`, `mq=on` with
143+
vCPU-scaled queue pairs, and the matching MSI-X vector count. The device is not
144+
measured, so attestation and app identity are unaffected. Before flipping it:
145+
146+
1. **Verify `/dev/vhost-net` is accessible to the account QEMU runs under.**
147+
It is `root:kvm 0660` on Debian-family hosts, where adding the account to
148+
the `kvm` group suffices, and `root:root 0600` on several others. If the
149+
account lacks access, QEMU exits at launch and every affected VM stops
150+
restarting. The VMM warns at startup when its own access fails, but it
151+
cannot refuse on that basis — QEMU need not share its credentials.
152+
153+
2. **Restart `netd` before or together with the VMM.** Multiqueue bridge NICs
154+
are prepared by `netd`, and the VMM checks that `netd` echoes the queue
155+
count it built. An older `netd` fails that check; the launch is rolled back
156+
and fails with the reason in the VMM log, but the VM does not start until
157+
`netd` is upgraded.
158+
159+
3. **Roll back by setting `vhost = false`.** The node value reaches every VM
160+
that did not pin `vhost` explicitly, from its next boot; a VM that pinned
161+
`vhost = true` keeps it until updated.
162+
131163
## What each mode supports
132164

133165
| Mode | netdev | vhost | queues > 1 |
@@ -144,8 +176,8 @@ vhost switches bridge mode to a `tap` netdev driven by the same setuid
144176
no compiled-in default path for the `tap` netdev, so the VMM probes the known
145177
distribution locations; set `cvm.qemu_bridge_helper` if yours is elsewhere. If
146178
no helper is found the NIC falls back to the non-vhost `bridge` netdev with a
147-
warning, because vhost is a default and a default must not stop a node from
148-
booting VMs.
179+
warning, because a node-wide setting must not stop a node from booting VMs
180+
that never asked for it.
149181

150182
The helper returns exactly one descriptor, which is why more than one queue
151183
pair in bridge mode is created by `netd` instead: it adds a persistent
@@ -224,10 +256,11 @@ the same calculation, against the node configuration and manifest as they stand
224256
now, rather than the ones a finished boot ran under.
225257

226258
If that account lacks access, QEMU exits at startup and the VM never boots. The
227-
VMM does not pre-check this: QEMU need not share the VMM's credentials, so
228-
refusing a launch on the VMM's own access would block deployments the host can
229-
run. It only warns when the device node is missing outright, which is a fact
230-
about the host rather than about either account.
259+
VMM does not refuse a launch over this: QEMU need not share the VMM's
260+
credentials, so a refusal based on the VMM's own access would block deployments
261+
the host can run. It warns instead — when the device node is missing outright,
262+
and when the VMM's own open is denied, since QEMU usually does share its
263+
account.
231264

232265
vhost-net works normally in a TDX guest: the virtio rings and buffers live in
233266
shared, unencrypted memory precisely so a host-side backend can reach them.

0 commit comments

Comments
 (0)