vfio_platform: Add support for power_{on,off}()
Add support for VFIO_DEVICE_FEATURE_LOW_POWER_{ENTRY,EXIT} of assigned
vfio-platform devices, enabling CrosVM to control the power state of the
devices without closing their corresponding FDs. Note that this ioctl
interface is currently only supported by the latest Android kernels [1],
although the arguments reuse their vfio-pci (upstream) counterparts.
Attach vfio-platform devices on AArch64 hosts advertising support for
the ioctls to a generic DevicePowerManager, to eventually provide
support for guest-controlled PM:
[TO GUEST...] <--> DevicePowerManager <--> VfioPlatformDevice
^
|
(ioctl)
|
v
Host (VFIO)
Guard the feature behind the --vfio-platform-pm CLI flag.
[1]: https://r.android.com/c/3888027
BUG=b:460349550
TEST=tools/presubmit
Change-Id: I82c058442b8fe51a585ad7cb0b00496fd267765c
Reviewed-on: https://chromium-review.googlesource.com/c/crosvm/crosvm/+/7421204
Auto-Submit: Pierre-Clément Tosi <ptosi@google.com>
Reviewed-by: Frederick Mayle <fmayle@google.com>
Commit-Queue: Frederick Mayle <fmayle@google.com>
This commit is contained in:
parent
7de63b3862
commit
03e3e09e39
10 changed files with 88 additions and 0 deletions
|
|
@ -37,6 +37,7 @@ use devices::Bus;
|
|||
use devices::BusDeviceObj;
|
||||
use devices::BusError;
|
||||
use devices::BusType;
|
||||
use devices::DevicePowerManager;
|
||||
use devices::IrqChip;
|
||||
use devices::IrqChipAArch64;
|
||||
use devices::IrqEventSource;
|
||||
|
|
@ -764,6 +765,8 @@ impl arch::LinuxArch for AArch64 {
|
|||
.into_iter()
|
||||
.map(|(dev, jail_orig)| (*(dev.into_platform_device().unwrap()), jail_orig))
|
||||
.collect();
|
||||
// vfio-platform is currently the only backend for PM of platform devices.
|
||||
let mut dev_pm = components.vfio_platform_pm.then(DevicePowerManager::new);
|
||||
let (platform_devices, mut platform_pid_debug_label_map, dev_resources) =
|
||||
arch::sys::linux::generate_platform_bus(
|
||||
platform_devices,
|
||||
|
|
@ -773,6 +776,7 @@ impl arch::LinuxArch for AArch64 {
|
|||
&mut vm,
|
||||
#[cfg(feature = "swap")]
|
||||
swap_controller,
|
||||
&mut dev_pm,
|
||||
components.hv_cfg.protection_type,
|
||||
)
|
||||
.map_err(Error::CreatePlatformBus)?;
|
||||
|
|
|
|||
|
|
@ -446,6 +446,8 @@ pub struct VmComponents {
|
|||
any(target_os = "android", target_os = "linux")
|
||||
))]
|
||||
pub vcpu_domains: BTreeMap<usize, u32>,
|
||||
#[cfg(any(target_os = "android", target_os = "linux"))]
|
||||
pub vfio_platform_pm: bool,
|
||||
#[cfg(all(
|
||||
target_arch = "aarch64",
|
||||
any(target_os = "android", target_os = "linux")
|
||||
|
|
@ -693,6 +695,8 @@ pub enum DeviceRegistrationError {
|
|||
#[cfg(any(target_os = "android", target_os = "linux"))]
|
||||
#[error("Allocating IRQ resource: {0}")]
|
||||
AllocateIrqResource(devices::vfio::VfioError),
|
||||
#[error("failed to attach the device to its power domain: {0}")]
|
||||
AttachDevicePowerDomain(anyhow::Error),
|
||||
/// Broken pci topology
|
||||
#[error("pci topology is broken")]
|
||||
BrokenPciTopology,
|
||||
|
|
|
|||
|
|
@ -7,10 +7,12 @@ use std::sync::Arc;
|
|||
|
||||
use acpi_tables::aml::Aml;
|
||||
use base::syslog;
|
||||
use base::warn;
|
||||
use base::AsRawDescriptors;
|
||||
use base::Tube;
|
||||
use devices::Bus;
|
||||
use devices::BusDevice;
|
||||
use devices::DevicePowerManager;
|
||||
use devices::IommuDevType;
|
||||
use devices::IrqChip;
|
||||
use devices::IrqEventSource;
|
||||
|
|
@ -139,6 +141,7 @@ pub struct PlatformBusResources {
|
|||
pub regions: Vec<(u64, u64)>, // (start address, size)
|
||||
pub irqs: Vec<(u32, u32)>, // (IRQ number, flags)
|
||||
pub iommus: Vec<(IommuDevType, Option<u32>, Vec<u32>)>, // (IOMMU type, IOMMU identifier, IDs)
|
||||
pub requires_power_domain: bool,
|
||||
}
|
||||
|
||||
impl PlatformBusResources {
|
||||
|
|
@ -151,6 +154,7 @@ impl PlatformBusResources {
|
|||
regions: vec![],
|
||||
irqs: vec![],
|
||||
iommus: vec![],
|
||||
requires_power_domain: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -164,6 +168,7 @@ pub fn generate_platform_bus(
|
|||
resources: &mut SystemAllocator,
|
||||
vm: &mut impl Vm,
|
||||
#[cfg(feature = "swap")] swap_controller: &mut Option<swap::SwapController>,
|
||||
dev_pm: &mut Option<DevicePowerManager>,
|
||||
protection_type: ProtectionType,
|
||||
) -> Result<
|
||||
(
|
||||
|
|
@ -272,6 +277,24 @@ pub fn generate_platform_bus(
|
|||
.map_err(DeviceRegistrationError::MmioInsert)?;
|
||||
device_resources.regions.push((range.0, range.1));
|
||||
}
|
||||
|
||||
if let Some(ref mut pm) = dev_pm {
|
||||
let supports_power_management = arced_dev.lock().supports_power_management();
|
||||
match supports_power_management {
|
||||
Err(e) => warn!("Error while detecting PM support, ignoring: {e:#}"),
|
||||
Ok(false) => {}
|
||||
Ok(true) => {
|
||||
// As we currently restrict one device per vPD and to simplify having a PD ID
|
||||
// which the VMM, hypervisor, and guest recognize, refer to a PD using the base
|
||||
// of the MMIO of the device it contains.
|
||||
let domain_id = ranges.first().unwrap().0.try_into().unwrap();
|
||||
pm.attach(arced_dev.clone(), domain_id)
|
||||
.map_err(DeviceRegistrationError::AttachDevicePowerDomain)?;
|
||||
device_resources.requires_power_domain = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bus_dev_resources.push(device_resources);
|
||||
}
|
||||
Ok((platform_devices, pid_labels, bus_dev_resources))
|
||||
|
|
|
|||
|
|
@ -72,6 +72,20 @@ impl BusDevice for VfioPlatformDevice {
|
|||
fn write(&mut self, info: BusAccessInfo, data: &[u8]) {
|
||||
self.write_mmio(info.address, data)
|
||||
}
|
||||
|
||||
fn supports_power_management(&self) -> anyhow::Result<bool> {
|
||||
Ok(self.device.supports_pm_low_power())
|
||||
}
|
||||
|
||||
fn power_on(&mut self) -> anyhow::Result<()> {
|
||||
self.device.pm_low_power_exit()?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn power_off(&mut self) -> anyhow::Result<()> {
|
||||
self.device.pm_low_power_enter()?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Suspendable for VfioPlatformDevice {}
|
||||
|
|
|
|||
|
|
@ -131,6 +131,10 @@ pub enum VfioError {
|
|||
VfioPmLowPowerEnter(Error),
|
||||
#[error("failed to exit vfio device's low power state: {0}")]
|
||||
VfioPmLowPowerExit(Error),
|
||||
#[error("failed to probe support for VFIO low power state entry: {0}")]
|
||||
VfioProbePmLowPowerEntry(Error),
|
||||
#[error("failed to probe support for VFIO low power state exit: {0}")]
|
||||
VfioProbePmLowPowerExit(Error),
|
||||
}
|
||||
|
||||
type Result<T> = std::result::Result<T, VfioError>;
|
||||
|
|
@ -1161,6 +1165,18 @@ impl VfioDevice {
|
|||
}
|
||||
}
|
||||
|
||||
/// Probes support for VFIO LOW_POWER features.
|
||||
pub fn supports_pm_low_power(&self) -> bool {
|
||||
if self.probe_pm_low_power_entry().is_err() {
|
||||
false
|
||||
} else if self.probe_pm_low_power_exit().is_err() {
|
||||
warn!("VFIO supports LOW_POWER_ENTRY but not LOW_POWER_EXIT: ignoring feature");
|
||||
false
|
||||
} else {
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
/// enter the device's low power state
|
||||
pub fn pm_low_power_enter(&self) -> Result<()> {
|
||||
self.device_feature(VFIO_DEVICE_FEATURE_SET | VFIO_DEVICE_FEATURE_LOW_POWER_ENTRY)
|
||||
|
|
@ -1205,6 +1221,16 @@ impl VfioDevice {
|
|||
.map_err(VfioError::VfioPmLowPowerExit)
|
||||
}
|
||||
|
||||
fn probe_pm_low_power_entry(&self) -> Result<()> {
|
||||
self.device_feature(VFIO_DEVICE_FEATURE_PROBE | VFIO_DEVICE_FEATURE_LOW_POWER_ENTRY)
|
||||
.map_err(VfioError::VfioProbePmLowPowerEntry)
|
||||
}
|
||||
|
||||
fn probe_pm_low_power_exit(&self) -> Result<()> {
|
||||
self.device_feature(VFIO_DEVICE_FEATURE_PROBE | VFIO_DEVICE_FEATURE_LOW_POWER_EXIT)
|
||||
.map_err(VfioError::VfioProbePmLowPowerExit)
|
||||
}
|
||||
|
||||
fn device_feature(&self, flags: u32) -> result::Result<(), Error> {
|
||||
let mut device_feature = vec_with_array_field::<vfio_device_feature, u8>(0);
|
||||
device_feature[0].argsz = mem::size_of::<vfio_device_feature>() as u32;
|
||||
|
|
|
|||
|
|
@ -303,6 +303,7 @@ impl arch::LinuxArch for Riscv64 {
|
|||
&mut vm,
|
||||
#[cfg(feature = "swap")]
|
||||
swap_controller,
|
||||
&mut None,
|
||||
components.hv_cfg.protection_type,
|
||||
)
|
||||
.map_err(Error::CreatePlatformBus)?;
|
||||
|
|
|
|||
|
|
@ -2099,6 +2099,15 @@ pub struct RunCommand {
|
|||
/// path to sysfs of platform pass through
|
||||
pub vfio_platform: Vec<VfioOption>,
|
||||
|
||||
#[cfg(all(
|
||||
target_arch = "aarch64",
|
||||
any(target_os = "android", target_os = "linux")
|
||||
))]
|
||||
#[argh(switch)]
|
||||
/// expose the LOW_POWER_ENTRY/EXIT feature of VFIO platform devices to guests, if available
|
||||
/// (EXPERIMENTAL) The host kernel may not support the API used by CrosVM
|
||||
pub vfio_platform_pm: Option<bool>,
|
||||
|
||||
#[cfg(any(target_os = "android", target_os = "linux"))]
|
||||
#[argh(switch)]
|
||||
/// (DEPRECATED): Use --net.
|
||||
|
|
@ -2333,6 +2342,7 @@ impl TryFrom<RunCommand> for super::config::Config {
|
|||
))]
|
||||
{
|
||||
cfg.smccc_trng = cmd.smccc_trng.unwrap_or_default();
|
||||
cfg.vfio_platform_pm = cmd.vfio_platform_pm.unwrap_or_default();
|
||||
cfg.virt_cpufreq = cmd.virt_cpufreq.unwrap_or_default();
|
||||
cfg.virt_cpufreq_v2 = cmd.virt_cpufreq_upstream.unwrap_or_default();
|
||||
if cfg.virt_cpufreq && cfg.virt_cpufreq_v2 {
|
||||
|
|
|
|||
|
|
@ -771,6 +771,8 @@ pub struct Config {
|
|||
#[cfg(any(target_os = "android", target_os = "linux"))]
|
||||
pub vfio_isolate_hotplug: bool,
|
||||
#[cfg(any(target_os = "android", target_os = "linux"))]
|
||||
pub vfio_platform_pm: bool,
|
||||
#[cfg(any(target_os = "android", target_os = "linux"))]
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
pub vhost_scmi: bool,
|
||||
#[cfg(any(target_os = "android", target_os = "linux"))]
|
||||
|
|
@ -1002,6 +1004,8 @@ impl Default for Config {
|
|||
#[cfg(any(target_os = "android", target_os = "linux"))]
|
||||
vfio_isolate_hotplug: false,
|
||||
#[cfg(any(target_os = "android", target_os = "linux"))]
|
||||
vfio_platform_pm: false,
|
||||
#[cfg(any(target_os = "android", target_os = "linux"))]
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
vhost_scmi: false,
|
||||
#[cfg(any(target_os = "android", target_os = "linux"))]
|
||||
|
|
|
|||
|
|
@ -1548,6 +1548,7 @@ fn setup_vm_components(cfg: &Config) -> Result<VmComponents> {
|
|||
pci_config: cfg.pci_config,
|
||||
dynamic_power_coefficient: cfg.dynamic_power_coefficient.clone(),
|
||||
boot_cpu: cfg.boot_cpu,
|
||||
vfio_platform_pm: cfg.vfio_platform_pm,
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
virt_cpufreq_v2: cfg.virt_cpufreq_v2,
|
||||
smccc_trng: cfg.smccc_trng,
|
||||
|
|
|
|||
|
|
@ -41,6 +41,7 @@ use crate::vfio::VFIO_BASE;
|
|||
pub use crate::vfio::VFIO_DEVICE_FEATURE_LOW_POWER_ENTRY;
|
||||
pub use crate::vfio::VFIO_DEVICE_FEATURE_LOW_POWER_ENTRY_WITH_WAKEUP;
|
||||
pub use crate::vfio::VFIO_DEVICE_FEATURE_LOW_POWER_EXIT;
|
||||
pub use crate::vfio::VFIO_DEVICE_FEATURE_PROBE;
|
||||
pub use crate::vfio::VFIO_DEVICE_FEATURE_SET;
|
||||
pub use crate::vfio::VFIO_DEVICE_FLAGS_PCI;
|
||||
pub use crate::vfio::VFIO_DEVICE_FLAGS_PLATFORM;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue