Skip to content

Commit 299f394

Browse files
committed
refactor(acpi): Propagate VMClock/VMGenID error
Now VMClock is the last device that gets GSI allocated. Because VMClock panics when GSI allocation fails, test_attach_too_many_devices now fails. Define error types and propagate errors so that the API client receives the error message. Signed-off-by: Takahiro Itazuri <itazur@amazon.com>
1 parent 2dd595b commit 299f394

6 files changed

Lines changed: 78 additions & 53 deletions

File tree

src/vmm/src/device_manager/acpi.rs

Lines changed: 16 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -3,21 +3,20 @@
33

44
#[cfg(target_arch = "x86_64")]
55
use acpi_tables::{Aml, aml};
6-
use vm_memory::GuestMemoryError;
76

87
use crate::Vm;
9-
use crate::devices::acpi::vmclock::VmClock;
10-
use crate::devices::acpi::vmgenid::VmGenId;
8+
use crate::devices::acpi::vmclock::{VmClock, VmClockError};
9+
use crate::devices::acpi::vmgenid::{VmGenId, VmGenIdError};
1110
use crate::vstate::memory::GuestMemoryMmap;
1211

1312
#[derive(Debug, thiserror::Error, displaydoc::Display)]
1413
pub enum ACPIDeviceError {
15-
/// Could not register GSI with KVM: {0}
14+
/// VMGenID: {0}
15+
VmGenId(#[from] VmGenIdError),
16+
/// VMClock: {0}
17+
VmClock(#[from] VmClockError),
18+
/// Could not register IRQ with KVM: {0}
1619
RegisterIrq(#[from] kvm_ioctls::Error),
17-
/// Could not write to guest memory: {0}
18-
WriteGuestMemory(#[from] GuestMemoryError),
19-
/// Could not notify guest: {0}
20-
NotifyGuest(#[from] std::io::Error),
2120
}
2221

2322
// Although both VMGenID and VMClock devices are always present, they should be instantiated when
@@ -39,12 +38,14 @@ impl ACPIDeviceManager {
3938
}
4039
}
4140

42-
pub fn attach_vmgenid(&mut self, vm: &Vm) {
43-
self.vmgenid = Some(VmGenId::new(&mut vm.resource_allocator()));
41+
pub fn attach_vmgenid(&mut self, vm: &Vm) -> Result<(), ACPIDeviceError> {
42+
self.vmgenid = Some(VmGenId::new(&mut vm.resource_allocator())?);
43+
Ok(())
4444
}
4545

46-
pub fn attach_vmclock(&mut self, vm: &Vm) {
47-
self.vmclock = Some(VmClock::new(&mut vm.resource_allocator()));
46+
pub fn attach_vmclock(&mut self, vm: &Vm) -> Result<(), ACPIDeviceError> {
47+
self.vmclock = Some(VmClock::new(&mut vm.resource_allocator())?);
48+
Ok(())
4849
}
4950

5051
pub fn vmgenid(&self) -> &VmGenId {
@@ -72,11 +73,12 @@ impl ACPIDeviceManager {
7273
Ok(())
7374
}
7475

75-
pub fn post_restore_vmclock(&mut self, mem: &GuestMemoryMmap) {
76+
pub fn post_restore_vmclock(&mut self, mem: &GuestMemoryMmap) -> Result<(), ACPIDeviceError> {
7677
self.vmclock
7778
.as_mut()
7879
.expect("Missing VMClock device")
79-
.post_restore(mem);
80+
.post_restore(mem)?;
81+
Ok(())
8082
}
8183
}
8284

src/vmm/src/device_manager/mod.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -233,13 +233,13 @@ impl DeviceManager {
233233
}
234234

235235
pub(crate) fn attach_vmgenid_device(&mut self, vm: &Vm) -> Result<(), AttachDeviceError> {
236-
self.acpi_devices.attach_vmgenid(vm);
236+
self.acpi_devices.attach_vmgenid(vm)?;
237237
self.acpi_devices.activate_vmgenid(vm)?;
238238
Ok(())
239239
}
240240

241241
pub(crate) fn attach_vmclock_device(&mut self, vm: &Vm) -> Result<(), AttachDeviceError> {
242-
self.acpi_devices.attach_vmclock(vm);
242+
self.acpi_devices.attach_vmclock(vm)?;
243243
self.acpi_devices.activate_vmclock(vm)?;
244244
Ok(())
245245
}
@@ -560,8 +560,8 @@ pub(crate) mod tests {
560560
let mut resource_allocator = ResourceAllocator::new();
561561
let mmio_devices = MMIODeviceManager::new();
562562
let acpi_devices = ACPIDeviceManager::new(
563-
VmGenId::new(&mut resource_allocator),
564-
VmClock::new(&mut resource_allocator),
563+
VmGenId::new(&mut resource_allocator).unwrap(),
564+
VmClock::new(&mut resource_allocator).unwrap(),
565565
);
566566
let pci_devices = PciDevices::new();
567567

src/vmm/src/device_manager/persist.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -194,7 +194,7 @@ impl<'a> Persist<'a> for ACPIDeviceManager {
194194
acpi_devices.post_restore_vmgenid()?;
195195

196196
acpi_devices.activate_vmclock(vm)?;
197-
acpi_devices.post_restore_vmclock(vm.guest_memory());
197+
acpi_devices.post_restore_vmclock(vm.guest_memory())?;
198198

199199
Ok(acpi_devices)
200200
}

src/vmm/src/devices/acpi/vmclock.rs

Lines changed: 27 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,20 @@ macro_rules! write_vmclock_field {
4343
};
4444
}
4545

46+
#[derive(Debug, thiserror::Error, displaydoc::Display)]
47+
pub enum VmClockError {
48+
/// Could not create EventFd: {0}
49+
CreateEventFd(std::io::Error),
50+
/// Could not allocate GSI: {0}
51+
AllocateGsi(vm_allocator::Error),
52+
/// Could not allocate guest memory: {0}
53+
AllocateMemory(vm_allocator::Error),
54+
/// Could not write VMClock data to guest memory: {0}
55+
WriteGuestMemory(#[from] GuestMemoryError),
56+
/// Could not notify guest: {0}
57+
NotifyGuest(std::io::Error),
58+
}
59+
4660
/// VMclock device
4761
///
4862
/// This device emulates the VMclock device which allows passing information to the guest related
@@ -62,22 +76,21 @@ pub struct VmClock {
6276

6377
impl VmClock {
6478
/// Create a new [`VmClock`] device for a newly booted VM
65-
pub fn new(resource_allocator: &mut ResourceAllocator) -> VmClock {
79+
pub fn new(resource_allocator: &mut ResourceAllocator) -> Result<VmClock, VmClockError> {
6680
let addr = resource_allocator
6781
.allocate_system_memory(
6882
VMCLOCK_SIZE as u64,
6983
VMCLOCK_SIZE as u64,
7084
AllocPolicy::LastMatch,
7185
)
72-
.expect("vmclock: could not allocate guest memory for device");
86+
.map_err(VmClockError::AllocateMemory)?;
7387

7488
let gsi = resource_allocator
7589
.allocate_gsi_legacy(1)
76-
.expect("vmclock: Could not allocate GSI for VMClock: {err}")[0];
90+
.map_err(VmClockError::AllocateGsi)?[0];
7791

7892
let interrupt_evt = EventFdTrigger::new(
79-
EventFd::new(libc::EFD_NONBLOCK)
80-
.expect("vmclock: Could not create EventFd for VMClock device: {err}"),
93+
EventFd::new(libc::EFD_NONBLOCK).map_err(VmClockError::CreateEventFd)?,
8194
);
8295

8396
let mut inner = vmclock_abi {
@@ -90,22 +103,22 @@ impl VmClock {
90103
..Default::default()
91104
};
92105

93-
VmClock {
106+
Ok(VmClock {
94107
guest_address: GuestAddress(addr),
95108
interrupt_evt,
96109
gsi,
97110
inner,
98-
}
111+
})
99112
}
100113

101114
/// Activate [`VmClock`] device
102-
pub fn activate(&self, mem: &GuestMemoryMmap) -> Result<(), GuestMemoryError> {
115+
pub fn activate(&self, mem: &GuestMemoryMmap) -> Result<(), VmClockError> {
103116
mem.write_slice(self.inner.as_slice(), self.guest_address)?;
104117
Ok(())
105118
}
106119

107120
/// Bump the VM generation counter and notify guest after snapshot restore
108-
pub fn post_restore(&mut self, mem: &GuestMemoryMmap) {
121+
pub fn post_restore(&mut self, mem: &GuestMemoryMmap) -> Result<(), VmClockError> {
109122
write_vmclock_field!(self, mem, seq_count, self.inner.seq_count | 1);
110123

111124
// This fence ensures guest sees all previous writes. It is matched to a
@@ -133,8 +146,9 @@ impl VmClock {
133146
write_vmclock_field!(self, mem, seq_count, self.inner.seq_count.wrapping_add(1));
134147
self.interrupt_evt
135148
.trigger()
136-
.expect("vmclock: could not send guest notification: {err}");
149+
.map_err(VmClockError::NotifyGuest)?;
137150
debug!("vmclock: notifying guest about VMClock updates");
151+
Ok(())
138152
}
139153
}
140154

@@ -154,7 +168,7 @@ pub struct VmClockState {
154168
impl<'a> Persist<'a> for VmClock {
155169
type State = VmClockState;
156170
type ConstructorArgs = ();
157-
type Error = Infallible;
171+
type Error = VmClockError;
158172

159173
fn save(&self) -> Self::State {
160174
VmClockState {
@@ -166,8 +180,7 @@ impl<'a> Persist<'a> for VmClock {
166180

167181
fn restore(vm: Self::ConstructorArgs, state: &Self::State) -> Result<Self, Self::Error> {
168182
let interrupt_evt = EventFdTrigger::new(
169-
EventFd::new(libc::EFD_NONBLOCK)
170-
.expect("vmclock: Could not create EventFd for VMClock device: {err}"),
183+
EventFd::new(libc::EFD_NONBLOCK).map_err(VmClockError::CreateEventFd)?,
171184
);
172185
Ok(VmClock {
173186
guest_address: GuestAddress(state.guest_address),
@@ -231,7 +244,7 @@ mod tests {
231244

232245
fn default_vmclock() -> VmClock {
233246
let mut resource_allocator = ResourceAllocator::new();
234-
VmClock::new(&mut resource_allocator)
247+
VmClock::new(&mut resource_allocator).unwrap()
235248
}
236249

237250
#[test]

src/vmm/src/devices/acpi/vmgenid.rs

Lines changed: 28 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,20 @@ use crate::vstate::resources::ResourceAllocator;
2020
/// Bytes of memory we allocate for VMGenID device
2121
pub const VMGENID_MEM_SIZE: u64 = 16;
2222

23+
#[derive(Debug, thiserror::Error, displaydoc::Display)]
24+
pub enum VmGenIdError {
25+
/// Could not create EventFd: {0}
26+
CreateEventFd(std::io::Error),
27+
/// Could not allocate GSI: {0}
28+
AllocateGsi(vm_allocator::Error),
29+
/// Could not allocate guest memory: {0}
30+
AllocateMemory(vm_allocator::Error),
31+
/// Could not write generation ID to guest memory: {0}
32+
WriteGuestMemory(#[from] GuestMemoryError),
33+
/// Could not notify guest: {0}
34+
NotifyGuest(std::io::Error),
35+
}
36+
2337
/// Virtual Machine Generation ID device
2438
///
2539
/// VMGenID is an emulated device which exposes to the guest a 128-bit cryptographically random
@@ -43,38 +57,37 @@ pub struct VmGenId {
4357
impl VmGenId {
4458
/// Create a new Vm Generation Id device using an address in the guest for writing the
4559
/// generation ID and a GSI for sending device notifications.
46-
pub fn from_parts(guest_address: GuestAddress, gsi: u32) -> Self {
60+
pub fn from_parts(guest_address: GuestAddress, gsi: u32) -> Result<Self, VmGenIdError> {
4761
debug!(
4862
"vmgenid: building VMGenID device. Address: {:#010x}. IRQ: {}",
4963
guest_address.0, gsi
5064
);
5165
let interrupt_evt = EventFdTrigger::new(
52-
EventFd::new(libc::EFD_NONBLOCK)
53-
.expect("vmgenid: Could not create EventFd for VMGenID device"),
66+
EventFd::new(libc::EFD_NONBLOCK).map_err(VmGenIdError::CreateEventFd)?,
5467
);
5568
let gen_id = Self::make_genid();
5669

57-
Self {
70+
Ok(Self {
5871
gen_id,
5972
interrupt_evt,
6073
guest_address,
6174
gsi,
62-
}
75+
})
6376
}
6477

6578
/// Create a new VMGenID device
6679
///
6780
/// Allocate memory and a GSI for sending notifications and build the device
68-
pub fn new(resource_allocator: &mut ResourceAllocator) -> Self {
81+
pub fn new(resource_allocator: &mut ResourceAllocator) -> Result<Self, VmGenIdError> {
6982
let gsi = resource_allocator
7083
.allocate_gsi_legacy(1)
71-
.expect("vmgenid: Could not allocate GSI for VMGenID");
84+
.map_err(VmGenIdError::AllocateGsi)?[0];
7285
// The generation ID needs to live in an 8-byte aligned buffer
7386
let addr = resource_allocator
7487
.allocate_system_memory(VMGENID_MEM_SIZE, 8, vm_allocator::AllocPolicy::LastMatch)
75-
.expect("vmgenid: Could not allocate guest RAM for VMGenID");
88+
.map_err(VmGenIdError::AllocateMemory)?;
7689

77-
Self::from_parts(GuestAddress(addr), gsi[0])
90+
Self::from_parts(GuestAddress(addr), gsi)
7891
}
7992

8093
// Create a 16-bytes random number
@@ -88,22 +101,22 @@ impl VmGenId {
88101
///
89102
/// This will only have effect if we have updated the generation ID in guest memory, i.e. when
90103
/// re-creating the device after snapshot resumption.
91-
pub fn post_restore(&self) -> Result<(), std::io::Error> {
104+
pub fn post_restore(&self) -> Result<(), VmGenIdError> {
92105
self.interrupt_evt
93106
.trigger()
94-
.inspect_err(|err| error!("vmgenid: could not send guest notification: {err}"))?;
107+
.map_err(VmGenIdError::NotifyGuest)?;
95108
debug!("vmgenid: notifying guest about new generation ID");
96109
Ok(())
97110
}
98111

99112
/// Attach the [`VmGenId`] device
100-
pub fn activate(&self, mem: &GuestMemoryMmap) -> Result<(), GuestMemoryError> {
113+
pub fn activate(&self, mem: &GuestMemoryMmap) -> Result<(), VmGenIdError> {
101114
debug!(
102115
"vmgenid: writing new generation ID to guest: {:#034x}",
103116
self.gen_id
104117
);
105118
mem.write_slice(&self.gen_id.to_le_bytes(), self.guest_address)
106-
.inspect_err(|err| error!("vmgenid: could not write generation ID to guest: {err}"))?;
119+
.map_err(VmGenIdError::WriteGuestMemory)?;
107120

108121
Ok(())
109122
}
@@ -122,7 +135,7 @@ pub struct VMGenIDState {
122135
impl<'a> Persist<'a> for VmGenId {
123136
type State = VMGenIDState;
124137
type ConstructorArgs = ();
125-
type Error = Infallible;
138+
type Error = VmGenIdError;
126139

127140
fn save(&self) -> Self::State {
128141
VMGenIDState {
@@ -132,7 +145,7 @@ impl<'a> Persist<'a> for VmGenId {
132145
}
133146

134147
fn restore(_: Self::ConstructorArgs, state: &Self::State) -> Result<Self, Self::Error> {
135-
Ok(Self::from_parts(GuestAddress(state.addr), state.gsi))
148+
Self::from_parts(GuestAddress(state.addr), state.gsi)
136149
}
137150
}
138151

tests/integration_tests/functional/test_max_devices.py

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -74,12 +74,9 @@ def test_attach_too_many_devices(uvm_plain):
7474
# Attempting to start a microVM with more than
7575
# `MAX_DEVICES_ATTACHED` devices should fail.
7676
error_str = (
77-
("Could not find an available device slot on the PCI bus.")
77+
"Could not find an available device slot on the PCI bus."
7878
if test_microvm.pci_enabled
79-
else (
80-
"Failed to allocate requested resource: The requested resource"
81-
" is not available."
82-
)
79+
else "Could not allocate GSI: The requested resource is not available."
8380
)
8481
with pytest.raises(RuntimeError, match=error_str):
8582
test_microvm.start()

0 commit comments

Comments
 (0)