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 <dverkamp@chromium.org>
Commit-Queue: Victor Ding <victording@chromium.org>
This commit is contained in:
Victor Ding 2021-12-16 13:21:36 +00:00 committed by crosvm LUCI
parent d4ec52642a
commit ec820fc4ae
5 changed files with 65 additions and 8 deletions

View file

@ -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]) {

View file

@ -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::<vfio_device_feature, u8>(0);
device_feature[0].argsz = mem::size_of::<vfio_device_feature>() 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::<vfio_device_feature, u8>(0);
device_feature[0].argsz = mem::size_of::<vfio_device_feature>() 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.

View file

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

View file

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

View file

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