@@ -19,6 +19,7 @@ use serde::{Deserialize, Serialize};
19
19
use crate :: arch:: x86_64:: interrupts;
20
20
use crate :: arch:: x86_64:: msr:: { create_boot_msr_entries, MsrError } ;
21
21
use crate :: arch:: x86_64:: regs:: { SetupFpuError , SetupRegistersError , SetupSpecialRegistersError } ;
22
+ use crate :: arch_gen:: x86:: msr_index:: MSR_IA32_TSC_DEADLINE ;
22
23
use crate :: cpu_config:: x86_64:: { cpuid, CpuConfiguration } ;
23
24
use crate :: logger:: { IncMetric , METRICS } ;
24
25
use crate :: vstate:: memory:: { Address , GuestAddress , GuestMemoryMmap } ;
@@ -143,6 +144,36 @@ pub(super) struct Peripherals {
143
144
pub mmio_bus : Option < crate :: devices:: Bus > ,
144
145
}
145
146
147
+ /// If any of per-vCPU IA32_TSC_DEADLINE MSRs are zero, update them
148
+ /// with the maximum value observed across all vCPUs.
149
+ ///
150
+ /// We observed that sometimes when taking a snapshot,
151
+ /// the IA32_TSC_DEADLINE MSR is cleared, but the interrupt is not
152
+ /// delivered to the guest, leading to a situation where one
153
+ /// of the vCPUs never receives TSC interrupts after restoring,
154
+ /// until the MSR is updated externally, eg by setting the system time.
155
+ pub fn fix_zero_tsc_deadline_msr ( vcpu_states : & mut Vec < VcpuState > ) {
156
+ let max_tsc_deadline_val = vcpu_states
157
+ . iter ( )
158
+ . flat_map ( |state| state. saved_msrs . iter ( ) . flat_map ( |msrs| msrs. as_slice ( ) ) )
159
+ . filter ( |msr| msr. index == MSR_IA32_TSC_DEADLINE )
160
+ . map ( |msr| msr. data )
161
+ . max ( )
162
+ . unwrap_or ( 0 ) ;
163
+
164
+ for state in vcpu_states. iter_mut ( ) {
165
+ for msr in state
166
+ . saved_msrs
167
+ . iter_mut ( )
168
+ . flat_map ( |msrs| msrs. as_mut_slice ( ) )
169
+ {
170
+ if msr. index == MSR_IA32_TSC_DEADLINE && msr. data == 0 {
171
+ msr. data = max_tsc_deadline_val;
172
+ }
173
+ }
174
+ }
175
+ }
176
+
146
177
impl KvmVcpu {
147
178
/// Constructs a new kvm vcpu with arch specific functionality.
148
179
///
@@ -490,6 +521,7 @@ impl KvmVcpu {
490
521
. set_lapic ( & state. lapic )
491
522
. map_err ( KvmVcpuError :: VcpuSetLapic ) ?;
492
523
for msrs in & state. saved_msrs {
524
+ println ! ( "{:?}" , msrs. as_slice( ) ) ;
493
525
let nmsrs = self . fd . set_msrs ( msrs) . map_err ( KvmVcpuError :: VcpuSetMsrs ) ?;
494
526
if nmsrs < msrs. as_fam_struct_ref ( ) . nmsrs as usize {
495
527
return Err ( KvmVcpuError :: VcpuSetMsrsIncomplete ) ;
@@ -594,10 +626,12 @@ mod tests {
594
626
595
627
use std:: os:: unix:: io:: AsRawFd ;
596
628
629
+ use kvm_bindings:: kvm_msr_entry;
597
630
use kvm_ioctls:: Cap ;
598
631
599
632
use super :: * ;
600
633
use crate :: arch:: x86_64:: cpu_model:: CpuModel ;
634
+ use crate :: arch_gen:: x86:: msr_index:: MSR_TSC_AUX ;
601
635
use crate :: cpu_config:: templates:: {
602
636
CpuConfiguration , CpuTemplateType , CustomCpuTemplate , GetCpuTemplate , GuestConfigError ,
603
637
StaticCpuTemplate ,
@@ -949,4 +983,66 @@ mod tests {
949
983
}
950
984
}
951
985
}
986
+
987
+ macro_rules! vcpu_state_with_msr {
988
+ ( $index: expr, $data: expr) => {
989
+ VcpuState {
990
+ saved_msrs: vec![ Msrs :: from_entries( & [ kvm_msr_entry {
991
+ index: $index,
992
+ data: $data,
993
+ ..Default :: default ( )
994
+ } ] )
995
+ . unwrap( ) ] ,
996
+ ..Default :: default ( )
997
+ }
998
+ } ;
999
+ }
1000
+
1001
+ macro_rules! assert_vcpu_state_msr {
1002
+ ( $vcpu_state: expr, $msr_index: expr, $expected_value: expr) => {
1003
+ assert_eq!(
1004
+ $vcpu_state
1005
+ . saved_msrs
1006
+ . iter( )
1007
+ . flat_map( |msrs_grp| msrs_grp. as_slice( ) )
1008
+ . filter( |msr| msr. index == $msr_index)
1009
+ . map( |msr| msr. data)
1010
+ . next( )
1011
+ . unwrap_or( 0 ) ,
1012
+ $expected_value
1013
+ ) ;
1014
+ } ;
1015
+ }
1016
+
1017
+ #[ test]
1018
+ fn test_fix_zero_tsc_deadline_msr_chooses_max ( ) {
1019
+ let mut vcpu_states = vec ! [
1020
+ vcpu_state_with_msr!( MSR_IA32_TSC_DEADLINE , 0 ) ,
1021
+ vcpu_state_with_msr!( MSR_IA32_TSC_DEADLINE , 1 ) ,
1022
+ vcpu_state_with_msr!( MSR_IA32_TSC_DEADLINE , 2 ) ,
1023
+ ] ;
1024
+
1025
+ fix_zero_tsc_deadline_msr ( & mut vcpu_states) ;
1026
+
1027
+ // We expect for vCPU 0 MSR to be updated with vCPU 2 MSR value
1028
+ assert_vcpu_state_msr ! ( vcpu_states[ 0 ] , MSR_IA32_TSC_DEADLINE , 2 ) ;
1029
+ assert_vcpu_state_msr ! ( vcpu_states[ 1 ] , MSR_IA32_TSC_DEADLINE , 1 ) ;
1030
+ assert_vcpu_state_msr ! ( vcpu_states[ 2 ] , MSR_IA32_TSC_DEADLINE , 2 ) ;
1031
+ }
1032
+
1033
+ #[ test]
1034
+ fn test_fix_zero_tsc_deadline_msr_does_not_fix_other_msrs ( ) {
1035
+ let mut vcpu_states = vec ! [
1036
+ vcpu_state_with_msr!( MSR_TSC_AUX , 0 ) ,
1037
+ vcpu_state_with_msr!( MSR_TSC_AUX , 1 ) ,
1038
+ vcpu_state_with_msr!( MSR_TSC_AUX , 2 ) ,
1039
+ ] ;
1040
+
1041
+ fix_zero_tsc_deadline_msr ( & mut vcpu_states) ;
1042
+
1043
+ // We expect for all MSRs to remain the same
1044
+ assert_vcpu_state_msr ! ( vcpu_states[ 0 ] , MSR_TSC_AUX , 0 ) ;
1045
+ assert_vcpu_state_msr ! ( vcpu_states[ 1 ] , MSR_TSC_AUX , 1 ) ;
1046
+ assert_vcpu_state_msr ! ( vcpu_states[ 2 ] , MSR_TSC_AUX , 2 ) ;
1047
+ }
952
1048
}
0 commit comments