vmm: config: Switch UserDeviceConfig to use PciDeviceCommonConfig

Switch UserDeviceConfig over to using the newly extracted struct members
as used by all PCI based devices. The use of #[serde(flatten)] means
that this change has no impact on the JSON format that the data is
stored as.

As VFIO user devices do not support being placed behind an IOMMU an
error is now raised if iommu is set. This can't happen via the CLI but
could via the JSON/API.

Signed-off-by: Rob Bradford <rbradford@meta.com>
This commit is contained in:
Rob Bradford
2026-04-03 12:11:01 -07:00
parent 47182201f9
commit 2c50be4753
3 changed files with 19 additions and 36 deletions

View File

@@ -2244,39 +2244,21 @@ impl UserDeviceConfig {
parser.add("socket").add("id").add("pci_segment");
parser.parse(user_device).map_err(Error::ParseUserDevice)?;
let pci_common = PciDeviceCommonConfig::parse(user_device)?;
let socket = parser
.get("socket")
.map(PathBuf::from)
.ok_or(Error::ParseUserDeviceSocketMissing)?;
let id = parser.get("id");
let pci_segment = parser
.convert::<u16>("pci_segment")
.map_err(Error::ParseUserDevice)?
.unwrap_or_default();
Ok(UserDeviceConfig {
socket,
id,
pci_segment,
})
Ok(UserDeviceConfig { pci_common, socket })
}
pub fn validate(&self, vm_config: &VmConfig) -> ValidationResult<()> {
if let Some(platform_config) = vm_config.platform.as_ref() {
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()
&& iommu_segments.contains(&self.pci_segment)
{
return Err(ValidationError::IommuNotSupportedOnSegment(
self.pci_segment,
));
}
if self.pci_common.iommu {
return Err(ValidationError::IommuNotSupported);
}
Ok(())
self.pci_common.validate(vm_config)
}
}
@@ -3052,7 +3034,7 @@ impl VmConfig {
for user_device in user_devices {
user_device.validate(self)?;
Self::validate_identifier(&mut id_list, &user_device.id)?;
Self::validate_identifier(&mut id_list, &user_device.pci_common.id)?;
}
}
@@ -3439,7 +3421,7 @@ impl VmConfig {
// Remove if VFIO user device
if let Some(user_devices) = self.user_devices.as_mut() {
let len = user_devices.len();
user_devices.retain(|dev| dev.id.as_ref().map(|id| id.as_ref()) != Some(id));
user_devices.retain(|dev| dev.pci_common.id.as_ref().map(|id| id.as_ref()) != Some(id));
removed |= user_devices.len() != len;
}
@@ -5409,9 +5391,11 @@ id=\"{id}\",pci_segment={pci_segment},queue_sizes={queue_sizes}"
..platform_fixture()
});
invalid_config.user_devices = Some(vec![UserDeviceConfig {
pci_segment: 1,
pci_common: PciDeviceCommonConfig {
pci_segment: 1,
..Default::default()
},
socket: PathBuf::new(),
id: None,
}]);
assert_eq!(
invalid_config.validate(),

View File

@@ -4120,16 +4120,16 @@ impl DeviceManager {
&mut self,
device_cfg: &mut UserDeviceConfig,
) -> DeviceManagerResult<(PciBdf, String)> {
let vfio_user_name = if let Some(id) = &device_cfg.id {
let vfio_user_name = if let Some(id) = &device_cfg.pci_common.id {
id.clone()
} else {
let id = self.next_device_name(VFIO_USER_DEVICE_NAME_PREFIX)?;
device_cfg.id = Some(id.clone());
device_cfg.pci_common.id = Some(id.clone());
id
};
let (pci_segment_id, pci_device_bdf, resources) =
self.pci_resources(&vfio_user_name, device_cfg.pci_segment)?;
self.pci_resources(&vfio_user_name, device_cfg.pci_common.pci_segment)?;
let legacy_interrupt_group =
if let Some(legacy_interrupt_manager) = &self.legacy_interrupt_manager {
@@ -4653,12 +4653,13 @@ impl DeviceManager {
&mut self,
device_cfg: &mut UserDeviceConfig,
) -> DeviceManagerResult<PciDeviceInfo> {
self.validate_identifier(&device_cfg.id)?;
self.validate_identifier(&device_cfg.pci_common.id)?;
let (bdf, device_name) = self.add_vfio_user_device(device_cfg)?;
// Update the PCIU bitmap
self.pci_segments[device_cfg.pci_segment as usize].pci_devices_up |= 1 << bdf.device();
self.pci_segments[device_cfg.pci_common.pci_segment as usize].pci_devices_up |=
1 << bdf.device();
Ok(PciDeviceInfo {
id: device_name,

View File

@@ -633,11 +633,9 @@ impl ApplyLandlock for DeviceConfig {
#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
pub struct UserDeviceConfig {
#[serde(flatten)]
pub pci_common: PciDeviceCommonConfig,
pub socket: PathBuf,
#[serde(default)]
pub id: Option<String>,
#[serde(default)]
pub pci_segment: u16,
}
impl ApplyLandlock for UserDeviceConfig {