vmm: Reject removal of already removed devices

A hot-unplug leaves the PCI node in the device tree until the guest
acknowledges the ejection, but VmConfig drops the device entry
immediately.

Move the config removal into DeviceManager::remove_device() and fail
when it returns false, so a second remove-device request cannot reuse
the stale device-tree node.

Assisted-by: OpenAI:Codex-GPT-5
Signed-off-by: Rob Bradford <rbradford@meta.com>
This commit is contained in:
Rob Bradford
2026-07-08 12:12:13 -07:00
committed by Bo Chen
parent b2d1065a55
commit 5b53f4202d
2 changed files with 31 additions and 23 deletions
+23 -11
View File
@@ -5023,15 +5023,18 @@ impl DeviceManager {
match device_type { match device_type {
VirtioDeviceType::Net => { VirtioDeviceType::Net => {
let mut config = self.config.lock().unwrap(); let mut config = self.config.lock().unwrap();
let nets = config.net.as_deref_mut().unwrap(); if let Some(fds) = config
let net_dev_cfg = nets .net
.iter_mut() .as_deref_mut()
.and_then(|nets| {
nets.iter_mut()
.find(|net| net.pci_common.id.as_deref() == Some(id)) .find(|net| net.pci_common.id.as_deref() == Some(id))
// unwrap: the device could not have been removed without an ID })
.unwrap(); .map(|net_dev_cfg| net_dev_cfg.fds.take().unwrap_or_default())
let fds = net_dev_cfg.fds.take().unwrap_or(Vec::new()); {
debug!(
debug!("Closing preserved FDs from virtio-net device: id={id}, fds={fds:?}"); "Closing preserved FDs from virtio-net device: id={id}, fds={fds:?}"
);
for fd in fds { for fd in fds {
config.preserved_fds.as_mut().unwrap().remove(&fd); config.preserved_fds.as_mut().unwrap().remove(&fd);
// SAFETY: We are closing the only remaining instance of this FD. // SAFETY: We are closing the only remaining instance of this FD.
@@ -5040,6 +5043,7 @@ impl DeviceManager {
} }
} }
} }
}
VirtioDeviceType::Block VirtioDeviceType::Block
| VirtioDeviceType::Pmem | VirtioDeviceType::Pmem
| VirtioDeviceType::Fs | VirtioDeviceType::Fs
@@ -5050,11 +5054,15 @@ impl DeviceManager {
// Cleanup externally-provided VFIO cdev FDs: remove the preserved // Cleanup externally-provided VFIO cdev FDs: remove the preserved
// original from VmConfig and close it. // original from VmConfig and close it.
let mut config = self.config.lock().unwrap(); let mut config = self.config.lock().unwrap();
if let Some(devices) = config.devices.as_deref_mut() if let Some(fd) = config
&& let Some(device_cfg) = devices .devices
.as_deref_mut()
.and_then(|devices| {
devices
.iter_mut() .iter_mut()
.find(|d| d.pci_common.id.as_deref() == Some(id)) .find(|d| d.pci_common.id.as_deref() == Some(id))
&& let Some(fd) = device_cfg.fd.take() })
.and_then(|device_cfg| device_cfg.fd.take())
{ {
debug!("Closing preserved FD from VFIO device: id={id}, fd={fd}"); debug!("Closing preserved FD from VFIO device: id={id}, fd={fd}");
let fd_removed = config.preserved_fds.as_mut().unwrap().remove(&fd); let fd_removed = config.preserved_fds.as_mut().unwrap().remove(&fd);
@@ -5069,6 +5077,10 @@ impl DeviceManager {
} }
} }
if !self.config.lock().unwrap().remove_device(id) {
return Err(DeviceManagerError::UnknownDeviceId(id.to_string()));
}
// Update the PCID bitmap // Update the PCID bitmap
self.pci_segments[pci_segment_id as usize].pci_devices_down |= 1 << pci_device_bdf.device(); self.pci_segments[pci_segment_id as usize].pci_devices_down |= 1 << pci_device_bdf.device();
-4
View File
@@ -2236,10 +2236,6 @@ impl Vm {
.remove_device(id) .remove_device(id)
.map_err(Error::DeviceManager)?; .map_err(Error::DeviceManager)?;
// Update VmConfig by removing the device. This is important to
// ensure the device would not be created in case of a reboot.
self.config.lock().unwrap().remove_device(id);
self.device_manager self.device_manager
.lock() .lock()
.unwrap() .unwrap()