@@ -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+
170175impl < ' 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