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 <philipp.schuster@cyberus-technology.de>
On-behalf-of: SAP philipp.schuster@sap.com
This commit is contained in:
Philipp Schuster
2025-06-30 15:07:04 +02:00
committed by Rob Bradford
parent f1206a19df
commit a6426e3615
2 changed files with 32 additions and 2 deletions

View File

@@ -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 => {}

View File

@@ -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<Vec<i32>>,
#[serde(default)]