@@ -25,6 +25,8 @@ extern crate mshv_bindings3 as mshv_bindings;
2525extern  crate  mshv_ioctls3 as  mshv_ioctls; 
2626
2727use  std:: fmt:: { Debug ,  Formatter } ; 
28+ use  std:: sync:: atomic:: { AtomicBool ,  AtomicU64 ,  Ordering } ; 
29+ use  std:: sync:: Arc ; 
2830
2931use  log:: { error,  LevelFilter } ; 
3032#[ cfg( mshv2) ]  
@@ -46,7 +48,7 @@ use mshv_bindings::{
4648    hv_partition_property_code_HV_PARTITION_PROPERTY_SYNTHETIC_PROC_FEATURES, 
4749    hv_partition_synthetic_processor_features, 
4850} ; 
49- use  mshv_ioctls:: { Mshv ,  VcpuFd ,  VmFd } ; 
51+ use  mshv_ioctls:: { Mshv ,  MshvError ,   VcpuFd ,  VmFd } ; 
5052use  tracing:: { instrument,  Span } ; 
5153
5254use  super :: fpu:: { FP_CONTROL_WORD_DEFAULT ,  FP_TAG_WORD_DEFAULT ,  MXCSR_DEFAULT } ; 
@@ -56,8 +58,9 @@ use super::gdb::{DebugCommChannel, DebugMsg, DebugResponse, GuestDebug, MshvDebu
5658use  super :: handlers:: DbgMemAccessHandlerWrapper ; 
5759use  super :: handlers:: { MemAccessHandlerWrapper ,  OutBHandlerWrapper } ; 
5860use  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 , 
6164} ; 
6265use  crate :: hypervisor:: HyperlightExit ; 
6366use  crate :: mem:: memory_region:: { MemoryRegion ,  MemoryRegionFlags } ; 
@@ -285,13 +288,14 @@ pub(crate) fn is_hypervisor_present() -> bool {
285288
286289/// A Hypervisor driver for HyperV-on-Linux. This hypervisor is often 
287290/// called the Microsoft Hypervisor (MSHV) 
288- pub ( super )  struct  HypervLinuxDriver  { 
291+ pub ( crate )  struct  HypervLinuxDriver  { 
289292    _mshv :  Mshv , 
290293    vm_fd :  VmFd , 
291294    vcpu_fd :  VcpuFd , 
292295    entrypoint :  u64 , 
293296    mem_regions :  Vec < MemoryRegion > , 
294297    orig_rsp :  GuestPtr , 
298+     interrupt_handle :  Arc < LinuxInterruptHandle > , 
295299
296300    #[ cfg( gdb) ]  
297301    debug :  Option < MshvDebug > , 
@@ -309,7 +313,7 @@ impl HypervLinuxDriver {
309313     /// `apply_registers` method to do that, or more likely call 
310314     /// `initialise` to do it for you. 
311315     #[ instrument( skip_all,  parent = Span :: current( ) ,  level = "Trace" ) ]  
312-     pub ( super )  fn  new ( 
316+     pub ( crate )  fn  new ( 
313317        mem_regions :  Vec < MemoryRegion > , 
314318        entrypoint_ptr :  GuestPtr , 
315319        rsp_ptr :  GuestPtr , 
@@ -389,6 +393,12 @@ impl HypervLinuxDriver {
389393            mem_regions, 
390394            entrypoint :  entrypoint_ptr. absolute ( ) ?, 
391395            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+             } ) , 
392402
393403            #[ cfg( gdb) ]  
394404            debug, 
@@ -572,15 +582,57 @@ impl Hypervisor for HypervLinuxDriver {
572582        #[ cfg( gdb) ]  
573583        const  EXCEPTION_INTERCEPT :  hv_message_type  = hv_message_type_HVMSG_X64_EXCEPTION_INTERCEPT; 
574584
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 ( ) 
579616        } ; 
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 { 
584636            Ok ( m)  => match  m. header . message_type  { 
585637                HALT_MESSAGE  => { 
586638                    crate :: debug!( "mshv - Halt Details : {:#?}" ,  & self ) ; 
@@ -657,7 +709,15 @@ impl Hypervisor for HypervLinuxDriver {
657709            } , 
658710            Err ( e)  => match  e. errno ( )  { 
659711                // 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+                 } 
661721                libc:: EAGAIN  => HyperlightExit :: Retry ( ) , 
662722                _ => { 
663723                    crate :: debug!( "mshv Error - Details: Error: {} \n  {:#?}" ,  e,  & self ) ; 
@@ -673,6 +733,10 @@ impl Hypervisor for HypervLinuxDriver {
673733        self  as  & mut  dyn  Hypervisor 
674734    } 
675735
736+     fn  interrupt_handle ( & self )  -> Arc < dyn  InterruptHandle >  { 
737+         self . interrupt_handle . clone ( ) 
738+     } 
739+ 
676740    #[ cfg( crashdump) ]  
677741    fn  get_memory_regions ( & self )  -> & [ MemoryRegion ]  { 
678742        & self . mem_regions 
@@ -727,6 +791,7 @@ impl Hypervisor for HypervLinuxDriver {
727791impl  Drop  for  HypervLinuxDriver  { 
728792    #[ instrument( skip_all,  parent = Span :: current( ) ,  level = "Trace" ) ]  
729793    fn  drop ( & mut  self )  { 
794+         self . interrupt_handle . dropped . store ( true ,  Ordering :: Relaxed ) ; 
730795        for  region in  & self . mem_regions  { 
731796            let  mshv_region:  mshv_user_mem_region  = region. to_owned ( ) . into ( ) ; 
732797            match  self . vm_fd . unmap_user_memory ( mshv_region)  { 
0 commit comments