@@ -25,6 +25,8 @@ extern crate mshv_bindings3 as mshv_bindings;
25
25
extern crate mshv_ioctls3 as mshv_ioctls;
26
26
27
27
use std:: fmt:: { Debug , Formatter } ;
28
+ use std:: sync:: atomic:: { AtomicBool , AtomicU64 , Ordering } ;
29
+ use std:: sync:: Arc ;
28
30
29
31
use log:: { error, LevelFilter } ;
30
32
#[ cfg( mshv2) ]
@@ -46,7 +48,7 @@ use mshv_bindings::{
46
48
hv_partition_property_code_HV_PARTITION_PROPERTY_SYNTHETIC_PROC_FEATURES,
47
49
hv_partition_synthetic_processor_features,
48
50
} ;
49
- use mshv_ioctls:: { Mshv , VcpuFd , VmFd } ;
51
+ use mshv_ioctls:: { Mshv , MshvError , VcpuFd , VmFd } ;
50
52
use tracing:: { instrument, Span } ;
51
53
52
54
use super :: fpu:: { FP_CONTROL_WORD_DEFAULT , FP_TAG_WORD_DEFAULT , MXCSR_DEFAULT } ;
@@ -56,8 +58,9 @@ use super::gdb::{DebugCommChannel, DebugMsg, DebugResponse, GuestDebug, MshvDebu
56
58
use super :: handlers:: DbgMemAccessHandlerWrapper ;
57
59
use super :: handlers:: { MemAccessHandlerWrapper , OutBHandlerWrapper } ;
58
60
use super :: {
59
- Hypervisor , VirtualCPU , CR0_AM , CR0_ET , CR0_MP , CR0_NE , CR0_PE , CR0_PG , CR0_WP , CR4_OSFXSR ,
60
- CR4_OSXMMEXCPT , CR4_PAE , EFER_LMA , EFER_LME , EFER_NX , EFER_SCE ,
61
+ Hypervisor , InterruptHandle , LinuxInterruptHandle , VirtualCPU , CR0_AM , CR0_ET , CR0_MP , CR0_NE ,
62
+ CR0_PE , CR0_PG , CR0_WP , CR4_OSFXSR , CR4_OSXMMEXCPT , CR4_PAE , EFER_LMA , EFER_LME , EFER_NX ,
63
+ EFER_SCE ,
61
64
} ;
62
65
use crate :: hypervisor:: HyperlightExit ;
63
66
use crate :: mem:: memory_region:: { MemoryRegion , MemoryRegionFlags } ;
@@ -285,13 +288,14 @@ pub(crate) fn is_hypervisor_present() -> bool {
285
288
286
289
/// A Hypervisor driver for HyperV-on-Linux. This hypervisor is often
287
290
/// called the Microsoft Hypervisor (MSHV)
288
- pub ( super ) struct HypervLinuxDriver {
291
+ pub ( crate ) struct HypervLinuxDriver {
289
292
_mshv : Mshv ,
290
293
vm_fd : VmFd ,
291
294
vcpu_fd : VcpuFd ,
292
295
entrypoint : u64 ,
293
296
mem_regions : Vec < MemoryRegion > ,
294
297
orig_rsp : GuestPtr ,
298
+ interrupt_handle : Arc < LinuxInterruptHandle > ,
295
299
296
300
#[ cfg( gdb) ]
297
301
debug : Option < MshvDebug > ,
@@ -309,7 +313,7 @@ impl HypervLinuxDriver {
309
313
/// `apply_registers` method to do that, or more likely call
310
314
/// `initialise` to do it for you.
311
315
#[ instrument( skip_all, parent = Span :: current( ) , level = "Trace" ) ]
312
- pub ( super ) fn new (
316
+ pub ( crate ) fn new (
313
317
mem_regions : Vec < MemoryRegion > ,
314
318
entrypoint_ptr : GuestPtr ,
315
319
rsp_ptr : GuestPtr ,
@@ -389,6 +393,12 @@ impl HypervLinuxDriver {
389
393
mem_regions,
390
394
entrypoint : entrypoint_ptr. absolute ( ) ?,
391
395
orig_rsp : rsp_ptr,
396
+ interrupt_handle : Arc :: new ( LinuxInterruptHandle {
397
+ running : AtomicBool :: new ( false ) ,
398
+ cancel_requested : AtomicBool :: new ( false ) ,
399
+ tid : AtomicU64 :: new ( unsafe { libc:: pthread_self ( ) } ) ,
400
+ dropped : AtomicBool :: new ( false ) ,
401
+ } ) ,
392
402
393
403
#[ cfg( gdb) ]
394
404
debug,
@@ -572,15 +582,57 @@ impl Hypervisor for HypervLinuxDriver {
572
582
#[ cfg( gdb) ]
573
583
const EXCEPTION_INTERCEPT : hv_message_type = hv_message_type_HVMSG_X64_EXCEPTION_INTERCEPT;
574
584
575
- #[ cfg( mshv2) ]
576
- let run_result = {
577
- let hv_message: hv_message = Default :: default ( ) ;
578
- & self . vcpu_fd . run ( hv_message)
585
+ self . interrupt_handle
586
+ . tid
587
+ . store ( unsafe { libc:: pthread_self ( ) as u64 } , Ordering :: Relaxed ) ;
588
+ // Note: if a `InterruptHandle::kill()` called while this thread is **here**
589
+ // Then this is fine since `cancel_requested` is set to true, so we will skip the `VcpuFd::run()` call
590
+ self . interrupt_handle . running . store ( true , Ordering :: Relaxed ) ;
591
+ // Don't run the vcpu if `cancel_requested` is true
592
+ //
593
+ // Note: if a `InterruptHandle::kill()` called while this thread is **here**
594
+ // Then this is fine since `cancel_requested` is set to true, so we will skip the `VcpuFd::run()` call
595
+ let exit_reason = if self
596
+ . interrupt_handle
597
+ . cancel_requested
598
+ . load ( Ordering :: Relaxed )
599
+ {
600
+ Err ( MshvError :: Errno ( vmm_sys_util:: errno:: Error :: new (
601
+ libc:: EINTR ,
602
+ ) ) )
603
+ } else {
604
+ // Note: if a `InterruptHandle::kill()` called while this thread is **here**
605
+ // Then the vcpu will run, but we will keep sending signals to this thread
606
+ // to interrupt it until `running` is set to false. The `vcpu_fd::run()` call will
607
+ // return either normally with an exit reason, or from being "kicked" by out signal handler, with an EINTR error,
608
+ // both of which are fine.
609
+ #[ cfg( mshv2) ]
610
+ {
611
+ let hv_message: hv_message = Default :: default ( ) ;
612
+ self . vcpu_fd . run ( hv_message)
613
+ }
614
+ #[ cfg( mshv3) ]
615
+ self . vcpu_fd . run ( )
579
616
} ;
580
- #[ cfg( mshv3) ]
581
- let run_result = & self . vcpu_fd . run ( ) ;
582
-
583
- let result = match run_result {
617
+ // Note: if a `InterruptHandle::kill()` called while this thread is **here**
618
+ // Then signals will be sent to this thread until `running` is set to false.
619
+ // This is fine since the signal handler is a no-op.
620
+ let cancel_requested = self
621
+ . interrupt_handle
622
+ . cancel_requested
623
+ . swap ( false , Ordering :: Relaxed ) ;
624
+ // Note: if a `InterruptHandle::kill()` called while this thread is **here**
625
+ // Then `cancel_requested` will be set to true again, which will cancel the **next vcpu run**.
626
+ // Additionally signals will be sent to this thread until `running` is set to false.
627
+ // This is fine since the signal handler is a no-op.
628
+ self . interrupt_handle
629
+ . running
630
+ . store ( false , Ordering :: Relaxed ) ;
631
+ // At this point, `running` is false so no more signals will be sent to this thread,
632
+ // but we may still receive async signals that were sent before this point.
633
+ // To prevent those signals from interrupting subsequent calls to `run()`,
634
+ // we make sure to check `cancel_requested` before cancelling (see `libc::EINTR` match-arm below).
635
+ let result = match exit_reason {
584
636
Ok ( m) => match m. header . message_type {
585
637
HALT_MESSAGE => {
586
638
crate :: debug!( "mshv - Halt Details : {:#?}" , & self ) ;
@@ -657,7 +709,15 @@ impl Hypervisor for HypervLinuxDriver {
657
709
} ,
658
710
Err ( e) => match e. errno ( ) {
659
711
// we send a signal to the thread to cancel execution this results in EINTR being returned by KVM so we return Cancelled
660
- libc:: EINTR => HyperlightExit :: Cancelled ( ) ,
712
+ libc:: EINTR => {
713
+ // If cancellation was not requested for this specific vm, the vcpu was interrupted because of stale signal
714
+ // that was meant to be delivered to a previous/other vcpu on this same thread, so let's ignore it
715
+ if !cancel_requested {
716
+ HyperlightExit :: Retry ( )
717
+ } else {
718
+ HyperlightExit :: Cancelled ( )
719
+ }
720
+ }
661
721
libc:: EAGAIN => HyperlightExit :: Retry ( ) ,
662
722
_ => {
663
723
crate :: debug!( "mshv Error - Details: Error: {} \n {:#?}" , e, & self ) ;
@@ -673,6 +733,10 @@ impl Hypervisor for HypervLinuxDriver {
673
733
self as & mut dyn Hypervisor
674
734
}
675
735
736
+ fn interrupt_handle ( & self ) -> Arc < dyn InterruptHandle > {
737
+ self . interrupt_handle . clone ( )
738
+ }
739
+
676
740
#[ cfg( crashdump) ]
677
741
fn get_memory_regions ( & self ) -> & [ MemoryRegion ] {
678
742
& self . mem_regions
@@ -727,6 +791,7 @@ impl Hypervisor for HypervLinuxDriver {
727
791
impl Drop for HypervLinuxDriver {
728
792
#[ instrument( skip_all, parent = Span :: current( ) , level = "Trace" ) ]
729
793
fn drop ( & mut self ) {
794
+ self . interrupt_handle . dropped . store ( true , Ordering :: Relaxed ) ;
730
795
for region in & self . mem_regions {
731
796
let mshv_region: mshv_user_mem_region = region. to_owned ( ) . into ( ) ;
732
797
match self . vm_fd . unmap_user_memory ( mshv_region) {
0 commit comments