Skip to main content

scx_cake/
topology.rs

1// SPDX-License-Identifier: GPL-2.0
2// Topology detection - CPUs, CCDs, P/E cores. Results passed to BPF as const volatile.
3
4use anyhow::Result;
5use scx_utils::{CoreType, Topology};
6
7/// Maximum supported CPUs (matches BPF array sizes)
8pub const MAX_CPUS: usize = 64;
9/// Maximum supported LLCs (matches BPF array sizes)
10pub const MAX_LLCS: usize = 8;
11
12/// Detected topology information
13#[derive(Debug, Clone)]
14pub struct TopologyInfo {
15    /// Number of online CPUs
16    pub nr_cpus: usize,
17
18    /// True if system has multiple L3 cache domains (CCDs)
19    pub has_dual_ccd: bool,
20
21    /// True if system has hybrid P/E cores (Intel hybrid or similar)
22    pub has_hybrid_cores: bool,
23
24    /// SMT enabled status
25    pub smt_enabled: bool,
26    /// Map of CPU ID -> Sibling CPU ID (or self if none/disabled)
27    pub cpu_sibling_map: [u8; MAX_CPUS],
28
29    // BPF Maps
30    pub cpu_llc_id: [u8; MAX_CPUS],
31    pub cpu_is_big: [u8; MAX_CPUS],
32    pub cpu_core_id: [u8; MAX_CPUS],
33    pub cpu_thread_bit: [u8; MAX_CPUS],
34    pub cpu_dsq_id: [u32; MAX_CPUS],
35    /// Pre-computed 64-bit mask of all CPUs in a physical core
36    pub core_cpu_mask: [u64; 32],
37    /// Bitmask requirement for a core to be "fully idle" (e.g. 0x3 for dual SMT)
38    pub core_thread_mask: [u8; 32],
39    pub llc_cpu_mask: [u64; MAX_LLCS],
40    pub big_cpu_mask: u64,
41
42    // Info
43    pub cpus_per_ccd: u32,
44}
45
46pub fn detect() -> Result<TopologyInfo> {
47    // robustly detect topology using scx_utils
48    let topo = Topology::new()?;
49
50    let nr_cpus = topo.all_cpus.len();
51    let nr_llcs = topo.all_llcs.len();
52
53    // Get sibling map directly from scx_utils
54    let siblings = topo.sibling_cpus();
55    let mut cpu_sibling_map = [0u8; MAX_CPUS];
56
57    // Default to self-mapping
58    for (i, sibling) in cpu_sibling_map.iter_mut().enumerate().take(MAX_CPUS) {
59        *sibling = i as u8;
60    }
61
62    // Populate with detected siblings
63    for (cpu, &sibling) in siblings.iter().enumerate() {
64        if cpu < MAX_CPUS && sibling >= 0 {
65            let sib = sibling as usize;
66            if sib < MAX_CPUS {
67                cpu_sibling_map[cpu] = sib as u8;
68            }
69        }
70    }
71
72    let mut info = TopologyInfo {
73        nr_cpus,
74        has_dual_ccd: nr_llcs > 1,
75        has_hybrid_cores: false, // Will detect below
76        smt_enabled: topo.smt_enabled,
77        cpu_sibling_map,
78        cpu_llc_id: [0; MAX_CPUS],
79        cpu_is_big: [1; MAX_CPUS], // Default to 1 (Big) to be safe
80        cpu_core_id: [0; MAX_CPUS],
81        cpu_thread_bit: [0; MAX_CPUS],
82        cpu_dsq_id: [0; MAX_CPUS],
83        core_cpu_mask: [0; 32],
84        core_thread_mask: [0; 32],
85        llc_cpu_mask: [0; MAX_LLCS],
86        big_cpu_mask: 0,
87        cpus_per_ccd: 0,
88    };
89
90    // 1. Map LLCs
91    // Note: topo.all_llcs keys are arbitrary kernel IDs. We must map them to 0..MAX_LLCS-1.
92    // We'll just use a simple counter 0,1,2... as we iterate.
93    let mut llc_idx = 0;
94
95    for llc in topo.all_llcs.values() {
96        if llc_idx >= MAX_LLCS {
97            break;
98        }
99
100        let mut mask = 0u64;
101        let mut core_count = 0;
102
103        for cpu_id in llc.all_cpus.keys() {
104            let cpu = *cpu_id;
105            if cpu < MAX_CPUS {
106                info.cpu_llc_id[cpu] = llc_idx as u8;
107                mask |= 1u64 << cpu;
108                core_count += 1;
109            }
110        }
111
112        info.llc_cpu_mask[llc_idx] = mask;
113        if info.cpus_per_ccd == 0 {
114            info.cpus_per_ccd = core_count;
115        } // Estimate
116
117        llc_idx += 1;
118    }
119
120    // 2. Identify P-cores vs E-cores
121    // Reset defaults to recalculate based on CoreType
122    info.cpu_is_big = [0; MAX_CPUS];
123    info.big_cpu_mask = 0;
124
125    let mut p_cores_found = 0;
126    let mut e_cores_found = 0;
127
128    for (core_id_usize, core) in &topo.all_cores {
129        let core_id = *core_id_usize;
130
131        // Determine is_big.
132        // If CoreType::Efficiency -> 0.
133        // If Performance or Unknown -> 1.
134        let is_big = match core.core_type {
135            CoreType::Little => 0,
136            _ => 1,
137        };
138
139        if is_big == 1 {
140            p_cores_found += 1;
141        } else {
142            e_cores_found += 1;
143        }
144
145        // Calculate SMT requirement mask for this core
146        if core_id < 32 {
147            info.core_thread_mask[core_id] = ((1u16 << core.cpus.len()) - 1) as u8;
148        }
149
150        // Iterate over CPUs in this core
151        let mut thread_idx = 0;
152        let mut sorted_cpus: Vec<_> = core.cpus.keys().collect();
153        sorted_cpus.sort();
154
155        for cpu_id in sorted_cpus {
156            let cpu = *cpu_id;
157            if cpu < MAX_CPUS {
158                info.cpu_is_big[cpu] = is_big;
159                info.cpu_core_id[cpu] = core_id as u8;
160                info.cpu_thread_bit[cpu] = 1 << thread_idx;
161                info.cpu_dsq_id[cpu] = 1000 /* CAKE_DSQ_LC_BASE */ + cpu as u32;
162
163                if core_id < 32 {
164                    info.core_cpu_mask[core_id] |= 1u64 << cpu;
165                }
166
167                if is_big == 1 {
168                    info.big_cpu_mask |= 1u64 << cpu;
169                }
170                thread_idx += 1;
171            }
172        }
173    }
174
175    // Update hybrid flag
176    if p_cores_found > 0 && e_cores_found > 0 {
177        info.has_hybrid_cores = true;
178    } else {
179        info.has_hybrid_cores = false;
180        // If not hybrid, ensure all marked as Big for consistency (though mask handles it)
181        if p_cores_found == 0 && e_cores_found > 0 {
182            // Weird case: All E-cores? Treat as "Big" relative to nothing.
183            // But we keep as is.
184        }
185    }
186
187    // Log detected topology (debug level - use RUST_LOG=debug to see)
188    log::debug!("Topology detected:");
189    log::debug!("  CPUs:          {}", info.nr_cpus);
190    log::debug!("  SMT Enabled:   {}", info.smt_enabled);
191    log::debug!("  Dual CCD:      {}", info.has_dual_ccd);
192    if info.has_dual_ccd {
193        log::debug!("    Masks:       {:x?}", &info.llc_cpu_mask[..llc_idx]);
194    }
195    log::debug!("  Hybrid cores:  {}", info.has_hybrid_cores);
196    if info.has_hybrid_cores {
197        log::debug!("    P-core mask: {:016x}", info.big_cpu_mask);
198    }
199
200    Ok(info)
201}