Skip to content

Commit 35c53ce

Browse files
authored
Merge pull request raspberrypi#33 from dschatzberg/fbprocfs
scx: atropos: Use fb-procfs crate
2 parents aca7309 + b428627 commit 35c53ce

File tree

2 files changed

+123
-77
lines changed

2 files changed

+123
-77
lines changed

tools/sched_ext/scx_atropos/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ anyhow = "1.0.65"
1111
bitvec = { version = "1.0", features = ["serde"] }
1212
clap = { version = "4.1", features = ["derive", "env", "unicode", "wrap_help"] }
1313
ctrlc = { version = "3.1", features = ["termination"] }
14+
fb_procfs = "0.7.0"
1415
hex = "0.4.3"
1516
libbpf-rs = "0.21.0"
1617
libbpf-sys = { version = "1.2.0", features = ["novendor", "static"] }

tools/sched_ext/scx_atropos/src/main.rs

Lines changed: 122 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ use std::sync::Arc;
1919
use std::time::Duration;
2020
use std::time::Instant;
2121

22+
use ::fb_procfs as procfs;
2223
use anyhow::anyhow;
2324
use anyhow::bail;
2425
use anyhow::Context;
@@ -157,77 +158,61 @@ fn format_cpumask(cpumask: &[u64], nr_cpus: usize) -> String {
157158
.fold(String::new(), |acc, x| format!("{} {:016X}", acc, x))
158159
}
159160

160-
// Neither procfs or fb_procfs can determine per-CPU utilization reliably
161-
// with CPU hot[un]plugs. Roll our own.
162-
//
163-
// https://github.com/eminence/procfs/issues/274
164-
// https://github.com/facebookincubator/below/issues/8190
165-
#[derive(Clone, Debug, Default)]
166-
struct MyCpuStat {
167-
user: u64,
168-
nice: u64,
169-
system: u64,
170-
idle: u64,
171-
iowait: u64,
172-
irq: u64,
173-
softirq: u64,
174-
steal: u64,
161+
fn read_total_cpu(reader: &procfs::ProcReader) -> Result<procfs::CpuStat> {
162+
Ok(reader
163+
.read_stat()
164+
.context("Failed to read procfs")?
165+
.total_cpu
166+
.ok_or_else(|| anyhow!("Could not read total cpu stat in proc"))?)
175167
}
176168

177-
impl MyCpuStat {
178-
fn busy_and_total(&self) -> (u64, u64) {
179-
let busy = self.user + self.system + self.nice + self.irq + self.softirq + self.steal;
180-
(busy, self.idle + busy + self.iowait)
181-
}
182-
183-
fn calc_util(&self, prev: &MyCpuStat) -> f64 {
184-
let (curr_busy, curr_total) = self.busy_and_total();
185-
let (prev_busy, prev_total) = prev.busy_and_total();
186-
let busy = curr_busy - prev_busy;
187-
let total = curr_total - prev_total;
188-
if total > 0 {
189-
((busy as f64) / (total as f64)).clamp(0.0, 1.0)
190-
} else {
191-
1.0
192-
}
193-
}
194-
}
195-
196-
#[derive(Clone, Debug, Default)]
197-
struct MyProcStat {
198-
total: MyCpuStat,
199-
cpus: BTreeMap<usize, MyCpuStat>,
200-
}
201-
202-
impl MyProcStat {
203-
fn read() -> Result<Self> {
204-
let mut result: MyProcStat = Default::default();
205-
for line in std::fs::read_to_string("/proc/stat")?.lines() {
206-
let mut toks = line.split_whitespace();
207-
208-
let key = toks.next().ok_or(anyhow!("no key"))?;
209-
if !key.starts_with("cpu") {
210-
break;
211-
}
212-
213-
let cputime = MyCpuStat {
214-
user: toks.next().ok_or(anyhow!("missing"))?.parse::<u64>()?,
215-
nice: toks.next().ok_or(anyhow!("missing"))?.parse::<u64>()?,
216-
system: toks.next().ok_or(anyhow!("missing"))?.parse::<u64>()?,
217-
idle: toks.next().ok_or(anyhow!("missing"))?.parse::<u64>()?,
218-
iowait: toks.next().ok_or(anyhow!("missing"))?.parse::<u64>()?,
219-
irq: toks.next().ok_or(anyhow!("missing"))?.parse::<u64>()?,
220-
softirq: toks.next().ok_or(anyhow!("missing"))?.parse::<u64>()?,
221-
steal: toks.next().ok_or(anyhow!("missing"))?.parse::<u64>()?,
222-
};
223-
224-
if key.len() == 3 {
225-
result.total = cputime;
169+
fn calc_util(curr: &procfs::CpuStat, prev: &procfs::CpuStat) -> Result<f64> {
170+
match (curr, prev) {
171+
(
172+
procfs::CpuStat {
173+
user_usec: Some(prev_user),
174+
nice_usec: Some(prev_nice),
175+
system_usec: Some(prev_system),
176+
idle_usec: Some(prev_idle),
177+
iowait_usec: Some(prev_iowait),
178+
irq_usec: Some(prev_irq),
179+
softirq_usec: Some(prev_softirq),
180+
stolen_usec: Some(prev_stolen),
181+
..
182+
},
183+
procfs::CpuStat {
184+
user_usec: Some(curr_user),
185+
nice_usec: Some(curr_nice),
186+
system_usec: Some(curr_system),
187+
idle_usec: Some(curr_idle),
188+
iowait_usec: Some(curr_iowait),
189+
irq_usec: Some(curr_irq),
190+
softirq_usec: Some(curr_softirq),
191+
stolen_usec: Some(curr_stolen),
192+
..
193+
},
194+
) => {
195+
let idle_usec = curr_idle - prev_idle;
196+
let iowait_usec = curr_iowait - prev_iowait;
197+
let user_usec = curr_user - prev_user;
198+
let system_usec = curr_system - prev_system;
199+
let nice_usec = curr_nice - prev_nice;
200+
let irq_usec = curr_irq - prev_irq;
201+
let softirq_usec = curr_softirq - prev_softirq;
202+
let stolen_usec = curr_stolen - prev_stolen;
203+
204+
let busy_usec =
205+
user_usec + system_usec + nice_usec + irq_usec + softirq_usec + stolen_usec;
206+
let total_usec = idle_usec + busy_usec + iowait_usec;
207+
if total_usec > 0 {
208+
return Ok(((busy_usec as f64) / (total_usec as f64)).clamp(0.0, 1.0));
226209
} else {
227-
result.cpus.insert(key[3..].parse::<usize>()?, cputime);
210+
return Ok(1.0);
228211
}
229212
}
230-
Ok(result)
213+
_ => {
214+
bail!("Missing stats in cpustat");
215+
}
231216
}
232217
}
233218

@@ -394,38 +379,50 @@ struct Tuner {
394379
top: Arc<Topology>,
395380
direct_greedy_under: f64,
396381
kick_greedy_under: f64,
397-
prev_cpu_stats: BTreeMap<usize, MyCpuStat>,
382+
proc_reader: procfs::ProcReader,
383+
prev_cpu_stats: BTreeMap<u32, procfs::CpuStat>,
398384
dom_utils: Vec<f64>,
399385
}
400386

401387
impl Tuner {
402388
fn new(top: Arc<Topology>, opts: &Opts) -> Result<Self> {
389+
let proc_reader = procfs::ProcReader::new();
390+
let prev_cpu_stats = proc_reader
391+
.read_stat()?
392+
.cpus_map
393+
.ok_or_else(|| anyhow!("Expected cpus_map to exist"))?;
403394
Ok(Self {
404395
direct_greedy_under: opts.direct_greedy_under / 100.0,
405396
kick_greedy_under: opts.kick_greedy_under / 100.0,
406-
prev_cpu_stats: MyProcStat::read()?.cpus,
397+
proc_reader,
398+
prev_cpu_stats,
407399
dom_utils: vec![0.0; top.nr_doms],
408400
top,
409401
})
410402
}
411403

412404
fn step(&mut self, skel: &mut AtroposSkel) -> Result<()> {
413-
let curr_cpu_stats = MyProcStat::read()?.cpus;
405+
let curr_cpu_stats = self
406+
.proc_reader
407+
.read_stat()?
408+
.cpus_map
409+
.ok_or_else(|| anyhow!("Expected cpus_map to exist"))?;
414410
let ti = &mut skel.bss().tune_input;
415411
let mut dom_nr_cpus = vec![0; self.top.nr_doms];
416412
let mut dom_util_sum = vec![0.0; self.top.nr_doms];
417413

418414
for cpu in 0..self.top.nr_cpus {
415+
let cpu32 = cpu as u32;
419416
// None domain indicates the CPU was offline during
420-
// initialization and None MyCpuStat indicates the CPU has gone
417+
// initialization and None CpuStat indicates the CPU has gone
421418
// down since then. Ignore both.
422419
if let (Some(dom), Some(curr), Some(prev)) = (
423420
self.top.cpu_dom[cpu],
424-
curr_cpu_stats.get(&cpu),
425-
self.prev_cpu_stats.get(&cpu),
421+
curr_cpu_stats.get(&cpu32),
422+
self.prev_cpu_stats.get(&cpu32),
426423
) {
427424
dom_nr_cpus[dom] += 1;
428-
dom_util_sum[dom] += curr.calc_util(prev);
425+
dom_util_sum[dom] += calc_util(curr, prev)?;
429426
}
430427
}
431428

@@ -843,9 +840,10 @@ struct Scheduler<'a> {
843840
balanced_kworkers: bool,
844841

845842
top: Arc<Topology>,
843+
proc_reader: procfs::ProcReader,
846844

847845
prev_at: Instant,
848-
prev_total_cpu: MyCpuStat,
846+
prev_total_cpu: procfs::CpuStat,
849847
task_loads: BTreeMap<i32, TaskLoad>,
850848

851849
nr_lb_data_errors: u64,
@@ -914,7 +912,8 @@ impl<'a> Scheduler<'a> {
914912
info!("Atropos Scheduler Attached");
915913

916914
// Other stuff.
917-
let prev_total_cpu = MyProcStat::read()?.total;
915+
let proc_reader = procfs::ProcReader::new();
916+
let prev_total_cpu = read_total_cpu(&proc_reader)?;
918917

919918
Ok(Self {
920919
skel,
@@ -927,6 +926,7 @@ impl<'a> Scheduler<'a> {
927926
balanced_kworkers: opts.balanced_kworkers,
928927

929928
top: top.clone(),
929+
proc_reader,
930930

931931
prev_at: Instant::now(),
932932
prev_total_cpu,
@@ -939,8 +939,53 @@ impl<'a> Scheduler<'a> {
939939
}
940940

941941
fn get_cpu_busy(&mut self) -> Result<f64> {
942-
let total_cpu = MyProcStat::read()?.total;
943-
let busy = total_cpu.calc_util(&self.prev_total_cpu);
942+
let total_cpu = read_total_cpu(&mut self.proc_reader)?;
943+
let busy = match (&self.prev_total_cpu, &total_cpu) {
944+
(
945+
procfs::CpuStat {
946+
user_usec: Some(prev_user),
947+
nice_usec: Some(prev_nice),
948+
system_usec: Some(prev_system),
949+
idle_usec: Some(prev_idle),
950+
iowait_usec: Some(prev_iowait),
951+
irq_usec: Some(prev_irq),
952+
softirq_usec: Some(prev_softirq),
953+
stolen_usec: Some(prev_stolen),
954+
guest_usec: _,
955+
guest_nice_usec: _,
956+
},
957+
procfs::CpuStat {
958+
user_usec: Some(curr_user),
959+
nice_usec: Some(curr_nice),
960+
system_usec: Some(curr_system),
961+
idle_usec: Some(curr_idle),
962+
iowait_usec: Some(curr_iowait),
963+
irq_usec: Some(curr_irq),
964+
softirq_usec: Some(curr_softirq),
965+
stolen_usec: Some(curr_stolen),
966+
guest_usec: _,
967+
guest_nice_usec: _,
968+
},
969+
) => {
970+
let idle_usec = curr_idle - prev_idle;
971+
let iowait_usec = curr_iowait - prev_iowait;
972+
let user_usec = curr_user - prev_user;
973+
let system_usec = curr_system - prev_system;
974+
let nice_usec = curr_nice - prev_nice;
975+
let irq_usec = curr_irq - prev_irq;
976+
let softirq_usec = curr_softirq - prev_softirq;
977+
let stolen_usec = curr_stolen - prev_stolen;
978+
979+
let busy_usec =
980+
user_usec + system_usec + nice_usec + irq_usec + softirq_usec + stolen_usec;
981+
let total_usec = idle_usec + busy_usec + iowait_usec;
982+
busy_usec as f64 / total_usec as f64
983+
}
984+
_ => {
985+
bail!("Some procfs stats are not populated!");
986+
}
987+
};
988+
944989
self.prev_total_cpu = total_cpu;
945990
Ok(busy)
946991
}

0 commit comments

Comments
 (0)