Skip to content

Commit 2e7ce50

Browse files
committed
chore: wip
1 parent 7cd2c5d commit 2e7ce50

1 file changed

Lines changed: 109 additions & 0 deletions

File tree

src/run/runner/helpers/isolation.rs

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,110 @@
1+
use std::io::Write;
2+
3+
use anyhow::Context;
4+
use itertools::Itertools;
5+
16
use super::setup::run_with_sudo;
27

8+
pub struct IrqIsolation {
9+
default_affinity: String,
10+
irq_affinity: Vec<(u32, String)>,
11+
}
12+
13+
const DEFAULT_AFFINITY_PATH: &str = "/proc/irq/default_smp_affinity";
14+
15+
impl IrqIsolation {
16+
fn irq_smp_affinity_path(irq: u32) -> String {
17+
format!("/proc/irq/{irq}/smp_affinity")
18+
}
19+
20+
/// Read from a virtual file and chomp away the trailing newline.
21+
fn read_vfile(path: &str) -> anyhow::Result<String> {
22+
let mut s =
23+
std::fs::read_to_string(path).with_context(|| format!("failed to read {path}"))?;
24+
if s.ends_with('\n') {
25+
s.pop();
26+
}
27+
Ok(s)
28+
}
29+
30+
/// Write to a virtual file within a single syscall.
31+
fn write_vfile(path: &str, content: &str) -> anyhow::Result<()> {
32+
std::fs::File::options()
33+
.write(true)
34+
.open(path)
35+
.map_err(Into::into)
36+
.and_then(|mut f| match f.write(content.as_bytes()) {
37+
Ok(n) if n == content.len() => Ok(()),
38+
Ok(n) => anyhow::bail!("partial write of {n} bytes"),
39+
Err(err) => Err(err.into()),
40+
})
41+
.with_context(|| format!("failed to write {content:?} to {path}"))
42+
}
43+
44+
pub fn new() -> anyhow::Result<Self> {
45+
let default_affinity = Self::read_vfile(DEFAULT_AFFINITY_PATH)?;
46+
47+
let mut irq_affinity = Vec::new();
48+
for ent in std::fs::read_dir("/proc/irq")?.filter_map(Result::ok) {
49+
if !ent.file_type()?.is_dir() {
50+
continue;
51+
}
52+
let Some(irq) = ent.file_name().to_str().and_then(|s| s.parse::<u32>().ok()) else {
53+
continue;
54+
};
55+
56+
irq_affinity.push((irq, Self::read_vfile(&Self::irq_smp_affinity_path(irq))?));
57+
}
58+
irq_affinity.sort_unstable_by_key(|(irq, ..)| *irq);
59+
60+
Ok(Self {
61+
default_affinity,
62+
irq_affinity,
63+
})
64+
}
65+
66+
pub fn isolate(&self, allowed_cpus: &[u32]) -> anyhow::Result<()> {
67+
let convert_mask = |mask: &str| {
68+
let mut masks = mask
69+
.split(',')
70+
.map(|seg| u32::from_str_radix(seg, 16))
71+
.collect::<Result<Vec<_>, _>>()?;
72+
73+
let mut changed = false;
74+
for &cpu in allowed_cpus {
75+
let (idx, bit) = (cpu as usize / 32, 1u32 << (cpu % 32));
76+
if masks[idx] & bit != 0 {
77+
changed = true;
78+
masks[idx] ^= bit;
79+
}
80+
}
81+
82+
// Skip uneffected IRQs.
83+
if !changed {
84+
return Ok(None);
85+
}
86+
87+
// Do not leave it empty, it would be invalid. CPU 0 must not be used by us.
88+
if masks.iter().all(|&m| m == 0) {
89+
masks[0] = 1;
90+
}
91+
92+
let new_masks = masks
93+
.iter()
94+
.format_with(",", |&m, f| f(&format_args!("{m:x}")))
95+
.to_string();
96+
Ok(Some(new_masks))
97+
};
98+
99+
if let Some(mask) = &self.default_affinity {
100+
// convert_mask(value)?
101+
// Self::write_vfile(DEFAULT_AFFINITY_PATH, )?;
102+
}
103+
104+
for (irq, _, new_masks) in &self.irq_affinity {}
105+
}
106+
}
107+
3108
pub struct CpuIsolation {
4109
nproc: u32,
5110
}
@@ -53,6 +158,10 @@ impl CpuIsolation {
53158

54159
Ok(())
55160
}
161+
162+
pub fn isolate_irqs(&self) -> anyhow::Result<()> {
163+
Ok(())
164+
}
56165
}
57166

58167
impl Drop for CpuIsolation {

0 commit comments

Comments
 (0)