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 //! Handles operations using platform Time Stamp Counter (TSC).
6*bb4ee6a4SAndroid Build Coastguard Worker
7*bb4ee6a4SAndroid Build Coastguard Worker // TODO(b/213149158): Remove after uses are added.
8*bb4ee6a4SAndroid Build Coastguard Worker #![allow(dead_code)]
9*bb4ee6a4SAndroid Build Coastguard Worker
10*bb4ee6a4SAndroid Build Coastguard Worker use std::arch::x86_64::_rdtsc;
11*bb4ee6a4SAndroid Build Coastguard Worker
12*bb4ee6a4SAndroid Build Coastguard Worker use anyhow::anyhow;
13*bb4ee6a4SAndroid Build Coastguard Worker use anyhow::Result;
14*bb4ee6a4SAndroid Build Coastguard Worker use base::debug;
15*bb4ee6a4SAndroid Build Coastguard Worker use base::error;
16*bb4ee6a4SAndroid Build Coastguard Worker use once_cell::sync::Lazy;
17*bb4ee6a4SAndroid Build Coastguard Worker
18*bb4ee6a4SAndroid Build Coastguard Worker mod calibrate;
19*bb4ee6a4SAndroid Build Coastguard Worker mod cpuid;
20*bb4ee6a4SAndroid Build Coastguard Worker mod grouping;
21*bb4ee6a4SAndroid Build Coastguard Worker
22*bb4ee6a4SAndroid Build Coastguard Worker pub use calibrate::*;
23*bb4ee6a4SAndroid Build Coastguard Worker pub use cpuid::*;
24*bb4ee6a4SAndroid Build Coastguard Worker
rdtsc_safe() -> u6425*bb4ee6a4SAndroid Build Coastguard Worker fn rdtsc_safe() -> u64 {
26*bb4ee6a4SAndroid Build Coastguard Worker // SAFETY:
27*bb4ee6a4SAndroid Build Coastguard Worker // Safe because _rdtsc takes no arguments
28*bb4ee6a4SAndroid Build Coastguard Worker unsafe { _rdtsc() }
29*bb4ee6a4SAndroid Build Coastguard Worker }
30*bb4ee6a4SAndroid Build Coastguard Worker
31*bb4ee6a4SAndroid Build Coastguard Worker // Singleton for getting the state of the host TSCs, to avoid calibrating multiple times.
32*bb4ee6a4SAndroid Build Coastguard Worker static TSC_STATE: Lazy<Option<TscState>> = Lazy::new(|| match calibrate_tsc_state() {
33*bb4ee6a4SAndroid Build Coastguard Worker Ok(tsc_state) => {
34*bb4ee6a4SAndroid Build Coastguard Worker debug!("Using calibrated tsc frequency: {} Hz", tsc_state.frequency);
35*bb4ee6a4SAndroid Build Coastguard Worker for (core, offset) in tsc_state.offsets.iter().enumerate() {
36*bb4ee6a4SAndroid Build Coastguard Worker debug!("Core {} has tsc offset of {:?} ns", core, offset);
37*bb4ee6a4SAndroid Build Coastguard Worker }
38*bb4ee6a4SAndroid Build Coastguard Worker Some(tsc_state)
39*bb4ee6a4SAndroid Build Coastguard Worker }
40*bb4ee6a4SAndroid Build Coastguard Worker Err(e) => {
41*bb4ee6a4SAndroid Build Coastguard Worker error!("Failed to calibrate tsc state: {:#}", e);
42*bb4ee6a4SAndroid Build Coastguard Worker None
43*bb4ee6a4SAndroid Build Coastguard Worker }
44*bb4ee6a4SAndroid Build Coastguard Worker });
45*bb4ee6a4SAndroid Build Coastguard Worker
46*bb4ee6a4SAndroid Build Coastguard Worker /// Returns the frequency of the host TSC. Calibration only happens once.
tsc_frequency() -> Result<u64>47*bb4ee6a4SAndroid Build Coastguard Worker pub fn tsc_frequency() -> Result<u64> {
48*bb4ee6a4SAndroid Build Coastguard Worker let state = TSC_STATE
49*bb4ee6a4SAndroid Build Coastguard Worker .as_ref()
50*bb4ee6a4SAndroid Build Coastguard Worker .ok_or(anyhow!("TSC calibration failed"))?;
51*bb4ee6a4SAndroid Build Coastguard Worker Ok(state.frequency)
52*bb4ee6a4SAndroid Build Coastguard Worker }
53*bb4ee6a4SAndroid Build Coastguard Worker
54*bb4ee6a4SAndroid Build Coastguard Worker /// Returns the state of the host TSCs. Calibration only happens once.
tsc_state() -> Result<TscState>55*bb4ee6a4SAndroid Build Coastguard Worker pub fn tsc_state() -> Result<TscState> {
56*bb4ee6a4SAndroid Build Coastguard Worker Ok(TSC_STATE
57*bb4ee6a4SAndroid Build Coastguard Worker .as_ref()
58*bb4ee6a4SAndroid Build Coastguard Worker .ok_or(anyhow!("TSC calibration failed"))?
59*bb4ee6a4SAndroid Build Coastguard Worker .clone())
60*bb4ee6a4SAndroid Build Coastguard Worker }
61*bb4ee6a4SAndroid Build Coastguard Worker
62*bb4ee6a4SAndroid Build Coastguard Worker #[derive(Default, Debug)]
63*bb4ee6a4SAndroid Build Coastguard Worker pub struct TscSyncMitigations {
64*bb4ee6a4SAndroid Build Coastguard Worker /// Vec of per-vcpu affinities to apply to each vcpu thread. If None, no affinity should be
65*bb4ee6a4SAndroid Build Coastguard Worker /// applied.
66*bb4ee6a4SAndroid Build Coastguard Worker pub affinities: Vec<Option<Vec<usize>>>,
67*bb4ee6a4SAndroid Build Coastguard Worker /// Vec of TSC offsets to set on each vcpu. If None, no offset should be applied.
68*bb4ee6a4SAndroid Build Coastguard Worker pub offsets: Vec<Option<u64>>,
69*bb4ee6a4SAndroid Build Coastguard Worker }
70*bb4ee6a4SAndroid Build Coastguard Worker
71*bb4ee6a4SAndroid Build Coastguard Worker impl TscSyncMitigations {
new(num_vcpus: usize) -> Self72*bb4ee6a4SAndroid Build Coastguard Worker fn new(num_vcpus: usize) -> Self {
73*bb4ee6a4SAndroid Build Coastguard Worker TscSyncMitigations {
74*bb4ee6a4SAndroid Build Coastguard Worker affinities: vec![None; num_vcpus],
75*bb4ee6a4SAndroid Build Coastguard Worker offsets: vec![None; num_vcpus],
76*bb4ee6a4SAndroid Build Coastguard Worker }
77*bb4ee6a4SAndroid Build Coastguard Worker }
78*bb4ee6a4SAndroid Build Coastguard Worker
get_vcpu_affinity(&self, cpu_id: usize) -> Option<Vec<usize>>79*bb4ee6a4SAndroid Build Coastguard Worker pub fn get_vcpu_affinity(&self, cpu_id: usize) -> Option<Vec<usize>> {
80*bb4ee6a4SAndroid Build Coastguard Worker self.affinities.get(cpu_id).unwrap().clone()
81*bb4ee6a4SAndroid Build Coastguard Worker }
82*bb4ee6a4SAndroid Build Coastguard Worker
get_vcpu_tsc_offset(&self, cpu_id: usize) -> Option<u64>83*bb4ee6a4SAndroid Build Coastguard Worker pub fn get_vcpu_tsc_offset(&self, cpu_id: usize) -> Option<u64> {
84*bb4ee6a4SAndroid Build Coastguard Worker *self.offsets.get(cpu_id).unwrap()
85*bb4ee6a4SAndroid Build Coastguard Worker }
86*bb4ee6a4SAndroid Build Coastguard Worker }
87*bb4ee6a4SAndroid Build Coastguard Worker
88*bb4ee6a4SAndroid Build Coastguard Worker /// Given the state of the host TSCs in `tsc_state`, and the number of vcpus that are intended to
89*bb4ee6a4SAndroid Build Coastguard Worker /// be run, return a set of affinities and TSC offsets to apply to those vcpus.
get_tsc_sync_mitigations(tsc_state: &TscState, num_vcpus: usize) -> TscSyncMitigations90*bb4ee6a4SAndroid Build Coastguard Worker pub fn get_tsc_sync_mitigations(tsc_state: &TscState, num_vcpus: usize) -> TscSyncMitigations {
91*bb4ee6a4SAndroid Build Coastguard Worker tsc_sync_mitigations_inner(tsc_state, num_vcpus, rdtsc_safe)
92*bb4ee6a4SAndroid Build Coastguard Worker }
93*bb4ee6a4SAndroid Build Coastguard Worker
tsc_sync_mitigations_inner( tsc_state: &TscState, num_vcpus: usize, rdtsc: fn() -> u64, ) -> TscSyncMitigations94*bb4ee6a4SAndroid Build Coastguard Worker fn tsc_sync_mitigations_inner(
95*bb4ee6a4SAndroid Build Coastguard Worker tsc_state: &TscState,
96*bb4ee6a4SAndroid Build Coastguard Worker num_vcpus: usize,
97*bb4ee6a4SAndroid Build Coastguard Worker rdtsc: fn() -> u64,
98*bb4ee6a4SAndroid Build Coastguard Worker ) -> TscSyncMitigations {
99*bb4ee6a4SAndroid Build Coastguard Worker let mut mitigations = TscSyncMitigations::new(num_vcpus);
100*bb4ee6a4SAndroid Build Coastguard Worker // If there's only one core grouping that means all the TSCs are in sync and no mitigations are
101*bb4ee6a4SAndroid Build Coastguard Worker // needed.
102*bb4ee6a4SAndroid Build Coastguard Worker if tsc_state.core_grouping.size() == 1 {
103*bb4ee6a4SAndroid Build Coastguard Worker return mitigations;
104*bb4ee6a4SAndroid Build Coastguard Worker }
105*bb4ee6a4SAndroid Build Coastguard Worker
106*bb4ee6a4SAndroid Build Coastguard Worker let largest_group = tsc_state.core_grouping.largest_group();
107*bb4ee6a4SAndroid Build Coastguard Worker let num_cores = tsc_state.offsets.len();
108*bb4ee6a4SAndroid Build Coastguard Worker
109*bb4ee6a4SAndroid Build Coastguard Worker // If the largest core group is larger than the number of vcpus, just pin all vcpus to that core
110*bb4ee6a4SAndroid Build Coastguard Worker // group, and no need to set offsets.
111*bb4ee6a4SAndroid Build Coastguard Worker if largest_group.cores.len() >= num_vcpus {
112*bb4ee6a4SAndroid Build Coastguard Worker let affinity: Vec<usize> = largest_group.cores.iter().map(|core| core.core).collect();
113*bb4ee6a4SAndroid Build Coastguard Worker for i in 0..num_vcpus {
114*bb4ee6a4SAndroid Build Coastguard Worker mitigations.affinities[i] = Some(affinity.clone());
115*bb4ee6a4SAndroid Build Coastguard Worker }
116*bb4ee6a4SAndroid Build Coastguard Worker } else {
117*bb4ee6a4SAndroid Build Coastguard Worker // Otherwise, we pin each vcpu to a core and set it's offset to compensate.
118*bb4ee6a4SAndroid Build Coastguard Worker let host_tsc_now = rdtsc();
119*bb4ee6a4SAndroid Build Coastguard Worker
120*bb4ee6a4SAndroid Build Coastguard Worker for i in 0..num_vcpus {
121*bb4ee6a4SAndroid Build Coastguard Worker // This handles the case where num_vcpus > num_cores, even though we try to avoid that
122*bb4ee6a4SAndroid Build Coastguard Worker // in practice.
123*bb4ee6a4SAndroid Build Coastguard Worker let pinned_core = i % num_cores;
124*bb4ee6a4SAndroid Build Coastguard Worker
125*bb4ee6a4SAndroid Build Coastguard Worker mitigations.affinities[i] = Some(vec![pinned_core]);
126*bb4ee6a4SAndroid Build Coastguard Worker // The guest TSC value is calculated like so:
127*bb4ee6a4SAndroid Build Coastguard Worker // host_tsc + tsc_offset = guest_tsc
128*bb4ee6a4SAndroid Build Coastguard Worker // If we assume that each host core has it's own error (core_offset), then it's more
129*bb4ee6a4SAndroid Build Coastguard Worker // like this:
130*bb4ee6a4SAndroid Build Coastguard Worker // host_tsc + core_offset + tsc_offset = guest_tsc
131*bb4ee6a4SAndroid Build Coastguard Worker // We want guest_tsc to be 0 at boot, so the formula is this:
132*bb4ee6a4SAndroid Build Coastguard Worker // host_tsc + core_offset + tsc_offset = 0
133*bb4ee6a4SAndroid Build Coastguard Worker // and then you subtract host_tsc and core_offset from both sides and you get:
134*bb4ee6a4SAndroid Build Coastguard Worker // tsc_offset = 0 - host_tsc - core_offset
135*bb4ee6a4SAndroid Build Coastguard Worker mitigations.offsets[i] = Some(
136*bb4ee6a4SAndroid Build Coastguard Worker 0u64.wrapping_sub(host_tsc_now)
137*bb4ee6a4SAndroid Build Coastguard Worker // Note: wrapping_add and casting tsc_state from an i64 to a u64 should be the
138*bb4ee6a4SAndroid Build Coastguard Worker // same as using the future wrapping_add_signed function, which is only in
139*bb4ee6a4SAndroid Build Coastguard Worker // nightly. This should be switched to using wrapping_add_signed once that is
140*bb4ee6a4SAndroid Build Coastguard Worker // in stable.
141*bb4ee6a4SAndroid Build Coastguard Worker .wrapping_add(tsc_state.offsets[pinned_core].1.wrapping_neg() as i64 as u64),
142*bb4ee6a4SAndroid Build Coastguard Worker );
143*bb4ee6a4SAndroid Build Coastguard Worker }
144*bb4ee6a4SAndroid Build Coastguard Worker }
145*bb4ee6a4SAndroid Build Coastguard Worker
146*bb4ee6a4SAndroid Build Coastguard Worker mitigations
147*bb4ee6a4SAndroid Build Coastguard Worker }
148*bb4ee6a4SAndroid Build Coastguard Worker
149*bb4ee6a4SAndroid Build Coastguard Worker #[cfg(test)]
150*bb4ee6a4SAndroid Build Coastguard Worker mod tests {
151*bb4ee6a4SAndroid Build Coastguard Worker use std::time::Duration;
152*bb4ee6a4SAndroid Build Coastguard Worker
153*bb4ee6a4SAndroid Build Coastguard Worker use super::*;
154*bb4ee6a4SAndroid Build Coastguard Worker use crate::tsc::grouping::CoreGroup;
155*bb4ee6a4SAndroid Build Coastguard Worker use crate::tsc::grouping::CoreGrouping;
156*bb4ee6a4SAndroid Build Coastguard Worker use crate::tsc::grouping::CoreOffset;
157*bb4ee6a4SAndroid Build Coastguard Worker
158*bb4ee6a4SAndroid Build Coastguard Worker #[test]
test_sync_mitigation_set_offsets()159*bb4ee6a4SAndroid Build Coastguard Worker fn test_sync_mitigation_set_offsets() {
160*bb4ee6a4SAndroid Build Coastguard Worker let offsets = vec![(0, 0), (1, 1000), (2, -1000), (3, 2000)];
161*bb4ee6a4SAndroid Build Coastguard Worker // frequency of 1GHz means 20 nanos is 20 ticks
162*bb4ee6a4SAndroid Build Coastguard Worker let state = TscState::new(1_000_000_000, offsets, Duration::from_nanos(20))
163*bb4ee6a4SAndroid Build Coastguard Worker .expect("TscState::new should not fail for this test");
164*bb4ee6a4SAndroid Build Coastguard Worker
165*bb4ee6a4SAndroid Build Coastguard Worker assert_eq!(
166*bb4ee6a4SAndroid Build Coastguard Worker state.core_grouping,
167*bb4ee6a4SAndroid Build Coastguard Worker CoreGrouping::new(vec![
168*bb4ee6a4SAndroid Build Coastguard Worker CoreGroup {
169*bb4ee6a4SAndroid Build Coastguard Worker cores: vec![CoreOffset {
170*bb4ee6a4SAndroid Build Coastguard Worker core: 2,
171*bb4ee6a4SAndroid Build Coastguard Worker offset: -1000
172*bb4ee6a4SAndroid Build Coastguard Worker }]
173*bb4ee6a4SAndroid Build Coastguard Worker },
174*bb4ee6a4SAndroid Build Coastguard Worker CoreGroup {
175*bb4ee6a4SAndroid Build Coastguard Worker cores: vec![CoreOffset { core: 0, offset: 0 }]
176*bb4ee6a4SAndroid Build Coastguard Worker },
177*bb4ee6a4SAndroid Build Coastguard Worker CoreGroup {
178*bb4ee6a4SAndroid Build Coastguard Worker cores: vec![CoreOffset {
179*bb4ee6a4SAndroid Build Coastguard Worker core: 1,
180*bb4ee6a4SAndroid Build Coastguard Worker offset: 1000
181*bb4ee6a4SAndroid Build Coastguard Worker }]
182*bb4ee6a4SAndroid Build Coastguard Worker },
183*bb4ee6a4SAndroid Build Coastguard Worker CoreGroup {
184*bb4ee6a4SAndroid Build Coastguard Worker cores: vec![CoreOffset {
185*bb4ee6a4SAndroid Build Coastguard Worker core: 3,
186*bb4ee6a4SAndroid Build Coastguard Worker offset: 2000
187*bb4ee6a4SAndroid Build Coastguard Worker }]
188*bb4ee6a4SAndroid Build Coastguard Worker },
189*bb4ee6a4SAndroid Build Coastguard Worker ])
190*bb4ee6a4SAndroid Build Coastguard Worker .expect("CoreGrouping::new should not fail here")
191*bb4ee6a4SAndroid Build Coastguard Worker );
192*bb4ee6a4SAndroid Build Coastguard Worker
193*bb4ee6a4SAndroid Build Coastguard Worker fn fake_rdtsc() -> u64 {
194*bb4ee6a4SAndroid Build Coastguard Worker u64::MAX
195*bb4ee6a4SAndroid Build Coastguard Worker }
196*bb4ee6a4SAndroid Build Coastguard Worker
197*bb4ee6a4SAndroid Build Coastguard Worker let mitigations = tsc_sync_mitigations_inner(&state, 4, fake_rdtsc);
198*bb4ee6a4SAndroid Build Coastguard Worker
199*bb4ee6a4SAndroid Build Coastguard Worker // core offsets are:
200*bb4ee6a4SAndroid Build Coastguard Worker // - core 0: has an offset of 0, so TSC offset = 0 - u64::MAX - 0 = 1
201*bb4ee6a4SAndroid Build Coastguard Worker // - core 1: has an offset of 1000, so TSC offset = 0 - u64::MAX - 1000 = -999
202*bb4ee6a4SAndroid Build Coastguard Worker // - core 2: has an offset of -1000, so TSC offset = 0 - u64::MAX + 1000 = 1001
203*bb4ee6a4SAndroid Build Coastguard Worker // - core 3: has an offset of 2000, so TSC offset = 0 - u64::MAX - 2000 = -1999
204*bb4ee6a4SAndroid Build Coastguard Worker let expected = [1, 1u64.wrapping_sub(1000), 1001u64, 1u64.wrapping_sub(2000)];
205*bb4ee6a4SAndroid Build Coastguard Worker
206*bb4ee6a4SAndroid Build Coastguard Worker for (i, expect) in expected.iter().enumerate() {
207*bb4ee6a4SAndroid Build Coastguard Worker assert_eq!(
208*bb4ee6a4SAndroid Build Coastguard Worker mitigations
209*bb4ee6a4SAndroid Build Coastguard Worker .get_vcpu_tsc_offset(i)
210*bb4ee6a4SAndroid Build Coastguard Worker .unwrap_or_else(|| panic!("core {} should have an offset of {}", i, expect)),
211*bb4ee6a4SAndroid Build Coastguard Worker *expect
212*bb4ee6a4SAndroid Build Coastguard Worker );
213*bb4ee6a4SAndroid Build Coastguard Worker
214*bb4ee6a4SAndroid Build Coastguard Worker assert_eq!(
215*bb4ee6a4SAndroid Build Coastguard Worker mitigations
216*bb4ee6a4SAndroid Build Coastguard Worker .get_vcpu_affinity(i)
217*bb4ee6a4SAndroid Build Coastguard Worker .unwrap_or_else(|| panic!("core {} should have an affinity of [{}]", i, i)),
218*bb4ee6a4SAndroid Build Coastguard Worker vec![i]
219*bb4ee6a4SAndroid Build Coastguard Worker );
220*bb4ee6a4SAndroid Build Coastguard Worker }
221*bb4ee6a4SAndroid Build Coastguard Worker }
222*bb4ee6a4SAndroid Build Coastguard Worker
223*bb4ee6a4SAndroid Build Coastguard Worker #[test]
test_sync_mitigation_large_group()224*bb4ee6a4SAndroid Build Coastguard Worker fn test_sync_mitigation_large_group() {
225*bb4ee6a4SAndroid Build Coastguard Worker // 8 cores, and cores 1,3,5,7 are in-sync at offset -1000
226*bb4ee6a4SAndroid Build Coastguard Worker let offsets = vec![
227*bb4ee6a4SAndroid Build Coastguard Worker (0, 0),
228*bb4ee6a4SAndroid Build Coastguard Worker (1, -1000),
229*bb4ee6a4SAndroid Build Coastguard Worker (2, 1000),
230*bb4ee6a4SAndroid Build Coastguard Worker (3, -1000),
231*bb4ee6a4SAndroid Build Coastguard Worker (4, 2000),
232*bb4ee6a4SAndroid Build Coastguard Worker (5, -1000),
233*bb4ee6a4SAndroid Build Coastguard Worker (6, 3000),
234*bb4ee6a4SAndroid Build Coastguard Worker (7, -1000),
235*bb4ee6a4SAndroid Build Coastguard Worker ];
236*bb4ee6a4SAndroid Build Coastguard Worker // frequency of 1GHz means 20 nanos is 20 ticks
237*bb4ee6a4SAndroid Build Coastguard Worker let state = TscState::new(1_000_000_000, offsets, Duration::from_nanos(20))
238*bb4ee6a4SAndroid Build Coastguard Worker .expect("TscState::new should not fail for this test");
239*bb4ee6a4SAndroid Build Coastguard Worker
240*bb4ee6a4SAndroid Build Coastguard Worker assert_eq!(
241*bb4ee6a4SAndroid Build Coastguard Worker state.core_grouping,
242*bb4ee6a4SAndroid Build Coastguard Worker CoreGrouping::new(vec![
243*bb4ee6a4SAndroid Build Coastguard Worker CoreGroup {
244*bb4ee6a4SAndroid Build Coastguard Worker cores: vec![
245*bb4ee6a4SAndroid Build Coastguard Worker CoreOffset {
246*bb4ee6a4SAndroid Build Coastguard Worker core: 1,
247*bb4ee6a4SAndroid Build Coastguard Worker offset: -1000
248*bb4ee6a4SAndroid Build Coastguard Worker },
249*bb4ee6a4SAndroid Build Coastguard Worker CoreOffset {
250*bb4ee6a4SAndroid Build Coastguard Worker core: 3,
251*bb4ee6a4SAndroid Build Coastguard Worker offset: -1000
252*bb4ee6a4SAndroid Build Coastguard Worker },
253*bb4ee6a4SAndroid Build Coastguard Worker CoreOffset {
254*bb4ee6a4SAndroid Build Coastguard Worker core: 5,
255*bb4ee6a4SAndroid Build Coastguard Worker offset: -1000
256*bb4ee6a4SAndroid Build Coastguard Worker },
257*bb4ee6a4SAndroid Build Coastguard Worker CoreOffset {
258*bb4ee6a4SAndroid Build Coastguard Worker core: 7,
259*bb4ee6a4SAndroid Build Coastguard Worker offset: -1000
260*bb4ee6a4SAndroid Build Coastguard Worker }
261*bb4ee6a4SAndroid Build Coastguard Worker ]
262*bb4ee6a4SAndroid Build Coastguard Worker },
263*bb4ee6a4SAndroid Build Coastguard Worker CoreGroup {
264*bb4ee6a4SAndroid Build Coastguard Worker cores: vec![CoreOffset { core: 0, offset: 0 }]
265*bb4ee6a4SAndroid Build Coastguard Worker },
266*bb4ee6a4SAndroid Build Coastguard Worker CoreGroup {
267*bb4ee6a4SAndroid Build Coastguard Worker cores: vec![CoreOffset {
268*bb4ee6a4SAndroid Build Coastguard Worker core: 2,
269*bb4ee6a4SAndroid Build Coastguard Worker offset: 1000
270*bb4ee6a4SAndroid Build Coastguard Worker }]
271*bb4ee6a4SAndroid Build Coastguard Worker },
272*bb4ee6a4SAndroid Build Coastguard Worker CoreGroup {
273*bb4ee6a4SAndroid Build Coastguard Worker cores: vec![CoreOffset {
274*bb4ee6a4SAndroid Build Coastguard Worker core: 4,
275*bb4ee6a4SAndroid Build Coastguard Worker offset: 2000
276*bb4ee6a4SAndroid Build Coastguard Worker }]
277*bb4ee6a4SAndroid Build Coastguard Worker },
278*bb4ee6a4SAndroid Build Coastguard Worker CoreGroup {
279*bb4ee6a4SAndroid Build Coastguard Worker cores: vec![CoreOffset {
280*bb4ee6a4SAndroid Build Coastguard Worker core: 6,
281*bb4ee6a4SAndroid Build Coastguard Worker offset: 3000
282*bb4ee6a4SAndroid Build Coastguard Worker }]
283*bb4ee6a4SAndroid Build Coastguard Worker },
284*bb4ee6a4SAndroid Build Coastguard Worker ])
285*bb4ee6a4SAndroid Build Coastguard Worker .expect("CoreGrouping::new should not fail here")
286*bb4ee6a4SAndroid Build Coastguard Worker );
287*bb4ee6a4SAndroid Build Coastguard Worker
288*bb4ee6a4SAndroid Build Coastguard Worker fn fake_rdtsc() -> u64 {
289*bb4ee6a4SAndroid Build Coastguard Worker u64::MAX
290*bb4ee6a4SAndroid Build Coastguard Worker }
291*bb4ee6a4SAndroid Build Coastguard Worker
292*bb4ee6a4SAndroid Build Coastguard Worker let num_vcpus = 4;
293*bb4ee6a4SAndroid Build Coastguard Worker let mitigations = tsc_sync_mitigations_inner(&state, num_vcpus, fake_rdtsc);
294*bb4ee6a4SAndroid Build Coastguard Worker
295*bb4ee6a4SAndroid Build Coastguard Worker let expected_affinity = vec![1, 3, 5, 7];
296*bb4ee6a4SAndroid Build Coastguard Worker for i in 0..num_vcpus {
297*bb4ee6a4SAndroid Build Coastguard Worker assert_eq!(
298*bb4ee6a4SAndroid Build Coastguard Worker mitigations.get_vcpu_affinity(i).unwrap_or_else(|| panic!(
299*bb4ee6a4SAndroid Build Coastguard Worker "core {} should have an affinity of {:?}",
300*bb4ee6a4SAndroid Build Coastguard Worker i, expected_affinity
301*bb4ee6a4SAndroid Build Coastguard Worker )),
302*bb4ee6a4SAndroid Build Coastguard Worker expected_affinity
303*bb4ee6a4SAndroid Build Coastguard Worker );
304*bb4ee6a4SAndroid Build Coastguard Worker assert_eq!(mitigations.get_vcpu_tsc_offset(i), None);
305*bb4ee6a4SAndroid Build Coastguard Worker }
306*bb4ee6a4SAndroid Build Coastguard Worker }
307*bb4ee6a4SAndroid Build Coastguard Worker
308*bb4ee6a4SAndroid Build Coastguard Worker #[test]
more_vcpus_than_cores()309*bb4ee6a4SAndroid Build Coastguard Worker fn more_vcpus_than_cores() {
310*bb4ee6a4SAndroid Build Coastguard Worker // 4 cores, two can be grouped but it doesn't matter because we'll have more vcpus than
311*bb4ee6a4SAndroid Build Coastguard Worker // the largest group.
312*bb4ee6a4SAndroid Build Coastguard Worker let offsets = vec![(0, 0), (1, 0), (2, 1000), (3, 2000)];
313*bb4ee6a4SAndroid Build Coastguard Worker // frequency of 1GHz means 20 nanos is 20 ticks
314*bb4ee6a4SAndroid Build Coastguard Worker let state = TscState::new(1_000_000_000, offsets, Duration::from_nanos(20))
315*bb4ee6a4SAndroid Build Coastguard Worker .expect("TscState::new should not fail for this test");
316*bb4ee6a4SAndroid Build Coastguard Worker
317*bb4ee6a4SAndroid Build Coastguard Worker assert_eq!(
318*bb4ee6a4SAndroid Build Coastguard Worker state.core_grouping,
319*bb4ee6a4SAndroid Build Coastguard Worker CoreGrouping::new(vec![
320*bb4ee6a4SAndroid Build Coastguard Worker CoreGroup {
321*bb4ee6a4SAndroid Build Coastguard Worker cores: vec![
322*bb4ee6a4SAndroid Build Coastguard Worker CoreOffset { core: 0, offset: 0 },
323*bb4ee6a4SAndroid Build Coastguard Worker CoreOffset { core: 1, offset: 0 }
324*bb4ee6a4SAndroid Build Coastguard Worker ]
325*bb4ee6a4SAndroid Build Coastguard Worker },
326*bb4ee6a4SAndroid Build Coastguard Worker CoreGroup {
327*bb4ee6a4SAndroid Build Coastguard Worker cores: vec![CoreOffset {
328*bb4ee6a4SAndroid Build Coastguard Worker core: 2,
329*bb4ee6a4SAndroid Build Coastguard Worker offset: 1000
330*bb4ee6a4SAndroid Build Coastguard Worker }]
331*bb4ee6a4SAndroid Build Coastguard Worker },
332*bb4ee6a4SAndroid Build Coastguard Worker CoreGroup {
333*bb4ee6a4SAndroid Build Coastguard Worker cores: vec![CoreOffset {
334*bb4ee6a4SAndroid Build Coastguard Worker core: 3,
335*bb4ee6a4SAndroid Build Coastguard Worker offset: 2000
336*bb4ee6a4SAndroid Build Coastguard Worker }]
337*bb4ee6a4SAndroid Build Coastguard Worker },
338*bb4ee6a4SAndroid Build Coastguard Worker ])
339*bb4ee6a4SAndroid Build Coastguard Worker .expect("CoreGrouping::new should not fail here")
340*bb4ee6a4SAndroid Build Coastguard Worker );
341*bb4ee6a4SAndroid Build Coastguard Worker
342*bb4ee6a4SAndroid Build Coastguard Worker fn fake_rdtsc() -> u64 {
343*bb4ee6a4SAndroid Build Coastguard Worker u64::MAX
344*bb4ee6a4SAndroid Build Coastguard Worker }
345*bb4ee6a4SAndroid Build Coastguard Worker
346*bb4ee6a4SAndroid Build Coastguard Worker // 8 vcpus, more than we have cores
347*bb4ee6a4SAndroid Build Coastguard Worker let num_vcpus = 8;
348*bb4ee6a4SAndroid Build Coastguard Worker let mitigations = tsc_sync_mitigations_inner(&state, num_vcpus, fake_rdtsc);
349*bb4ee6a4SAndroid Build Coastguard Worker let expected_offsets = [1, 1, 1u64.wrapping_sub(1000), 1u64.wrapping_sub(2000)];
350*bb4ee6a4SAndroid Build Coastguard Worker
351*bb4ee6a4SAndroid Build Coastguard Worker for i in 0..num_vcpus {
352*bb4ee6a4SAndroid Build Coastguard Worker assert_eq!(
353*bb4ee6a4SAndroid Build Coastguard Worker mitigations.get_vcpu_affinity(i).unwrap_or_else(|| panic!(
354*bb4ee6a4SAndroid Build Coastguard Worker "core {} should have an affinity of {:?}",
355*bb4ee6a4SAndroid Build Coastguard Worker i,
356*bb4ee6a4SAndroid Build Coastguard Worker i % 4
357*bb4ee6a4SAndroid Build Coastguard Worker )),
358*bb4ee6a4SAndroid Build Coastguard Worker // expected affinity is the vcpu modulo 4
359*bb4ee6a4SAndroid Build Coastguard Worker vec![i % 4]
360*bb4ee6a4SAndroid Build Coastguard Worker );
361*bb4ee6a4SAndroid Build Coastguard Worker assert_eq!(
362*bb4ee6a4SAndroid Build Coastguard Worker mitigations.get_vcpu_tsc_offset(i).unwrap_or_else(|| panic!(
363*bb4ee6a4SAndroid Build Coastguard Worker "core {} should have an offset of {:?}",
364*bb4ee6a4SAndroid Build Coastguard Worker i,
365*bb4ee6a4SAndroid Build Coastguard Worker expected_offsets[i % 4]
366*bb4ee6a4SAndroid Build Coastguard Worker )),
367*bb4ee6a4SAndroid Build Coastguard Worker expected_offsets[i % 4]
368*bb4ee6a4SAndroid Build Coastguard Worker );
369*bb4ee6a4SAndroid Build Coastguard Worker }
370*bb4ee6a4SAndroid Build Coastguard Worker }
371*bb4ee6a4SAndroid Build Coastguard Worker }
372