main, vmm: add explicit PCI BDF support for Rng device

This was missing in [0] but is required for proper explicit PCI BDF
management, e.g., when a VM is created via libvirt and each device has
an explicit BDF.

[0] https://github.com/cloud-hypervisor/cloud-hypervisor/pull/7965

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-07 15:53:51 +02:00
committed by Rob Bradford
parent 18bbc71b59
commit bad10f3026
7 changed files with 68 additions and 35 deletions
+5 -5
View File
@@ -34,7 +34,7 @@ use vmm::vm_config::IvshmemConfig;
use vmm::vm_config::{ use vmm::vm_config::{
BalloonConfig, DeviceConfig, DiskConfig, FsConfig, GenericVhostUserConfig, LandlockConfig, BalloonConfig, DeviceConfig, DiskConfig, FsConfig, GenericVhostUserConfig, LandlockConfig,
NetConfig, NumaConfig, PciSegmentConfig, PlatformConfig, PmemConfig, RateLimiterGroupConfig, NetConfig, NumaConfig, PciSegmentConfig, PlatformConfig, PmemConfig, RateLimiterGroupConfig,
TpmConfig, UserDeviceConfig, VdpaConfig, VmConfig, VsockConfig, RngConfig, TpmConfig, UserDeviceConfig, VdpaConfig, VmConfig, VsockConfig,
}; };
use vmm_sys_util::eventfd::EventFd; use vmm_sys_util::eventfd::EventFd;
use vmm_sys_util::signal::block_signal; use vmm_sys_util::signal::block_signal;
@@ -174,7 +174,7 @@ fn default_memory() -> String {
} }
fn default_rng() -> String { fn default_rng() -> String {
format!("src={}", vm_config::DEFAULT_RNG_SOURCE) format!("src={}", RngConfig::DEFAULT_RNG_SOURCE)
} }
/// Returns all [`Arg`]s in alphabetical order. This is the order used in the /// Returns all [`Arg`]s in alphabetical order. This is the order used in the
@@ -426,7 +426,7 @@ fn get_cli_options_sorted(
.group("vmm-config"), .group("vmm-config"),
Arg::new("rng") Arg::new("rng")
.long("rng") .long("rng")
.help("Random number generator parameters \"src=<entropy_source_path>,iommu=on|off\"") .help(RngConfig::SYNTAX)
.default_value(default_rng) .default_value(default_rng)
.group("vm-config"), .group("vm-config"),
Arg::new("seccomp") Arg::new("seccomp")
@@ -953,7 +953,7 @@ mod unit_tests {
use vmm::vm_config::DebugConsoleConfig; use vmm::vm_config::DebugConsoleConfig;
use vmm::vm_config::{ use vmm::vm_config::{
ConsoleConfig, ConsoleOutputMode, CoreScheduling, CpuFeatures, CpusConfig, HotplugMethod, ConsoleConfig, ConsoleOutputMode, CoreScheduling, CpuFeatures, CpusConfig, HotplugMethod,
MemoryConfig, PayloadConfig, RngConfig, VmConfig, MemoryConfig, PayloadConfig, PciDeviceCommonConfig, RngConfig, VmConfig,
}; };
use crate::test_util::assert_args_sorted; use crate::test_util::assert_args_sorted;
@@ -1036,7 +1036,7 @@ mod unit_tests {
net: None, net: None,
rng: RngConfig { rng: RngConfig {
src: PathBuf::from("/dev/urandom"), src: PathBuf::from("/dev/urandom"),
iommu: false, pci_common: PciDeviceCommonConfig::default(),
}, },
balloon: None, balloon: None,
fs: None, fs: None,
+1 -1
View File
@@ -165,7 +165,7 @@ impl RequestHandler for StubApiRequestHandler {
net: None, net: None,
rng: RngConfig { rng: RngConfig {
src: PathBuf::from("/dev/urandom"), src: PathBuf::from("/dev/urandom"),
iommu: false, pci_common: PciDeviceCommonConfig::default(),
}, },
balloon: None, balloon: None,
fs: None, fs: None,
+9 -1
View File
@@ -1073,11 +1073,19 @@ components:
- src - src
type: object type: object
properties: properties:
src: id:
type: string type: string
pci_segment:
type: integer
format: int16
pci_device_id:
type: integer
format: uint8
iommu: iommu:
type: boolean type: boolean
default: false default: false
src:
type: string
BalloonConfig: BalloonConfig:
required: required:
+32 -13
View File
@@ -1750,23 +1750,30 @@ impl NetConfig {
} }
impl RngConfig { impl RngConfig {
pub const SYNTAX: &'static str = "Random number generator parameters \"\
src=<entropy_source_path>,iommu=on|off,pci_segment=<segment_id>,\
pci_device_id=<pci_slot>\"";
pub fn parse(rng: &str) -> Result<Self> { pub fn parse(rng: &str) -> Result<Self> {
let mut parser = OptionParser::new(); let mut parser = OptionParser::new();
parser.add("src").add("iommu"); parser
.add("src")
.add_all(PciDeviceCommonConfig::OPTIONS_IOMMU);
parser.parse(rng).map_err(Error::ParseRng)?; parser.parse(rng).map_err(Error::ParseRng)?;
let src = PathBuf::from( let src = PathBuf::from(
parser parser
.get("src") .get("src")
.unwrap_or_else(|| DEFAULT_RNG_SOURCE.to_owned()), .unwrap_or_else(|| Self::DEFAULT_RNG_SOURCE.to_owned()),
); );
let iommu = parser
.convert::<Toggle>("iommu")
.map_err(Error::ParseRng)?
.unwrap_or(Toggle(false))
.0;
Ok(RngConfig { src, iommu }) let pci_common = PciDeviceCommonConfig::parse(rng)?;
Ok(RngConfig { src, pci_common })
}
pub fn validate(&self, vm_config: &VmConfig) -> ValidationResult<()> {
self.pci_common.validate(vm_config)
} }
} }
@@ -2972,6 +2979,8 @@ impl VmConfig {
} }
} }
self.rng.validate(self)?;
if let Some(nets) = &self.net { if let Some(nets) = &self.net {
for net in nets { for net in nets {
if net.vhost_user && !self.backed_by_shared_memory() { if net.vhost_user && !self.backed_by_shared_memory() {
@@ -3018,7 +3027,7 @@ impl VmConfig {
} }
} }
self.iommu |= self.rng.iommu; self.iommu |= self.rng.pci_common.iommu;
self.iommu |= self.console.iommu; self.iommu |= self.console.iommu;
if let Some(t) = &self.cpus.topology { if let Some(t) = &self.cpus.topology {
@@ -4184,16 +4193,26 @@ mod unit_tests {
} }
); );
assert_eq!( assert_eq!(
RngConfig::parse("src=/dev/random,iommu=on")?, RngConfig::parse("src=/dev/random,iommu=on,pci_segment=1,pci_device_id=7")?,
RngConfig { RngConfig {
src: PathBuf::from("/dev/random"), src: PathBuf::from("/dev/random"),
iommu: true, pci_common: PciDeviceCommonConfig {
id: None,
iommu: true,
pci_segment: 1,
pci_device_id: Some(7),
},
} }
); );
assert_eq!( assert_eq!(
RngConfig::parse("iommu=on")?, RngConfig::parse("iommu=on")?,
RngConfig { RngConfig {
iommu: true, pci_common: PciDeviceCommonConfig {
id: None,
iommu: true,
pci_segment: 0,
pci_device_id: None,
},
..Default::default() ..Default::default()
} }
); );
@@ -5010,7 +5029,7 @@ id=\"{id}\",pci_segment={pci_segment},queue_sizes={queue_sizes}"
net: None, net: None,
rng: RngConfig { rng: RngConfig {
src: PathBuf::from("/dev/urandom"), src: PathBuf::from("/dev/urandom"),
iommu: false, pci_common: PciDeviceCommonConfig::default(),
}, },
balloon: None, balloon: None,
fs: None, fs: None,
+12 -8
View File
@@ -3006,16 +3006,24 @@ impl DeviceManager {
fn make_virtio_rng_devices(&mut self) -> DeviceManagerResult<()> { fn make_virtio_rng_devices(&mut self) -> DeviceManagerResult<()> {
// Add virtio-rng if required // Add virtio-rng if required
let rng_config = self.config.lock().unwrap().rng.clone(); let mut rng_config = self.config.lock().unwrap().rng.clone();
if let Some(rng_path) = rng_config.src.to_str() { if let Some(rng_path) = rng_config.src.to_str() {
info!("Creating virtio-rng device: {rng_config:?}"); info!("Creating virtio-rng device: {rng_config:?}");
let id = String::from(RNG_DEVICE_NAME);
let id = match rng_config.pci_common.id.as_ref() {
Some(id) => id.clone(),
None => rng_config
.pci_common
.id
.insert(RNG_DEVICE_NAME.to_string())
.clone(),
};
let virtio_rng_device = Arc::new(Mutex::new( let virtio_rng_device = Arc::new(Mutex::new(
virtio_devices::Rng::new( virtio_devices::Rng::new(
id.clone(), id.clone(),
rng_path, rng_path,
self.force_access_platform | rng_config.iommu, self.force_access_platform | rng_config.pci_common.iommu,
self.seccomp_action.clone(), self.seccomp_action.clone(),
self.exit_evt self.exit_evt
.try_clone() .try_clone()
@@ -3028,11 +3036,7 @@ impl DeviceManager {
self.virtio_devices.push(MetaVirtioDevice { self.virtio_devices.push(MetaVirtioDevice {
virtio_device: Arc::clone(&virtio_rng_device) virtio_device: Arc::clone(&virtio_rng_device)
as Arc<Mutex<dyn virtio_devices::VirtioDevice>>, as Arc<Mutex<dyn virtio_devices::VirtioDevice>>,
pci_common: PciDeviceCommonConfig { pci_common: rng_config.pci_common.clone(),
id: Some(id.clone()),
iommu: rng_config.iommu,
..Default::default()
},
dma_handler: None, dma_handler: None,
}); });
+2 -2
View File
@@ -2654,7 +2654,7 @@ mod unit_tests {
use crate::vm_config::DebugConsoleConfig; use crate::vm_config::DebugConsoleConfig;
use crate::vm_config::{ use crate::vm_config::{
ConsoleConfig, ConsoleOutputMode, CoreScheduling, CpuFeatures, CpusConfig, HotplugMethod, ConsoleConfig, ConsoleOutputMode, CoreScheduling, CpuFeatures, CpusConfig, HotplugMethod,
MemoryConfig, PayloadConfig, RngConfig, MemoryConfig, PayloadConfig, PciDeviceCommonConfig, RngConfig,
}; };
fn create_dummy_vmm() -> Vmm { fn create_dummy_vmm() -> Vmm {
@@ -2716,7 +2716,7 @@ mod unit_tests {
net: None, net: None,
rng: RngConfig { rng: RngConfig {
src: PathBuf::from("/dev/urandom"), src: PathBuf::from("/dev/urandom"),
iommu: false, pci_common: PciDeviceCommonConfig::default(),
}, },
balloon: None, balloon: None,
fs: None, fs: None,
+7 -5
View File
@@ -432,18 +432,20 @@ where
#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)] #[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
pub struct RngConfig { pub struct RngConfig {
#[serde(flatten)]
pub pci_common: PciDeviceCommonConfig,
pub src: PathBuf, pub src: PathBuf,
#[serde(default)]
pub iommu: bool,
} }
pub const DEFAULT_RNG_SOURCE: &str = "/dev/urandom"; impl RngConfig {
pub const DEFAULT_RNG_SOURCE: &str = "/dev/urandom";
}
impl Default for RngConfig { impl Default for RngConfig {
fn default() -> Self { fn default() -> Self {
RngConfig { RngConfig {
src: PathBuf::from(DEFAULT_RNG_SOURCE), src: PathBuf::from(Self::DEFAULT_RNG_SOURCE),
iommu: false, pci_common: PciDeviceCommonConfig::default(),
} }
} }
} }