1use std::path::Path;
8
9use anyhow::{Context, Result};
10use log::info;
11
12use scx_utils::Topology;
13
14pub fn init_topology(skel: &mut crate::BpfSkel<'_>) -> Result<()> {
15 let topo = Topology::new().context("Failed to read CPU topology")?;
16 let nr_cpus = topo.all_cpus.len();
17 let bss = skel
18 .maps
19 .bss_data
20 .as_mut()
21 .context("bss_data missing — BPF object has no .bss section")?;
22
23 info!(
24 "Discovered {} CPUs — reading max frequencies, LLC topology, SMT",
25 nr_cpus
26 );
27
28 let mut core_to_cpus: Vec<Vec<usize>> = vec![Vec::new(); 1024];
29 let mut system_total_khz: u64 = 0;
30
31 for (id, cpu) in &topo.all_cpus {
32 if *id >= 1024 {
33 log::warn!("CPU {} exceeds max supported (1024), skipping", id);
34 continue;
35 }
36 let freq_khz = cpu.max_freq as u64;
37 let llc_id = read_cpu_llc_id(*id);
38 let core_id = read_cpu_core_id(*id);
39
40 bss.per_cpu_max_freq_khz[*id] = freq_khz;
41 bss.per_cpu_llc_id[*id] = llc_id;
42
43 if core_id < 1024 {
44 core_to_cpus[core_id].push(*id);
45 }
46
47 info!(
48 " CPU {:3}: max_freq = {:>6} MHz, LLC = {}, core_id = {}",
49 id,
50 freq_khz / 1000,
51 llc_id,
52 core_id,
53 );
54 }
55
56 for (core_id, cpus) in core_to_cpus.iter().enumerate() {
61 let count = cpus.len();
62 if count > 1 {
63 for (j, &cpu_id) in cpus.iter().enumerate() {
64 bss.per_cpu_sibling_count[cpu_id] = count as u64;
65 if j == 0 {
66 info!(
67 " CPU {:3}: primary (core {}: {} CPUs)",
68 cpu_id, core_id, count
69 );
70 } else {
71 bss.per_cpu_is_smt[cpu_id] = 1;
72 info!(" CPU {:3}: SMT sibling (core {})", cpu_id, core_id);
73 }
74 }
75 } else if count == 1 {
76 bss.per_cpu_sibling_count[cpus[0]] = 1;
77 }
78 }
79
80 for (id, cpu) in &topo.all_cpus {
81 let freq_khz = cpu.max_freq as u64;
82 let core_id = read_cpu_core_id(*id);
83 let siblings = if core_id < 1024 {
84 core_to_cpus[core_id].len()
85 } else {
86 1
87 };
88 let thread_bw = if siblings > 1 {
89 freq_khz / siblings as u64
90 } else {
91 freq_khz
92 };
93 system_total_khz += thread_bw;
94 }
95 bss.system_total_khz = system_total_khz;
96
97 info!(
98 " System total effective bandwidth: {} MHz ({} cores, SMT halved)",
99 system_total_khz / 1000,
100 nr_cpus,
101 );
102
103 Ok(())
104}
105
106fn read_cpu_llc_id(cpu: usize) -> u64 {
107 let p = format!("/sys/devices/system/cpu/cpu{}/cache/index3/id", cpu);
108 let path = Path::new(&p);
109 if path.exists() {
110 return std::fs::read_to_string(path)
111 .ok()
112 .and_then(|s| s.trim().parse::<u64>().ok())
113 .unwrap_or(0);
114 }
115 let fb = format!("/sys/devices/system/cpu/cpu{}/topology/llc_id", cpu);
116 let fb_path = Path::new(&fb);
117 if fb_path.exists() {
118 return std::fs::read_to_string(fb_path)
119 .ok()
120 .and_then(|s| s.trim().parse::<u64>().ok())
121 .unwrap_or(0);
122 }
123 0
124}
125
126fn read_cpu_core_id(cpu: usize) -> usize {
127 let p = format!("/sys/devices/system/cpu/cpu{}/topology/core_id", cpu);
128 let path = Path::new(&p);
129 if !path.exists() {
130 log::warn!("CPU {}: topology/core_id not found", cpu);
131 return 0;
132 }
133 std::fs::read_to_string(path)
134 .ok()
135 .and_then(|s| s.trim().parse::<usize>().ok())
136 .unwrap_or(0)
137}