vmm: support removing devices before VM is booted

If the VM has been configured but not yet booted, all we need to do to
support removing a device is to remove it from the config, so it will
never be created.

Signed-off-by: Alyssa Ross <hi@alyssa.is>
This commit is contained in:
Alyssa Ross 2023-07-10 08:53:34 +00:00 committed by Bo Chen
parent 87d81dd2b1
commit 69bd0036d9
3 changed files with 76 additions and 45 deletions

View File

@ -2101,6 +2101,69 @@ impl VmConfig {
Ok(config)
}
pub fn remove_device(&mut self, id: &str) -> bool {
let mut removed = false;
// Remove if VFIO device
if let Some(devices) = self.devices.as_mut() {
let len = devices.len();
devices.retain(|dev| dev.id.as_ref().map(|id| id.as_ref()) != Some(id));
removed |= devices.len() != len;
}
// Remove if VFIO user device
if let Some(user_devices) = self.user_devices.as_mut() {
let len = user_devices.len();
user_devices.retain(|dev| dev.id.as_ref().map(|id| id.as_ref()) != Some(id));
removed |= user_devices.len() != len;
}
// Remove if disk device
if let Some(disks) = self.disks.as_mut() {
let len = disks.len();
disks.retain(|dev| dev.id.as_ref().map(|id| id.as_ref()) != Some(id));
removed |= disks.len() != len;
}
// Remove if fs device
if let Some(fs) = self.fs.as_mut() {
let len = fs.len();
fs.retain(|dev| dev.id.as_ref().map(|id| id.as_ref()) != Some(id));
removed |= fs.len() != len;
}
// Remove if net device
if let Some(net) = self.net.as_mut() {
let len = net.len();
net.retain(|dev| dev.id.as_ref().map(|id| id.as_ref()) != Some(id));
removed |= net.len() != len;
}
// Remove if pmem device
if let Some(pmem) = self.pmem.as_mut() {
let len = pmem.len();
pmem.retain(|dev| dev.id.as_ref().map(|id| id.as_ref()) != Some(id));
removed |= pmem.len() != len;
}
// Remove if vDPA device
if let Some(vdpa) = self.vdpa.as_mut() {
let len = vdpa.len();
vdpa.retain(|dev| dev.id.as_ref().map(|id| id.as_ref()) != Some(id));
removed |= vdpa.len() != len;
}
// Remove if vsock device
if let Some(vsock) = self.vsock.as_ref() {
if vsock.id.as_ref().map(|id| id.as_ref()) == Some(id) {
self.vsock = None;
removed = true;
}
}
removed
}
/// # Safety
/// To use this safely, the caller must guarantee that the input
/// fds are all valid.

View File

@ -1025,13 +1025,20 @@ impl Vmm {
fn vm_remove_device(&mut self, id: String) -> result::Result<(), VmError> {
if let Some(ref mut vm) = self.vm {
if let Err(e) = vm.remove_device(id) {
error!("Error when removing new device to the VM: {:?}", e);
error!("Error when removing device from the VM: {:?}", e);
Err(e)
} else {
Ok(())
}
} else if let Some(ref config) = self.vm_config {
let mut config = config.lock().unwrap();
if config.remove_device(&id) {
Ok(())
} else {
Err(VmError::NoDeviceToRemove(id))
}
} else {
Err(VmError::VmNotRunning)
Err(VmError::VmNotCreated)
}
}

View File

@ -137,6 +137,9 @@ pub enum Error {
#[error("Error from device manager: {0:?}")]
DeviceManager(DeviceManagerError),
#[error("No device with id {0:?} to remove")]
NoDeviceToRemove(String),
#[error("Cannot spawn a signal handler thread: {0}")]
SignalHandlerSpawn(#[source] io::Error),
@ -1406,49 +1409,7 @@ impl Vm {
// Update VmConfig by removing the device. This is important to
// ensure the device would not be created in case of a reboot.
let mut config = self.config.lock().unwrap();
// Remove if VFIO device
if let Some(devices) = config.devices.as_mut() {
devices.retain(|dev| dev.id.as_ref() != Some(&id));
}
// Remove if VFIO user device
if let Some(user_devices) = config.user_devices.as_mut() {
user_devices.retain(|dev| dev.id.as_ref() != Some(&id));
}
// Remove if disk device
if let Some(disks) = config.disks.as_mut() {
disks.retain(|dev| dev.id.as_ref() != Some(&id));
}
// Remove if fs device
if let Some(fs) = config.fs.as_mut() {
fs.retain(|dev| dev.id.as_ref() != Some(&id));
}
// Remove if net device
if let Some(net) = config.net.as_mut() {
net.retain(|dev| dev.id.as_ref() != Some(&id));
}
// Remove if pmem device
if let Some(pmem) = config.pmem.as_mut() {
pmem.retain(|dev| dev.id.as_ref() != Some(&id));
}
// Remove if vDPA device
if let Some(vdpa) = config.vdpa.as_mut() {
vdpa.retain(|dev| dev.id.as_ref() != Some(&id));
}
// Remove if vsock device
if let Some(vsock) = config.vsock.as_ref() {
if vsock.id.as_ref() == Some(&id) {
config.vsock = None;
}
}
self.config.lock().unwrap().remove_device(&id);
self.device_manager
.lock()