virtio-devices, vmm: replace Vec<T> with Box<[T]> in config structs

I started by looking at all `Option<Vec<T>>` values in config.rs and
vm_config.rs, and replaced them with `Option<Box<[T]>>`. This has the
advantage that one now can see at a glance if this field will ever
resize during operation or not, reducing cognitive load and increasing
maintainability. All fields that need the properties of a Ver or where
this change was not trivial are kept intact.

On-behalf-of: SAP philipp.schuster@sap.com
Signed-off-by: Philipp Schuster <philipp.schuster@cyberus-technology.de>
This commit is contained in:
Philipp Schuster
2026-05-15 10:26:59 +02:00
committed by Rob Bradford
parent 29e2319247
commit ba04c4f318
3 changed files with 93 additions and 87 deletions

View File

@@ -155,7 +155,7 @@ struct BlockEpollHandler {
disk_image: Box<dyn AsyncIo>,
disk_nsectors: Arc<AtomicU64>,
interrupt_cb: Arc<dyn VirtioInterrupt>,
serial: Vec<u8>,
serial: Box<[u8]>,
kill_evt: EventFd,
pause_evt: EventFd,
writeback: Arc<AtomicBool>,
@@ -164,7 +164,7 @@ struct BlockEpollHandler {
inflight_requests: VecDeque<(u16, Request)>,
rate_limiter: Option<RateLimiterGroupHandle>,
access_platform: Option<Arc<dyn AccessPlatform>>,
host_cpus: Option<Vec<usize>>,
host_cpus: Option<Box<[usize]>>,
acked_features: u64,
disable_sector0_writes: bool,
}
@@ -723,8 +723,8 @@ pub struct Block {
seccomp_action: SeccompAction,
rate_limiter: Option<Arc<RateLimiterGroup>>,
exit_evt: EventFd,
serial: Vec<u8>,
queue_affinity: BTreeMap<u16, Vec<usize>>,
serial: Box<[u8]>,
queue_affinity: BTreeMap<u16, Box<[usize]>>,
disable_sector0_writes: bool,
lock_granularity_choice: LockGranularityChoice,
device_status: Arc<AtomicU8>,
@@ -755,7 +755,7 @@ impl Block {
rate_limiter: Option<Arc<RateLimiterGroup>>,
exit_evt: EventFd,
state: Option<BlockState>,
queue_affinity: BTreeMap<u16, Vec<usize>>,
queue_affinity: BTreeMap<u16, Box<[usize]>>,
sparse: bool,
disable_sector0_writes: bool,
lock_granularity: LockGranularityChoice,
@@ -862,7 +862,9 @@ impl Block {
(disk_nsectors, avail_features, 0, config, false)
};
let serial = serial.map_or_else(|| build_serial(&disk_path), Vec::from);
let serial = serial
.map_or_else(|| build_serial(&disk_path), Vec::from)
.into_boxed_slice();
Ok(Block {
common: VirtioCommon {

View File

@@ -889,7 +889,7 @@ impl PlatformConfig {
let oem_strings = parser
.convert::<StringList>("oem_strings")
.map_err(Error::ParsePlatform)?
.map(|v| v.0);
.map(|v| v.0.into_boxed_slice());
let iommufd = parser
.convert::<Toggle>("iommufd")
.map_err(Error::ParsePlatform)?
@@ -1390,7 +1390,7 @@ impl DiskConfig {
v.0.iter()
.map(|(e1, e2)| VirtQueueAffinity {
queue_index: *e1,
host_cpus: e2.clone(),
host_cpus: e2.clone().into_boxed_slice(),
})
.collect()
});
@@ -2469,7 +2469,7 @@ impl NumaConfig {
let memory_zones = parser
.convert::<StringList>("memory_zones")
.map_err(Error::ParseNuma)?
.map(|v| v.0);
.map(|v| v.0.into_boxed_slice());
let pci_segments = parser
.convert::<IntegerList>("pci_segments")
.map_err(Error::ParseNuma)?
@@ -3250,14 +3250,14 @@ impl VmConfig {
}
pub fn parse(vm_params: VmParams) -> Result<Self> {
let mut rate_limit_groups: Option<Vec<RateLimiterGroupConfig>> = None;
let mut rate_limit_groups: Option<Box<[RateLimiterGroupConfig]>> = None;
if let Some(rate_limit_group_list) = &vm_params.rate_limit_groups {
let mut rate_limit_group_config_list = Vec::new();
for item in rate_limit_group_list.iter() {
let rate_limit_group_config = RateLimiterGroupConfig::parse(item)?;
rate_limit_group_config_list.push(rate_limit_group_config);
}
rate_limit_groups = Some(rate_limit_group_config_list);
rate_limit_groups = Some(rate_limit_group_config_list.into_boxed_slice());
}
let mut disks: Option<Vec<DiskConfig>> = None;
@@ -3369,26 +3369,26 @@ impl VmConfig {
vsock = Some(vsock_config);
}
let mut pci_segments: Option<Vec<PciSegmentConfig>> = None;
let mut pci_segments: Option<Box<[PciSegmentConfig]>> = None;
if let Some(pci_segment_list) = &vm_params.pci_segments {
let mut pci_segment_config_list = Vec::new();
for item in pci_segment_list.iter() {
let pci_segment_config = PciSegmentConfig::parse(item)?;
pci_segment_config_list.push(pci_segment_config);
}
pci_segments = Some(pci_segment_config_list);
pci_segments = Some(pci_segment_config_list.into_boxed_slice());
}
let platform = vm_params.platform.map(PlatformConfig::parse).transpose()?;
let mut numa: Option<Vec<NumaConfig>> = None;
let mut numa: Option<Box<[NumaConfig]>> = None;
if let Some(numa_list) = &vm_params.numa {
let mut numa_config_list = Vec::new();
for item in numa_list.iter() {
let numa_config = NumaConfig::parse(item)?;
numa_config_list.push(numa_config);
}
numa = Some(numa_config_list);
numa = Some(numa_config_list.into_boxed_slice());
}
#[cfg(not(feature = "igvm"))]
@@ -3426,13 +3426,14 @@ impl VmConfig {
#[cfg(feature = "guest_debug")]
let gdb = vm_params.gdb;
let mut landlock_rules: Option<Vec<LandlockConfig>> = None;
let mut landlock_rules: Option<Box<[LandlockConfig]>> = None;
if let Some(ll_rules) = vm_params.landlock_rules {
landlock_rules = Some(
ll_rules
.iter()
.map(|rule| LandlockConfig::parse(rule))
.collect::<Result<Vec<LandlockConfig>>>()?,
.collect::<Result<Vec<LandlockConfig>>>()?
.into_boxed_slice(),
);
}
@@ -4085,24 +4086,24 @@ mod unit_tests {
assert_eq!(
DiskConfig::parse("path=/path/to_file,queue_affinity=[0@[1],1@[2],2@[3,4],3@[5-8]]")?,
DiskConfig {
queue_affinity: Some(vec![
queue_affinity: Some(Box::new([
VirtQueueAffinity {
queue_index: 0,
host_cpus: vec![1],
host_cpus: Box::new([1]),
},
VirtQueueAffinity {
queue_index: 1,
host_cpus: vec![2],
host_cpus: Box::new([2]),
},
VirtQueueAffinity {
queue_index: 2,
host_cpus: vec![3, 4],
host_cpus: Box::new([3, 4]),
},
VirtQueueAffinity {
queue_index: 3,
host_cpus: vec![5, 6, 7, 8],
host_cpus: Box::new([5, 6, 7, 8]),
}
]),
])),
..disk_fixture()
}
);
@@ -4615,14 +4616,14 @@ id=\"{id}\",pci_segment={pci_segment},queue_sizes={queue_sizes}"
memory_zones=[mem0],pci_segments=[0]";
let expected_standard = NumaConfig {
guest_numa_id: 1,
cpus: Some(vec![2, 3]),
distances: Some(vec![NumaDistance {
cpus: Some(Box::new([2, 3])),
distances: Some(Box::new([NumaDistance {
destination: 0,
distance: 20,
}]),
}])),
device_id: None,
memory_zones: Some(vec!["mem0".to_string()]),
pci_segments: Some(vec![0]),
memory_zones: Some(Box::new(["mem0".to_string()])),
pci_segments: Some(Box::new([0])),
};
assert_eq!(NumaConfig::parse(standard_input)?, expected_standard);
// Successful generic initiator config parse
@@ -4630,13 +4631,13 @@ id=\"{id}\",pci_segment={pci_segment},queue_sizes={queue_sizes}"
let expected_gi = NumaConfig {
guest_numa_id: 2,
cpus: None,
distances: Some(vec![NumaDistance {
distances: Some(Box::new([NumaDistance {
destination: 0,
distance: 30,
}]),
}])),
device_id: Some("vfio1".to_string()),
memory_zones: None,
pci_segments: Some(vec![1]),
pci_segments: Some(Box::new([1])),
};
assert_eq!(NumaConfig::parse(gi_input)?, expected_gi);
Ok(())
@@ -4648,10 +4649,10 @@ id=\"{id}\",pci_segment={pci_segment},queue_sizes={queue_sizes}"
let config = NumaConfig {
guest_numa_id: 0,
cpus: None,
distances: Some(vec![NumaDistance {
distances: Some(Box::new([NumaDistance {
destination: 1,
distance: 20,
}]),
}])),
memory_zones: None,
device_id: Some("vfio0".to_string()),
pci_segments: None,
@@ -4679,7 +4680,7 @@ id=\"{id}\",pci_segment={pci_segment},queue_sizes={queue_sizes}"
// device_id and cpus specified
let config = NumaConfig {
guest_numa_id: 0,
cpus: Some(vec![0, 1]),
cpus: Some(Box::new([0, 1])),
distances: None,
device_id: Some("vfio0".to_string()),
memory_zones: None,
@@ -4696,7 +4697,7 @@ id=\"{id}\",pci_segment={pci_segment},queue_sizes={queue_sizes}"
cpus: None,
distances: None,
device_id: Some("vfio0".to_string()),
memory_zones: Some(vec!["mem0".to_string()]),
memory_zones: Some(Box::new(["mem0".to_string()])),
pci_segments: None,
};
assert!(config.validate().is_err());
@@ -4707,13 +4708,13 @@ id=\"{id}\",pci_segment={pci_segment},queue_sizes={queue_sizes}"
// No device_id
let config = NumaConfig {
guest_numa_id: 0,
cpus: Some(vec![0, 1]),
distances: Some(vec![NumaDistance {
cpus: Some(Box::new([0, 1])),
distances: Some(Box::new([NumaDistance {
destination: 1,
distance: 20,
}]),
}])),
device_id: None,
memory_zones: Some(vec!["mem0".to_string()]),
memory_zones: Some(Box::new(["mem0".to_string()])),
pci_segments: None,
};
config.validate().unwrap();
@@ -5424,14 +5425,17 @@ id=\"{id}\",pci_segment={pci_segment},queue_sizes={queue_sizes}"
let mut still_valid_config = valid_config.clone();
still_valid_config.platform = Some(PlatformConfig {
iommu_segments: Some(vec![1, 2, 3]),
iommu_segments: Some(Box::new([1, 2, 3])),
..platform_fixture()
});
still_valid_config.validate().unwrap();
let mut invalid_config = valid_config.clone();
invalid_config.platform = Some(PlatformConfig {
iommu_segments: Some(vec![MAX_NUM_PCI_SEGMENTS + 1, MAX_NUM_PCI_SEGMENTS + 2]),
iommu_segments: Some(Box::new([
MAX_NUM_PCI_SEGMENTS + 1,
MAX_NUM_PCI_SEGMENTS + 2,
])),
..platform_fixture()
});
assert_eq!(
@@ -5453,7 +5457,7 @@ id=\"{id}\",pci_segment={pci_segment},queue_sizes={queue_sizes}"
let mut still_valid_config = valid_config.clone();
still_valid_config.platform = Some(PlatformConfig {
iommu_segments: Some(vec![1, 2, 3]),
iommu_segments: Some(Box::new([1, 2, 3])),
..platform_fixture()
});
still_valid_config.disks = Some(vec![DiskConfig {
@@ -5468,7 +5472,7 @@ id=\"{id}\",pci_segment={pci_segment},queue_sizes={queue_sizes}"
let mut still_valid_config = valid_config.clone();
still_valid_config.platform = Some(PlatformConfig {
iommu_segments: Some(vec![1, 2, 3]),
iommu_segments: Some(Box::new([1, 2, 3])),
..platform_fixture()
});
still_valid_config.net = Some(vec![NetConfig {
@@ -5483,7 +5487,7 @@ id=\"{id}\",pci_segment={pci_segment},queue_sizes={queue_sizes}"
let mut still_valid_config = valid_config.clone();
still_valid_config.platform = Some(PlatformConfig {
iommu_segments: Some(vec![1, 2, 3]),
iommu_segments: Some(Box::new([1, 2, 3])),
..platform_fixture()
});
still_valid_config.pmem = Some(vec![PmemConfig {
@@ -5498,7 +5502,7 @@ id=\"{id}\",pci_segment={pci_segment},queue_sizes={queue_sizes}"
let mut still_valid_config = valid_config.clone();
still_valid_config.platform = Some(PlatformConfig {
iommu_segments: Some(vec![1, 2, 3]),
iommu_segments: Some(Box::new([1, 2, 3])),
..platform_fixture()
});
still_valid_config.devices = Some(vec![DeviceConfig {
@@ -5513,7 +5517,7 @@ id=\"{id}\",pci_segment={pci_segment},queue_sizes={queue_sizes}"
let mut still_valid_config = valid_config.clone();
still_valid_config.platform = Some(PlatformConfig {
iommu_segments: Some(vec![1, 2, 3]),
iommu_segments: Some(Box::new([1, 2, 3])),
..platform_fixture()
});
still_valid_config.vsock = Some(VsockConfig {
@@ -5529,7 +5533,7 @@ id=\"{id}\",pci_segment={pci_segment},queue_sizes={queue_sizes}"
let mut invalid_config = valid_config.clone();
invalid_config.platform = Some(PlatformConfig {
iommu_segments: Some(vec![1, 2, 3]),
iommu_segments: Some(Box::new([1, 2, 3])),
..platform_fixture()
});
invalid_config.disks = Some(vec![DiskConfig {
@@ -5547,7 +5551,7 @@ id=\"{id}\",pci_segment={pci_segment},queue_sizes={queue_sizes}"
let mut invalid_config = valid_config.clone();
invalid_config.platform = Some(PlatformConfig {
iommu_segments: Some(vec![1, 2, 3]),
iommu_segments: Some(Box::new([1, 2, 3])),
..platform_fixture()
});
invalid_config.net = Some(vec![NetConfig {
@@ -5566,7 +5570,7 @@ id=\"{id}\",pci_segment={pci_segment},queue_sizes={queue_sizes}"
let mut invalid_config = valid_config.clone();
invalid_config.platform = Some(PlatformConfig {
num_pci_segments: MAX_NUM_PCI_SEGMENTS,
iommu_segments: Some(vec![1, 2, 3]),
iommu_segments: Some(Box::new([1, 2, 3])),
..platform_fixture()
});
invalid_config.pmem = Some(vec![PmemConfig {
@@ -5584,7 +5588,7 @@ id=\"{id}\",pci_segment={pci_segment},queue_sizes={queue_sizes}"
let mut invalid_config = valid_config.clone();
invalid_config.platform = Some(PlatformConfig {
num_pci_segments: MAX_NUM_PCI_SEGMENTS,
iommu_segments: Some(vec![1, 2, 3]),
iommu_segments: Some(Box::new([1, 2, 3])),
..platform_fixture()
});
invalid_config.devices = Some(vec![DeviceConfig {
@@ -5601,7 +5605,7 @@ id=\"{id}\",pci_segment={pci_segment},queue_sizes={queue_sizes}"
let mut invalid_config = valid_config.clone();
invalid_config.platform = Some(PlatformConfig {
iommu_segments: Some(vec![1, 2, 3]),
iommu_segments: Some(Box::new([1, 2, 3])),
..platform_fixture()
});
invalid_config.vsock = Some(VsockConfig {
@@ -5620,7 +5624,7 @@ id=\"{id}\",pci_segment={pci_segment},queue_sizes={queue_sizes}"
let mut invalid_config = valid_config.clone();
invalid_config.memory.shared = true;
invalid_config.platform = Some(PlatformConfig {
iommu_segments: Some(vec![1, 2, 3]),
iommu_segments: Some(Box::new([1, 2, 3])),
..platform_fixture()
});
invalid_config.user_devices = Some(vec![UserDeviceConfig {
@@ -5637,7 +5641,7 @@ id=\"{id}\",pci_segment={pci_segment},queue_sizes={queue_sizes}"
let mut invalid_config = valid_config.clone();
invalid_config.platform = Some(PlatformConfig {
iommu_segments: Some(vec![1, 2, 3]),
iommu_segments: Some(Box::new([1, 2, 3])),
..platform_fixture()
});
invalid_config.vdpa = Some(vec![VdpaConfig {
@@ -5655,7 +5659,7 @@ id=\"{id}\",pci_segment={pci_segment},queue_sizes={queue_sizes}"
let mut invalid_config = valid_config.clone();
invalid_config.memory.shared = true;
invalid_config.platform = Some(PlatformConfig {
iommu_segments: Some(vec![1, 2, 3]),
iommu_segments: Some(Box::new([1, 2, 3])),
..platform_fixture()
});
invalid_config.fs = Some(vec![FsConfig {
@@ -5675,81 +5679,81 @@ id=\"{id}\",pci_segment={pci_segment},queue_sizes={queue_sizes}"
num_pci_segments: 2,
..platform_fixture()
});
invalid_config.numa = Some(vec![
invalid_config.numa = Some(Box::new([
NumaConfig {
guest_numa_id: 0,
cpus: Some(vec![0]),
pci_segments: Some(vec![1]),
cpus: Some(Box::new([0])),
pci_segments: Some(Box::new([1])),
..numa_fixture()
},
NumaConfig {
guest_numa_id: 1,
cpus: Some(vec![1]),
pci_segments: Some(vec![1]),
cpus: Some(Box::new([1])),
pci_segments: Some(Box::new([1])),
..numa_fixture()
},
]);
]));
assert_eq!(
invalid_config.validate(),
Err(ValidationError::PciSegmentReused(1, 0, 1))
);
let mut invalid_config = valid_config.clone();
invalid_config.pci_segments = Some(vec![PciSegmentConfig {
invalid_config.pci_segments = Some(Box::new([PciSegmentConfig {
pci_segment: 0,
mmio32_aperture_weight: 1,
mmio64_aperture_weight: 0,
}]);
}]));
assert_eq!(
invalid_config.validate(),
Err(ValidationError::InvalidPciSegmentApertureWeight(0))
);
let mut invalid_config = valid_config.clone();
invalid_config.pci_segments = Some(vec![PciSegmentConfig {
invalid_config.pci_segments = Some(Box::new([PciSegmentConfig {
pci_segment: 0,
mmio32_aperture_weight: 0,
mmio64_aperture_weight: 1,
}]);
}]));
assert_eq!(
invalid_config.validate(),
Err(ValidationError::InvalidPciSegmentApertureWeight(0))
);
let mut invalid_config = valid_config.clone();
invalid_config.numa = Some(vec![
invalid_config.numa = Some(Box::new([
NumaConfig {
guest_numa_id: 0,
cpus: Some(vec![0]),
cpus: Some(Box::new([0])),
..numa_fixture()
},
NumaConfig {
guest_numa_id: 1,
cpus: Some(vec![1]),
pci_segments: Some(vec![0]),
cpus: Some(Box::new([1])),
pci_segments: Some(Box::new([0])),
..numa_fixture()
},
]);
]));
assert_eq!(
invalid_config.validate(),
Err(ValidationError::DefaultPciSegmentInvalidNode(1))
);
let mut invalid_config = valid_config.clone();
invalid_config.numa = Some(vec![
invalid_config.numa = Some(Box::new([
NumaConfig {
guest_numa_id: 0,
cpus: Some(vec![0]),
pci_segments: Some(vec![0]),
cpus: Some(Box::new([0])),
pci_segments: Some(Box::new([0])),
..numa_fixture()
},
NumaConfig {
guest_numa_id: 1,
cpus: Some(vec![1]),
pci_segments: Some(vec![1]),
cpus: Some(Box::new([1])),
pci_segments: Some(Box::new([1])),
..numa_fixture()
},
]);
]));
assert_eq!(
invalid_config.validate(),
Err(ValidationError::InvalidPciSegment(1))

View File

@@ -122,7 +122,7 @@ pub struct PlatformConfig {
#[serde(default = "default_platformconfig_num_pci_segments")]
pub num_pci_segments: u16,
#[serde(default)]
pub iommu_segments: Option<Vec<u16>>,
pub iommu_segments: Option<Box<[u16]>>,
#[serde(default = "default_platformconfig_iommu_address_width_bits")]
pub iommu_address_width_bits: u8,
#[serde(default)]
@@ -130,7 +130,7 @@ pub struct PlatformConfig {
#[serde(default)]
pub uuid: Option<String>,
#[serde(default)]
pub oem_strings: Option<Vec<String>>,
pub oem_strings: Option<Box<[String]>>,
#[cfg(feature = "tdx")]
#[serde(default)]
pub tdx: bool,
@@ -270,7 +270,7 @@ pub struct RateLimiterGroupConfig {
#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
pub struct VirtQueueAffinity {
pub queue_index: u16,
pub host_cpus: Vec<usize>,
pub host_cpus: Box<[usize]>,
}
#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize, Default)]
@@ -314,7 +314,7 @@ pub struct DiskConfig {
#[serde(default)]
pub serial: Option<String>,
#[serde(default)]
pub queue_affinity: Option<Vec<VirtQueueAffinity>>,
pub queue_affinity: Option<Box<[VirtQueueAffinity]>>,
#[serde(default)]
pub backing_files: bool,
#[serde(default = "default_diskconfig_sparse")]
@@ -782,13 +782,13 @@ pub struct NumaDistance {
pub struct NumaConfig {
pub guest_numa_id: u32,
#[serde(default)]
pub cpus: Option<Vec<u32>>,
pub cpus: Option<Box<[u32]>>,
#[serde(default)]
pub distances: Option<Vec<NumaDistance>>,
pub distances: Option<Box<[NumaDistance]>>,
#[serde(default)]
pub memory_zones: Option<Vec<String>>,
pub memory_zones: Option<Box<[String]>>,
#[serde(default)]
pub pci_segments: Option<Vec<u16>>,
pub pci_segments: Option<Box<[u16]>>,
#[serde(default)]
pub device_id: Option<String>,
}
@@ -1020,7 +1020,7 @@ pub struct VmConfig {
#[serde(default)]
pub memory: MemoryConfig,
pub payload: Option<PayloadConfig>,
pub rate_limit_groups: Option<Vec<RateLimiterGroupConfig>>,
pub rate_limit_groups: Option<Box<[RateLimiterGroupConfig]>>,
pub disks: Option<Vec<DiskConfig>>,
pub net: Option<Vec<NetConfig>>,
#[serde(default)]
@@ -1047,13 +1047,13 @@ pub struct VmConfig {
pub pvpanic: bool,
#[serde(default)]
pub iommu: bool,
pub numa: Option<Vec<NumaConfig>>,
pub numa: Option<Box<[NumaConfig]>>,
#[serde(default)]
pub watchdog: bool,
#[cfg(feature = "guest_debug")]
#[serde(default)]
pub gdb: bool,
pub pci_segments: Option<Vec<PciSegmentConfig>>,
pub pci_segments: Option<Box<[PciSegmentConfig]>>,
pub platform: Option<PlatformConfig>,
pub tpm: Option<TpmConfig>,
// Preserved FDs are the ones that share the same life-time as its holding
@@ -1068,7 +1068,7 @@ pub struct VmConfig {
pub preserved_fds: Option<Vec<i32>>,
#[serde(default)]
pub landlock_enable: bool,
pub landlock_rules: Option<Vec<LandlockConfig>>,
pub landlock_rules: Option<Box<[LandlockConfig]>>,
#[cfg(feature = "ivshmem")]
pub ivshmem: Option<IvshmemConfig>,
}