@@ -338,8 +338,23 @@ pub trait InterruptHandle: Send + Sync {
338338#[ cfg( any( kvm, mshv) ) ]
339339#[ derive( Debug ) ]
340340pub ( super ) struct LinuxInterruptHandle {
341- /// Invariant: vcpu is running => `running` is true. (Neither converse nor inverse is true)
342- running : AtomicBool ,
341+ /// Invariant: vcpu is running => most significant bit (63) of `running` is set. (Neither converse nor inverse is true)
342+ ///
343+ /// Additionally, bit 0-62 tracks how many times the VCPU has been run. Incremented each time `run()` is called.
344+ ///
345+ /// This prevents an ABA problem where:
346+ /// 1. The VCPU is running (generation N),
347+ /// 2. It gets cancelled,
348+ /// 3. Then quickly restarted (generation N+1),
349+ /// before the original thread has observed that it was cancelled.
350+ ///
351+ /// Without this generation counter, the interrupt logic might assume the VCPU is still
352+ /// in the *original* run (generation N), see that it's `running`, and re-send the signal.
353+ /// But the new VCPU run (generation N+1) would treat this as a stale signal and ignore it,
354+ /// potentially causing an infinite loop where no effective interrupt is delivered.
355+ ///
356+ /// Invariant: If the VCPU is running, `run_generation[bit 0-62]` matches the current run's generation.
357+ running : AtomicU64 ,
343358 /// Invariant: vcpu is running => `tid` is the thread on which it is running.
344359 /// Note: multiple vms may have the same `tid`, but at most one vm will have `running` set to true.
345360 tid : AtomicU64 ,
@@ -359,15 +374,60 @@ pub(super) struct LinuxInterruptHandle {
359374 sig_rt_min_offset : u8 ,
360375}
361376
377+ impl LinuxInterruptHandle {
378+ const RUNNING_BIT : u64 = 1 << 63 ;
379+ const MAX_GENERATION : u64 = Self :: RUNNING_BIT - 1 ;
380+
381+ // set running to true and increment the generation. Generation will wrap around at `MAX_GENERATION`.
382+ fn set_running_and_increment_generation ( & self ) -> std:: result:: Result < u64 , u64 > {
383+ self . running
384+ . fetch_update ( Ordering :: Relaxed , Ordering :: Relaxed , |raw| {
385+ let generation = raw & !Self :: RUNNING_BIT ;
386+ if generation == Self :: MAX_GENERATION {
387+ // restart generation from 0
388+ return Some ( Self :: RUNNING_BIT ) ;
389+ }
390+ Some ( ( generation + 1 ) | Self :: RUNNING_BIT )
391+ } )
392+ }
393+
394+ // clear the running bit and return the generation
395+ fn clear_running_bit ( & self ) -> u64 {
396+ self . running
397+ . fetch_and ( !Self :: RUNNING_BIT , Ordering :: Relaxed )
398+ }
399+
400+ fn get_running_and_generation ( & self ) -> ( bool , u64 ) {
401+ let raw = self . running . load ( Ordering :: Relaxed ) ;
402+ let running = raw & Self :: RUNNING_BIT != 0 ;
403+ let generation = raw & !Self :: RUNNING_BIT ;
404+ ( running, generation)
405+ }
406+ }
407+
362408#[ cfg( any( kvm, mshv) ) ]
363409impl InterruptHandle for LinuxInterruptHandle {
364410 fn kill ( & self ) -> bool {
365411 self . cancel_requested . store ( true , Ordering :: Relaxed ) ;
366412
367413 let signal_number = libc:: SIGRTMIN ( ) + self . sig_rt_min_offset as libc:: c_int ;
368414 let mut sent_signal = false ;
415+ let mut target_generation: Option < u64 > = None ;
416+
417+ loop {
418+ let ( running, generation) = self . get_running_and_generation ( ) ;
419+
420+ if !running {
421+ break ;
422+ }
423+
424+ match target_generation {
425+ None => target_generation = Some ( generation) ,
426+ // prevent ABA problem
427+ Some ( expected) if expected != generation => break ,
428+ _ => { }
429+ }
369430
370- while self . running . load ( Ordering :: Relaxed ) {
371431 log:: info!( "Sending signal to kill vcpu thread..." ) ;
372432 sent_signal = true ;
373433 unsafe {
0 commit comments