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:
Wang Ningyuan 2023-06-13 19:51:42 +09:00 committed by crosvm LUCI
parent c1e471e52a
commit 1ba428004e
5 changed files with 177 additions and 1 deletions

View file

@ -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.

View file

@ -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;

View file

@ -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;

View file

@ -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,
}

View file

@ -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,
}