Skip to content

Commit 291c8d4

Browse files
committed
Add cache topology verification to guest_cpu_topo_test
Verify that Linux guest observes correct cache sharing topology from /sys/devices/system/cpu/cpu0/cache/. With SMT enabled, L1 and L2 caches should report sharing by SMT siblings while L3 should be shared across all vCPUs. Signed-off-by: Amey Narkhede <ameynarkhede03@gmail.com>
1 parent fcdac0e commit 291c8d4

1 file changed

Lines changed: 66 additions & 0 deletions

File tree

phd-tests/tests/src/cpuid.rs

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -167,11 +167,20 @@ struct LinuxGuestTopo<'a> {
167167
vm: &'a TestVm,
168168
}
169169

170+
struct CacheInfo {
171+
level: u8,
172+
shared_cpu_list: String,
173+
}
174+
170175
impl<'a> LinuxGuestTopo<'a> {
171176
fn cpu_stem(vcpu: u8) -> String {
172177
format!("/sys/devices/system/cpu/cpu{vcpu}/topology")
173178
}
174179

180+
fn cache_stem(vcpu: u8, index: u8) -> String {
181+
format!("/sys/devices/system/cpu/cpu{vcpu}/cache/index{index}")
182+
}
183+
175184
async fn new(vm: &'a TestVm) -> Self {
176185
let this = Self { vm };
177186
// Expect Linux numbers CPUs as 0 through vCPU-1 (inclusive).
@@ -292,6 +301,37 @@ impl<'a> LinuxGuestTopo<'a> {
292301
.expect("thread_siblings should be valid hex");
293302
value.count_ones() > 1
294303
}
304+
305+
async fn cache_info(&self) -> Vec<CacheInfo> {
306+
let mut result = Vec::new();
307+
for index in 0u8.. {
308+
let stem = Self::cache_stem(0, index);
309+
let level_out = self
310+
.vm
311+
.run_shell_command(&format!("cat {stem}/level 2>/dev/null"))
312+
.await
313+
.expect("can run cat");
314+
315+
let level_out = level_out.trim();
316+
if level_out.is_empty() || level_out.contains("file not found") {
317+
break;
318+
}
319+
320+
let level: u8 =
321+
level_out.parse().expect("cache level parses");
322+
323+
let shared_cpu_list = self
324+
.vm
325+
.run_shell_command(&format!("cat {stem}/shared_cpu_list"))
326+
.await
327+
.expect("can read shared_cpu_list")
328+
.trim()
329+
.to_string();
330+
331+
result.push(CacheInfo { level, shared_cpu_list });
332+
}
333+
result
334+
}
295335
}
296336

297337
#[phd_testcase]
@@ -358,4 +398,30 @@ async fn guest_cpu_topo_test(ctx: &Framework) {
358398
}));
359399
}
360400
}
401+
402+
// Check cache topology. With SMT, L1/L2 should be shared by SMT siblings
403+
// while L3 is shared across all vCPUs.
404+
let caches = guest_topo.cache_info().await;
405+
let num_cpus = guest_topo.cpus().await;
406+
407+
for cache in &caches {
408+
match cache.level {
409+
1 | 2 => {
410+
let expected = if has_smt { "0-1" } else { "0" };
411+
assert_eq!(cache.shared_cpu_list, expected);
412+
}
413+
3 => {
414+
let expected = format!("0-{}", num_cpus - 1);
415+
assert_eq!(cache.shared_cpu_list, expected);
416+
}
417+
other => {
418+
panic!("unexpected cache level {other}");
419+
}
420+
}
421+
}
422+
423+
let l1_count = caches.iter().filter(|c| c.level == 1).count();
424+
let l2_count = caches.iter().filter(|c| c.level == 2).count();
425+
assert!(l1_count >= 2, "expected at least L1d and L1i caches");
426+
assert!(l2_count >= 1, "expected at least one L2 cache");
361427
}

0 commit comments

Comments
 (0)