-
Notifications
You must be signed in to change notification settings - Fork 157
virtio: switch to bitfield_struct #2207
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull Request Overview
This PR moves virtio flags from simple integer values to structured bitfield types for improved type safety and readability. The changes replace raw u64 device features with a new VirtioDeviceFeatures
struct that uses bitfield_struct for organized feature management.
- Replace raw u64 features with structured
VirtioDeviceFeatures
type - Introduce bitfield structs for VirtioDeviceStatus and feature banks
- Update all virtio device implementations to use the new structured features
Reviewed Changes
Copilot reviewed 11 out of 11 changed files in this pull request and generated 3 comments.
Show a summary per file
File | Description |
---|---|
vm/devices/virtio/virtiofs/src/virtio.rs | Updates VirtioFS device to use VirtioDeviceFeatures::new() instead of 0 |
vm/devices/virtio/virtio_serial/src/lib.rs | Converts feature constants from u64 to u32 and uses structured features |
vm/devices/virtio/virtio_pmem/src/lib.rs | Updates PMEM device to use VirtioDeviceFeatures::new() |
vm/devices/virtio/virtio_p9/src/lib.rs | Converts P9 device to use structured features with bank system |
vm/devices/virtio/virtio_net/src/lib.rs | Major refactor of network features to use bitfield structs |
vm/devices/virtio/virtio/src/transport/pci.rs | Updates PCI transport to use structured device status and features |
vm/devices/virtio/virtio/src/transport/mmio.rs | Updates MMIO transport to use structured features and status |
vm/devices/virtio/virtio/src/tests.rs | Extensive test updates to use new structured feature types |
vm/devices/virtio/virtio/src/spec.rs | Adds bitfield struct definitions for VirtioDeviceFeatures and VirtioDeviceStatus |
vm/devices/virtio/virtio/src/queue.rs | Updates queue core to accept VirtioDeviceFeatures instead of u64 |
vm/devices/virtio/virtio/src/common.rs | Updates common types and traits to use VirtioDeviceFeatures |
// _reserved: bool, | ||
// pub guest_hdrlen: bool, | ||
// pub rss: bool, | ||
// pub rsc_ext: bool, | ||
// pub standby: bool, | ||
// pub speed_duplex: bool, | ||
// } | ||
// #[bitfield(u32)] | ||
// #[derive(IntoBytes, Immutable, KnownLayout, FromBytes)] | ||
// struct NetworkFeaturesBank2 { | ||
// pub rss_context: bool // VIRTIO_NET_F_RSS_CONTEXT(64) | ||
// pub guest_udp_tunnel_gso: bool, | ||
// pub guest_udp_tunnel_gso_csum: bool, | ||
// pub host_udp_tunnel_gso: bool, | ||
// pub host_udp_tunnel_gso_csum: bool, | ||
// pub out_net_header: bool, | ||
// pub ipsec: bool, | ||
// } |
Copilot
AI
Oct 18, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[nitpick] This large block of commented-out code should be removed if it's not needed, or converted to proper documentation comments (///) with a TODO if future implementation is planned.
// _reserved: bool, | |
// pub guest_hdrlen: bool, | |
// pub rss: bool, | |
// pub rsc_ext: bool, | |
// pub standby: bool, | |
// pub speed_duplex: bool, | |
// } | |
// #[bitfield(u32)] | |
// #[derive(IntoBytes, Immutable, KnownLayout, FromBytes)] | |
// struct NetworkFeaturesBank2 { | |
// pub rss_context: bool // VIRTIO_NET_F_RSS_CONTEXT(64) | |
// pub guest_udp_tunnel_gso: bool, | |
// pub guest_udp_tunnel_gso_csum: bool, | |
// pub host_udp_tunnel_gso: bool, | |
// pub host_udp_tunnel_gso_csum: bool, | |
// pub out_net_header: bool, | |
// pub ipsec: bool, | |
// } | |
/// TODO: Implement additional network feature banks if needed in the future. | |
/// | |
/// The following structs are placeholders for future expansion of network features: | |
/// - `NetworkFeaturesBank1` | |
/// - `NetworkFeaturesBank2` | |
/// | |
/// Refer to the Virtio specification for details on feature flags. |
Copilot uses AI. Check for mistakes.
pub fn set_bank(&mut self, index: usize, val: u32) { | ||
if self.0.len() <= index { | ||
self.0.resize(index + 1, 0); | ||
} | ||
self.0[index] = val; | ||
} |
Copilot
AI
Oct 18, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[nitpick] The resize
operation could be inefficient for large indices. Consider adding bounds checking or documentation about expected index ranges to prevent excessive memory allocation.
Copilot uses AI. Check for mistakes.
} else { | ||
self.driver_feature | ||
.set_bank(bank, val & self.device_feature.bank(bank)); | ||
} |
Copilot
AI
Oct 18, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[nitpick] The conditional logic could be simplified by inverting the condition and using early return or guard clause pattern for better readability.
} else { | |
self.driver_feature | |
.set_bank(bank, val & self.device_feature.bank(bank)); | |
} | |
return; | |
} | |
self.driver_feature | |
.set_bank(bank, val & self.device_feature.bank(bank)); |
Copilot uses AI. Check for mistakes.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Couple minor nits, but I don't think they are required to commit this.
Overall this looks good to me. I'd like to see someone more familiar with this code sign off in case of gremlins, but can take another look if other reviewers aren't able to get back to this.
let features = VirtioDeviceFeatures::new().with_bank( | ||
0, | ||
if self.config.max_ports > 1 { | ||
VIRTIO_CONSOLE_F_MULTIPORT | ||
} else { | ||
0 | ||
}, | ||
); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: why not this:
let features = VirtioDeviceFeatures::new().with_bank( | |
0, | |
if self.config.max_ports > 1 { | |
VIRTIO_CONSOLE_F_MULTIPORT | |
} else { | |
0 | |
}, | |
); | |
let features = VirtioDeviceFeatures::new().with_bank0(VirtioDeviceFeaturesBank0::new().with_device_specific( | |
if self.config.max_ports > 1 { | |
VIRTIO_CONSOLE_F_MULTIPORT | |
} else { | |
0 | |
},) | |
); |
// if multi-port is set, start the control port thread | ||
VirtioState::Running(run_state) => { | ||
if run_state.features & VIRTIO_CONSOLE_F_MULTIPORT != 0 { | ||
if run_state.features.bank(0) & VIRTIO_CONSOLE_F_MULTIPORT != 0 { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit:
if run_state.features.bank(0) & VIRTIO_CONSOLE_F_MULTIPORT != 0 { | |
if run_state.features.bank0().device_specific() & VIRTIO_CONSOLE_F_MULTIPORT != 0 { |
pub fn with_bank(mut self, index: usize, val: u32) -> Self { | ||
self.set_bank(index, val); | ||
self | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we need the non-typed version, or can everything be done with the typed helpers below?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The spec supports up to 256 "banks" (8192 feature bits), though currently I think the high-water mark is the network device with bit 70. We could define explicit accessors for three banks and would be unlikely to hit any problems for a number of years, but I was just going with the spirit of "device specific number of interesting bits"
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Gotcha. We should definitely update most of the uses in this PR to use the typed versions instead of hardcoding with_bank(0,
or similar though.
pub const VIRTIO_FAILED: u32 = 0x80; | ||
#[bitfield(u32)] | ||
#[derive(IntoBytes, Immutable, KnownLayout, FromBytes)] | ||
pub struct VirtioDeviceFeaturesBank0 { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should these bank types be Copy too?
pub struct VirtioDeviceFeatures(Vec<u32>); | ||
impl VirtioDeviceFeatures { | ||
pub fn new() -> Self { | ||
Self(Vec::with_capacity(2)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do you only want capacity? Or do you want to actually have two zeros in the vec? Should this be a statically sized array?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These feature bits are accessed by setting a selector index and accessing the feature data location. For example, if you set the selector index to 0x10 and access the data you will get bits 0x200-0x21f. In practice though almost every device uses just the first 64 bits, which is why the capacity is set to two. If the driver requests index 0x10 we don't normally need to store any information -- we can just return '0' / no bits set. There is also an address for the driver to write its own bits, but it happens that the driver can also only request/set bits that the device supports, so if they try to write using index 0x10, since the device normally has no bits defined there, it is filtered back to '0', which again we don't actually have to store any value to represent.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we consider some sort of sparse storage solution here, or is a Vec the right data structure? Like if a device does actually define a bank 10 does that definitely mean it's going to use banks 0-9? If we expect every device to use banks 0 & 1 maybe they should be hardcoded in as separate fields, and then any additional banks could be in a heap-allocated structure? Just spitballing.
Move virtio flags to a bitfield_struct for easier usage