From a6426e3615ae2cfa10f4c20e13546bf1e28726c6 Mon Sep 17 00:00:00 2001 From: Philipp Schuster Date: Mon, 30 Jun 2025 15:07:04 +0200 Subject: [PATCH] devices: gracefully close preserved FDs on device remove For a graceful resource management of externally provided FDs in Cloud Hypervisor, corresponding FDs need to be closed on a device removal. This is the case for virtio-net devices using external FDs, for example. With the fix introduced in this commit, we allow management software to properly clean up resources, e.g., libvirt can clean up tap devices. PS: CHV uses "added" and "removed", which has the same meaning as hot device attach/hotplug and hot device detach/unplug. Signed-off-by: Philipp Schuster On-behalf-of: SAP philipp.schuster@sap.com --- vmm/src/device_manager.rs | 30 ++++++++++++++++++++++++++++-- vmm/src/vm_config.rs | 4 ++++ 2 files changed, 32 insertions(+), 2 deletions(-) diff --git a/vmm/src/device_manager.rs b/vmm/src/device_manager.rs index 9c14f28eb..a580f5282 100644 --- a/vmm/src/device_manager.rs +++ b/vmm/src/device_manager.rs @@ -4512,9 +4512,35 @@ impl DeviceManager { .unwrap() .device_type(), ); + // When the device is added, we close all file descriptors + // opened externally for this device. This allows management + // software to properly clean up resources, e.g., libvirt can clean + // up tap devices. + // + // TODO: once we allow externally opened FDs for other devices as well, + // we should create a descriptive abstraction/function for this + // functionality. match device_type { - VirtioDeviceType::Net - | VirtioDeviceType::Block + VirtioDeviceType::Net => { + let mut config = self.config.lock().unwrap(); + let nets = config.net.as_deref_mut().unwrap(); + let net_dev_cfg = nets + .iter_mut() + .find(|net| net.id.as_ref() == Some(&id)) + // unwrap: the device could not have been removed without an ID + .unwrap(); + let fds = net_dev_cfg.fds.take().unwrap_or(Vec::new()); + + debug!("Closing preserved FDs from virtio-net device: id={id}, fds={fds:?}"); + for fd in fds { + config.preserved_fds.as_mut().unwrap().retain(|x| *x != fd); + // SAFETY: We are closing the only remaining instance of this FD. + unsafe { + libc::close(fd); + } + } + } + VirtioDeviceType::Block | VirtioDeviceType::Pmem | VirtioDeviceType::Fs | VirtioDeviceType::Vsock => {} diff --git a/vmm/src/vm_config.rs b/vmm/src/vm_config.rs index 7021f9e9c..380c20434 100644 --- a/vmm/src/vm_config.rs +++ b/vmm/src/vm_config.rs @@ -926,6 +926,10 @@ pub struct VmConfig { // VmConfig instance, such as FDs for creating TAP devices. // Preserved FDs will stay open as long as the holding VmConfig instance is // valid, and will be closed when the holding VmConfig instance is destroyed. + // + // This is populated as devices are added at runtime. Removing them again + // causes the FDs to be closed early. This allows management software to + // gracefully clean up resources (e.g., libvirt closes tap devices). #[serde(skip)] pub preserved_fds: Option>, #[serde(default)]