1use scx_utils::Topology;
10use scx_utils::{Core, Llc};
11
12use std::ffi::CString;
13use std::os::raw::c_ulong;
14use std::sync::Arc;
15
16use anyhow::Result;
17use anyhow::bail;
18
19use libbpf_rs::AsRawLibbpf;
20use libbpf_rs::Object;
21use libbpf_rs::ProgramInput;
22use libbpf_rs::ProgramMut;
23use libbpf_rs::libbpf_sys;
24
25mod types {
26 use std::os::raw::c_ulong;
27
28 #[repr(C)]
29 pub(super) struct ArenaInitArgs {
30 pub task_ctx_size: c_ulong,
31 pub task_ctx_align: c_ulong,
32 }
33
34 #[repr(C)]
35 pub(super) struct ArenaAllocMaskArgs {
36 pub bitmap: c_ulong,
37 }
38
39 #[repr(C)]
40 pub(super) struct ArenaTopologyNodeInitArgs {
41 pub bitmap: c_ulong,
42 pub data_size: c_ulong,
43 pub id: c_ulong,
44 }
45
46 #[repr(C)]
47 pub(super) struct ArenaTopologyInitArgs {
48 pub max_children: [u32; 5],
49 }
50}
51
52#[must_use]
56#[derive(Debug)]
57pub struct ScxLibArena {
58 _watcher: crate::Daemon,
59 _urcu: Option<crate::Daemon>,
60}
61
62impl ScxLibArena {
63 fn nr_cpumask_words(nr_cpus: usize) -> usize {
65 nr_cpus.div_ceil(64)
66 }
67
68 fn run_prog_by_name(obj: &Object, name: &str, input: ProgramInput) -> Result<i32> {
69 let c_name = CString::new(name)?;
70 let ptr = unsafe {
71 libbpf_sys::bpf_object__find_program_by_name(
72 obj.as_libbpf_object().as_ptr(),
73 c_name.as_ptr(),
74 )
75 };
76 if ptr as u64 == 0_u64 {
77 bail!("No program with name {} found in object", name);
78 }
79
80 let bpfprog = unsafe { &mut *ptr };
81 let prog = ProgramMut::new_mut(bpfprog);
82
83 let output = prog.test_run(input)?;
84
85 Ok(output.return_value as i32)
89 }
90
91 fn setup_arena(obj: &Object, task_size: usize, task_align: usize) -> Result<()> {
93 let input = ProgramInput {
94 context_in: None,
95 ..Default::default()
96 };
97 let ret = Self::run_prog_by_name(obj, "arena_buddy_reset", input)?;
98 if ret != 0 {
99 bail!("Could not initialize libarena buddy allocator: {}", ret);
100 }
101
102 let mut args = types::ArenaInitArgs {
106 task_ctx_size: task_size as c_ulong,
107 task_ctx_align: task_align as c_ulong,
108 };
109
110 let input = ProgramInput {
111 context_in: Some(unsafe {
112 std::slice::from_raw_parts_mut(
113 &mut args as *mut _ as *mut u8,
114 std::mem::size_of_val(&args),
115 )
116 }),
117 ..Default::default()
118 };
119
120 let ret = Self::run_prog_by_name(obj, "arena_init", input)?;
121 if ret != 0 {
122 bail!("Could not initialize arenas, setup_arenas returned {}", ret);
123 }
124
125 Ok(())
126 }
127
128 fn setup_topology_node(obj: &Object, nr_cpus: usize, mask: &[u64], id: usize) -> Result<()> {
129 let nr_words = Self::nr_cpumask_words(nr_cpus);
130 if mask.len() < nr_words {
131 bail!(
132 "CPU mask has {} words, expected at least {}",
133 mask.len(),
134 nr_words
135 );
136 }
137 let mask = &mask[..nr_words];
138
139 let mut args = types::ArenaAllocMaskArgs {
140 bitmap: 0 as c_ulong,
141 };
142
143 if mask.iter().all(|&b| b == 0) {
145 return Ok(());
146 }
147
148 let input = ProgramInput {
149 context_in: Some(unsafe {
150 std::slice::from_raw_parts_mut(
151 &mut args as *mut _ as *mut u8,
152 std::mem::size_of_val(&args),
153 )
154 }),
155 ..Default::default()
156 };
157
158 let ret = Self::run_prog_by_name(obj, "arena_alloc_mask", input)?;
159
160 if ret != 0 {
161 bail!(
162 "Could not initialize arenas, setup_topology_node returned {}",
163 ret
164 );
165 }
166
167 let valid_mask = unsafe {
168 std::slice::from_raw_parts_mut(
169 std::ptr::with_exposed_provenance_mut::<u64>(args.bitmap.try_into().unwrap()),
170 nr_words,
171 )
172 };
173 valid_mask.copy_from_slice(mask);
174
175 let mut args = types::ArenaTopologyNodeInitArgs {
176 bitmap: args.bitmap as c_ulong,
177 data_size: 0 as c_ulong,
178 id: id as c_ulong,
179 };
180
181 let input = ProgramInput {
182 context_in: Some(unsafe {
183 std::slice::from_raw_parts_mut(
184 &mut args as *mut _ as *mut u8,
185 std::mem::size_of_val(&args),
186 )
187 }),
188 ..Default::default()
189 };
190
191 let ret = Self::run_prog_by_name(obj, "arena_topology_node_init", input)?;
192 if ret != 0 {
193 bail!("arena_topology_node_init returned {}", ret);
194 }
195
196 Ok(())
197 }
198
199 fn setup_topology_max_children(obj: &Object, topo: &Topology) -> Result<()> {
209 let max_children: [u32; 5] = [
216 topo.nodes.len() as u32,
217 topo.nodes.values().map(|n| n.llcs.len()).max().unwrap_or(0) as u32,
218 topo.all_llcs
219 .values()
220 .map(|l| l.cores.len())
221 .max()
222 .unwrap_or(0) as u32,
223 topo.all_cores
224 .values()
225 .map(|c| c.cpus.len())
226 .max()
227 .unwrap_or(0) as u32,
228 0,
229 ];
230
231 let mut args = types::ArenaTopologyInitArgs { max_children };
232
233 let input = ProgramInput {
234 context_in: Some(unsafe {
235 std::slice::from_raw_parts_mut(
236 &mut args as *mut _ as *mut u8,
237 std::mem::size_of_val(&args),
238 )
239 }),
240 ..Default::default()
241 };
242
243 let ret = Self::run_prog_by_name(obj, "arena_topology_init", input)?;
244 if ret != 0 {
245 bail!("arena_topology_init returned {}", ret);
246 }
247
248 Ok(())
249 }
250
251 fn setup_topology(obj: &Object, nr_cpus: usize) -> Result<()> {
252 let topo = Topology::new().expect("Failed to build host topology");
253
254 Self::setup_topology_max_children(obj, &topo)?;
255
256 Self::setup_topology_node(obj, nr_cpus, topo.span.as_raw_slice(), 0)?;
258
259 for (node_id, node) in topo.nodes {
260 Self::setup_topology_node(obj, nr_cpus, node.span.as_raw_slice(), node_id)?;
261 }
262
263 for (llc_id, llc) in topo.all_llcs {
265 Self::setup_topology_node(
266 obj,
267 nr_cpus,
268 Arc::<Llc>::into_inner(llc)
269 .expect("missing llc")
270 .span
271 .as_raw_slice(),
272 llc_id,
273 )?;
274 }
275
276 for (core_id, core) in topo.all_cores {
277 Self::setup_topology_node(
278 obj,
279 nr_cpus,
280 Arc::<Core>::into_inner(core)
281 .expect("missing core")
282 .span
283 .as_raw_slice(),
284 core_id,
285 )?;
286 }
287 for (_, cpu) in topo.all_cpus {
288 let mut mask = vec![0; Self::nr_cpumask_words(nr_cpus)];
289 mask[cpu.id / 64] |= 1 << (cpu.id % 64);
290 Self::setup_topology_node(obj, nr_cpus, &mask, cpu.id)?;
291 }
292
293 Ok(())
294 }
295
296 pub fn setup(
301 obj: &Object,
302 task_size: usize,
303 task_align: usize,
304 nr_cpus: usize,
305 ) -> Result<ScxLibArena> {
306 Self::setup_arena(obj, task_size, task_align)?;
307 Self::setup_topology(obj, nr_cpus)?;
308
309 Self::start(obj)
310 }
311
312 pub fn start(obj: &Object) -> Result<ScxLibArena> {
319 Ok(ScxLibArena {
320 _watcher: crate::stream_watcher_spawn(obj)?,
321 _urcu: crate::urcu_spawn(obj)?,
322 })
323 }
324}