scx_layered: Read CPU topology for building CpuPool

Building CpuPool from cache-cpu topology did not apply on arm, because
`/sys/devices/system/cpu/cpu{}/cache/index{}/id` file is unavailable.

Read CPU topology instead.

Signed-off-by: Ming Yang <minos.future@gmail.com>
This commit is contained in:
Ming Yang 2024-10-06 15:51:21 -07:00
parent f37bc0db7f
commit f3f4726c09
2 changed files with 79 additions and 70 deletions

View File

@ -73,6 +73,8 @@ use crate::misc::read_file_usize;
use crate::Cpumask;
use anyhow::bail;
use anyhow::Result;
use bitvec::bitvec;
use bitvec::vec::BitVec;
use glob::glob;
use sscanf::sscanf;
use std::collections::BTreeMap;
@ -373,6 +375,39 @@ impl Topology {
pub fn has_little_cores(&self) -> bool {
self.cores.iter().any(|c| c.core_type == CoreType::Little)
}
/// Returns a BitVec of online CPUs.
pub fn cpus_bitvec(&self) -> BitVec {
let mut cpus = bitvec![0; *NR_CPUS_POSSIBLE];
for (id, _) in self.cpus.iter() {
cpus.set(*id, true);
}
cpus
}
/// Returns a vector that maps the index of each logical core to the sibling core.
/// This represents the "next sibling" core within a package in systems that support SMT.
/// The sibling core is the other logical core that shares the physical resources
/// of the same physical core.
///
/// Assuming each core holds exactly at most two cpus.
pub fn sibling_cpus(&self) -> Vec<i32> {
let mut sibling_cpu = vec![-1i32; *NR_CPUS_POSSIBLE];
for core in self.cores() {
let mut first = -1i32;
for (cpu_id, _) in core.cpus() {
let cpu = *cpu_id;
if first < 0 {
first = cpu as i32;
} else {
sibling_cpu[first as usize] = cpu as i32;
sibling_cpu[cpu as usize] = first;
break;
}
}
}
sibling_cpu
}
}
/// Generate a topology map from a Topology object, represented as an array of arrays.
@ -416,6 +451,39 @@ impl TopologyMap {
pub fn iter(&self) -> Iter<Vec<usize>> {
self.map.iter()
}
/// Returns a vector of bit masks, each representing the mapping between
/// physical cores and the logical cores that run on them.
/// The index in the vector represents the physical core, and each bit in the
/// corresponding `BitVec` represents whether a logical core belongs to that physical core.
pub fn core_cpus_bitvec(&self) -> Vec<BitVec> {
let mut core_cpus = Vec::<BitVec>::new();
for (core_id, core) in self.iter().enumerate() {
if core_cpus.len() < core_id + 1 {
core_cpus.resize(core_id + 1, bitvec![0; *NR_CPUS_POSSIBLE]);
}
for cpu in core {
core_cpus[core_id].set(*cpu, true);
}
}
core_cpus
}
/// Returns mapping between logical core and physical core ids
/// The index in the vector represents the logical core, and each corresponding value
/// represents whether the physical core id of the logical core.
pub fn cpu_core_mapping(&self) -> Vec<usize> {
let mut cpu_core_mapping = Vec::new();
for (core_id, core) in self.iter().enumerate() {
for cpu in core {
if cpu_core_mapping.len() < cpu + 1 {
cpu_core_mapping.resize(cpu + 1, 0);
}
cpu_core_mapping[*cpu] = core_id;
}
}
cpu_core_mapping
}
}
/**********************************************

View File

@ -8,10 +8,8 @@ mod layer_core_growth;
pub mod bpf_intf;
use std::collections::BTreeMap;
use std::collections::BTreeSet;
use anyhow::bail;
use anyhow::Context;
use anyhow::Result;
use bitvec::prelude::*;
pub use config::LayerConfig;
@ -23,9 +21,9 @@ use log::debug;
use log::info;
use scx_utils::Core;
use scx_utils::Topology;
use scx_utils::TopologyMap;
const MAX_CPUS: usize = bpf_intf::consts_MAX_CPUS as usize;
const CORE_CACHE_LEVEL: u32 = 2;
lazy_static::lazy_static! {
static ref NR_POSSIBLE_CPUS: usize = libbpf_rs::num_possible_cpus().unwrap();
@ -61,8 +59,9 @@ pub struct CpuPool {
/// of the same physical core.
pub sibling_cpu: Vec<i32>,
/// A list of physical core IDs.
/// Each entry in this vector corresponds to a unique physical core.
/// Mapping between logical core and physical core ids
/// The index in the vector represents the logical core, and each corresponding value
/// represents whether the physical core id of the logical core.
cpu_core: Vec<usize>,
/// A bit mask representing all available physical cores.
@ -93,72 +92,14 @@ impl CpuPool {
);
}
let mut cpu_to_cache = vec![]; // (cpu_id, Option<cache_id>)
let mut cache_ids = BTreeSet::<usize>::new();
let mut nr_offline = 0;
let topo_map = TopologyMap::new(&topo).unwrap();
// Build cpu -> cache ID mapping.
for cpu in 0..*NR_POSSIBLE_CPUS {
let path = format!(
"/sys/devices/system/cpu/cpu{}/cache/index{}/id",
cpu, CORE_CACHE_LEVEL
);
let id = match std::fs::read_to_string(&path) {
Ok(val) => Some(val.trim().parse::<usize>().with_context(|| {
format!("Failed to parse {:?}'s content {:?}", &path, &val)
})?),
Err(e) if e.kind() == std::io::ErrorKind::NotFound => {
nr_offline += 1;
None
}
Err(e) => return Err(e).with_context(|| format!("Failed to open {:?}", &path)),
};
cpu_to_cache.push(id);
if let Some(id) = id {
cache_ids.insert(id);
}
}
let nr_cpus = *NR_POSSIBLE_CPUS - nr_offline;
// Cache IDs may have holes. Assign consecutive core IDs to existing
// cache IDs.
let mut cache_to_core = BTreeMap::<usize, usize>::new();
let mut nr_cores = 0;
for cache_id in cache_ids.iter() {
cache_to_core.insert(*cache_id, nr_cores);
nr_cores += 1;
}
// Build core -> cpumask and cpu -> core mappings.
let mut all_cpus = bitvec![0; *NR_POSSIBLE_CPUS];
let mut core_cpus = vec![bitvec![0; *NR_POSSIBLE_CPUS]; nr_cores];
let mut cpu_core = vec![];
for (cpu, cache) in cpu_to_cache.iter().enumerate().take(*NR_POSSIBLE_CPUS) {
if let Some(cache_id) = cache {
let core_id = cache_to_core[cache_id];
all_cpus.set(cpu, true);
core_cpus[core_id].set(cpu, true);
cpu_core.push(core_id);
}
}
// Build sibling_cpu[]
let mut sibling_cpu = vec![-1i32; *NR_POSSIBLE_CPUS];
for cpus in &core_cpus {
let mut first = -1i32;
for cpu in cpus.iter_ones() {
if first < 0 {
first = cpu as i32;
} else {
sibling_cpu[first as usize] = cpu as i32;
sibling_cpu[cpu as usize] = first;
break;
}
}
}
let all_cpus = topo.cpus_bitvec();
let nr_cpus = topo.nr_cpus_online();
let core_cpus = topo_map.core_cpus_bitvec();
let nr_cores = topo.cores().len();
let sibling_cpu = topo.sibling_cpus();
let cpu_core = topo_map.cpu_core_mapping();
// Build core_topology_to_id
let mut core_topology_to_id = BTreeMap::new();