mirror of
https://chromium.googlesource.com/crosvm/crosvm
synced 2024-11-24 20:48:55 +00:00
devices: PCI hotplug ResourceCarrier definition
This CL defines the ResourceCarrier. A ResourceCarrier is created on the crosvm main process, take resources needed for configuring target PCI device, then sent to a separate process. In that process, the device is converted to the target type with the resources. virtio-net on linux is the only currently supported type. TEST=tools/presubmit BUG=b:243767476 Change-Id: I21edeadb6543c2cc989df7430fc734c94c5132ea Reviewed-on: https://chromium-review.googlesource.com/c/crosvm/crosvm/+/4615384 Reviewed-by: Daniel Verkamp <dverkamp@chromium.org> Reviewed-by: Ryuichiro Chiba <chibar@chromium.org> Reviewed-by: Keiichi Watanabe <keiichiw@chromium.org>
This commit is contained in:
parent
c1e471e52a
commit
1ba428004e
5 changed files with 177 additions and 1 deletions
|
@ -7,6 +7,8 @@ use base::AsRawDescriptors;
|
|||
use base::Event;
|
||||
use base::RawDescriptor;
|
||||
use base::Result;
|
||||
use serde::Deserialize;
|
||||
use serde::Serialize;
|
||||
|
||||
/// A structure suitable for implementing edge triggered interrupts in device backends.
|
||||
pub struct IrqEdgeEvent(Event);
|
||||
|
@ -45,6 +47,7 @@ impl IrqEdgeEvent {
|
|||
/// the interrupt with an End of Interrupt (EOI) command, the IRQ chip will signal the resample
|
||||
/// event. Each time the resample event is signalled, the device should re-check its state and call
|
||||
/// [`IrqLevelEvent::trigger()`] again if the interrupt should still be asserted.
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub struct IrqLevelEvent {
|
||||
/// An event used by the device backend to signal hypervisor/VM about data or new unit
|
||||
/// of work being available.
|
||||
|
|
|
@ -107,8 +107,14 @@ pub use self::pci::BarRange;
|
|||
pub use self::pci::CrosvmDeviceId;
|
||||
#[cfg(feature = "pci-hotplug")]
|
||||
pub use self::pci::HotPluggable;
|
||||
#[cfg(feature = "pci-hotplug")]
|
||||
pub use self::pci::IntxParameter;
|
||||
#[cfg(feature = "pci-hotplug")]
|
||||
pub use self::pci::NetResourceCarrier;
|
||||
pub use self::pci::PciAddress;
|
||||
pub use self::pci::PciAddressError;
|
||||
pub use self::pci::PciBarConfiguration;
|
||||
pub use self::pci::PciBarIndex;
|
||||
pub use self::pci::PciBus;
|
||||
pub use self::pci::PciClassCode;
|
||||
pub use self::pci::PciConfigIo;
|
||||
|
@ -120,6 +126,9 @@ pub use self::pci::PciRoot;
|
|||
pub use self::pci::PciRootCommand;
|
||||
pub use self::pci::PciVirtualConfigMmio;
|
||||
pub use self::pci::PreferredIrq;
|
||||
#[cfg(feature = "pci-hotplug")]
|
||||
pub use self::pci::ResourceCarrier;
|
||||
|
||||
pub use self::pci::StubPciDevice;
|
||||
pub use self::pci::StubPciParameters;
|
||||
pub use self::pflash::Pflash;
|
||||
|
|
|
@ -78,6 +78,12 @@ pub use self::pci_device::PciDevice;
|
|||
pub use self::pci_device::PreferredIrq;
|
||||
#[cfg(feature = "pci-hotplug")]
|
||||
pub use self::pci_hotplug::HotPluggable;
|
||||
#[cfg(feature = "pci-hotplug")]
|
||||
pub use self::pci_hotplug::IntxParameter;
|
||||
#[cfg(feature = "pci-hotplug")]
|
||||
pub use self::pci_hotplug::NetResourceCarrier;
|
||||
#[cfg(feature = "pci-hotplug")]
|
||||
pub use self::pci_hotplug::ResourceCarrier;
|
||||
pub use self::pci_root::PciConfigIo;
|
||||
pub use self::pci_root::PciConfigMmio;
|
||||
pub use self::pci_root::PciRoot;
|
||||
|
|
|
@ -2,16 +2,73 @@
|
|||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
//! Trait definitions for PCI hotplug.
|
||||
//! Trait definitions and implementations for PCI hotplug.
|
||||
|
||||
#![deny(missing_docs)]
|
||||
|
||||
use base::AsRawDescriptor;
|
||||
use base::AsRawDescriptors;
|
||||
use base::RawDescriptor;
|
||||
use base::Tube;
|
||||
use resources::Alloc;
|
||||
use serde::Deserialize;
|
||||
use serde::Serialize;
|
||||
use vm_control::api::VmMemoryClient;
|
||||
|
||||
use crate::virtio::NetParameters;
|
||||
use crate::IrqLevelEvent;
|
||||
use crate::PciAddress;
|
||||
use crate::PciDevice;
|
||||
use crate::PciDeviceError;
|
||||
use crate::PciInterruptPin;
|
||||
|
||||
pub type Result<T> = std::result::Result<T, PciDeviceError>;
|
||||
|
||||
/// A ResourceCarrier moves resources for PCI device across process boundary.
|
||||
///
|
||||
/// ResourceCarrier can be sent across processes using De/Serialize. All the variants shall be able
|
||||
/// to convert into a HotPlugPluggable device.
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub enum ResourceCarrier {
|
||||
/// virtio-net device.
|
||||
VirtioNet(NetResourceCarrier),
|
||||
}
|
||||
|
||||
impl ResourceCarrier {
|
||||
/// Returns debug label for the target device.
|
||||
pub fn debug_label(&self) -> String {
|
||||
match self {
|
||||
ResourceCarrier::VirtioNet(c) => c.debug_label(),
|
||||
}
|
||||
}
|
||||
|
||||
/// A vector of device-specific file descriptors that must be kept open
|
||||
/// after jailing. Must be called before the process is jailed.
|
||||
pub fn keep_rds(&self) -> Vec<RawDescriptor> {
|
||||
match self {
|
||||
ResourceCarrier::VirtioNet(c) => c.keep_rds(),
|
||||
}
|
||||
}
|
||||
/// Allocate the preferred address to the device.
|
||||
pub fn allocate_address(
|
||||
&mut self,
|
||||
preferred_address: PciAddress,
|
||||
resources: &mut resources::SystemAllocator,
|
||||
) -> Result<()> {
|
||||
match self {
|
||||
ResourceCarrier::VirtioNet(c) => c.allocate_address(preferred_address, resources),
|
||||
}
|
||||
}
|
||||
/// Assign a legacy PCI IRQ to this device.
|
||||
/// The device may write to `irq_evt` to trigger an interrupt.
|
||||
/// When `irq_resample_evt` is signaled, the device should re-assert `irq_evt` if necessary.
|
||||
pub fn assign_irq(&mut self, irq_evt: IrqLevelEvent, pin: PciInterruptPin, irq_num: u32) {
|
||||
match self {
|
||||
ResourceCarrier::VirtioNet(c) => c.assign_irq(irq_evt, pin, irq_num),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Additional requirements for a PciDevice to support hotplug.
|
||||
/// A hotplug device can be configured without access to the SystemAllocator.
|
||||
pub trait HotPluggable: PciDevice {
|
||||
|
@ -38,3 +95,101 @@ impl<T: HotPluggable + ?Sized> HotPluggable for Box<T> {
|
|||
(**self).configure_device_bars()
|
||||
}
|
||||
}
|
||||
|
||||
/// A NetResourceCarrier is a ResourceCarrier specialization for virtio-net devices.
|
||||
///
|
||||
/// TODO(b/289155315): make members private.
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub struct NetResourceCarrier {
|
||||
/// NetParameters for constructing tap device
|
||||
pub net_param: NetParameters,
|
||||
/// msi_device_tube for VirtioPciDevice constructor
|
||||
pub msi_device_tube: Tube,
|
||||
/// ioevent_vm_memory_client for VirtioPciDevice constructor
|
||||
pub ioevent_vm_memory_client: VmMemoryClient,
|
||||
/// pci_address for the hotplugged device
|
||||
pub pci_address: Option<PciAddress>,
|
||||
/// intx_parameter for assign_irq
|
||||
pub intx_parameter: Option<IntxParameter>,
|
||||
}
|
||||
|
||||
impl NetResourceCarrier {
|
||||
///Constructs NetResourceCarrier.
|
||||
pub fn new(
|
||||
net_param: NetParameters,
|
||||
msi_device_tube: Tube,
|
||||
ioevent_vm_memory_client: VmMemoryClient,
|
||||
) -> Self {
|
||||
Self {
|
||||
net_param,
|
||||
msi_device_tube,
|
||||
ioevent_vm_memory_client,
|
||||
pci_address: None,
|
||||
intx_parameter: None,
|
||||
}
|
||||
}
|
||||
|
||||
fn debug_label(&self) -> String {
|
||||
"virtio-net".to_owned()
|
||||
}
|
||||
|
||||
fn keep_rds(&self) -> Vec<RawDescriptor> {
|
||||
let mut keep_rds = vec![
|
||||
self.msi_device_tube.as_raw_descriptor(),
|
||||
self.ioevent_vm_memory_client.as_raw_descriptor(),
|
||||
];
|
||||
if let Some(intx_parameter) = &self.intx_parameter {
|
||||
keep_rds.extend(intx_parameter.irq_evt.as_raw_descriptors());
|
||||
}
|
||||
keep_rds
|
||||
}
|
||||
|
||||
fn allocate_address(
|
||||
&mut self,
|
||||
preferred_address: PciAddress,
|
||||
resources: &mut resources::SystemAllocator,
|
||||
) -> Result<()> {
|
||||
match self.pci_address {
|
||||
None => {
|
||||
if resources.reserve_pci(
|
||||
Alloc::PciBar {
|
||||
bus: preferred_address.bus,
|
||||
dev: preferred_address.dev,
|
||||
func: preferred_address.func,
|
||||
bar: 0,
|
||||
},
|
||||
self.debug_label(),
|
||||
) {
|
||||
self.pci_address = Some(preferred_address);
|
||||
} else {
|
||||
return Err(PciDeviceError::PciAllocationFailed);
|
||||
}
|
||||
}
|
||||
Some(pci_address) => {
|
||||
if pci_address != preferred_address {
|
||||
return Err(PciDeviceError::PciAllocationFailed);
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn assign_irq(&mut self, irq_evt: IrqLevelEvent, pin: PciInterruptPin, irq_num: u32) {
|
||||
self.intx_parameter = Some(IntxParameter {
|
||||
irq_evt,
|
||||
pin,
|
||||
irq_num,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/// Parameters for legacy INTx interrrupt.
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub struct IntxParameter {
|
||||
/// interrupt level event
|
||||
pub irq_evt: IrqLevelEvent,
|
||||
/// INTx interrupt pin
|
||||
pub pin: PciInterruptPin,
|
||||
/// irq num
|
||||
pub irq_num: u32,
|
||||
}
|
||||
|
|
|
@ -13,6 +13,8 @@ use base::TubeError;
|
|||
use hypervisor::Datamatch;
|
||||
use remain::sorted;
|
||||
use resources::Alloc;
|
||||
use serde::Deserialize;
|
||||
use serde::Serialize;
|
||||
use thiserror::Error;
|
||||
use vm_memory::GuestAddress;
|
||||
|
||||
|
@ -38,6 +40,7 @@ pub enum ApiClientError {
|
|||
|
||||
pub type Result<T> = std::result::Result<T, ApiClientError>;
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub struct VmMemoryClient {
|
||||
tube: Tube,
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue