xref: /aosp_15_r20/external/crosvm/devices/src/tsc/calibrate.rs (revision bb4ee6a4ae7042d18b07a98463b9c8b875e44b39)
1*bb4ee6a4SAndroid Build Coastguard Worker // Copyright 2022 The ChromiumOS Authors
2*bb4ee6a4SAndroid Build Coastguard Worker // Use of this source code is governed by a BSD-style license that can be
3*bb4ee6a4SAndroid Build Coastguard Worker // found in the LICENSE file.
4*bb4ee6a4SAndroid Build Coastguard Worker 
5*bb4ee6a4SAndroid Build Coastguard Worker use std::collections::HashSet;
6*bb4ee6a4SAndroid Build Coastguard Worker use std::iter::FromIterator;
7*bb4ee6a4SAndroid Build Coastguard Worker use std::time::Duration;
8*bb4ee6a4SAndroid Build Coastguard Worker use std::time::Instant;
9*bb4ee6a4SAndroid Build Coastguard Worker 
10*bb4ee6a4SAndroid Build Coastguard Worker use anyhow::anyhow;
11*bb4ee6a4SAndroid Build Coastguard Worker use anyhow::Context;
12*bb4ee6a4SAndroid Build Coastguard Worker use anyhow::Result;
13*bb4ee6a4SAndroid Build Coastguard Worker use base::set_cpu_affinity;
14*bb4ee6a4SAndroid Build Coastguard Worker use base::warn;
15*bb4ee6a4SAndroid Build Coastguard Worker use remain::sorted;
16*bb4ee6a4SAndroid Build Coastguard Worker use thiserror::Error;
17*bb4ee6a4SAndroid Build Coastguard Worker 
18*bb4ee6a4SAndroid Build Coastguard Worker use super::grouping::*;
19*bb4ee6a4SAndroid Build Coastguard Worker use super::rdtsc_safe;
20*bb4ee6a4SAndroid Build Coastguard Worker 
21*bb4ee6a4SAndroid Build Coastguard Worker const TSC_CALIBRATION_SAMPLES: usize = 10;
22*bb4ee6a4SAndroid Build Coastguard Worker const TSC_CALIBRATION_DURATION: Duration = Duration::from_millis(100);
23*bb4ee6a4SAndroid Build Coastguard Worker // remove data that is outside 3 standard deviations off the median
24*bb4ee6a4SAndroid Build Coastguard Worker const TSC_CALIBRATION_STANDARD_DEVIATION_LIMIT: f64 = 3.0;
25*bb4ee6a4SAndroid Build Coastguard Worker // We consider two TSC cores to be in sync if they are within 2 microseconds of each other.
26*bb4ee6a4SAndroid Build Coastguard Worker // An optimal context switch takes about 1-3 microseconds.
27*bb4ee6a4SAndroid Build Coastguard Worker const TSC_OFFSET_GROUPING_THRESHOLD: Duration = Duration::from_micros(2);
28*bb4ee6a4SAndroid Build Coastguard Worker 
29*bb4ee6a4SAndroid Build Coastguard Worker #[sorted]
30*bb4ee6a4SAndroid Build Coastguard Worker #[derive(Error, Debug)]
31*bb4ee6a4SAndroid Build Coastguard Worker pub enum TscCalibrationError {
32*bb4ee6a4SAndroid Build Coastguard Worker     /// Received `err` when setting the cpu affinity to `core`
33*bb4ee6a4SAndroid Build Coastguard Worker     #[error("failed to set thread cpu affinity to core {core}: {err}")]
34*bb4ee6a4SAndroid Build Coastguard Worker     SetCpuAffinityError { core: usize, err: base::Error },
35*bb4ee6a4SAndroid Build Coastguard Worker }
36*bb4ee6a4SAndroid Build Coastguard Worker 
37*bb4ee6a4SAndroid Build Coastguard Worker /// Get the standard deviation of a `Vec<T>`.
standard_deviation<T: num_traits::ToPrimitive + num_traits::Num + Copy>(items: &[T]) -> f6438*bb4ee6a4SAndroid Build Coastguard Worker pub fn standard_deviation<T: num_traits::ToPrimitive + num_traits::Num + Copy>(items: &[T]) -> f64 {
39*bb4ee6a4SAndroid Build Coastguard Worker     let sum: T = items.iter().fold(T::zero(), |acc: T, elem| acc + *elem);
40*bb4ee6a4SAndroid Build Coastguard Worker     let count = items.len();
41*bb4ee6a4SAndroid Build Coastguard Worker 
42*bb4ee6a4SAndroid Build Coastguard Worker     let mean: f64 = sum.to_f64().unwrap_or(0.0) / count as f64;
43*bb4ee6a4SAndroid Build Coastguard Worker 
44*bb4ee6a4SAndroid Build Coastguard Worker     let variance = items
45*bb4ee6a4SAndroid Build Coastguard Worker         .iter()
46*bb4ee6a4SAndroid Build Coastguard Worker         .map(|x| {
47*bb4ee6a4SAndroid Build Coastguard Worker             let diff = mean - (x.to_f64().unwrap_or(0.0));
48*bb4ee6a4SAndroid Build Coastguard Worker             diff * diff
49*bb4ee6a4SAndroid Build Coastguard Worker         })
50*bb4ee6a4SAndroid Build Coastguard Worker         .sum::<f64>();
51*bb4ee6a4SAndroid Build Coastguard Worker     (variance / count as f64).sqrt()
52*bb4ee6a4SAndroid Build Coastguard Worker }
53*bb4ee6a4SAndroid Build Coastguard Worker 
sort_and_get_bounds(items: &mut [i128], stdev_limit: f64) -> (f64, f64)54*bb4ee6a4SAndroid Build Coastguard Worker fn sort_and_get_bounds(items: &mut [i128], stdev_limit: f64) -> (f64, f64) {
55*bb4ee6a4SAndroid Build Coastguard Worker     items.sort_unstable();
56*bb4ee6a4SAndroid Build Coastguard Worker     let median = items[items.len() / 2];
57*bb4ee6a4SAndroid Build Coastguard Worker 
58*bb4ee6a4SAndroid Build Coastguard Worker     let standard_deviation = standard_deviation(items);
59*bb4ee6a4SAndroid Build Coastguard Worker     let lower_bound = median as f64 - stdev_limit * standard_deviation;
60*bb4ee6a4SAndroid Build Coastguard Worker     let upper_bound = median as f64 + stdev_limit * standard_deviation;
61*bb4ee6a4SAndroid Build Coastguard Worker     (lower_bound, upper_bound)
62*bb4ee6a4SAndroid Build Coastguard Worker }
63*bb4ee6a4SAndroid Build Coastguard Worker 
64*bb4ee6a4SAndroid Build Coastguard Worker /// Represents the host monotonic time and the TSC value at a single moment in time.
65*bb4ee6a4SAndroid Build Coastguard Worker #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
66*bb4ee6a4SAndroid Build Coastguard Worker struct TscMoment {
67*bb4ee6a4SAndroid Build Coastguard Worker     time: Instant,
68*bb4ee6a4SAndroid Build Coastguard Worker     tsc: u64,
69*bb4ee6a4SAndroid Build Coastguard Worker }
70*bb4ee6a4SAndroid Build Coastguard Worker 
71*bb4ee6a4SAndroid Build Coastguard Worker impl TscMoment {
now(rdtsc: fn() -> u64) -> Self72*bb4ee6a4SAndroid Build Coastguard Worker     fn now(rdtsc: fn() -> u64) -> Self {
73*bb4ee6a4SAndroid Build Coastguard Worker         TscMoment {
74*bb4ee6a4SAndroid Build Coastguard Worker             time: Instant::now(),
75*bb4ee6a4SAndroid Build Coastguard Worker             tsc: rdtsc(),
76*bb4ee6a4SAndroid Build Coastguard Worker         }
77*bb4ee6a4SAndroid Build Coastguard Worker     }
78*bb4ee6a4SAndroid Build Coastguard Worker 
79*bb4ee6a4SAndroid Build Coastguard Worker     /// Measure the tsc frequency using two `TscMoment`s.
measure_tsc_frequency(first: &TscMoment, second: &TscMoment) -> i12880*bb4ee6a4SAndroid Build Coastguard Worker     fn measure_tsc_frequency(first: &TscMoment, second: &TscMoment) -> i128 {
81*bb4ee6a4SAndroid Build Coastguard Worker         // handle case where first is actually second in time
82*bb4ee6a4SAndroid Build Coastguard Worker         let (first, second) = if first.time > second.time {
83*bb4ee6a4SAndroid Build Coastguard Worker             (second, first)
84*bb4ee6a4SAndroid Build Coastguard Worker         } else {
85*bb4ee6a4SAndroid Build Coastguard Worker             (first, second)
86*bb4ee6a4SAndroid Build Coastguard Worker         };
87*bb4ee6a4SAndroid Build Coastguard Worker 
88*bb4ee6a4SAndroid Build Coastguard Worker         let time_delta = second.time - first.time;
89*bb4ee6a4SAndroid Build Coastguard Worker         let tsc_delta = second.tsc as i128 - first.tsc as i128;
90*bb4ee6a4SAndroid Build Coastguard Worker 
91*bb4ee6a4SAndroid Build Coastguard Worker         tsc_delta * 1_000_000_000i128 / time_delta.as_nanos() as i128
92*bb4ee6a4SAndroid Build Coastguard Worker     }
93*bb4ee6a4SAndroid Build Coastguard Worker 
94*bb4ee6a4SAndroid Build Coastguard Worker     /// Measure the tsc offset using two `TscMoment`s and the TSC frequency.
measure_tsc_offset(first: &TscMoment, second: &TscMoment, tsc_frequency: u64) -> i12895*bb4ee6a4SAndroid Build Coastguard Worker     fn measure_tsc_offset(first: &TscMoment, second: &TscMoment, tsc_frequency: u64) -> i128 {
96*bb4ee6a4SAndroid Build Coastguard Worker         // handle case where first is actually second in time
97*bb4ee6a4SAndroid Build Coastguard Worker         let (first, second) = if first.time > second.time {
98*bb4ee6a4SAndroid Build Coastguard Worker             (second, first)
99*bb4ee6a4SAndroid Build Coastguard Worker         } else {
100*bb4ee6a4SAndroid Build Coastguard Worker             (first, second)
101*bb4ee6a4SAndroid Build Coastguard Worker         };
102*bb4ee6a4SAndroid Build Coastguard Worker 
103*bb4ee6a4SAndroid Build Coastguard Worker         let tsc_delta = second.tsc as i128 - first.tsc as i128;
104*bb4ee6a4SAndroid Build Coastguard Worker         let time_delta_as_tsc_ticks =
105*bb4ee6a4SAndroid Build Coastguard Worker             (second.time - first.time).as_nanos() * tsc_frequency as u128 / 1_000_000_000u128;
106*bb4ee6a4SAndroid Build Coastguard Worker         tsc_delta - time_delta_as_tsc_ticks as i128
107*bb4ee6a4SAndroid Build Coastguard Worker     }
108*bb4ee6a4SAndroid Build Coastguard Worker }
109*bb4ee6a4SAndroid Build Coastguard Worker 
110*bb4ee6a4SAndroid Build Coastguard Worker #[derive(Default, Debug, Clone)]
111*bb4ee6a4SAndroid Build Coastguard Worker pub struct TscState {
112*bb4ee6a4SAndroid Build Coastguard Worker     pub frequency: u64,
113*bb4ee6a4SAndroid Build Coastguard Worker     pub offsets: Vec<(usize, i128)>,
114*bb4ee6a4SAndroid Build Coastguard Worker     pub core_grouping: CoreGrouping,
115*bb4ee6a4SAndroid Build Coastguard Worker }
116*bb4ee6a4SAndroid Build Coastguard Worker 
117*bb4ee6a4SAndroid Build Coastguard Worker impl TscState {
new( tsc_frequency: u64, offsets: Vec<(usize, i128)>, in_sync_threshold: Duration, ) -> Result<Self>118*bb4ee6a4SAndroid Build Coastguard Worker     pub(crate) fn new(
119*bb4ee6a4SAndroid Build Coastguard Worker         tsc_frequency: u64,
120*bb4ee6a4SAndroid Build Coastguard Worker         offsets: Vec<(usize, i128)>,
121*bb4ee6a4SAndroid Build Coastguard Worker         in_sync_threshold: Duration,
122*bb4ee6a4SAndroid Build Coastguard Worker     ) -> Result<Self> {
123*bb4ee6a4SAndroid Build Coastguard Worker         let core_grouping = group_core_offsets(&offsets, in_sync_threshold, tsc_frequency)
124*bb4ee6a4SAndroid Build Coastguard Worker             .context("Failed to group cores by their TSC offsets")?;
125*bb4ee6a4SAndroid Build Coastguard Worker         Ok(TscState {
126*bb4ee6a4SAndroid Build Coastguard Worker             frequency: tsc_frequency,
127*bb4ee6a4SAndroid Build Coastguard Worker             offsets,
128*bb4ee6a4SAndroid Build Coastguard Worker             core_grouping,
129*bb4ee6a4SAndroid Build Coastguard Worker         })
130*bb4ee6a4SAndroid Build Coastguard Worker     }
131*bb4ee6a4SAndroid Build Coastguard Worker }
132*bb4ee6a4SAndroid Build Coastguard Worker 
133*bb4ee6a4SAndroid Build Coastguard Worker /// Calibrate the TSC frequency of `core`.
134*bb4ee6a4SAndroid Build Coastguard Worker ///
135*bb4ee6a4SAndroid Build Coastguard Worker /// This function first pins itself to `core`, generates `num_samples` start `TscMoment`s, sleeps
136*bb4ee6a4SAndroid Build Coastguard Worker /// for `calibration_duration`, and then generates `num_samples` end `TscMoment`s. For each pair
137*bb4ee6a4SAndroid Build Coastguard Worker /// of start and end moments, a TSC frequency value is calculated. Any frequencies that are
138*bb4ee6a4SAndroid Build Coastguard Worker /// outside of `stddev_limit` standard deviations from the median offset are discarded, because
139*bb4ee6a4SAndroid Build Coastguard Worker /// they may represent an interrupt that occurred while a TscMoment was generated. The remaining
140*bb4ee6a4SAndroid Build Coastguard Worker /// non-discarded frequencies are then averaged. The function returns the TSC frequency average, as
141*bb4ee6a4SAndroid Build Coastguard Worker /// well as a Vec of `TscMoment`s, which are all of the end moments that were associated with at
142*bb4ee6a4SAndroid Build Coastguard Worker /// least one non-discarded frequency.
143*bb4ee6a4SAndroid Build Coastguard Worker ///
144*bb4ee6a4SAndroid Build Coastguard Worker /// # Arguments
145*bb4ee6a4SAndroid Build Coastguard Worker /// * `core` - Core that this function should run on.
146*bb4ee6a4SAndroid Build Coastguard Worker /// * `rdtsc` - Function for reading the TSC value, usually just runs RDTSC instruction.
147*bb4ee6a4SAndroid Build Coastguard Worker /// * `num_samples` - Number of start and end `TscMoment`s to generate.
148*bb4ee6a4SAndroid Build Coastguard Worker /// * `calibration_duration` - How long to sleep in between gathering start and end moments.
149*bb4ee6a4SAndroid Build Coastguard Worker /// * `stdev_limit` - Number of standard deviations outside of which frequencies are discarded.
calibrate_tsc_frequency( rdtsc: fn() -> u64, core: usize, num_samples: usize, calibration_duration: Duration, stdev_limit: f64, ) -> std::result::Result<(i128, Vec<TscMoment>), TscCalibrationError>150*bb4ee6a4SAndroid Build Coastguard Worker fn calibrate_tsc_frequency(
151*bb4ee6a4SAndroid Build Coastguard Worker     rdtsc: fn() -> u64,
152*bb4ee6a4SAndroid Build Coastguard Worker     core: usize,
153*bb4ee6a4SAndroid Build Coastguard Worker     num_samples: usize,
154*bb4ee6a4SAndroid Build Coastguard Worker     calibration_duration: Duration,
155*bb4ee6a4SAndroid Build Coastguard Worker     stdev_limit: f64,
156*bb4ee6a4SAndroid Build Coastguard Worker ) -> std::result::Result<(i128, Vec<TscMoment>), TscCalibrationError> {
157*bb4ee6a4SAndroid Build Coastguard Worker     set_cpu_affinity(vec![core])
158*bb4ee6a4SAndroid Build Coastguard Worker         .map_err(|e| TscCalibrationError::SetCpuAffinityError { core, err: e })?;
159*bb4ee6a4SAndroid Build Coastguard Worker 
160*bb4ee6a4SAndroid Build Coastguard Worker     let starts: Vec<TscMoment> = (0..num_samples).map(|_| TscMoment::now(rdtsc)).collect();
161*bb4ee6a4SAndroid Build Coastguard Worker 
162*bb4ee6a4SAndroid Build Coastguard Worker     std::thread::sleep(calibration_duration);
163*bb4ee6a4SAndroid Build Coastguard Worker 
164*bb4ee6a4SAndroid Build Coastguard Worker     let ends: Vec<TscMoment> = (0..num_samples).map(|_| TscMoment::now(rdtsc)).collect();
165*bb4ee6a4SAndroid Build Coastguard Worker 
166*bb4ee6a4SAndroid Build Coastguard Worker     let mut freqs = Vec::with_capacity(num_samples * num_samples);
167*bb4ee6a4SAndroid Build Coastguard Worker     for start in &starts {
168*bb4ee6a4SAndroid Build Coastguard Worker         for end in &ends {
169*bb4ee6a4SAndroid Build Coastguard Worker             freqs.push(TscMoment::measure_tsc_frequency(start, end))
170*bb4ee6a4SAndroid Build Coastguard Worker         }
171*bb4ee6a4SAndroid Build Coastguard Worker     }
172*bb4ee6a4SAndroid Build Coastguard Worker 
173*bb4ee6a4SAndroid Build Coastguard Worker     let (lower_bound, upper_bound) = sort_and_get_bounds(&mut freqs, stdev_limit);
174*bb4ee6a4SAndroid Build Coastguard Worker 
175*bb4ee6a4SAndroid Build Coastguard Worker     let mut good_samples: Vec<i128> = Vec::with_capacity(num_samples * num_samples);
176*bb4ee6a4SAndroid Build Coastguard Worker     let mut good_end_moments: HashSet<TscMoment> = HashSet::new();
177*bb4ee6a4SAndroid Build Coastguard Worker     for i in 0..num_samples {
178*bb4ee6a4SAndroid Build Coastguard Worker         for j in 0..num_samples {
179*bb4ee6a4SAndroid Build Coastguard Worker             let freq = freqs[i * num_samples + j];
180*bb4ee6a4SAndroid Build Coastguard Worker 
181*bb4ee6a4SAndroid Build Coastguard Worker             if lower_bound < (freq as f64) && (freq as f64) < upper_bound {
182*bb4ee6a4SAndroid Build Coastguard Worker                 good_end_moments.insert(ends[j]);
183*bb4ee6a4SAndroid Build Coastguard Worker                 good_samples.push(freq);
184*bb4ee6a4SAndroid Build Coastguard Worker             }
185*bb4ee6a4SAndroid Build Coastguard Worker         }
186*bb4ee6a4SAndroid Build Coastguard Worker     }
187*bb4ee6a4SAndroid Build Coastguard Worker 
188*bb4ee6a4SAndroid Build Coastguard Worker     Ok((
189*bb4ee6a4SAndroid Build Coastguard Worker         good_samples.iter().sum::<i128>() / good_samples.len() as i128,
190*bb4ee6a4SAndroid Build Coastguard Worker         Vec::from_iter(good_end_moments),
191*bb4ee6a4SAndroid Build Coastguard Worker     ))
192*bb4ee6a4SAndroid Build Coastguard Worker }
193*bb4ee6a4SAndroid Build Coastguard Worker 
194*bb4ee6a4SAndroid Build Coastguard Worker /// Measure the TSC offset for `core` from core 0 where `reference_moments` were gathered.
195*bb4ee6a4SAndroid Build Coastguard Worker ///
196*bb4ee6a4SAndroid Build Coastguard Worker /// This function first pins itself to `core`, then generates `num_samples` `TscMoment`s for this
197*bb4ee6a4SAndroid Build Coastguard Worker /// core, and then measures the TSC offset between those moments and all `reference_moments`. Any
198*bb4ee6a4SAndroid Build Coastguard Worker /// moments that are outside of `stddev_limit` standard deviations from the median offset are
199*bb4ee6a4SAndroid Build Coastguard Worker /// discarded, because they may represent an interrupt that occurred while a TscMoment was
200*bb4ee6a4SAndroid Build Coastguard Worker /// generated. The remaining offsets are averaged and returned as nanoseconds.
201*bb4ee6a4SAndroid Build Coastguard Worker ///
202*bb4ee6a4SAndroid Build Coastguard Worker /// # Arguments
203*bb4ee6a4SAndroid Build Coastguard Worker /// * `core` - Core that this function should run on.
204*bb4ee6a4SAndroid Build Coastguard Worker /// * `rdtsc` - Function for reading the TSC value, usually just runs RDTSC instruction.
205*bb4ee6a4SAndroid Build Coastguard Worker /// * `tsc_frequency` - TSC frequency measured from core 0.
206*bb4ee6a4SAndroid Build Coastguard Worker /// * `reference_moments` - `TscMoment`s gathered from core 0.
207*bb4ee6a4SAndroid Build Coastguard Worker /// * `num_samples` - Number of `TscMoment`s to generate on this thread for measuring the offset.
208*bb4ee6a4SAndroid Build Coastguard Worker /// * `stdev_limit` - Number of standard deviations outside of which offsets are discarded.
measure_tsc_offset( core: usize, rdtsc: fn() -> u64, tsc_frequency: u64, reference_moments: Vec<TscMoment>, num_samples: usize, stdev_limit: f64, ) -> std::result::Result<i128, TscCalibrationError>209*bb4ee6a4SAndroid Build Coastguard Worker fn measure_tsc_offset(
210*bb4ee6a4SAndroid Build Coastguard Worker     core: usize,
211*bb4ee6a4SAndroid Build Coastguard Worker     rdtsc: fn() -> u64,
212*bb4ee6a4SAndroid Build Coastguard Worker     tsc_frequency: u64,
213*bb4ee6a4SAndroid Build Coastguard Worker     reference_moments: Vec<TscMoment>,
214*bb4ee6a4SAndroid Build Coastguard Worker     num_samples: usize,
215*bb4ee6a4SAndroid Build Coastguard Worker     stdev_limit: f64,
216*bb4ee6a4SAndroid Build Coastguard Worker ) -> std::result::Result<i128, TscCalibrationError> {
217*bb4ee6a4SAndroid Build Coastguard Worker     set_cpu_affinity(vec![core])
218*bb4ee6a4SAndroid Build Coastguard Worker         .map_err(|e| TscCalibrationError::SetCpuAffinityError { core, err: e })?;
219*bb4ee6a4SAndroid Build Coastguard Worker 
220*bb4ee6a4SAndroid Build Coastguard Worker     let mut diffs: Vec<i128> = Vec::with_capacity(num_samples);
221*bb4ee6a4SAndroid Build Coastguard Worker 
222*bb4ee6a4SAndroid Build Coastguard Worker     for _ in 0..num_samples {
223*bb4ee6a4SAndroid Build Coastguard Worker         let now = TscMoment::now(rdtsc);
224*bb4ee6a4SAndroid Build Coastguard Worker         for reference_moment in &reference_moments {
225*bb4ee6a4SAndroid Build Coastguard Worker             diffs.push(TscMoment::measure_tsc_offset(
226*bb4ee6a4SAndroid Build Coastguard Worker                 reference_moment,
227*bb4ee6a4SAndroid Build Coastguard Worker                 &now,
228*bb4ee6a4SAndroid Build Coastguard Worker                 tsc_frequency,
229*bb4ee6a4SAndroid Build Coastguard Worker             ));
230*bb4ee6a4SAndroid Build Coastguard Worker         }
231*bb4ee6a4SAndroid Build Coastguard Worker     }
232*bb4ee6a4SAndroid Build Coastguard Worker 
233*bb4ee6a4SAndroid Build Coastguard Worker     let (lower_bound, upper_bound) = sort_and_get_bounds(&mut diffs, stdev_limit);
234*bb4ee6a4SAndroid Build Coastguard Worker 
235*bb4ee6a4SAndroid Build Coastguard Worker     let mut good_samples: Vec<i128> = Vec::with_capacity(num_samples);
236*bb4ee6a4SAndroid Build Coastguard Worker     for diff in &diffs {
237*bb4ee6a4SAndroid Build Coastguard Worker         if lower_bound < (*diff as f64) && (*diff as f64) < upper_bound {
238*bb4ee6a4SAndroid Build Coastguard Worker             good_samples.push(*diff);
239*bb4ee6a4SAndroid Build Coastguard Worker         }
240*bb4ee6a4SAndroid Build Coastguard Worker     }
241*bb4ee6a4SAndroid Build Coastguard Worker 
242*bb4ee6a4SAndroid Build Coastguard Worker     let average_diff = good_samples.iter().sum::<i128>() / good_samples.len() as i128;
243*bb4ee6a4SAndroid Build Coastguard Worker 
244*bb4ee6a4SAndroid Build Coastguard Worker     // Convert the diff to nanoseconds using the tsc_frequency
245*bb4ee6a4SAndroid Build Coastguard Worker     Ok(average_diff * 1_000_000_000 / tsc_frequency as i128)
246*bb4ee6a4SAndroid Build Coastguard Worker }
247*bb4ee6a4SAndroid Build Coastguard Worker 
248*bb4ee6a4SAndroid Build Coastguard Worker /// Calibrate the TSC state.
249*bb4ee6a4SAndroid Build Coastguard Worker ///
250*bb4ee6a4SAndroid Build Coastguard Worker /// This function first runs a TSC frequency calibration thread for 100ms, which is pinned to
251*bb4ee6a4SAndroid Build Coastguard Worker /// core0. The TSC calibration thread returns both the calibrated frequency, as well as a Vec of
252*bb4ee6a4SAndroid Build Coastguard Worker /// TscMoment objects which were validated to be accurate (meaning it's unlikely an interrupt
253*bb4ee6a4SAndroid Build Coastguard Worker /// occurred between moment's `time` and `tsc` values). This function then runs a tsc offset
254*bb4ee6a4SAndroid Build Coastguard Worker /// measurement thread for each core, which takes the TSC frequency and the Vec of TscMoments and
255*bb4ee6a4SAndroid Build Coastguard Worker /// measures whether or not the TSC values for that core are offset from core 0, and by how much.
256*bb4ee6a4SAndroid Build Coastguard Worker /// The frequency and the per-core offsets are returned as a TscState.
calibrate_tsc_state() -> Result<TscState>257*bb4ee6a4SAndroid Build Coastguard Worker pub fn calibrate_tsc_state() -> Result<TscState> {
258*bb4ee6a4SAndroid Build Coastguard Worker     calibrate_tsc_state_inner(
259*bb4ee6a4SAndroid Build Coastguard Worker         rdtsc_safe,
260*bb4ee6a4SAndroid Build Coastguard Worker         (0..base::number_of_logical_cores().context("Failed to get number of logical cores")?)
261*bb4ee6a4SAndroid Build Coastguard Worker             .collect(),
262*bb4ee6a4SAndroid Build Coastguard Worker     )
263*bb4ee6a4SAndroid Build Coastguard Worker }
264*bb4ee6a4SAndroid Build Coastguard Worker 
265*bb4ee6a4SAndroid Build Coastguard Worker /// Actually calibrate the TSC state.
266*bb4ee6a4SAndroid Build Coastguard Worker ///
267*bb4ee6a4SAndroid Build Coastguard Worker /// This function takes a customizable version of rdtsc and a specific set of cores to calibrate,
268*bb4ee6a4SAndroid Build Coastguard Worker /// which is helpful for testing calibration logic and error handling.
269*bb4ee6a4SAndroid Build Coastguard Worker ///
270*bb4ee6a4SAndroid Build Coastguard Worker /// # Arguments
271*bb4ee6a4SAndroid Build Coastguard Worker ///
272*bb4ee6a4SAndroid Build Coastguard Worker /// * `rdtsc` - Function for reading the TSC value, usually just runs RDTSC instruction.
273*bb4ee6a4SAndroid Build Coastguard Worker /// * `cores` - Cores to measure the TSC offset of.
calibrate_tsc_state_inner(rdtsc: fn() -> u64, cores: Vec<usize>) -> Result<TscState>274*bb4ee6a4SAndroid Build Coastguard Worker fn calibrate_tsc_state_inner(rdtsc: fn() -> u64, cores: Vec<usize>) -> Result<TscState> {
275*bb4ee6a4SAndroid Build Coastguard Worker     // For loops can't return values unfortunately
276*bb4ee6a4SAndroid Build Coastguard Worker     let mut calibration_contents: Option<(u64, Vec<TscMoment>)> = None;
277*bb4ee6a4SAndroid Build Coastguard Worker     for core in &cores {
278*bb4ee6a4SAndroid Build Coastguard Worker         // Copy the value of core to a moveable variable now.
279*bb4ee6a4SAndroid Build Coastguard Worker         let moved_core = *core;
280*bb4ee6a4SAndroid Build Coastguard Worker         let handle = std::thread::Builder::new()
281*bb4ee6a4SAndroid Build Coastguard Worker             .name(format!("tsc_calibration_core_{}", core).to_string())
282*bb4ee6a4SAndroid Build Coastguard Worker             .spawn(move || {
283*bb4ee6a4SAndroid Build Coastguard Worker                 calibrate_tsc_frequency(
284*bb4ee6a4SAndroid Build Coastguard Worker                     rdtsc,
285*bb4ee6a4SAndroid Build Coastguard Worker                     moved_core,
286*bb4ee6a4SAndroid Build Coastguard Worker                     TSC_CALIBRATION_SAMPLES,
287*bb4ee6a4SAndroid Build Coastguard Worker                     TSC_CALIBRATION_DURATION,
288*bb4ee6a4SAndroid Build Coastguard Worker                     TSC_CALIBRATION_STANDARD_DEVIATION_LIMIT,
289*bb4ee6a4SAndroid Build Coastguard Worker                 )
290*bb4ee6a4SAndroid Build Coastguard Worker             })
291*bb4ee6a4SAndroid Build Coastguard Worker             .map_err(|e| {
292*bb4ee6a4SAndroid Build Coastguard Worker                 anyhow!(
293*bb4ee6a4SAndroid Build Coastguard Worker                     "TSC frequency calibration thread for core {} failed: {:?}",
294*bb4ee6a4SAndroid Build Coastguard Worker                     core,
295*bb4ee6a4SAndroid Build Coastguard Worker                     e
296*bb4ee6a4SAndroid Build Coastguard Worker                 )
297*bb4ee6a4SAndroid Build Coastguard Worker             })?;
298*bb4ee6a4SAndroid Build Coastguard Worker 
299*bb4ee6a4SAndroid Build Coastguard Worker         match handle.join() {
300*bb4ee6a4SAndroid Build Coastguard Worker             Ok(calibrate_result) => match calibrate_result {
301*bb4ee6a4SAndroid Build Coastguard Worker                 Ok((freq, reference_moments)) => {
302*bb4ee6a4SAndroid Build Coastguard Worker                     if freq <= 0 {
303*bb4ee6a4SAndroid Build Coastguard Worker                         warn!(
304*bb4ee6a4SAndroid Build Coastguard Worker                             "TSC calibration on core {} resulted in TSC frequency of {} Hz, \
305*bb4ee6a4SAndroid Build Coastguard Worker                     trying on another core.",
306*bb4ee6a4SAndroid Build Coastguard Worker                             core, freq
307*bb4ee6a4SAndroid Build Coastguard Worker                         );
308*bb4ee6a4SAndroid Build Coastguard Worker                         continue;
309*bb4ee6a4SAndroid Build Coastguard Worker                     };
310*bb4ee6a4SAndroid Build Coastguard Worker                     calibration_contents = Some((freq as u64, reference_moments));
311*bb4ee6a4SAndroid Build Coastguard Worker                     break;
312*bb4ee6a4SAndroid Build Coastguard Worker                 }
313*bb4ee6a4SAndroid Build Coastguard Worker 
314*bb4ee6a4SAndroid Build Coastguard Worker                 Err(TscCalibrationError::SetCpuAffinityError { core, err }) => {
315*bb4ee6a4SAndroid Build Coastguard Worker                     // There are several legitimate reasons why it might not be possible for crosvm
316*bb4ee6a4SAndroid Build Coastguard Worker                     // to run on some cores:
317*bb4ee6a4SAndroid Build Coastguard Worker                     //  1. Some cores may be offline.
318*bb4ee6a4SAndroid Build Coastguard Worker                     //  2. On Windows, the process affinity mask may not contain all cores.
319*bb4ee6a4SAndroid Build Coastguard Worker                     //
320*bb4ee6a4SAndroid Build Coastguard Worker                     // We thus just warn in this situation.
321*bb4ee6a4SAndroid Build Coastguard Worker                     warn!(
322*bb4ee6a4SAndroid Build Coastguard Worker                         "Failed to set thread affinity to {} during tsc frequency calibration due \
323*bb4ee6a4SAndroid Build Coastguard Worker                             to {}. This core is probably offline.",
324*bb4ee6a4SAndroid Build Coastguard Worker                         core, err
325*bb4ee6a4SAndroid Build Coastguard Worker                     );
326*bb4ee6a4SAndroid Build Coastguard Worker                 }
327*bb4ee6a4SAndroid Build Coastguard Worker             },
328*bb4ee6a4SAndroid Build Coastguard Worker             // thread failed
329*bb4ee6a4SAndroid Build Coastguard Worker             Err(e) => {
330*bb4ee6a4SAndroid Build Coastguard Worker                 return Err(anyhow!(
331*bb4ee6a4SAndroid Build Coastguard Worker                     "TSC frequency calibration thread for core {} failed: {:?}",
332*bb4ee6a4SAndroid Build Coastguard Worker                     core,
333*bb4ee6a4SAndroid Build Coastguard Worker                     e
334*bb4ee6a4SAndroid Build Coastguard Worker                 ));
335*bb4ee6a4SAndroid Build Coastguard Worker             }
336*bb4ee6a4SAndroid Build Coastguard Worker         };
337*bb4ee6a4SAndroid Build Coastguard Worker     }
338*bb4ee6a4SAndroid Build Coastguard Worker 
339*bb4ee6a4SAndroid Build Coastguard Worker     let (freq, reference_moments) =
340*bb4ee6a4SAndroid Build Coastguard Worker         calibration_contents.ok_or(anyhow!("Failed to calibrate TSC frequency on all cores"))?;
341*bb4ee6a4SAndroid Build Coastguard Worker 
342*bb4ee6a4SAndroid Build Coastguard Worker     let mut offsets: Vec<(usize, i128)> = Vec::with_capacity(cores.len());
343*bb4ee6a4SAndroid Build Coastguard Worker     for core in cores {
344*bb4ee6a4SAndroid Build Coastguard Worker         let thread_reference_moments = reference_moments.clone();
345*bb4ee6a4SAndroid Build Coastguard Worker         let handle = std::thread::Builder::new()
346*bb4ee6a4SAndroid Build Coastguard Worker             .name(format!("measure_tsc_offset_core_{}", core).to_string())
347*bb4ee6a4SAndroid Build Coastguard Worker             .spawn(move || {
348*bb4ee6a4SAndroid Build Coastguard Worker                 measure_tsc_offset(
349*bb4ee6a4SAndroid Build Coastguard Worker                     core,
350*bb4ee6a4SAndroid Build Coastguard Worker                     rdtsc,
351*bb4ee6a4SAndroid Build Coastguard Worker                     freq,
352*bb4ee6a4SAndroid Build Coastguard Worker                     thread_reference_moments,
353*bb4ee6a4SAndroid Build Coastguard Worker                     TSC_CALIBRATION_SAMPLES,
354*bb4ee6a4SAndroid Build Coastguard Worker                     TSC_CALIBRATION_STANDARD_DEVIATION_LIMIT,
355*bb4ee6a4SAndroid Build Coastguard Worker                 )
356*bb4ee6a4SAndroid Build Coastguard Worker             })
357*bb4ee6a4SAndroid Build Coastguard Worker             .map_err(|e| {
358*bb4ee6a4SAndroid Build Coastguard Worker                 anyhow!(
359*bb4ee6a4SAndroid Build Coastguard Worker                     "TSC offset measurement thread for core {} failed: {:?}",
360*bb4ee6a4SAndroid Build Coastguard Worker                     core,
361*bb4ee6a4SAndroid Build Coastguard Worker                     e
362*bb4ee6a4SAndroid Build Coastguard Worker                 )
363*bb4ee6a4SAndroid Build Coastguard Worker             })?;
364*bb4ee6a4SAndroid Build Coastguard Worker         let offset = match handle.join() {
365*bb4ee6a4SAndroid Build Coastguard Worker             // thread succeeded
366*bb4ee6a4SAndroid Build Coastguard Worker             Ok(measurement_result) => match measurement_result {
367*bb4ee6a4SAndroid Build Coastguard Worker                 Ok(offset) => Some(offset),
368*bb4ee6a4SAndroid Build Coastguard Worker                 Err(TscCalibrationError::SetCpuAffinityError { core, err }) => {
369*bb4ee6a4SAndroid Build Coastguard Worker                     // There are several legitimate reasons why it might not be possible for crosvm
370*bb4ee6a4SAndroid Build Coastguard Worker                     // to run on some cores:
371*bb4ee6a4SAndroid Build Coastguard Worker                     //  1. Some cores may be offline.
372*bb4ee6a4SAndroid Build Coastguard Worker                     //  2. On Windows, the process affinity mask may not contain all cores.
373*bb4ee6a4SAndroid Build Coastguard Worker                     //
374*bb4ee6a4SAndroid Build Coastguard Worker                     // We thus just warn in this situation.
375*bb4ee6a4SAndroid Build Coastguard Worker                     warn!(
376*bb4ee6a4SAndroid Build Coastguard Worker                         "Failed to set thread affinity to {} during tsc offset measurement due \
377*bb4ee6a4SAndroid Build Coastguard Worker                         to {}. This core is probably offline.",
378*bb4ee6a4SAndroid Build Coastguard Worker                         core, err
379*bb4ee6a4SAndroid Build Coastguard Worker                     );
380*bb4ee6a4SAndroid Build Coastguard Worker                     None
381*bb4ee6a4SAndroid Build Coastguard Worker                 }
382*bb4ee6a4SAndroid Build Coastguard Worker             },
383*bb4ee6a4SAndroid Build Coastguard Worker             // thread failed
384*bb4ee6a4SAndroid Build Coastguard Worker             Err(e) => {
385*bb4ee6a4SAndroid Build Coastguard Worker                 return Err(anyhow!(
386*bb4ee6a4SAndroid Build Coastguard Worker                     "TSC offset measurement thread for core {} failed: {:?}",
387*bb4ee6a4SAndroid Build Coastguard Worker                     core,
388*bb4ee6a4SAndroid Build Coastguard Worker                     e
389*bb4ee6a4SAndroid Build Coastguard Worker                 ));
390*bb4ee6a4SAndroid Build Coastguard Worker             }
391*bb4ee6a4SAndroid Build Coastguard Worker         };
392*bb4ee6a4SAndroid Build Coastguard Worker 
393*bb4ee6a4SAndroid Build Coastguard Worker         if let Some(offset) = offset {
394*bb4ee6a4SAndroid Build Coastguard Worker             offsets.push((core, offset));
395*bb4ee6a4SAndroid Build Coastguard Worker         }
396*bb4ee6a4SAndroid Build Coastguard Worker     }
397*bb4ee6a4SAndroid Build Coastguard Worker 
398*bb4ee6a4SAndroid Build Coastguard Worker     TscState::new(freq, offsets, TSC_OFFSET_GROUPING_THRESHOLD)
399*bb4ee6a4SAndroid Build Coastguard Worker }
400*bb4ee6a4SAndroid Build Coastguard Worker 
401*bb4ee6a4SAndroid Build Coastguard Worker #[cfg(test)]
402*bb4ee6a4SAndroid Build Coastguard Worker mod tests {
403*bb4ee6a4SAndroid Build Coastguard Worker     use std::arch::x86_64::__rdtscp;
404*bb4ee6a4SAndroid Build Coastguard Worker     use std::arch::x86_64::_rdtsc;
405*bb4ee6a4SAndroid Build Coastguard Worker 
406*bb4ee6a4SAndroid Build Coastguard Worker     use super::*;
407*bb4ee6a4SAndroid Build Coastguard Worker 
408*bb4ee6a4SAndroid Build Coastguard Worker     const ACCEPTABLE_OFFSET_MEASUREMENT_ERROR: i128 = 2_000i128;
409*bb4ee6a4SAndroid Build Coastguard Worker 
410*bb4ee6a4SAndroid Build Coastguard Worker     #[test]
test_handle_offline_core()411*bb4ee6a4SAndroid Build Coastguard Worker     fn test_handle_offline_core() {
412*bb4ee6a4SAndroid Build Coastguard Worker         // This test imitates what would happen if a core is offline, and set_cpu_affinity fails.
413*bb4ee6a4SAndroid Build Coastguard Worker         // The calibration should not fail, and the extra core should not appear in the list of
414*bb4ee6a4SAndroid Build Coastguard Worker         // offsets.
415*bb4ee6a4SAndroid Build Coastguard Worker 
416*bb4ee6a4SAndroid Build Coastguard Worker         let num_cores =
417*bb4ee6a4SAndroid Build Coastguard Worker             base::number_of_logical_cores().expect("number of logical cores should not fail");
418*bb4ee6a4SAndroid Build Coastguard Worker 
419*bb4ee6a4SAndroid Build Coastguard Worker         let too_may_cores = num_cores + 2;
420*bb4ee6a4SAndroid Build Coastguard Worker         let host_state = calibrate_tsc_state_inner(rdtsc_safe, (0..too_may_cores).collect())
421*bb4ee6a4SAndroid Build Coastguard Worker             .expect("calibrate tsc state should not fail");
422*bb4ee6a4SAndroid Build Coastguard Worker 
423*bb4ee6a4SAndroid Build Coastguard Worker         // First assert that the number of offsets measured is at most num_cores (it might be
424*bb4ee6a4SAndroid Build Coastguard Worker         // less if the current host has some offline cores).
425*bb4ee6a4SAndroid Build Coastguard Worker         assert!(host_state.offsets.len() <= num_cores);
426*bb4ee6a4SAndroid Build Coastguard Worker 
427*bb4ee6a4SAndroid Build Coastguard Worker         for (core, _) in host_state.offsets {
428*bb4ee6a4SAndroid Build Coastguard Worker             // Assert that all offsets that we have are for cores 0..num_cores.
429*bb4ee6a4SAndroid Build Coastguard Worker             assert!(core < num_cores);
430*bb4ee6a4SAndroid Build Coastguard Worker         }
431*bb4ee6a4SAndroid Build Coastguard Worker     }
432*bb4ee6a4SAndroid Build Coastguard Worker 
433*bb4ee6a4SAndroid Build Coastguard Worker     #[test]
test_frequency_higher_than_u32()434*bb4ee6a4SAndroid Build Coastguard Worker     fn test_frequency_higher_than_u32() {
435*bb4ee6a4SAndroid Build Coastguard Worker         // This test is making sure that we're not truncating our TSC frequencies in the case that
436*bb4ee6a4SAndroid Build Coastguard Worker         // they are greater than u32::MAX.
437*bb4ee6a4SAndroid Build Coastguard Worker 
438*bb4ee6a4SAndroid Build Coastguard Worker         let host_state = calibrate_tsc_state_inner(
439*bb4ee6a4SAndroid Build Coastguard Worker             rdtsc_safe,
440*bb4ee6a4SAndroid Build Coastguard Worker             (0..base::number_of_logical_cores().expect("number of logical cores should not fail"))
441*bb4ee6a4SAndroid Build Coastguard Worker                 .collect(),
442*bb4ee6a4SAndroid Build Coastguard Worker         )
443*bb4ee6a4SAndroid Build Coastguard Worker         .expect("failed to calibrate host freq");
444*bb4ee6a4SAndroid Build Coastguard Worker 
445*bb4ee6a4SAndroid Build Coastguard Worker         // We use a static multiplier of 1000 here because the function has to be static (fn).
446*bb4ee6a4SAndroid Build Coastguard Worker         // 1000 should work for tsc frequency > 4.2MHz, which should apply to basically any
447*bb4ee6a4SAndroid Build Coastguard Worker         // processor. This if statement checks and bails early if that's not the case.
448*bb4ee6a4SAndroid Build Coastguard Worker         if host_state.frequency * 1000 < (u32::MAX as u64) {
449*bb4ee6a4SAndroid Build Coastguard Worker             return;
450*bb4ee6a4SAndroid Build Coastguard Worker         }
451*bb4ee6a4SAndroid Build Coastguard Worker 
452*bb4ee6a4SAndroid Build Coastguard Worker         fn rdtsc_frequency_higher_than_u32() -> u64 {
453*bb4ee6a4SAndroid Build Coastguard Worker             // SAFETY: trivially safe
454*bb4ee6a4SAndroid Build Coastguard Worker             unsafe { _rdtsc() }.wrapping_mul(1000)
455*bb4ee6a4SAndroid Build Coastguard Worker         }
456*bb4ee6a4SAndroid Build Coastguard Worker 
457*bb4ee6a4SAndroid Build Coastguard Worker         let state = calibrate_tsc_state_inner(
458*bb4ee6a4SAndroid Build Coastguard Worker             rdtsc_frequency_higher_than_u32,
459*bb4ee6a4SAndroid Build Coastguard Worker             (0..base::number_of_logical_cores().expect("number of logical cores should not fail"))
460*bb4ee6a4SAndroid Build Coastguard Worker                 .collect(),
461*bb4ee6a4SAndroid Build Coastguard Worker         )
462*bb4ee6a4SAndroid Build Coastguard Worker         .unwrap();
463*bb4ee6a4SAndroid Build Coastguard Worker 
464*bb4ee6a4SAndroid Build Coastguard Worker         let expected_freq = host_state.frequency * 1000;
465*bb4ee6a4SAndroid Build Coastguard Worker         let margin_of_error = expected_freq / 100;
466*bb4ee6a4SAndroid Build Coastguard Worker         assert!(state.frequency < expected_freq + margin_of_error);
467*bb4ee6a4SAndroid Build Coastguard Worker         assert!(state.frequency > expected_freq - margin_of_error);
468*bb4ee6a4SAndroid Build Coastguard Worker     }
469*bb4ee6a4SAndroid Build Coastguard Worker 
470*bb4ee6a4SAndroid Build Coastguard Worker     #[test]
471*bb4ee6a4SAndroid Build Coastguard Worker     #[ignore]
test_offset_identification_core_0()472*bb4ee6a4SAndroid Build Coastguard Worker     fn test_offset_identification_core_0() {
473*bb4ee6a4SAndroid Build Coastguard Worker         fn rdtsc_with_core_0_offset_by_100_000() -> u64 {
474*bb4ee6a4SAndroid Build Coastguard Worker             let mut id = 0u32;
475*bb4ee6a4SAndroid Build Coastguard Worker             // SAFETY: trivially safe
476*bb4ee6a4SAndroid Build Coastguard Worker             let mut value = unsafe { __rdtscp(&mut id as *mut u32) };
477*bb4ee6a4SAndroid Build Coastguard Worker             if id == 0 {
478*bb4ee6a4SAndroid Build Coastguard Worker                 value += 100_000;
479*bb4ee6a4SAndroid Build Coastguard Worker             }
480*bb4ee6a4SAndroid Build Coastguard Worker 
481*bb4ee6a4SAndroid Build Coastguard Worker             value
482*bb4ee6a4SAndroid Build Coastguard Worker         }
483*bb4ee6a4SAndroid Build Coastguard Worker 
484*bb4ee6a4SAndroid Build Coastguard Worker         // This test only works if the host has >=2 logical cores.
485*bb4ee6a4SAndroid Build Coastguard Worker         let num_cores =
486*bb4ee6a4SAndroid Build Coastguard Worker             base::number_of_logical_cores().expect("Failed to get number of logical cores");
487*bb4ee6a4SAndroid Build Coastguard Worker         if num_cores < 2 {
488*bb4ee6a4SAndroid Build Coastguard Worker             return;
489*bb4ee6a4SAndroid Build Coastguard Worker         }
490*bb4ee6a4SAndroid Build Coastguard Worker 
491*bb4ee6a4SAndroid Build Coastguard Worker         let state = calibrate_tsc_state_inner(
492*bb4ee6a4SAndroid Build Coastguard Worker             rdtsc_with_core_0_offset_by_100_000,
493*bb4ee6a4SAndroid Build Coastguard Worker             (0..base::number_of_logical_cores().expect("number of logical cores should not fail"))
494*bb4ee6a4SAndroid Build Coastguard Worker                 .collect(),
495*bb4ee6a4SAndroid Build Coastguard Worker         )
496*bb4ee6a4SAndroid Build Coastguard Worker         .unwrap();
497*bb4ee6a4SAndroid Build Coastguard Worker 
498*bb4ee6a4SAndroid Build Coastguard Worker         for core in 0..num_cores {
499*bb4ee6a4SAndroid Build Coastguard Worker             let expected_offset_ns = if core > 0 {
500*bb4ee6a4SAndroid Build Coastguard Worker                 -100_000i128 * 1_000_000_000i128 / state.frequency as i128
501*bb4ee6a4SAndroid Build Coastguard Worker             } else {
502*bb4ee6a4SAndroid Build Coastguard Worker                 0i128
503*bb4ee6a4SAndroid Build Coastguard Worker             };
504*bb4ee6a4SAndroid Build Coastguard Worker             assert!(
505*bb4ee6a4SAndroid Build Coastguard Worker                 state.offsets[core].1 < expected_offset_ns + ACCEPTABLE_OFFSET_MEASUREMENT_ERROR
506*bb4ee6a4SAndroid Build Coastguard Worker             );
507*bb4ee6a4SAndroid Build Coastguard Worker             assert!(
508*bb4ee6a4SAndroid Build Coastguard Worker                 state.offsets[core].1 > expected_offset_ns - ACCEPTABLE_OFFSET_MEASUREMENT_ERROR
509*bb4ee6a4SAndroid Build Coastguard Worker             );
510*bb4ee6a4SAndroid Build Coastguard Worker         }
511*bb4ee6a4SAndroid Build Coastguard Worker     }
512*bb4ee6a4SAndroid Build Coastguard Worker 
513*bb4ee6a4SAndroid Build Coastguard Worker     #[test]
514*bb4ee6a4SAndroid Build Coastguard Worker     #[ignore]
test_offset_identification_core_1()515*bb4ee6a4SAndroid Build Coastguard Worker     fn test_offset_identification_core_1() {
516*bb4ee6a4SAndroid Build Coastguard Worker         fn rdtsc_with_core_1_offset_by_100_000() -> u64 {
517*bb4ee6a4SAndroid Build Coastguard Worker             let mut id = 0u32;
518*bb4ee6a4SAndroid Build Coastguard Worker             // SAFETY: trivially safe
519*bb4ee6a4SAndroid Build Coastguard Worker             let mut value = unsafe { __rdtscp(&mut id as *mut u32) };
520*bb4ee6a4SAndroid Build Coastguard Worker             if id == 1 {
521*bb4ee6a4SAndroid Build Coastguard Worker                 value += 100_000;
522*bb4ee6a4SAndroid Build Coastguard Worker             }
523*bb4ee6a4SAndroid Build Coastguard Worker 
524*bb4ee6a4SAndroid Build Coastguard Worker             value
525*bb4ee6a4SAndroid Build Coastguard Worker         }
526*bb4ee6a4SAndroid Build Coastguard Worker 
527*bb4ee6a4SAndroid Build Coastguard Worker         // This test only works if the host has >=2 logical cores.
528*bb4ee6a4SAndroid Build Coastguard Worker         let num_cores =
529*bb4ee6a4SAndroid Build Coastguard Worker             base::number_of_logical_cores().expect("Failed to get number of logical cores");
530*bb4ee6a4SAndroid Build Coastguard Worker         if num_cores < 2 {
531*bb4ee6a4SAndroid Build Coastguard Worker             return;
532*bb4ee6a4SAndroid Build Coastguard Worker         }
533*bb4ee6a4SAndroid Build Coastguard Worker 
534*bb4ee6a4SAndroid Build Coastguard Worker         let state = calibrate_tsc_state_inner(
535*bb4ee6a4SAndroid Build Coastguard Worker             rdtsc_with_core_1_offset_by_100_000,
536*bb4ee6a4SAndroid Build Coastguard Worker             (0..base::number_of_logical_cores().expect("number of logical cores should not fail"))
537*bb4ee6a4SAndroid Build Coastguard Worker                 .collect(),
538*bb4ee6a4SAndroid Build Coastguard Worker         )
539*bb4ee6a4SAndroid Build Coastguard Worker         .unwrap();
540*bb4ee6a4SAndroid Build Coastguard Worker 
541*bb4ee6a4SAndroid Build Coastguard Worker         for core in 0..num_cores {
542*bb4ee6a4SAndroid Build Coastguard Worker             let expected_offset_ns = if core == 1 {
543*bb4ee6a4SAndroid Build Coastguard Worker                 100_000i128 * 1_000_000_000i128 / state.frequency as i128
544*bb4ee6a4SAndroid Build Coastguard Worker             } else {
545*bb4ee6a4SAndroid Build Coastguard Worker                 0i128
546*bb4ee6a4SAndroid Build Coastguard Worker             };
547*bb4ee6a4SAndroid Build Coastguard Worker             assert!(
548*bb4ee6a4SAndroid Build Coastguard Worker                 state.offsets[core].1 < expected_offset_ns + ACCEPTABLE_OFFSET_MEASUREMENT_ERROR
549*bb4ee6a4SAndroid Build Coastguard Worker             );
550*bb4ee6a4SAndroid Build Coastguard Worker             assert!(
551*bb4ee6a4SAndroid Build Coastguard Worker                 state.offsets[core].1 > expected_offset_ns - ACCEPTABLE_OFFSET_MEASUREMENT_ERROR
552*bb4ee6a4SAndroid Build Coastguard Worker             );
553*bb4ee6a4SAndroid Build Coastguard Worker         }
554*bb4ee6a4SAndroid Build Coastguard Worker     }
555*bb4ee6a4SAndroid Build Coastguard Worker }
556