From ec820fc4ae2d536ec22aa32913cc0e7be048f9c9 Mon Sep 17 00:00:00 2001 From: Victor Ding Date: Thu, 16 Dec 2021 13:21:36 +0000 Subject: [PATCH] vfio_pci: add support for runtime power management Allow devices to perform runtime power management via VFIO_DEVICE_FEATURE IOCTL. BUG=b:194390621 TEST=host's VFIO_DEVICE_FEATURE can be triggered from guest Change-Id: Id649a1e5fe317dfb315f841a152ec8b81fab8e7c Reviewed-on: https://chromium-review.googlesource.com/c/crosvm/crosvm/+/3822004 Reviewed-by: Daniel Verkamp Commit-Queue: Victor Ding --- devices/src/pci/vfio_pci.rs | 24 +++++++++++++++++------ devices/src/vfio.rs | 32 +++++++++++++++++++++++++++++++ seccomp/x86_64/vfio_device.policy | 7 +++++-- vfio_sys/src/lib.rs | 1 + vfio_sys/src/vfio.rs | 9 +++++++++ 5 files changed, 65 insertions(+), 8 deletions(-) diff --git a/devices/src/pci/vfio_pci.rs b/devices/src/pci/vfio_pci.rs index ec3aad41ae..7ab97a56f8 100644 --- a/devices/src/pci/vfio_pci.rs +++ b/devices/src/pci/vfio_pci.rs @@ -2026,12 +2026,24 @@ impl PciDevice for VfioPciDevice { 0 } - fn write_virtual_config_register(&mut self, reg_idx: usize, _value: u32) { - warn!( - "{} write unsupported register {}", - self.debug_label(), - reg_idx - ) + fn write_virtual_config_register(&mut self, reg_idx: usize, value: u32) { + match reg_idx { + 0 => { + match value { + 0 => { + let _ = self.device.pm_low_power_enter(); + } + _ => { + let _ = self.device.pm_low_power_exit(); + } + }; + } + _ => warn!( + "{} write unsupported register {}", + self.debug_label(), + reg_idx + ), + }; } fn read_bar(&mut self, addr: u64, data: &mut [u8]) { diff --git a/devices/src/vfio.rs b/devices/src/vfio.rs index 7a40925410..1ec85c2431 100644 --- a/devices/src/vfio.rs +++ b/devices/src/vfio.rs @@ -104,6 +104,10 @@ pub enum VfioError { VfioIrqMask(Error), #[error("failed to unmask vfio deviece's irq: {0}")] VfioIrqUnmask(Error), + #[error("failed to enter vfio deviece's low power state: {0}")] + VfioPmLowPowerEnter(Error), + #[error("failed to exit vfio deviece's low power state: {0}")] + VfioPmLowPowerExit(Error), #[error("container dones't support VfioType1V2 IOMMU driver type")] VfioType1V2, } @@ -849,6 +853,34 @@ impl VfioDevice { &self.name } + /// enter the device's low power state + pub fn pm_low_power_enter(&self) -> Result<()> { + let mut device_feature = vec_with_array_field::(0); + device_feature[0].argsz = mem::size_of::() as u32; + device_feature[0].flags = VFIO_DEVICE_FEATURE_SET | VFIO_DEVICE_FEATURE_LOW_POWER_ENTRY; + // Safe as we are the owner of self and power_management which are valid value + let ret = unsafe { ioctl_with_ref(&self.dev, VFIO_DEVICE_FEATURE(), &device_feature[0]) }; + if ret < 0 { + Err(VfioError::VfioPmLowPowerEnter(get_error())) + } else { + Ok(()) + } + } + + /// exit the device's low power state + pub fn pm_low_power_exit(&self) -> Result<()> { + let mut device_feature = vec_with_array_field::(0); + device_feature[0].argsz = mem::size_of::() as u32; + device_feature[0].flags = VFIO_DEVICE_FEATURE_SET | VFIO_DEVICE_FEATURE_LOW_POWER_EXIT; + // Safe as we are the owner of self and power_management which are valid value + let ret = unsafe { ioctl_with_ref(&self.dev, VFIO_DEVICE_FEATURE(), &device_feature[0]) }; + if ret < 0 { + Err(VfioError::VfioPmLowPowerExit(get_error())) + } else { + Ok(()) + } + } + /// Enable vfio device's irq and associate Irqfd Event with device. /// When MSIx is enabled, multi vectors will be supported, and vectors starting from subindex to subindex + /// descriptors length will be assigned with irqfd in the descriptors array. diff --git a/seccomp/x86_64/vfio_device.policy b/seccomp/x86_64/vfio_device.policy index 34ae00463e..4484e1587a 100644 --- a/seccomp/x86_64/vfio_device.policy +++ b/seccomp/x86_64/vfio_device.policy @@ -4,8 +4,11 @@ @include /usr/share/policy/crosvm/common_device.policy -# VFIO_DEVICE_SET_IRQS, VFIO_IOMMU_MAP/UNMAP_DMA -ioctl: arg1 == 0x3B6E || arg1 == 0x3B71 || arg1 == 0x3B72 +# 0x3B6E: VFIO_DEVICE_SET_IRQS +# 0x3B71: VFIO_IOMMU_MAP_DMA +# 0x3B72: VFIO_IOMMU_UNMAP_DMA +# 0x3B75: VFIO_DEVICE_FEATURE +ioctl: arg1 == 0x3B6E || arg1 == 0x3B71 || arg1 == 0x3B72 || arg1 == 0x3B75 open: return ENOENT openat: return ENOENT pread64: 1 diff --git a/vfio_sys/src/lib.rs b/vfio_sys/src/lib.rs index c257f916b0..4c12a430b4 100644 --- a/vfio_sys/src/lib.rs +++ b/vfio_sys/src/lib.rs @@ -43,6 +43,7 @@ ioctl_io_nr!(VFIO_IOMMU_MAP_DMA, VFIO_TYPE, VFIO_BASE + 13); ioctl_io_nr!(VFIO_IOMMU_UNMAP_DMA, VFIO_TYPE, VFIO_BASE + 14); ioctl_io_nr!(VFIO_IOMMU_ENABLE, VFIO_TYPE, VFIO_BASE + 15); ioctl_io_nr!(VFIO_IOMMU_DISABLE, VFIO_TYPE, VFIO_BASE + 16); +ioctl_io_nr!(VFIO_DEVICE_FEATURE, VFIO_TYPE, VFIO_BASE + 17); ioctl_io_nr!( PLAT_IRQ_FORWARD_SET, diff --git a/vfio_sys/src/vfio.rs b/vfio_sys/src/vfio.rs index b50aa2bf5e..48bbc60245 100644 --- a/vfio_sys/src/vfio.rs +++ b/vfio_sys/src/vfio.rs @@ -147,6 +147,9 @@ pub const VFIO_DEVICE_FEATURE_GET: u32 = 65536; pub const VFIO_DEVICE_FEATURE_SET: u32 = 131072; pub const VFIO_DEVICE_FEATURE_PROBE: u32 = 262144; pub const VFIO_DEVICE_FEATURE_PCI_VF_TOKEN: u32 = 0; +pub const VFIO_DEVICE_FEATURE_LOW_POWER_ENTRY: u32 = 3; +pub const VFIO_DEVICE_FEATURE_LOW_POWER_ENTRY_WITH_WAKEUP: u32 = 4; +pub const VFIO_DEVICE_FEATURE_LOW_POWER_EXIT: u32 = 5; pub const VFIO_IOMMU_INFO_PGSIZES: u32 = 1; pub const VFIO_IOMMU_INFO_CAPS: u32 = 2; pub const VFIO_IOMMU_TYPE1_INFO_CAP_IOVA_RANGE: u32 = 1; @@ -390,6 +393,12 @@ pub struct vfio_device_feature { } #[repr(C)] #[derive(Debug, Default, Copy, Clone)] +pub struct vfio_device_low_power_entry_with_wakeup { + pub wakeup_eventfd: i32, + pub reserved: u32, +} +#[repr(C)] +#[derive(Debug, Default, Copy, Clone)] pub struct vfio_iommu_type1_info { pub argsz: u32, pub flags: u32,