Skip to content

Commit 4abb03b

Browse files
authored
Merge branch 'main' into vmclock-snapsafe
2 parents d7972ff + 841ce52 commit 4abb03b

File tree

3 files changed

+24
-85
lines changed

3 files changed

+24
-85
lines changed

src/vmm/src/arch/x86_64/vcpu.rs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -746,8 +746,6 @@ impl Peripherals {
746746
}
747747
unexpected_exit => {
748748
METRICS.vcpu.failures.inc();
749-
// TODO: Are we sure we want to finish running a vcpu upon
750-
// receiving a vm exit that is not necessarily an error?
751749
error!("Unexpected exit reason on vcpu run: {:?}", unexpected_exit);
752750
Err(VcpuError::UnhandledKvmExit(format!(
753751
"{:?}",

src/vmm/src/lib.rs

Lines changed: 11 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -651,39 +651,8 @@ impl Vmm {
651651

652652
/// Signals Vmm to stop and exit.
653653
pub fn stop(&mut self, exit_code: FcExitCode) {
654-
// To avoid cycles, all teardown paths take the following route:
655-
// +------------------------+----------------------------+------------------------+
656-
// | Vmm | Action | Vcpu |
657-
// +------------------------+----------------------------+------------------------+
658-
// 1 | | | vcpu.exit(exit_code) |
659-
// 2 | | | vcpu.exit_evt.write(1) |
660-
// 3 | | <--- EventFd::exit_evt --- | |
661-
// 4 | vmm.stop() | | |
662-
// 5 | | --- VcpuEvent::Finish ---> | |
663-
// 6 | | | StateMachine::finish() |
664-
// 7 | VcpuHandle::join() | | |
665-
// 8 | vmm.shutdown_exit_code becomes Some(exit_code) breaking the main event loop |
666-
// +------------------------+----------------------------+------------------------+
667-
// Vcpu initiated teardown starts from `fn Vcpu::exit()` (step 1).
668-
// Vmm initiated teardown starts from `pub fn Vmm::stop()` (step 4).
669-
// Once `vmm.shutdown_exit_code` becomes `Some(exit_code)`, it is the upper layer's
670-
// responsibility to break main event loop and propagate the exit code value.
671654
info!("Vmm is stopping.");
672655

673-
// We send a "Finish" event. If a VCPU has already exited, this is the only
674-
// message it will accept... but running and paused will take it as well.
675-
// It breaks out of the state machine loop so that the thread can be joined.
676-
for (idx, handle) in self.vcpus_handles.iter_mut().enumerate() {
677-
if let Err(err) = handle.send_event(VcpuEvent::Finish) {
678-
error!("Failed to send VcpuEvent::Finish to vCPU {}: {}", idx, err);
679-
}
680-
}
681-
// The actual thread::join() that runs to release the thread's resource is done in
682-
// the VcpuHandle's Drop trait. We can trigger that to happen now by clearing the
683-
// list of handles. Do it here instead of Vmm::Drop to avoid dependency cycles.
684-
// (Vmm's Drop will also check if this list is empty).
685-
self.vcpus_handles.clear();
686-
687656
// Break the main event loop, propagating the Vmm exit-code.
688657
self.shutdown_exit_code = Some(exit_code);
689658
}
@@ -722,26 +691,17 @@ fn construct_kvm_mpidrs(vcpu_states: &[VcpuState]) -> Vec<u64> {
722691

723692
impl Drop for Vmm {
724693
fn drop(&mut self) {
725-
// There are two cases when `drop()` is called:
726-
// 1) before the Vmm has been mutexed and subscribed to the event manager, or
727-
// 2) after the Vmm has been registered as a subscriber to the event manager.
728-
//
729-
// The first scenario is bound to happen if an error is raised during
730-
// Vmm creation (for example, during snapshot load), before the Vmm has
731-
// been subscribed to the event manager. If that happens, the `drop()`
732-
// function is called right before propagating the error. In order to
733-
// be able to gracefully exit Firecracker with the correct fault
734-
// message, we need to prepare the Vmm contents for the tear down
735-
// (join the vcpu threads). Explicitly calling `stop()` allows the
736-
// Vmm to be successfully dropped and firecracker to propagate the
737-
// error.
738-
//
739-
// In the second case, before dropping the Vmm object, the event
740-
// manager calls `stop()`, which sends a `Finish` event to the vcpus
741-
// and joins the vcpu threads. The Vmm is dropped after everything is
742-
// ready to be teared down. The line below is a no-op, because the Vmm
743-
// has already been stopped by the event manager at this point.
744-
self.stop(self.shutdown_exit_code.unwrap_or(FcExitCode::Ok));
694+
info!("Killing vCPU threads");
695+
696+
// Send a "Finish" event to the vCPU threads so that they terminate.
697+
for (idx, handle) in self.vcpus_handles.iter_mut().enumerate() {
698+
if let Err(err) = handle.send_event(VcpuEvent::Finish) {
699+
error!("Failed to send VcpuEvent::Finish to vCPU {}: {}", idx, err);
700+
}
701+
}
702+
703+
// Join the vCPU threads by running VcpuHandle::drop().
704+
self.vcpus_handles.clear();
745705

746706
if let Err(err) = std::io::stdin().lock().set_canon_mode() {
747707
warn!("Cannot set canonical mode for the terminal. {:?}", err);

src/vmm/src/vstate/vcpu.rs

Lines changed: 13 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -226,10 +226,9 @@ impl Vcpu {
226226
Ok(VcpuEmulation::Handled) => (),
227227
// Emulation was interrupted, check external events.
228228
Ok(VcpuEmulation::Interrupted) => break,
229-
// If the guest was rebooted or halted:
230-
// - vCPU0 will always exit out of `KVM_RUN` with KVM_EXIT_SHUTDOWN or KVM_EXIT_HLT.
231-
// - the other vCPUs won't ever exit out of `KVM_RUN`, but they won't consume CPU.
232-
// So we pause vCPU0 and send a signal to the emulation thread to stop the VMM.
229+
// The guest requested a SHUTDOWN or RESET. This is ARM
230+
// specific. On x86 the i8042 emulation signals the main thread
231+
// directly without calling Vcpu::exit().
233232
Ok(VcpuEmulation::Stopped) => return self.exit(FcExitCode::Ok),
234233
// If the emulation requests a pause lets do this
235234
#[cfg(feature = "gdb")]
@@ -362,25 +361,9 @@ impl Vcpu {
362361
}
363362

364363
// Transition to the exited state and finish on command.
364+
// Note that this function isn't called when the guest asks for a CPU
365+
// reset via the i8042 controller on x86.
365366
fn exit(&mut self, exit_code: FcExitCode) -> StateMachine<Self> {
366-
// To avoid cycles, all teardown paths take the following route:
367-
// +------------------------+----------------------------+------------------------+
368-
// | Vmm | Action | Vcpu |
369-
// +------------------------+----------------------------+------------------------+
370-
// 1 | | | vcpu.exit(exit_code) |
371-
// 2 | | | vcpu.exit_evt.write(1) |
372-
// 3 | | <--- EventFd::exit_evt --- | |
373-
// 4 | vmm.stop() | | |
374-
// 5 | | --- VcpuEvent::Finish ---> | |
375-
// 6 | | | StateMachine::finish() |
376-
// 7 | VcpuHandle::join() | | |
377-
// 8 | vmm.shutdown_exit_code becomes Some(exit_code) breaking the main event loop |
378-
// +------------------------+----------------------------+------------------------+
379-
// Vcpu initiated teardown starts from `fn Vcpu::exit()` (step 1).
380-
// Vmm initiated teardown starts from `pub fn Vmm::stop()` (step 4).
381-
// Once `vmm.shutdown_exit_code` becomes `Some(exit_code)`, it is the upper layer's
382-
// responsibility to break main event loop and propagate the exit code value.
383-
// Signal Vmm of Vcpu exit.
384367
if let Err(err) = self.exit_evt.write(1) {
385368
METRICS.vcpu.failures.inc();
386369
error!("Failed signaling vcpu exit event: {}", err);
@@ -456,14 +439,6 @@ fn handle_kvm_exit(
456439
}
457440
Ok(VcpuEmulation::Handled)
458441
}
459-
VcpuExit::Hlt => {
460-
info!("Received KVM_EXIT_HLT signal");
461-
Ok(VcpuEmulation::Stopped)
462-
}
463-
VcpuExit::Shutdown => {
464-
info!("Received KVM_EXIT_SHUTDOWN signal");
465-
Ok(VcpuEmulation::Stopped)
466-
}
467442
// Documentation specifies that below kvm exits are considered
468443
// errors.
469444
VcpuExit::FailEntry(hardware_entry_failure_reason, cpu) => {
@@ -713,10 +688,16 @@ pub(crate) mod tests {
713688
fn test_handle_kvm_exit() {
714689
let (_, _, mut vcpu) = setup_vcpu(0x1000);
715690
let res = handle_kvm_exit(&mut vcpu.kvm_vcpu.peripherals, Ok(VcpuExit::Hlt));
716-
assert_eq!(res.unwrap(), VcpuEmulation::Stopped);
691+
assert!(matches!(
692+
res,
693+
Err(EmulationError::UnhandledKvmExit(s)) if s == "Hlt",
694+
));
717695

718696
let res = handle_kvm_exit(&mut vcpu.kvm_vcpu.peripherals, Ok(VcpuExit::Shutdown));
719-
assert_eq!(res.unwrap(), VcpuEmulation::Stopped);
697+
assert!(matches!(
698+
res,
699+
Err(EmulationError::UnhandledKvmExit(s)) if s == "Shutdown",
700+
));
720701

721702
let res = handle_kvm_exit(
722703
&mut vcpu.kvm_vcpu.peripherals,

0 commit comments

Comments
 (0)