1#![allow(dead_code)]
9
10use std::arch::x86_64::_rdtsc;
11use std::sync::LazyLock;
12
13use anyhow::anyhow;
14use anyhow::Result;
15use base::debug;
16use base::error;
17
18mod calibrate;
19mod cpuid;
20mod grouping;
21
22pub use calibrate::*;
23pub use cpuid::*;
24
25fn rdtsc_safe() -> u64 {
26 unsafe { _rdtsc() }
29}
30
31static TSC_STATE: LazyLock<Option<TscState>> = LazyLock::new(|| match calibrate_tsc_state() {
33 Ok(tsc_state) => {
34 debug!("Using calibrated tsc frequency: {} Hz", tsc_state.frequency);
35 for (core, offset) in tsc_state.offsets.iter().enumerate() {
36 debug!("Core {} has tsc offset of {:?} ns", core, offset);
37 }
38 Some(tsc_state)
39 }
40 Err(e) => {
41 error!("Failed to calibrate tsc state: {:#}", e);
42 None
43 }
44});
45
46pub fn tsc_frequency() -> Result<u64> {
48 let state = TSC_STATE
49 .as_ref()
50 .ok_or(anyhow!("TSC calibration failed"))?;
51 Ok(state.frequency)
52}
53
54pub fn tsc_state() -> Result<TscState> {
56 Ok(TSC_STATE
57 .as_ref()
58 .ok_or(anyhow!("TSC calibration failed"))?
59 .clone())
60}
61
62#[derive(Default, Debug)]
63pub struct TscSyncMitigations {
64 pub affinities: Vec<Option<Vec<usize>>>,
67 pub offsets: Vec<Option<u64>>,
69}
70
71impl TscSyncMitigations {
72 fn new(num_vcpus: usize) -> Self {
73 TscSyncMitigations {
74 affinities: vec![None; num_vcpus],
75 offsets: vec![None; num_vcpus],
76 }
77 }
78
79 pub fn get_vcpu_affinity(&self, cpu_id: usize) -> Option<Vec<usize>> {
80 self.affinities.get(cpu_id).unwrap().clone()
81 }
82
83 pub fn get_vcpu_tsc_offset(&self, cpu_id: usize) -> Option<u64> {
84 *self.offsets.get(cpu_id).unwrap()
85 }
86}
87
88pub fn get_tsc_sync_mitigations(tsc_state: &TscState, num_vcpus: usize) -> TscSyncMitigations {
91 tsc_sync_mitigations_inner(tsc_state, num_vcpus, rdtsc_safe)
92}
93
94fn tsc_sync_mitigations_inner(
95 tsc_state: &TscState,
96 num_vcpus: usize,
97 rdtsc: fn() -> u64,
98) -> TscSyncMitigations {
99 let mut mitigations = TscSyncMitigations::new(num_vcpus);
100 if tsc_state.core_grouping.size() == 1 {
103 return mitigations;
104 }
105
106 let largest_group = tsc_state.core_grouping.largest_group();
107 let num_cores = tsc_state.offsets.len();
108
109 if largest_group.cores.len() >= num_vcpus {
112 let affinity: Vec<usize> = largest_group.cores.iter().map(|core| core.core).collect();
113 for i in 0..num_vcpus {
114 mitigations.affinities[i] = Some(affinity.clone());
115 }
116 } else {
117 let host_tsc_now = rdtsc();
119
120 for i in 0..num_vcpus {
121 let pinned_core = i % num_cores;
124
125 mitigations.affinities[i] = Some(vec![pinned_core]);
126 mitigations.offsets[i] = Some(
136 0u64.wrapping_sub(host_tsc_now)
137 .wrapping_add(tsc_state.offsets[pinned_core].1.wrapping_neg() as i64 as u64),
142 );
143 }
144 }
145
146 mitigations
147}
148
149#[cfg(test)]
150mod tests {
151 use std::time::Duration;
152
153 use super::*;
154 use crate::tsc::grouping::CoreGroup;
155 use crate::tsc::grouping::CoreGrouping;
156 use crate::tsc::grouping::CoreOffset;
157
158 #[test]
159 fn test_sync_mitigation_set_offsets() {
160 let offsets = vec![(0, 0), (1, 1000), (2, -1000), (3, 2000)];
161 let state = TscState::new(1_000_000_000, offsets, Duration::from_nanos(20))
163 .expect("TscState::new should not fail for this test");
164
165 assert_eq!(
166 state.core_grouping,
167 CoreGrouping::new(vec![
168 CoreGroup {
169 cores: vec![CoreOffset {
170 core: 2,
171 offset: -1000
172 }]
173 },
174 CoreGroup {
175 cores: vec![CoreOffset { core: 0, offset: 0 }]
176 },
177 CoreGroup {
178 cores: vec![CoreOffset {
179 core: 1,
180 offset: 1000
181 }]
182 },
183 CoreGroup {
184 cores: vec![CoreOffset {
185 core: 3,
186 offset: 2000
187 }]
188 },
189 ])
190 .expect("CoreGrouping::new should not fail here")
191 );
192
193 fn fake_rdtsc() -> u64 {
194 u64::MAX
195 }
196
197 let mitigations = tsc_sync_mitigations_inner(&state, 4, fake_rdtsc);
198
199 let expected = [1, 1u64.wrapping_sub(1000), 1001u64, 1u64.wrapping_sub(2000)];
205
206 for (i, expect) in expected.iter().enumerate() {
207 assert_eq!(
208 mitigations
209 .get_vcpu_tsc_offset(i)
210 .unwrap_or_else(|| panic!("core {i} should have an offset of {expect}")),
211 *expect
212 );
213
214 assert_eq!(
215 mitigations
216 .get_vcpu_affinity(i)
217 .unwrap_or_else(|| panic!("core {i} should have an affinity of [{i}]")),
218 vec![i]
219 );
220 }
221 }
222
223 #[test]
224 fn test_sync_mitigation_large_group() {
225 let offsets = vec![
227 (0, 0),
228 (1, -1000),
229 (2, 1000),
230 (3, -1000),
231 (4, 2000),
232 (5, -1000),
233 (6, 3000),
234 (7, -1000),
235 ];
236 let state = TscState::new(1_000_000_000, offsets, Duration::from_nanos(20))
238 .expect("TscState::new should not fail for this test");
239
240 assert_eq!(
241 state.core_grouping,
242 CoreGrouping::new(vec![
243 CoreGroup {
244 cores: vec![
245 CoreOffset {
246 core: 1,
247 offset: -1000
248 },
249 CoreOffset {
250 core: 3,
251 offset: -1000
252 },
253 CoreOffset {
254 core: 5,
255 offset: -1000
256 },
257 CoreOffset {
258 core: 7,
259 offset: -1000
260 }
261 ]
262 },
263 CoreGroup {
264 cores: vec![CoreOffset { core: 0, offset: 0 }]
265 },
266 CoreGroup {
267 cores: vec![CoreOffset {
268 core: 2,
269 offset: 1000
270 }]
271 },
272 CoreGroup {
273 cores: vec![CoreOffset {
274 core: 4,
275 offset: 2000
276 }]
277 },
278 CoreGroup {
279 cores: vec![CoreOffset {
280 core: 6,
281 offset: 3000
282 }]
283 },
284 ])
285 .expect("CoreGrouping::new should not fail here")
286 );
287
288 fn fake_rdtsc() -> u64 {
289 u64::MAX
290 }
291
292 let num_vcpus = 4;
293 let mitigations = tsc_sync_mitigations_inner(&state, num_vcpus, fake_rdtsc);
294
295 let expected_affinity = vec![1, 3, 5, 7];
296 for i in 0..num_vcpus {
297 assert_eq!(
298 mitigations.get_vcpu_affinity(i).unwrap_or_else(|| panic!(
299 "core {i} should have an affinity of {expected_affinity:?}"
300 )),
301 expected_affinity
302 );
303 assert_eq!(mitigations.get_vcpu_tsc_offset(i), None);
304 }
305 }
306
307 #[test]
308 fn more_vcpus_than_cores() {
309 let offsets = vec![(0, 0), (1, 0), (2, 1000), (3, 2000)];
312 let state = TscState::new(1_000_000_000, offsets, Duration::from_nanos(20))
314 .expect("TscState::new should not fail for this test");
315
316 assert_eq!(
317 state.core_grouping,
318 CoreGrouping::new(vec![
319 CoreGroup {
320 cores: vec![
321 CoreOffset { core: 0, offset: 0 },
322 CoreOffset { core: 1, offset: 0 }
323 ]
324 },
325 CoreGroup {
326 cores: vec![CoreOffset {
327 core: 2,
328 offset: 1000
329 }]
330 },
331 CoreGroup {
332 cores: vec![CoreOffset {
333 core: 3,
334 offset: 2000
335 }]
336 },
337 ])
338 .expect("CoreGrouping::new should not fail here")
339 );
340
341 fn fake_rdtsc() -> u64 {
342 u64::MAX
343 }
344
345 let num_vcpus = 8;
347 let mitigations = tsc_sync_mitigations_inner(&state, num_vcpus, fake_rdtsc);
348 let expected_offsets = [1, 1, 1u64.wrapping_sub(1000), 1u64.wrapping_sub(2000)];
349
350 for i in 0..num_vcpus {
351 assert_eq!(
352 mitigations.get_vcpu_affinity(i).unwrap_or_else(|| panic!(
353 "core {} should have an affinity of {:?}",
354 i,
355 i % 4
356 )),
357 vec![i % 4]
359 );
360 assert_eq!(
361 mitigations.get_vcpu_tsc_offset(i).unwrap_or_else(|| panic!(
362 "core {} should have an offset of {:?}",
363 i,
364 expected_offsets[i % 4]
365 )),
366 expected_offsets[i % 4]
367 );
368 }
369 }
370}