Merge branch 'sched-ext:main' into main

This commit is contained in:
Avraham Hollander 2024-08-27 23:07:54 -04:00 committed by GitHub
commit 6c5d85401d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 73 additions and 80 deletions

View File

@ -17,9 +17,20 @@
//! hexadecimal string:
//!
//!```
//! let all_zeroes = Cpumask::new();
//! let all_zeroes = cpumask::new();
//! let str = "0xff00ff00";
//! let from_str_mask = Cpumask::from_string(str);
//! let from_str_mask = cpumask::from_string(str);
//!```
//!
//! The hexadecimal string also supports the special values "none" and "all",
//! respectively to specify no CPU (empty mask) or all CPUs (full mask):
//!
//!```
//! let str = "none";
//! let all_zeroes = cpumask::from_string(str);
//!
//! let str = "all";
//! let all_ones = cpumask::from_string(str);
//!```
//!
//! A Cpumask can be queried and updated using its helper functions:
@ -75,6 +86,17 @@ impl Cpumask {
/// Build a Cpumask object from a hexadecimal string.
pub fn from_str(cpumask: &String) -> Result<Cpumask> {
match cpumask.as_str() {
"none" => {
let mask = bitvec![u64, Lsb0; 0; *NR_CPU_IDS];
return Ok(Self { mask });
},
"all" => {
let mask = bitvec![u64, Lsb0; 1; *NR_CPU_IDS];
return Ok(Self { mask });
},
_ => {},
}
let hex_str = {
let mut tmp_str = cpumask
.strip_prefix("0x")
@ -114,7 +136,9 @@ impl Cpumask {
}
pub fn from_vec(vec: Vec<u64>) -> Self {
Self { mask: BitVec::from_vec(vec) }
Self {
mask: BitVec::from_vec(vec),
}
}
/// Return a slice of u64's whose bits reflect the Cpumask.

View File

@ -61,6 +61,7 @@ pub mod ravg;
mod topology;
pub use topology::Cache;
pub use topology::Core;
pub use topology::CoreType;
pub use topology::Cpu;
pub use topology::Node;
pub use topology::Topology;

View File

@ -74,7 +74,7 @@ use anyhow::Result;
use glob::glob;
use sscanf::sscanf;
use std::collections::BTreeMap;
use std::path::{Path, PathBuf};
use std::path::Path;
use std::slice::Iter;
lazy_static::lazy_static! {
@ -97,7 +97,7 @@ lazy_static::lazy_static! {
#[derive(Debug, Clone, Eq, Hash, PartialEq)]
pub enum CoreType {
Big,
Big { turbo: bool },
Little,
}
@ -421,7 +421,7 @@ fn create_insert_cpu(
cpu_id: usize,
node: &mut Node,
online_mask: &Cpumask,
avg_cpu_freq: Option<usize>,
avg_cpu_freq: Option<(usize, usize)>,
) -> Result<()> {
// CPU is offline. The Topology hierarchy is read-only, and assumes
// that hotplug will cause the scheduler to restart. Thus, we can
@ -463,16 +463,16 @@ fn create_insert_cpu(
});
let core_type = match avg_cpu_freq {
Some(avg_freq) => {
if max_freq >= avg_freq {
CoreType::Big
} else if max_freq < avg_freq {
CoreType::Little
Some((avg_base_freq, top_max_freq)) => {
if max_freq == top_max_freq {
CoreType::Big { turbo: true }
} else if base_freq >= avg_base_freq {
CoreType::Big { turbo: false }
} else {
CoreType::Big
CoreType::Little
}
}
None => CoreType::Big,
None => CoreType::Big { turbo: false },
};
let core = cache.cores.entry(core_id).or_insert(Core {
@ -525,24 +525,28 @@ fn read_cpu_ids() -> Result<Vec<usize>> {
Ok(cpu_ids)
}
fn avg_cpu_freq() -> Option<usize> {
let mut avg_freq = 0;
// Return the average base frequency across all CPUs and the highest maximum frequency.
fn avg_cpu_freq() -> Option<(usize, usize)> {
let mut top_max_freq = 0;
let mut avg_base_freq = 0;
let mut nr_cpus = 0;
let cpu_paths = glob("/sys/devices/system/cpu/cpu[0-9]*").ok()?;
for cpu_path in cpu_paths.filter_map(Result::ok) {
let mut buf = PathBuf::from(&cpu_path);
buf.push("cpufreq");
buf.push("scaling_max_freq");
let max_freq = read_file_usize(buf.as_path()).unwrap_or(0);
if max_freq > 0 {
avg_freq += max_freq;
let freq_path = cpu_path.join("cpufreq");
let max_freq = read_file_usize(&freq_path.join("scaling_max_freq")).unwrap_or(0);
let base_freq = read_file_usize(&freq_path.join("base_frequency")).unwrap_or(max_freq);
if base_freq > 0 {
if max_freq > top_max_freq {
top_max_freq = max_freq;
}
avg_base_freq += base_freq;
nr_cpus += 1;
}
}
if avg_freq == 0 {
if avg_base_freq == 0 {
return None;
}
Some(avg_freq / nr_cpus)
Some((avg_base_freq / nr_cpus, top_max_freq))
}
fn create_default_node(online_mask: &Cpumask) -> Result<Vec<Node>> {

View File

@ -48,6 +48,7 @@ use scx_utils::scx_ops_load;
use scx_utils::scx_ops_open;
use scx_utils::uei_exited;
use scx_utils::uei_report;
use scx_utils::CoreType;
use scx_utils::Cpumask;
use scx_utils::Topology;
use scx_utils::UserExitInfo;
@ -65,61 +66,28 @@ enum Powermode {
fn get_primary_cpus(mode: Powermode) -> std::io::Result<Vec<usize>> {
let topo = Topology::new().unwrap();
// Iterate over each CPU directory and collect CPU ID and its base operational frequency to
// distinguish between fast and slow cores.
let mut cpu_freqs = Vec::new();
let mut max_cpu_freqs = Vec::new();
for core in topo.cores().into_iter() {
for (cpu_id, cpu) in core.cpus() {
cpu_freqs.push((*cpu_id, cpu.base_freq()));
max_cpu_freqs.push((*cpu_id, cpu.max_freq()));
}
}
if cpu_freqs.is_empty() {
return Ok(Vec::new());
}
let cpus: Vec<usize> = topo
.cores()
.into_iter()
.flat_map(|core| core.cpus())
.filter_map(|(cpu_id, cpu)| match (&mode, &cpu.core_type) {
// Turbo mode: only add turbo-boosted CPUs
(Powermode::Turbo, CoreType::Big { turbo: true }) |
// Performance mode: add all the Big CPUs (either Turbo or non-Turbo)
(Powermode::Performance, CoreType::Big { .. }) |
// Powersave mode: add all the Little CPUs
(Powermode::Powersave, CoreType::Little) => Some(*cpu_id),
_ => None,
})
.collect();
// Find the smallest maximum frequency.
let min_freq = cpu_freqs.iter().map(|&(_, freq)| freq).min().unwrap();
// Find the highest maximum frequency.
let max_freq = max_cpu_freqs.iter().map(|&(_, freq)| freq).max().unwrap();
// Check if all CPUs have the smallest frequency.
let all_have_min_freq = cpu_freqs.iter().all(|&(_, freq)| freq == min_freq);
let selected_cpu_ids: Vec<usize> = if mode == Powermode::Turbo {
// Turbo: return the CPUs with the highest max frequency.
max_cpu_freqs
.into_iter()
.filter(|&(_, freq)| freq == max_freq)
.map(|(cpu_id, _)| cpu_id)
.collect()
} else if all_have_min_freq || mode == Powermode::Powersave {
// Powersave: return the CPUs with the smallest base frequency.
cpu_freqs
.into_iter()
.filter(|&(_, freq)| freq == min_freq)
.map(|(cpu_id, _)| cpu_id)
.collect()
} else if mode == Powermode::Performance {
// Performance: return the CPUs with a base frequency greater than the minimum.
cpu_freqs
.into_iter()
.filter(|&(_, freq)| freq > min_freq)
.map(|(cpu_id, _)| cpu_id)
.collect()
} else {
Vec::new()
};
Ok(selected_cpu_ids)
Ok(cpus)
}
// Convert an array of CPUs to the corresponding cpumask of any arbitrary size.
fn cpus_to_cpumask(cpus: &Vec<usize>) -> String {
if cpus.is_empty() {
return String::from("0x0");
return String::from("all");
}
// Determine the maximum CPU ID to create a sufficiently large byte vector.
@ -157,11 +125,7 @@ fn parse_cpumask(cpu_str: &str) -> Result<Cpumask, anyhow::Error> {
}
"auto" => Cpumask::new(),
_ if !cpu_str.is_empty() => Cpumask::from_str(&cpu_str.to_string()),
_ => {
let mut cpumask = Cpumask::new()?;
cpumask.setall();
Ok(cpumask)
}
_ => Cpumask::from_str(&"all".to_string()),
}
}
@ -222,9 +186,10 @@ struct Opts {
/// - "performance" = automatically detect and use the fastest CPUs
/// - "powersave" = automatically detect and use the slowest CPUs
/// - "auto" = automatically detect the CPUs based on the current energy profile
/// - "all" = all CPUs assigned to the primary domain
///
/// By default all CPUs are used for the primary scheduling domain.
#[clap(short = 'm', long, default_value = "", value_parser = parse_cpumask)]
#[clap(short = 'm', long, default_value = "all", value_parser = parse_cpumask)]
primary_domain: Cpumask,
/// Disable L2 cache awareness.
@ -437,8 +402,7 @@ impl<'a> Scheduler<'a> {
// If no turbo-boosted CPUs are selected, use an empty CPU mask, so that tasks are
// scheduled directly to the primary domain, bypassing the turbo boost domain.
if cpus.is_empty() {
let mut cpumask = Cpumask::new()?;
cpumask
Cpumask::new()?
} else {
Cpumask::from_str(&cpus_to_cpumask(&cpus))?
}