Skip to content

Commit 837a23e

Browse files
roypatShadowCurse
andcommitted
refactor(test): Eliminate mocking/cfg(not(test)) from vcpu/mod.rs
Separate the call to `KVM_RUN` from the handling of the result value. This makes the handling of the `VcpuExit` unit-testable without needing to hack in `cfg(not(test))` code that compiles out the `KVM_RUN` call at compile time. Co-Authored-By: Egor Lazarchuk <[email protected]> Signed-off-by: Patrick Roy <[email protected]>
1 parent 9ae2e6d commit 837a23e

File tree

1 file changed

+67
-94
lines changed

1 file changed

+67
-94
lines changed

src/vmm/src/vstate/vcpu/mod.rs

Lines changed: 67 additions & 94 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,6 @@
88
use std::cell::Cell;
99
use std::sync::atomic::{fence, Ordering};
1010
use std::sync::mpsc::{channel, Receiver, Sender, TryRecvError};
11-
#[cfg(test)]
12-
use std::sync::Mutex;
1311
use std::sync::{Arc, Barrier};
1412
use std::{fmt, io, thread};
1513

@@ -104,10 +102,6 @@ pub struct Vcpu {
104102
response_receiver: Option<Receiver<VcpuResponse>>,
105103
/// The transmitting end of the responses channel owned by the vcpu side.
106104
response_sender: Sender<VcpuResponse>,
107-
108-
/// Exit reason used to test run_emulation function.
109-
#[cfg(test)]
110-
test_vcpu_exit_reason: Mutex<Option<Result<VcpuExit<'static>, errno::Error>>>,
111105
}
112106

113107
impl Vcpu {
@@ -218,8 +212,6 @@ impl Vcpu {
218212
response_sender,
219213
kvm_vcpu,
220214
fd,
221-
#[cfg(test)]
222-
test_vcpu_exit_reason: Mutex::new(None),
223215
})
224216
}
225217

@@ -449,15 +441,6 @@ impl Vcpu {
449441
StateMachine::finish()
450442
}
451443

