@@ -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