From 439c8e8ee17adc58b52da20f684d7bd3aa378c4c Mon Sep 17 00:00:00 2001 From: Alex Lyn Date: Sun, 12 Apr 2026 16:27:39 +0200 Subject: [PATCH] runtime-rs: Add QMP iommufd hotplug and timeout bumps MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add hotplug_iommufd() to QmpClient for attaching VFIO devices through the iommufd backend at runtime via QMP object-add + device_add. Bump QMP connection and command timeouts from 10s to 30s to accommodate the longer initialization time when VFIO devices are cold-plugged (IOMMU domain setup and device reset can be slow for GPUs). Re-export cmdline_generator types from qemu/mod.rs for downstream use. Signed-off-by: Alex Lyn Signed-off-by: Fabiano FidĂȘncio --- .../crates/hypervisor/src/qemu/qmp.rs | 57 ++++++++++++++++++- 1 file changed, 55 insertions(+), 2 deletions(-) diff --git a/src/runtime-rs/crates/hypervisor/src/qemu/qmp.rs b/src/runtime-rs/crates/hypervisor/src/qemu/qmp.rs index a6bde80f26..81a379cf21 100644 --- a/src/runtime-rs/crates/hypervisor/src/qemu/qmp.rs +++ b/src/runtime-rs/crates/hypervisor/src/qemu/qmp.rs @@ -33,7 +33,7 @@ use std::time::Instant; /// default qmp connection read timeout const DEFAULT_QMP_READ_TIMEOUT: u64 = 250; -const DEFAULT_QMP_CONNECT_DEADLINE_MS: u64 = 5000; +const DEFAULT_QMP_CONNECT_DEADLINE_MS: u64 = 50000; const DEFAULT_QMP_RETRY_SLEEP_MS: u64 = 50; pub struct Qmp { @@ -72,7 +72,7 @@ impl Qmp { let stream = UnixStream::connect(qmp_sock_path)?; stream - .set_read_timeout(Some(Duration::from_millis(DEFAULT_QMP_READ_TIMEOUT))) + .set_read_timeout(Some(Duration::from_millis(5000))) .context("set qmp read timeout")?; let mut qmp = Qmp { @@ -292,6 +292,59 @@ impl Qmp { Ok(hotplugged_mem_size) } + /// Hotplug an iommufd QOM object in QEMU and return the object id. + #[allow(dead_code)] + pub fn hotplug_iommufd( + &mut self, + suffix_or_id: &str, + external_fdname: Option<&str>, + ) -> Result { + // Object id in QEMU (also used as fdname for getfd) + let obj_id = if suffix_or_id.starts_with("iommufd") { + suffix_or_id.to_string() + } else { + format!("iommufd{suffix_or_id}") + }; + + { + let file = std::fs::OpenOptions::new() + .read(true) + .write(true) + .open("/dev/iommu") + .context("open /dev/iommu failed")?; + self.pass_fd(file.as_raw_fd(), &obj_id)?; + } + + let obj = match external_fdname { + None => qmp::object_add(qapi_qmp::ObjectOptions::iommufd { + id: obj_id.clone(), + iommufd: qapi_qmp::IOMMUFDProperties { fd: None }, + }), + Some(_fdname) => qmp::object_add(qapi_qmp::ObjectOptions::iommufd { + id: obj_id.clone(), + iommufd: qapi_qmp::IOMMUFDProperties { + fd: Some(obj_id.to_string()), + }, + }), + }; + + match self.qmp.execute(&obj) { + Ok(_) => Ok(obj_id), + Err(e) => { + let msg = format!("{e:#}"); + if msg.contains("duplicate ID") + || msg.contains("already exists") + || msg.contains("exists") + { + Ok(obj_id) + } else { + Err(anyhow!(e)) + .with_context(|| format!("object-add iommufd failed (id={obj_id})")) + } + } + } + } + pub fn hotplug_memory(&mut self, size: u64) -> Result<()> { let memdev_idx = self .qmp