@@ -338,8 +338,23 @@ pub trait InterruptHandle: Send + Sync {
338
338
#[ cfg( any( kvm, mshv) ) ]
339
339
#[ derive( Debug ) ]
340
340
pub ( 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 ,
343
358
/// Invariant: vcpu is running => `tid` is the thread on which it is running.
344
359
/// Note: multiple vms may have the same `tid`, but at most one vm will have `running` set to true.
345
360
tid : AtomicU64 ,
@@ -359,15 +374,60 @@ pub(super) struct LinuxInterruptHandle {
359
374
sig_rt_min_offset : u8 ,
360
375
}
361
376
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
+
362
408
#[ cfg( any( kvm, mshv) ) ]
363
409
impl InterruptHandle for LinuxInterruptHandle {
364
410
fn kill ( & self ) -> bool {
365
411
self . cancel_requested . store ( true , Ordering :: Relaxed ) ;
366
412
367
413
let signal_number = libc:: SIGRTMIN ( ) + self . sig_rt_min_offset as libc:: c_int ;
368
414
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
+ }
369
430
370
- while self . running . load ( Ordering :: Relaxed ) {
371
431
log:: info!( "Sending signal to kill vcpu thread..." ) ;
372
432
sent_signal = true ;
373
433
unsafe {
0 commit comments