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