Skip to content

viona: support (enough) VIRTIO_NET_F_CTRL_RX to leave all-multicast mode#1125

Open
FelixMcFelix wants to merge 3 commits intomasterfrom
felixmcfelix/viona-guest-promisc
Open

viona: support (enough) VIRTIO_NET_F_CTRL_RX to leave all-multicast mode#1125
FelixMcFelix wants to merge 3 commits intomasterfrom
felixmcfelix/viona-guest-promisc

Conversation

@FelixMcFelix
Copy link
Copy Markdown
Contributor

@FelixMcFelix FelixMcFelix commented Apr 24, 2026

This PR adds support for the VIRTIO_NET_F_CTRL_RX capability, which allows guests to request specific MAC address filters in addition to their primary MAC, and to explicitly inform the host whether the device should be shifted into promiscuous mode.

The hope is that this allows us to take a guest out of all-multicast promiscuous mode in viona itself, which generates extra per-packet work (checking "is this packet multicast?") and contention in the Tx and Rx paths as both reach for and refcount the same list of promisc callbacks on the client.

Testing in falcon makes it look as though Debian will just immediately insert multicast entries, although I don't know if that's purely an artefact of my environment. Using one of the helios-dev images appears to correctly move out of promiscuous mode and keep the filter tables empty.

Closes #1122. Closes #1127.

@FelixMcFelix FelixMcFelix changed the title viona: support (enough) VIRTIO_NET_F_CTRL_RX to remove promisc viona: support (enough) VIRTIO_NET_F_CTRL_RX to leave all-multicast mode Apr 24, 2026
@FelixMcFelix
Copy link
Copy Markdown
Contributor Author

I've tested this so far on Alpine Linux 3.23.3, debian 13.2, and the helios-2.8 image we use for a4x2 testing. The first two are multiqueue-capable, whereas illumos is not -- control requests appear to be correctly served on queue 22 for the former and queue 2 for the latter.

The typical initialisation sequence looks to be that guests will send explicit promisc off signals for alongside every MAC filter update. snoop -rd vioif0 will also have illumos explicitly move us in and out of promiscuous mode.

Running on top of OPTE and/or in omicron standalone doesn't really stop Linux from asking for multicast MACs (i.e., they're not a result of anything coming in link-local). So for most guests we will need to get explicit MAC filter support into viona proper, or they'll just put themselves back into all-multicast mode. illumos, at least, keeps the tables empty. On Debian, I'm seeing:

Got MAC list [
    MacAddr([33, 33, 0, 0, 0, 1]), MacAddr([1, 0, 5e, 0, 0, 1]), MacAddr([1, 80, c2, 0, 0, 0]),
    MacAddr([1, 80, c2, 0, 0, 3]), MacAddr([1, 80, c2, 0, 0, e]), MacAddr([33, 33, ff, e7, 9c, e4]),
    MacAddr([33, 33, 0, 1, 0, 3]), MacAddr([1, 0, 5e, 0, 0, fc]), MacAddr([33, 33, ff, 0, 6, da])
]

With visible group subscriptions on:

IPv6/IPv4 Group Memberships
Interface       RefCnt Group
--------------- ------ ---------------------
lo              1      224.0.0.1
enp0s6          1      224.0.0.252
enp0s6          1      224.0.0.1
lo              1      ff02::1
lo              1      ff01::1
enp0s6          1      ff02::1:ff00:6da
enp0s6          1      ff02::1:3
enp0s6          2      ff02::1:ffe7:9ce4
enp0s6          2      ff02::1
enp0s6          1      ff01::1

So the MAC table can be decoded as:

IPv6(ff01::1 [All Nodes, Node Local], ff02::1 [All Nodes, Link Local]),
IPv4(224.0.0.1 [All Systems on Subnet]),
STP/LLDP(additional),
LLDP(additional),
LLDP(primary)/PTP,
IPv6(ff02::1:ffe7:9ce4 [solicited node addr]),
IPv6(ff02::1:3 [LLMNR]),
IPv4(224.0.0.252 [LLMNR]),
IPv6(ff02::1:ff00:6da [solicited node addr])

ip link set multicast off dev enp0s6 still leaves a bunch of active MACs including registered multicast addresses for all-/solicited-node, but those are sort of intrinsic to the stack. I think I'm happy at least that we're at parity with how cbhyve uses this machinery in pci_viona_eval_promisc.

@FelixMcFelix FelixMcFelix marked this pull request as ready for review April 29, 2026 11:28
Comment on lines -376 to -378
if let Some(ctlq) = queues.get(2) {
ctlq.set_control();
}
Copy link
Copy Markdown
Contributor Author

@FelixMcFelix FelixMcFelix Apr 29, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't know what effect putting receiveq2 in this state would have, but we never unset this flag when the control queue index updated. So in theory this would have affected ring_init/reset/kick/... for this queue?

Comment on lines +1331 to +1342
let input: migrate::VionaStateV1 = offer.take()?;

let mut state = self.inner.lock().unwrap();
state.filter = FilterState::from_bits_retain(input.filter);
state.unicast_mac_filters = input.unicast_mac_filters.into();
state.unicast_mac_filters = input.multicast_mac_filters.into();
self.set_promisc(input.promisc, &mut state).map_err(|_| {
MigrateStateError::ImportFailed(format!(
"Could not move device promisc level to {:?}.",
input.promisc
))
})
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm pretty unfamiliar with the propolis migration machinery, hopefully this does the job?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Viona control queue index is incorrect Viona links never leave VIONA_PROMISC_MULTI

1 participant