1pub use crate::bpf_skel::types;
10
11use scx_utils::Topology;
12use scx_utils::{Core, Llc};
13
14use std::ffi::CString;
15use std::os::raw::c_ulong;
16use std::sync::Arc;
17
18use anyhow::bail;
19use anyhow::Result;
20
21use libbpf_rs::libbpf_sys;
22use libbpf_rs::AsRawLibbpf;
23use libbpf_rs::Object;
24use libbpf_rs::ProgramInput;
25use libbpf_rs::ProgramMut;
26
27const MAX_CPU_SUPPORTED: usize = 640;
32
33#[must_use]
37#[derive(Debug)]
38pub struct ArenaLib {
39 _watcher: crate::Daemon,
40 _urcu: Option<crate::Daemon>,
41}
42
43impl ArenaLib {
44 const MAX_CPU_ARRSZ: usize = (MAX_CPU_SUPPORTED + 63) / 64;
46
47 const STATIC_ALLOC_PAGES_GRANULARITY: c_ulong = 8;
49
50 fn run_prog_by_name(obj: &Object, name: &str, input: ProgramInput) -> Result<i32> {
51 let c_name = CString::new(name)?;
52 let ptr = unsafe {
53 libbpf_sys::bpf_object__find_program_by_name(
54 obj.as_libbpf_object().as_ptr(),
55 c_name.as_ptr(),
56 )
57 };
58 if ptr as u64 == 0 as u64 {
59 bail!("No program with name {} found in object", name);
60 }
61
62 let bpfprog = unsafe { &mut *ptr };
63 let prog = ProgramMut::new_mut(bpfprog);
64
65 let output = prog.test_run(input)?;
66
67 return Ok(output.return_value as i32);
71 }
72
73 fn setup_arena(obj: &Object, task_size: usize, task_align: usize) -> Result<()> {
75 let mut args = types::arena_init_args {
79 static_pages: Self::STATIC_ALLOC_PAGES_GRANULARITY as c_ulong,
80 task_ctx_size: task_size as c_ulong,
81 task_ctx_align: task_align as c_ulong,
82 };
83
84 let input = ProgramInput {
85 context_in: Some(unsafe {
86 std::slice::from_raw_parts_mut(
87 &mut args as *mut _ as *mut u8,
88 std::mem::size_of_val(&args),
89 )
90 }),
91 ..Default::default()
92 };
93
94 let ret = Self::run_prog_by_name(obj, "arena_init", input)?;
95 if ret != 0 {
96 bail!("Could not initialize arenas, setup_arenas returned {}", ret);
97 }
98
99 Ok(())
100 }
101
102 fn setup_topology_node(obj: &Object, mask: &[u64], id: usize) -> Result<()> {
103 let mut args = types::arena_alloc_mask_args {
104 bitmap: 0 as c_ulong,
105 };
106
107 if mask.into_iter().all(|&b| b == 0) {
109 return Ok(());
110 }
111
112 let input = ProgramInput {
113 context_in: Some(unsafe {
114 std::slice::from_raw_parts_mut(
115 &mut args as *mut _ as *mut u8,
116 std::mem::size_of_val(&args),
117 )
118 }),
119 ..Default::default()
120 };
121
122 let ret = Self::run_prog_by_name(obj, "arena_alloc_mask", input)?;
123
124 if ret != 0 {
125 bail!(
126 "Could not initialize arenas, setup_topology_node returned {}",
127 ret
128 );
129 }
130
131 let ptr = unsafe {
132 &mut *std::ptr::with_exposed_provenance_mut::<[u64; 640]>(
133 args.bitmap.try_into().unwrap(),
134 )
135 };
136
137 let (valid_mask, _) = ptr.split_at_mut(mask.len());
138 valid_mask.clone_from_slice(mask);
139
140 let mut args = types::arena_topology_node_init_args {
141 bitmap: args.bitmap as c_ulong,
142 data_size: 0 as c_ulong,
143 id: id as c_ulong,
144 };
145
146 let input = ProgramInput {
147 context_in: Some(unsafe {
148 std::slice::from_raw_parts_mut(
149 &mut args as *mut _ as *mut u8,
150 std::mem::size_of_val(&args),
151 )
152 }),
153 ..Default::default()
154 };
155
156 let ret = Self::run_prog_by_name(obj, "arena_topology_node_init", input)?;
157 if ret != 0 {
158 bail!("arena_topology_node_init returned {}", ret);
159 }
160
161 Ok(())
162 }
163
164 fn setup_topology_max_children(obj: &Object, topo: &Topology) -> Result<()> {
174 let max_children: [u32; 5] = [
181 topo.nodes.len() as u32,
182 topo.nodes.values().map(|n| n.llcs.len()).max().unwrap_or(0) as u32,
183 topo.all_llcs
184 .values()
185 .map(|l| l.cores.len())
186 .max()
187 .unwrap_or(0) as u32,
188 topo.all_cores
189 .values()
190 .map(|c| c.cpus.len())
191 .max()
192 .unwrap_or(0) as u32,
193 0,
194 ];
195
196 let mut args = types::arena_topology_init_args { max_children };
197
198 let input = ProgramInput {
199 context_in: Some(unsafe {
200 std::slice::from_raw_parts_mut(
201 &mut args as *mut _ as *mut u8,
202 std::mem::size_of_val(&args),
203 )
204 }),
205 ..Default::default()
206 };
207
208 let ret = Self::run_prog_by_name(obj, "arena_topology_init", input)?;
209 if ret != 0 {
210 bail!("arena_topology_init returned {}", ret);
211 }
212
213 Ok(())
214 }
215
216 fn setup_topology(obj: &Object) -> Result<()> {
217 let topo = Topology::new().expect("Failed to build host topology");
218
219 Self::setup_topology_max_children(obj, &topo)?;
220
221 Self::setup_topology_node(obj, topo.span.as_raw_slice(), 0)?;
223
224 for (node_id, node) in topo.nodes {
225 Self::setup_topology_node(obj, node.span.as_raw_slice(), node_id)?;
226 }
227
228 for (llc_id, llc) in topo.all_llcs {
230 Self::setup_topology_node(
231 obj,
232 Arc::<Llc>::into_inner(llc)
233 .expect("missing llc")
234 .span
235 .as_raw_slice(),
236 llc_id,
237 )?;
238 }
239
240 for (core_id, core) in topo.all_cores {
241 Self::setup_topology_node(
242 obj,
243 Arc::<Core>::into_inner(core)
244 .expect("missing core")
245 .span
246 .as_raw_slice(),
247 core_id,
248 )?;
249 }
250 for (_, cpu) in topo.all_cpus {
251 let mut mask = [0; Self::MAX_CPU_ARRSZ - 1];
252 mask[cpu.id / 64] |= 1 << (cpu.id % 64);
253 Self::setup_topology_node(obj, &mask, cpu.id)?;
254 }
255
256 Ok(())
257 }
258
259 pub fn setup(
264 obj: &Object,
265 task_size: usize,
266 task_align: usize,
267 nr_cpus: usize,
268 ) -> Result<ArenaLib> {
269 if nr_cpus >= MAX_CPU_SUPPORTED {
270 bail!("Scheduler specifies too many CPUs");
271 }
272
273 Self::setup_arena(obj, task_size, task_align)?;
274 Self::setup_topology(obj)?;
275
276 Self::start(obj)
277 }
278
279 pub fn start(obj: &Object) -> Result<ArenaLib> {
286 Ok(ArenaLib {
287 _watcher: crate::stream_watcher_spawn(obj)?,
288 _urcu: crate::urcu_spawn(obj)?,
289 })
290 }
291}