vmm: config: Validate IOMMU configuration

Ensure devices that are specified to be on a PCI segment that is behind
the IOMMU are IOMMU enabled if possible or error out for those devices
that do not support it.

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
This commit is contained in:
Rob Bradford 2022-03-18 16:33:08 +00:00
parent 6d2224f1ba
commit 0834eca8d4
2 changed files with 228 additions and 2 deletions

View File

@ -2130,7 +2130,13 @@ mod parallel {
let (cmd_success, cmd_output) = remote_command_w_output(
&api_socket,
"add-disk",
Some(format!("path={},id=test0,pci_segment=1", test_disk_path.as_str()).as_str()),
Some(
format!(
"path={},id=test0,pci_segment=1,iommu=on",
test_disk_path.as_str()
)
.as_str(),
),
);
assert!(cmd_success);
assert!(String::from_utf8_lossy(&cmd_output)

View File

@ -164,6 +164,10 @@ pub enum ValidationError {
InvalidPciSegment(u16),
/// Balloon too big
BalloonLargerThanRam(u64, u64),
/// On a IOMMU segment but not behind IOMMU
OnIommuSegment(u16),
// On a IOMMU segment but IOMMU not suported
IommuNotSupported(u16),
}
type ValidationResult<T> = std::result::Result<T, ValidationError>;
@ -236,6 +240,20 @@ impl fmt::Display for ValidationError {
balloon_size, ram_size
)
}
OnIommuSegment(pci_segment) => {
write!(
f,
"Device is on an IOMMU PCI segment ({}) but not placed behind IOMMU",
pci_segment
)
}
IommuNotSupported(pci_segment) => {
write!(
f,
"Device is on an IOMMU PCI segment ({}) but does support being placed behind IOMMU",
pci_segment
)
}
}
}
}
@ -1091,6 +1109,12 @@ impl DiskConfig {
if self.pci_segment >= platform_config.num_pci_segments {
return Err(ValidationError::InvalidPciSegment(self.pci_segment));
}
if let Some(iommu_segments) = platform_config.iommu_segments.as_ref() {
if iommu_segments.contains(&self.pci_segment) && !self.iommu {
return Err(ValidationError::OnIommuSegment(self.pci_segment));
}
}
}
Ok(())
@ -1381,6 +1405,12 @@ impl NetConfig {
if self.pci_segment >= platform_config.num_pci_segments {
return Err(ValidationError::InvalidPciSegment(self.pci_segment));
}
if let Some(iommu_segments) = platform_config.iommu_segments.as_ref() {
if iommu_segments.contains(&self.pci_segment) && !self.iommu {
return Err(ValidationError::OnIommuSegment(self.pci_segment));
}
}
}
Ok(())
@ -1597,6 +1627,12 @@ impl FsConfig {
if self.pci_segment >= platform_config.num_pci_segments {
return Err(ValidationError::InvalidPciSegment(self.pci_segment));
}
if let Some(iommu_segments) = platform_config.iommu_segments.as_ref() {
if iommu_segments.contains(&self.pci_segment) {
return Err(ValidationError::IommuNotSupported(self.pci_segment));
}
}
}
Ok(())
@ -1678,6 +1714,12 @@ impl PmemConfig {
if self.pci_segment >= platform_config.num_pci_segments {
return Err(ValidationError::InvalidPciSegment(self.pci_segment));
}
if let Some(iommu_segments) = platform_config.iommu_segments.as_ref() {
if iommu_segments.contains(&self.pci_segment) && !self.iommu {
return Err(ValidationError::OnIommuSegment(self.pci_segment));
}
}
}
Ok(())
@ -1810,6 +1852,12 @@ impl DeviceConfig {
if self.pci_segment >= platform_config.num_pci_segments {
return Err(ValidationError::InvalidPciSegment(self.pci_segment));
}
if let Some(iommu_segments) = platform_config.iommu_segments.as_ref() {
if iommu_segments.contains(&self.pci_segment) && !self.iommu {
return Err(ValidationError::OnIommuSegment(self.pci_segment));
}
}
}
Ok(())
@ -1855,6 +1903,12 @@ impl UserDeviceConfig {
if self.pci_segment >= platform_config.num_pci_segments {
return Err(ValidationError::InvalidPciSegment(self.pci_segment));
}
if let Some(iommu_segments) = platform_config.iommu_segments.as_ref() {
if iommu_segments.contains(&self.pci_segment) {
return Err(ValidationError::IommuNotSupported(self.pci_segment));
}
}
}
Ok(())
@ -1916,6 +1970,12 @@ impl VdpaConfig {
if self.pci_segment >= platform_config.num_pci_segments {
return Err(ValidationError::InvalidPciSegment(self.pci_segment));
}
if let Some(iommu_segments) = platform_config.iommu_segments.as_ref() {
if iommu_segments.contains(&self.pci_segment) {
return Err(ValidationError::IommuNotSupported(self.pci_segment));
}
}
}
Ok(())
@ -1980,6 +2040,12 @@ impl VsockConfig {
if self.pci_segment >= platform_config.num_pci_segments {
return Err(ValidationError::InvalidPciSegment(self.pci_segment));
}
if let Some(iommu_segments) = platform_config.iommu_segments.as_ref() {
if iommu_segments.contains(&self.pci_segment) && !self.iommu {
return Err(ValidationError::OnIommuSegment(self.pci_segment));
}
}
}
Ok(())
@ -3333,11 +3399,165 @@ mod tests {
});
assert!(still_valid_config.validate().is_ok());
let mut invalid_config = valid_config;
let mut invalid_config = valid_config.clone();
invalid_config.platform = Some(PlatformConfig {
num_pci_segments: 16,
iommu_segments: Some(vec![17, 18]),
});
assert!(invalid_config.validate().is_err());
let mut still_valid_config = valid_config.clone();
still_valid_config.platform = Some(PlatformConfig {
num_pci_segments: 16,
iommu_segments: Some(vec![1, 2, 3]),
});
still_valid_config.disks = Some(vec![DiskConfig {
iommu: true,
pci_segment: 1,
..Default::default()
}]);
assert!(still_valid_config.validate().is_ok());
let mut still_valid_config = valid_config.clone();
still_valid_config.platform = Some(PlatformConfig {
num_pci_segments: 16,
iommu_segments: Some(vec![1, 2, 3]),
});
still_valid_config.net = Some(vec![NetConfig {
iommu: true,
pci_segment: 1,
..Default::default()
}]);
assert!(still_valid_config.validate().is_ok());
let mut still_valid_config = valid_config.clone();
still_valid_config.platform = Some(PlatformConfig {
num_pci_segments: 16,
iommu_segments: Some(vec![1, 2, 3]),
});
still_valid_config.pmem = Some(vec![PmemConfig {
iommu: true,
pci_segment: 1,
..Default::default()
}]);
assert!(still_valid_config.validate().is_ok());
let mut still_valid_config = valid_config.clone();
still_valid_config.platform = Some(PlatformConfig {
num_pci_segments: 16,
iommu_segments: Some(vec![1, 2, 3]),
});
still_valid_config.devices = Some(vec![DeviceConfig {
iommu: true,
pci_segment: 1,
..Default::default()
}]);
assert!(still_valid_config.validate().is_ok());
let mut still_valid_config = valid_config.clone();
still_valid_config.platform = Some(PlatformConfig {
num_pci_segments: 16,
iommu_segments: Some(vec![1, 2, 3]),
});
still_valid_config.vsock = Some(VsockConfig {
iommu: true,
pci_segment: 1,
..Default::default()
});
assert!(still_valid_config.validate().is_ok());
let mut invalid_config = valid_config.clone();
invalid_config.platform = Some(PlatformConfig {
num_pci_segments: 16,
iommu_segments: Some(vec![1, 2, 3]),
});
invalid_config.disks = Some(vec![DiskConfig {
iommu: false,
pci_segment: 1,
..Default::default()
}]);
assert!(invalid_config.validate().is_err());
let mut invalid_config = valid_config.clone();
invalid_config.platform = Some(PlatformConfig {
num_pci_segments: 16,
iommu_segments: Some(vec![1, 2, 3]),
});
invalid_config.net = Some(vec![NetConfig {
iommu: false,
pci_segment: 1,
..Default::default()
}]);
assert!(invalid_config.validate().is_err());
let mut invalid_config = valid_config.clone();
invalid_config.platform = Some(PlatformConfig {
num_pci_segments: 16,
iommu_segments: Some(vec![1, 2, 3]),
});
invalid_config.pmem = Some(vec![PmemConfig {
iommu: false,
pci_segment: 1,
..Default::default()
}]);
assert!(invalid_config.validate().is_err());
let mut invalid_config = valid_config.clone();
invalid_config.platform = Some(PlatformConfig {
num_pci_segments: 16,
iommu_segments: Some(vec![1, 2, 3]),
});
invalid_config.devices = Some(vec![DeviceConfig {
iommu: false,
pci_segment: 1,
..Default::default()
}]);
assert!(invalid_config.validate().is_err());
let mut invalid_config = valid_config.clone();
invalid_config.platform = Some(PlatformConfig {
num_pci_segments: 16,
iommu_segments: Some(vec![1, 2, 3]),
});
invalid_config.vsock = Some(VsockConfig {
iommu: false,
pci_segment: 1,
..Default::default()
});
assert!(invalid_config.validate().is_err());
let mut invalid_config = valid_config.clone();
invalid_config.memory.shared = true;
invalid_config.platform = Some(PlatformConfig {
num_pci_segments: 16,
iommu_segments: Some(vec![1, 2, 3]),
});
invalid_config.user_devices = Some(vec![UserDeviceConfig {
pci_segment: 1,
..Default::default()
}]);
assert!(invalid_config.validate().is_err());
let mut invalid_config = valid_config.clone();
invalid_config.platform = Some(PlatformConfig {
num_pci_segments: 16,
iommu_segments: Some(vec![1, 2, 3]),
});
invalid_config.vdpa = Some(vec![VdpaConfig {
pci_segment: 1,
..Default::default()
}]);
assert!(invalid_config.validate().is_err());
let mut invalid_config = valid_config;
invalid_config.platform = Some(PlatformConfig {
num_pci_segments: 16,
iommu_segments: Some(vec![1, 2, 3]),
});
invalid_config.fs = Some(vec![FsConfig {
pci_segment: 1,
..Default::default()
}]);
assert!(invalid_config.validate().is_err());
}
}