Skip to content

WIP: Refactor device managers, preparing for PCIe devices #5174

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

Draft
wants to merge 8 commits into
base: feature/pcie
Choose a base branch
from

Conversation

bchalios
Copy link
Contributor

Changes

Group all device managers in a single top-level device manager, which now becomes the owner of the MMIO and Port IO buses along with the resource allocator. Also, it gets rid of the our Bus implementation in favour of the vm-device one from Cloud Hypervisor.

Reason

PCIe devices also need to be registered to the MMIO and Port IO buses. So, it makes more sense to keep the buses outside of MMIO and PortIO device managers.

Regarding switching to using vm_devices::Bus, PCIe implementation is using it and it was quite simpler to switch our own implementation to the upstream type rather than the other way around.

License Acceptance

By submitting this pull request, I confirm that my contribution is made under
the terms of the Apache 2.0 license. For more information on following Developer
Certificate of Origin and signing off your commits, please check
CONTRIBUTING.md.

PR Checklist

  • I have read and understand CONTRIBUTING.md.
  • I have run tools/devtool checkstyle to verify that the PR passes the
    automated style checks.
  • I have described what is done in these changes, why they are needed, and
    how they are solving the problem in a clear and encompassing way.
  • I have updated any relevant documentation (both in code and in the docs)
    in the PR.
  • I have mentioned all user-facing changes in CHANGELOG.md.
  • If a specific issue led to this PR, this PR closes the issue.
  • When making API changes, I have followed the
    Runbook for Firecracker API changes.
  • I have tested all new and changed functionalities in unit tests and/or
    integration tests.
  • I have linked an issue to every new TODO.

  • This functionality cannot be added in rust-vmm.

@bchalios bchalios force-pushed the device_manager branch 4 times, most recently from 503fa5d to a7ec377 Compare April 30, 2025 15:41
@bchalios bchalios force-pushed the device_manager branch 3 times, most recently from 903e15a to 564c7c0 Compare May 2, 2025 13:05
Copy link

codecov bot commented May 2, 2025

Codecov Report

Attention: Patch coverage is 84.33121% with 123 lines in your changes missing coverage. Please review.

Project coverage is 83.23%. Comparing base (c4e3bf8) to head (dd990d4).

Files with missing lines Patch % Lines
src/vmm/src/device_manager/mmio.rs 73.23% 38 Missing ⚠️
src/vmm/src/lib.rs 13.33% 26 Missing ⚠️
src/vmm/src/devices/legacy/rtc_pl031.rs 33.33% 12 Missing ⚠️
src/vm-device/src/bus.rs 91.66% 11 Missing ⚠️
src/vm-device/src/interrupt/mod.rs 0.00% 10 Missing ⚠️
src/vmm/src/device_manager/mod.rs 96.66% 7 Missing ⚠️
src/vmm/src/builder.rs 92.72% 4 Missing ⚠️
src/vmm/src/devices/pseudo/boot_timer.rs 0.00% 4 Missing ⚠️
src/vmm/src/devices/virtio/transport/mmio.rs 55.55% 4 Missing ⚠️
src/vmm/src/persist.rs 50.00% 3 Missing ⚠️
... and 2 more
Additional details and impacted files
@@               Coverage Diff                @@
##           feature/pcie    #5174      +/-   ##
================================================
+ Coverage         82.93%   83.23%   +0.29%     
================================================
  Files               251      253       +2     
  Lines             27073    27172      +99     
