-
Notifications
You must be signed in to change notification settings - Fork 0
interrupt: Initial interrupt manager traits #1
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
Conversation
andreeaflorescu
left a comment
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.
Can I add a commit on top of your commit with some other fixes?
| /// Data type to store an interrupt source identifier. | ||
| pub type InterruptIndex = u32; | ||
|
|
||
| /// Data type to store an interrupt source type. |
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.
It is going to be tricky to handle the interrupt type. We might want to explain here why we want it to be a u32 instead of an enum and how do we expect this to be used.
src/interrupt/mod.rs
Outdated
| /// Data type to store an interrupt event. | ||
| pub type InterruptEvent = u32; | ||
|
|
||
| pub const IRQ_TRIGGERED: InterruptEvent = 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.
What were these 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.
Leftovers, they need to be removed.
src/interrupt/mod.rs
Outdated
| /// # Arguments | ||
| /// * index: sub-index into the group. | ||
| /// * config: configuration data for the interrupt source. | ||
| fn modify(&self, index: InterruptIndex, config: &MsiIrqSourceConfig) -> Result<()>; |
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: rename to update?
src/interrupt/mod.rs
Outdated
| fn enable(&self) -> Result<()>; | ||
|
|
||
| /// Disable the interrupt sources in the group to generate interrupts. | ||
| fn disable(&self) -> Result<()>; |
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.
Can you pls remind me why were these needed?
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.
Because the guest driver can decide to disable and re-enable interrupts.
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 guest kernel may choose different interrupt mode. For PCI device, guest kernel may trigger following transitions:
- interrupt disabled -> interrupt enabled
- legacy interrupt -> PCI MSI interrupt
- PCI MSI interrupt -> legacy interrupt
- legacy interrupt -> PCI MSIx interrupt
- PCI MSIx interrupt -> legacy interrupt.
On mode transitation,disable()is called on the old interrupt group object andenable()is called on the new interrupt group.
src/interrupt/mod.rs
Outdated
| /// An interrupt notifier allows for external components and processes | ||
| /// to inject interrupts into a guest, by writing to the file returned | ||
| /// by this method. | ||
| fn notifier(&self, index: InterruptIndex) -> Option<File>; |
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.
Not sure if File is the best option here. Maybe we can have something that implement the traits needed (write, read?). I don't know which traits are needed actually. I'll experiment with this a bit.
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.
We should also have a default implementation that returns none as this should not be implemented by non vhost-user backends.
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.
Not sure if File is the best option here. Maybe we can have something that implement the traits needed (write, read?). I don't know which traits are needed actually. I'll experiment with this a bit.
It's not really about traits that need to be implemented but about the fact that it can be made into a raw fd, to pass it down to the vhost-user backend process.
A File is something that would work (That's the inner type for Eventfd...) and is the least common denominator I found between Windows and Linux.
233af25 to
05a5f7c
Compare
|
Thanks for putting things together, I will compare this PR with my original implementation. |
Is the expectation to use
I looked at the device implementations in crosvm and firecracker and it looks like Basically, you would have a VirtioInterrupt in pub enum InterruptError {
SingalUsedQueueFailed(io::Error),
SignalConfigurationChangedFailed(io::Error),
}
pub trait VirtioInterrupt {
// The `vector` parameter is only used with MSIx
fn signal_used_queue(&self, vector: u16) -> InterruptError;
fn signal_configuration_change(&self, vector: u16) -> InterruptError;
fn ack(&self, bit_mask: usize) {
// With MSIx this is not used so the default here is no-op.
}
}
I think the interrupt controller should not be defined in vm-device and should be separate because we have different use cases and I don't think we can merge them into something that works/is good for both Firecracker and Cloud Hypervisor/Dragonball. |
|
When design the interrupt subsystem, we need to think about both the abstraction and the way to use the interrupt abstraction. instead of For a device supporting both multi-queues and MSI, the code is almost the same: For PCI and MMIO device with MSI, we need a DeviceInterruptManager to manage the backend device's interrupt mode. Current the DeviceInterruptManager is defined as: And take firecracker/device/src/virtio/net.rs as an example, we have simplified it as below in dragonball: |
It's a common trait for all devices, but live-upgrading has dependency on InterruptSourceGroup::{interrupt_type,len,base}. Actually all of InterruptSourceGroup::{interrupt_type,len,base} are simple accessors.
It's a common design pattern for a hardware device to have a interrupt status register. When the device want to signal an interrupt, it does:
Yes , we have originally put DeviceInterruptManager in file dragonball/device/src/interrupt_manager.rs, instead of put it into the vm-device trait. |
So your live upgrade workflow, for a given device, would be to get a serialized
Do you agree self.config
.trigger(0)
.map_err(|err| DeviceError::IOError(err))while for a virtio devices, using legacy interrupt it would be slightly more complex: self.interrupt_status
.fetch_or(VIRTIO_MMIO_INT_VRING as usize, Ordering::SeqCst);
self.config
.trigger(0)
.map_err(|err| DeviceError::IOError(err))The way the interrupt is actually delivered is abstracted from the device implementation in both cases. I agree you could argue that setting the interrupt_status is part of the delivery, but what I'm saying is that it's specific to virtio+legacy afaik.
Looking back at it a few weeks after our original discussion, I now tend to agree with that although the |
Yes, these three methods are getters. But information is not gotten from firmware or guest OS, it's defined by the device interrupt working mode and resource allocation.
It's true that ack() and flags are only used by legacy irqs. But ack() and flags are not limited to virtio + legacy(firecracker works in this way), it's almost for all legacy device. Let's take some examples:
And for firecracker project, So abstracting away the interrupt status does help to reduce duplicated code.
That's the really key difference. instead of |
Use u64 for guest memory address type since this can make vm-device independent from on vm-memory. Signed-off-by: Jing Liu <jing2.liu@linux.intel.com>
Change DeviceIo interface parameters to base and offset, so that devices with several IO ranges can use it to locate right range. Signed-off-by: Jing Liu <jing2.liu@linux.intel.com>
As suggested from rust-vmm#18 (comment) Suggested-by: Andreea Florescu <fandree@amazon.com> Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
Signed-off-by: Jing Liu <jing2.liu@linux.intel.com>
In order to get a real multiple threads handling to enhance performance, the DeviceIo trait need adopt interior mutability pattern. Signed-off-by: Jing Liu <jing2.liu@linux.intel.com>
Based on resources definition, this adds device IO manager to manage all devices IO ranges. Signed-off-by: Jing Liu <jing2.liu@linux.intel.com>
IO manager is responsible for handling IO operation when VMExit. It works out the specific device according to the address range and hand over to DeviceIo trait. Signed-off-by: Jing Liu <jing2.liu@linux.intel.com>
Unit tests for IO manager. Signed-off-by: Jing Liu <jing2.liu@linux.intel.com>
Append missing tests for resources and fix some typo. Signed-off-by: Jing Liu <jing2.liu@linux.intel.com>
You're right, they're basically defined by the VMM or a device. What I'm trying to say here is that the entity (VMM or device) that creates the interrupt source group on the source VMM is responsible to recreate it on the destination VMM, and since it was able to create it on the source side it should pass the information needed for creation on the target side.
Yes, I think I agree with that. As a matter of fact, in Cloud Hypervisor we have transitioned to a InterruptSourceGroup and InterruptManager, see cloud-hypervisor/cloud-hypervisor#623 and cloud-hypervisor/cloud-hypervisor#637 The
Overall I feel this trait is a reasonable compromise between our 2 proposals. In practice, this simplifies a lot our code base and how we manage interrupts. It's a significant win in terms of abstractions and I think we could all benefit from it. @jiangliu @andreeaflorescu Please let me know what you think. |
I'm OK with the proposal, and I could add another layer over interrupt source group to solve the legacy interrupt flags issue. If we are all agree with this, I will update the PR accordingly. But I still haven't gotten the point why we can't support base()/len()/type() getters on the interrupt source group trait:( |
Cool. Let's see what @andreeaflorescu thinks.
I'm not sure we absolutely need it in this form. If what we're looking for is a way to serialize an interrupt source group, then we may want to have a specific method for that. And since I think an interrupt source group creator should also be the one serializing it for migrating/upgrading, it should have all the information it needs to do so (because it created it). I guess we'll hit that issue with Cloud hypervisor soon, so by then we can propose to extend the trait accordingly. For now I'd leave those getters out of it. |
|
Hey, I am a bit busy on another thread now. I can take a look at this tomorrow or on Monday. Is that okay with you? |
Of course it's fine. |
My concern with having an enum was not necessarily related to breaking compatibility. I was more thinking about a situation in which you have a custom, non-rust-vmm implementation of InterruptSourceGroup. Say that that implementation is actually of type LegacyInterrupt, but the implementation is custom and cannot be pushed to rust-vmm. Not having a type would force you to properly use generics and not actual types. If we can't have another workaround for it, we should just pay extra attention into how this interface is used in rust-vmm crates. Should we submit a PR to vm-device with the proposed changes? |
Let me update that PR here, and then we could morph @jiangliu 's pending PR into it? I find this would be less confusing. |
|
What does morph mean? :))) |
Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
Wrapped lines at 100 chars. Added more details about the InterruptType. Signed-off-by: Andreea Florescu <fandree@amazon.com>
In the InterruptSourceGroup trait the only method that is going to be implemented for every interrupt type is the trigger method. We can make it easier for simple VMMs (like the ones using only legacy IRQs) to implement this trait by provided no-op implementation for all the other trait methods. Signed-off-by: Andreea Florescu <fandree@amazon.com>
Most methods in the traits need to mutate the state. The inital idea was that save mutability can be achieved by wrapping the trait implementers in a Mutex. I find this to be misleading when reading the code. The proposal is to use mut where needed unless there is a good reason not to. Signed-off-by: Andreea Florescu <fandree@amazon.com>
We extend the trait to support the mask/unmask operations. We also merge the MSI interrupt source group trait into the main one, which leads to using interrupt source type and config enums instead of type wrappers. Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
fbb2d7c to
f886e49
Compare
Transform :-)
Fine with me, but then we should close @jiangliu PR to avoid having 2 PRs, from mostly the same people, about the same topic. |
|
I updated this current PR with the discussed/agreed upon modifications. |
|
@jiangliu are you okay with that? The problem is that PR already has 100 comments on it and they are not referring to the code we want to merge any more. |
Sure:)
|
This current PR (#1) already contains the changes from Cloud Hypervisor, this step would not be needed.
We should squash this one and make it a single commit PR, if that's what you mean.
That sounds fine to me, yes. |
|
Cool. Let's close the existing PR and open a new one with these changes. I'll try to get some people from my team to review it as well. |
When supporting VFIO later, we may have a code snippet as /// Type of interrupt source.
#[derive(Clone)]
pub enum InterruptSourceType {
#[cfg(feature = "legacy_irq")]
/// Legacy Pin-based Interrupt.
/// On x86 platforms, legacy interrupts are routed through 8259 PICs and/or IOAPICs.
LegacyIrq,
#[cfg(feature = "pci_msi_irq")]
/// Message Signaled Interrupt (PCI MSI/PCI MSIx).
/// Some non-PCI devices (like HPET on x86) make use of generic MSI in platform specific ways.
PciMsiIrq,
#[cfg(feature = "vfio_msi_irq")]
/// Message Signalled Interrupt for PCI MSI/PCI MSIx based VFIO devices.
VfioMsiIrq(Arc<VfioDevice>, u32),
}So it would be better to keep it as an enum so we could attach more data to Irq type. |
That's actually what we included in the last version of the proposal. The idea, since I believe we all seem to agree with this last proposal, would be to replace rust-vmm#21 with the equivalent of PR #3. We should merge the interface first (based, again, on PR #3) and then discuss about about a KVM implementation through a follow up PR. |
|
PR #3 supersedes that one. |
Signed-off-by: Samuel Ortiz sameo@linux.intel.com