452-
#[cfg(not(test))]
453-
/// Calls `KVM_RUN` with this [`Vcpu`]'s underlying file descriptor.
454-
///
455-
/// Blocks until a `VM_EXIT` is received, in which case this function returns a [`VcpuExit`]
456-
/// containing the reason.
457-
pub fn emulate(&self) -> Result<VcpuExit, errno::Error> {
458-
self.fd.run()
459-
}
460-
461444
/// Runs the vCPU in KVM context and handles the kvm exit reason.
462445
///
463446
/// Returns error or enum specifying whether emulation was handled or interrupted.
@@ -467,18 +450,36 @@ impl Vcpu {
467450
self.fd.set_kvm_immediate_exit(0);
468451
return Ok(VcpuEmulation::Interrupted);
469452
}
470-
match self.emulate() {
453+
454+
match self.fd.run() {
455+
Err(ref err) if err.errno() == libc::EINTR => {
456+
self.fd.set_kvm_immediate_exit(0);
457+
// Notify that this KVM_RUN was interrupted.
458+
Ok(VcpuEmulation::Interrupted)
459+
}
460+
emulation_result => self.kvm_vcpu.handle_kvm_exit(emulation_result),
461+
}
462+
}
463+
}
464+
465+
impl KvmVcpu {
466+
/// Handle the return value of a call to [`VcpuFd::run`] and update our emulation accordingly
467+
pub fn handle_kvm_exit(
468+
&mut self,
469+
emulation_result: Result<VcpuExit, errno::Error>,
470+
) -> Result<VcpuEmulation, VcpuError> {
471+
match emulation_result {
471472
Ok(run) => match run {
472473
VcpuExit::MmioRead(addr, data) => {
473-
if let Some(mmio_bus) = &self.kvm_vcpu.mmio_bus {
474+
if let Some(mmio_bus) = &self.mmio_bus {
474475
let _metric = METRICS.vcpu.exit_mmio_read_agg.record_latency_metrics();
475476
mmio_bus.read(addr, data);
476477
METRICS.vcpu.exit_mmio_read.inc();
477478
}
478479
Ok(VcpuEmulation::Handled)
479480
}
480481
VcpuExit::MmioWrite(addr, data) => {
481-
if let Some(mmio_bus) = &self.kvm_vcpu.mmio_bus {
482+
if let Some(mmio_bus) = &self.mmio_bus {
482483
let _metric = METRICS.vcpu.exit_mmio_write_agg.record_latency_metrics();
483484
mmio_bus.write(addr, data);
484485
METRICS.vcpu.exit_mmio_write.inc();
@@ -538,36 +539,27 @@ impl Vcpu {
538539
},
539540
arch_specific_reason => {
540541
// run specific architecture emulation.
541-
self.kvm_vcpu.run_arch_emulation(arch_specific_reason)
542+
self.run_arch_emulation(arch_specific_reason)
542543
}
543544
},
544545
// The unwrap on raw_os_error can only fail if we have a logic
545546
// error in our code in which case it is better to panic.
546-
Err(ref err) => {
547-
match err.errno() {
548-
libc::EAGAIN => Ok(VcpuEmulation::Handled),
549-
libc::EINTR => {
550-
self.fd.set_kvm_immediate_exit(0);
551-
// Notify that this KVM_RUN was interrupted.
552-
Ok(VcpuEmulation::Interrupted)
553-
}
554-
libc::ENOSYS => {
555-
METRICS.vcpu.failures.inc();
556-
error!(
557-
"Received ENOSYS error because KVM failed to emulate an instruction."
558-
);
559-
Err(VcpuError::FaultyKvmExit(
560-
"Received ENOSYS error because KVM failed to emulate an instruction."
561-
.to_string(),
562-
))
563-
}
564-
_ => {
565-
METRICS.vcpu.failures.inc();
566-
error!("Failure during vcpu run: {}", err);
567-
Err(VcpuError::FaultyKvmExit(format!("{}", err)))
568-
}
547+
Err(ref err) => match err.errno() {
548+
libc::EAGAIN => Ok(VcpuEmulation::Handled),
549+
libc::ENOSYS => {
550+
METRICS.vcpu.failures.inc();
551+
error!("Received ENOSYS error because KVM failed to emulate an instruction.");
552+
Err(VcpuError::FaultyKvmExit(
553+
"Received ENOSYS error because KVM failed to emulate an instruction."
554+
.to_string(),
555+
))
569556
}
570-
}
557+
_ => {
558+
METRICS.vcpu.failures.inc();
559+
error!("Failure during vcpu run: {}", err);
560+
Err(VcpuError::FaultyKvmExit(format!("{}", err)))
561+
}
562+
},
571563
}
572564
}
573565
}
@@ -730,29 +722,16 @@ pub mod tests {
730722
use crate::vstate::vm::Vm;
731723
use crate::RECV_TIMEOUT_SEC;
732724

733-
impl Vcpu {
734-
pub fn emulate(&self) -> Result<VcpuExit, errno::Error> {
735-
self.test_vcpu_exit_reason
736-
.lock()
737-
.unwrap()
738-
.take()
739-
.unwrap_or_else(|| Err(errno::Error::new(libc::SIGILL)))
740-
}
741-
}
742-
743725
#[test]
744-
fn test_run_emulation() {
726+
fn test_handle_kvm_exit() {
745727
let (_vm, mut vcpu, _vm_mem) = setup_vcpu(0x1000);
746-
vcpu.test_vcpu_exit_reason = Mutex::new(Some(Ok(VcpuExit::Hlt)));
747-
let res = vcpu.run_emulation();
728+
let res = vcpu.kvm_vcpu.handle_kvm_exit(Ok(VcpuExit::Hlt));
748729
assert_eq!(res.unwrap(), VcpuEmulation::Stopped);
749730

750-
*(vcpu.test_vcpu_exit_reason.lock().unwrap()) = Some(Ok(VcpuExit::Shutdown));
751-
let res = vcpu.run_emulation();
731+
let res = vcpu.kvm_vcpu.handle_kvm_exit(Ok(VcpuExit::Shutdown));
752732
assert_eq!(res.unwrap(), VcpuEmulation::Stopped);
753733

754-
*(vcpu.test_vcpu_exit_reason.lock().unwrap()) = Some(Ok(VcpuExit::FailEntry(0, 0)));
755-
let res = vcpu.run_emulation();
734+
let res = vcpu.kvm_vcpu.handle_kvm_exit(Ok(VcpuExit::FailEntry(0, 0)));
756735
assert_eq!(
757736
format!("{:?}", res.unwrap_err()),
758737
format!(
@@ -761,8 +740,7 @@ pub mod tests {
761740
)
762741
);
763742

764-
*(vcpu.test_vcpu_exit_reason.lock().unwrap()) = Some(Ok(VcpuExit::InternalError));
765-
let res = vcpu.run_emulation();
743+
let res = vcpu.kvm_vcpu.handle_kvm_exit(Ok(VcpuExit::InternalError));
766744
assert_eq!(
767745
format!("{:?}", res.unwrap_err()),
768746
format!(
@@ -771,16 +749,19 @@ pub mod tests {
771749
)
772750
);
773751

774-
*(vcpu.test_vcpu_exit_reason.lock().unwrap()) = Some(Ok(VcpuExit::SystemEvent(2, &[])));
775-
let res = vcpu.run_emulation();
752+
let res = vcpu
753+
.kvm_vcpu
754+
.handle_kvm_exit(Ok(VcpuExit::SystemEvent(2, &[])));
776755
assert_eq!(res.unwrap(), VcpuEmulation::Stopped);
777756

778-
*(vcpu.test_vcpu_exit_reason.lock().unwrap()) = Some(Ok(VcpuExit::SystemEvent(1, &[])));
779-
let res = vcpu.run_emulation();
757+
let res = vcpu
758+
.kvm_vcpu
759+
.handle_kvm_exit(Ok(VcpuExit::SystemEvent(1, &[])));
780760
assert_eq!(res.unwrap(), VcpuEmulation::Stopped);
781761

782-
*(vcpu.test_vcpu_exit_reason.lock().unwrap()) = Some(Ok(VcpuExit::SystemEvent(3, &[])));
783-
let res = vcpu.run_emulation();
762+
let res = vcpu
763+
.kvm_vcpu
764+
.handle_kvm_exit(Ok(VcpuExit::SystemEvent(3, &[])));
784765
assert_eq!(
785766
format!("{:?}", res.unwrap_err()),
786767
format!(
@@ -790,19 +771,20 @@ pub mod tests {
790771
);
791772

792773
// Check what happens with an unhandled exit reason.
793-
*(vcpu.test_vcpu_exit_reason.lock().unwrap()) = Some(Ok(VcpuExit::Unknown));
794-
let res = vcpu.run_emulation();
774+
let res = vcpu.kvm_vcpu.handle_kvm_exit(Ok(VcpuExit::Unknown));
795775
assert_eq!(
796776
res.unwrap_err().to_string(),
797777
"Unexpected kvm exit received: Unknown".to_string()
798778
);
799779

800-
*(vcpu.test_vcpu_exit_reason.lock().unwrap()) = Some(Err(errno::Error::new(libc::EAGAIN)));
801-
let res = vcpu.run_emulation();
780+
let res = vcpu
781+
.kvm_vcpu
782+
.handle_kvm_exit(Err(errno::Error::new(libc::EAGAIN)));
802783
assert_eq!(res.unwrap(), VcpuEmulation::Handled);
803784

804-
*(vcpu.test_vcpu_exit_reason.lock().unwrap()) = Some(Err(errno::Error::new(libc::ENOSYS)));
805-
let res = vcpu.run_emulation();
785+
let res = vcpu
786+
.kvm_vcpu
787+
.handle_kvm_exit(Err(errno::Error::new(libc::ENOSYS)));
806788
assert_eq!(
807789
format!("{:?}", res.unwrap_err()),
808790
format!(
@@ -814,12 +796,9 @@ pub mod tests {
814796
)
815797
);
816798

817-
*(vcpu.test_vcpu_exit_reason.lock().unwrap()) = Some(Err(errno::Error::new(libc::EINTR)));
818-
let res = vcpu.run_emulation();
819-
assert_eq!(res.unwrap(), VcpuEmulation::Interrupted);
820-
821-
*(vcpu.test_vcpu_exit_reason.lock().unwrap()) = Some(Err(errno::Error::new(libc::EINVAL)));
822-
let res = vcpu.run_emulation();
799+
let res = vcpu
800+
.kvm_vcpu
801+
.handle_kvm_exit(Err(errno::Error::new(libc::EINVAL)));
823802
assert_eq!(
824803
format!("{:?}", res.unwrap_err()),
825804
format!(
@@ -833,20 +812,15 @@ pub mod tests {
833812
bus.insert(dummy, 0x10, 0x10).unwrap();
834813
vcpu.set_mmio_bus(bus);
835814
let addr = 0x10;
836-
static mut DATA: [u8; 4] = [0, 0, 0, 0];
837815

838-
unsafe {
839-
*(vcpu.test_vcpu_exit_reason.lock().unwrap()) =
840-
Some(Ok(VcpuExit::MmioRead(addr, &mut DATA)));
841-
}
842-
let res = vcpu.run_emulation();
816+
let res = vcpu
817+
.kvm_vcpu
818+
.handle_kvm_exit(Ok(VcpuExit::MmioRead(addr, &mut [0, 0, 0, 0])));
843819
assert_eq!(res.unwrap(), VcpuEmulation::Handled);
844820

845-
unsafe {
846-
*(vcpu.test_vcpu_exit_reason.lock().unwrap()) =
847-
Some(Ok(VcpuExit::MmioWrite(addr, &DATA)));
848-
}
849-
let res = vcpu.run_emulation();
821+
let res = vcpu
822+
.kvm_vcpu
823+
.handle_kvm_exit(Ok(VcpuExit::MmioWrite(addr, &[0, 0, 0, 0])));
850824
assert_eq!(res.unwrap(), VcpuEmulation::Handled);
851825
}
852826

@@ -1083,7 +1057,6 @@ pub mod tests {
10831057

10841058
vcpu.fd.set_kvm_immediate_exit(1);
10851059
// Set a dummy value to be returned by the emulate call
1086-
*(vcpu.test_vcpu_exit_reason.lock().unwrap()) = Some(Ok(VcpuExit::Shutdown));
10871060
let result = vcpu.run_emulation().expect("Failed to run emulation");
10881061
assert_eq!(
10891062
result,

0 commit comments

Comments
 (0)