================================================
+ Hits              22454    22617     +163     
+ Misses             4619     4555      -64     
Flag Coverage Δ
5.10-c5n.metal 83.54% <83.05%> (+0.16%) ⬆️
5.10-m5n.metal 83.54% <83.05%> (+0.16%) ⬆️
5.10-m6a.metal 82.77% <83.05%> (+0.17%) ⬆️
5.10-m6g.metal 79.75% <78.21%> (+0.53%) ⬆️
5.10-m6i.metal 83.53% <83.05%> (+0.15%) ⬆️
5.10-m7a.metal-48xl 82.75% <83.05%> (+0.17%) ⬆️
5.10-m7g.metal 79.75% <78.21%> (+0.53%) ⬆️
5.10-m7i.metal-24xl 83.50% <83.05%> (+0.16%) ⬆️
5.10-m7i.metal-48xl 83.50% <83.05%> (+0.16%) ⬆️
5.10-m8g.metal-24xl 79.75% <78.21%> (+0.53%) ⬆️
5.10-m8g.metal-48xl 79.75% <78.21%> (+0.53%) ⬆️
6.1-c5n.metal 83.59% <83.05%> (+0.16%) ⬆️
6.1-m5n.metal 83.58% <83.05%> (+0.16%) ⬆️
6.1-m6a.metal 82.82% <83.05%> (+0.17%) ⬆️
6.1-m6g.metal 79.75% <78.21%> (+0.53%) ⬆️
6.1-m6i.metal 83.57% <83.05%> (+0.16%) ⬆️
6.1-m7a.metal-48xl 82.80% <83.05%> (+0.18%) ⬆️
6.1-m7g.metal 79.75% <78.21%> (+0.53%) ⬆️
6.1-m7i.metal-24xl 83.60% <83.05%> (+0.16%) ⬆️
6.1-m7i.metal-48xl 83.60% <83.05%> (+0.15%) ⬆️
6.1-m8g.metal-24xl 79.75% <78.21%> (+0.53%) ⬆️
6.1-m8g.metal-48xl 79.75% <78.21%> (+0.53%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@bchalios bchalios force-pushed the device_manager branch 3 times, most recently from f84229f to e6560c9 Compare May 2, 2025 23:08
Comment on lines 321 to 322
match &self.serial {
Some(device) => {
Copy link
Contributor

Choose a reason for hiding this comment

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

This can be an assert, as the serial is registered right before this call. Otherwise maybe just remove this function and to the addition inline in the attach_legacy_devices_aarch64? (like we do for block devices)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

done

balloon.process_virtio_queues();
}
let _: Result<(), MmioError> = self.for_each_virtio_device(|virtio_type, id, device| {
let mmio_transport_locked = device.inner.lock().expect("Poisoned locked");
Copy link
Contributor

Choose a reason for hiding this comment

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

s/locked/lock/

Copy link
Contributor Author

Choose a reason for hiding this comment

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

mmio_transport_locked now is essentially a reference to the actual MMIO transport (after being locked). So... semantics.

impl MmioTransport {
pub fn bus_read(&mut self, offset: u64, data: &mut [u8]) {
impl vm_device::BusDevice for MmioTransport {
fn read(&mut self, base: u64, offset: u64, data: &mut [u8]) {
Copy link
Contributor

Choose a reason for hiding this comment

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

I see that the base arg is never used, so maybe it is worth removing it from a trait definition?
And same question about Option<Arc<Barrier>> return type in write function.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

These come from the external crate. So I assume they are used somewhere even if we don't use them today in Firecracker.

Comment on lines 168 to 171
let i8042 = Arc::new(Mutex::new(I8042Device::new(
reset_evt,
EventFd::new(libc::EFD_NONBLOCK).map_err(VmmError::EventFd)?,
)));
Copy link
Contributor

Choose a reason for hiding this comment

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

note: the I8042Device::new can be simplified by moving EventFd creation inside and asserting on it.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

done

Copy link
Contributor Author

Choose a reason for hiding this comment

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

although, I didn't assert. I'm still returning an Error. This code is in the InstanceStart API call path.

Copy link
Contributor

Choose a reason for hiding this comment

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

What are the gaps between the rust-vmm vm-devices create and this one? Is there any possibility of upstreaming these changes to the crate?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

They have diverged significantly, but I think nobody is actually using the rust-vmm one, so in theory we should be able to upstream the CH one.

})));
let serial = Arc::new(Mutex::new(BusDevice::Serial(
SerialDevice::new(Some(std::io::stdin()), SerialOut::Stdout(std::io::stdout()))
.map_err(VmmError::EventFd)?,
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: can this fail only due to EventFd?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yup, it essentially fails only when we can't create the EventFd.

Comment on lines 63 to 64
device_info: &HashMap<(DeviceType, String), MMIODeviceInfo>,
virtio_devices: Vec<&MMIODeviceInfo>,
rtc: Option<&MMIODeviceInfo>,
serial: Option<&MMIODeviceInfo>,
Copy link
Contributor

Choose a reason for hiding this comment

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

can this be simplified by passing the DeviceManager as we do in ACPI?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I tried to do that, but that breaks hopelessly unit tests. It's gonna be a mess fixing it.

@@ -615,7 +615,11 @@ fn attach_legacy_devices_aarch64(
if cmdline_contains_console {
// Make stdout non-blocking.
set_stdout_nonblocking();
let serial = setup_serial_device(event_manager)?;
Copy link
Contributor

Choose a reason for hiding this comment

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

I didn't follow why we're inlining the function here for arm but not for x86. Also, this could be moved to the earlier commit.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

At this commit, the setup_serial_device still returns the old BusDevice thingy. The serial device on ARM is a MMIO device so it is not part of BusDevice any more, because it's an MMIO device, so it temporarily diverge. We revert to unifying them again when IO devices become vm_device::Bus as well.

Comment on lines -133 to 148
#[cfg(target_arch = "x86_64")]
pub(crate) dsdt_data: Vec<u8>,
Copy link
Contributor

Choose a reason for hiding this comment

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

any reason to leave this in in ARM?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The cfg just moved above the comment.

let _: Result<(), MmioError> = self.for_each_virtio_device(|virtio_type, id, device| {
let mmio_transport_locked = device.inner.lock().expect("Poisoned locked");
let mut virtio = mmio_transport_locked.locked_device();
match *virtio_type {
Copy link
Contributor

Choose a reason for hiding this comment

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

unrelated to the scope of this PR, but I'm wondering if the "kick" can become a trait function that we call on every device, rather than having this combination of downcast and device-specific logic.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

yup, that's not such a bad idea.

Comment on lines 371 to 372
self.bus
.insert(device.clone(), device_info.addr, device_info.len)?;
Copy link
Contributor

Choose a reason for hiding this comment

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

does the "register" function no longer actually register the device with the bus?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

oh, good catch

Copy link
Contributor Author

Choose a reason for hiding this comment

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

fixed

let boot_timer = Arc::new(Mutex::new(BootTimer::new(request_ts)));

self.mmio_devices
.register_mmio_boot_timer(&mut self.resource_allocator, boot_timer)?;
Copy link
Contributor

Choose a reason for hiding this comment

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

does this not get registered with the Bus?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

fixed

Ok(serial)
}

#[cfg_attr(target_arch = "aarch64", allow(unused))]
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: why allow(unused)?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

event_manager and vcpu_exit_evt are only used by the Port IO initialization code.

@bchalios bchalios force-pushed the device_manager branch 7 times, most recently from c6e5fcd to 0a85b66 Compare May 8, 2025 13:53
bchalios and others added 8 commits May 8, 2025 16:42
Bring in the vm-device crate from CloudHypervisor. We will be using it
for adding PCIe support.

Signed-off-by: Babis Chalios <[email protected]>
We use `SerialDevice` with Stdin as the input source. Encode this in the
type so that we don't spill the generic all over the place.

Signed-off-by: Babis Chalios <[email protected]>
Co-authored-by: Egor Lazarchuk <[email protected]>
Signed-off-by: Egor Lazarchuk <[email protected]>
Signed-off-by: Babis Chalios <[email protected]>
Use the vm_device::Bus bus for all MMIO devices. This mainly to prepare
for using it for PCIe devices. Also, sepate VirtIO devices from other
MMIO devices inside the MMIODeviceManager struct. This makes iterating
over VirtIO devices since we don't need to access two data structures as
to get a reference to a VirtIO device any more.

Signed-off-by: Babis Chalios <[email protected]>
We were always constructing RTCDevice using a set of metrics that were
defined in the RTC module itself. Don't leak the metrics to other
modules. Instead, create a new() function that always constructs it the
correct way.

Signed-off-by: Babis Chalios <[email protected]>
Use the vm_device::Bus bus for PortIO devices on x86. PCIe devices will
use this as well.

Signed-off-by: Babis Chalios <[email protected]>
PCIe spec mandates that software can access the configuration space of
PCIe devices both via MMIO and Port IO accesses. As a result, PCIe
devices will need to register to both buses (on x86).

Change the organization of devices, so that MMIO and PIO device managers
do not own the buses. Instead, introduce a DeviceManager object which
holds the buses, the resource allocator and includes also all types of
device managers (at the moment MMIO, PIO and ACPI).

Signed-off-by: Babis Chalios <[email protected]>
We always create anew the keyboard interrupt event. Just create it
inside `I8042Device::new()` and return an error if that fails.

Signed-off-by: Babis Chalios <[email protected]>
To see if we the memory increase increases.

Signed-off-by: Babis Chalios <[email protected]>
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.

3 participants