tuwunel_core/utils/sys/
compute.rs1use std::{cell::Cell, fmt::Debug, path::PathBuf, sync::LazyLock};
4
5use crate::{Result, is_equal_to};
6
7type Id = usize;
8
9type Mask = u128;
10type Masks = [Mask; MASK_BITS];
11
12const MASK_BITS: usize = CORES_MAX;
13
14pub const CORES_MAX: usize = 128;
17
18static CORES_AVAILABLE: LazyLock<Mask> = LazyLock::new(|| into_mask(query_cores_available()));
20
21static SMT_TOPOLOGY: LazyLock<Masks> = LazyLock::new(init_smt_topology);
24
25static NODE_TOPOLOGY: LazyLock<Masks> = LazyLock::new(init_node_topology);
28
29thread_local! {
30 static CORE_AFFINITY: Cell<Mask> = const { Cell::new(0) };
33}
34
35#[tracing::instrument(
38 level = "debug",
39 skip_all,
40 fields(
41 id = ?std::thread::current().id(),
42 name = %std::thread::current().name().unwrap_or("None"),
43 set = ?ids.clone().collect::<Vec<_>>(),
44 CURRENT = %format!("[b{:b}]", CORE_AFFINITY.get()),
45 AVAILABLE = %format!("[b{:b}]", *CORES_AVAILABLE),
46 ),
47)]
48pub fn set_affinity<I>(mut ids: I)
49where
50 I: Iterator<Item = Id> + Clone + Debug,
51{
52 use core_affinity::{CoreId, set_each_for_current, set_for_current};
53
54 let n = ids.clone().count();
55 let mask: Mask = ids.clone().fold(0, |mask, id| {
56 debug_assert!(is_core_available(id), "setting affinity to unavailable core");
57 mask | (1 << id)
58 });
59
60 if n > 1 {
61 set_each_for_current(ids.map(|id| CoreId { id }));
62 } else if n > 0 {
63 set_for_current(CoreId { id: ids.next().expect("n > 0") });
64 }
65
66 if mask.count_ones() > 0 {
67 CORE_AFFINITY.replace(mask);
68 }
69}
70
71pub fn get_affinity() -> impl Iterator<Item = Id> {
73 CORE_AFFINITY
74 .get()
75 .ne(&0)
76 .then_some(from_mask(CORE_AFFINITY.get()))
77 .or_else(|| Some(from_mask(*CORES_AVAILABLE)))
78 .into_iter()
79 .flatten()
80}
81
82pub fn smt_siblings() -> impl Iterator<Item = Id> {
84 from_mask(get_affinity().fold(0_u128, |mask, id| {
85 mask | SMT_TOPOLOGY
86 .get(id)
87 .expect("ID must not exceed max cpus")
88 }))
89}
90
91pub fn node_siblings() -> impl Iterator<Item = Id> {
94 from_mask(get_affinity().fold(0_u128, |mask, id| {
95 mask | NODE_TOPOLOGY
96 .get(id)
97 .expect("Id must not exceed max cpus")
98 }))
99}
100
101#[inline]
103pub fn smt_affinity(id: Id) -> impl Iterator<Item = Id> {
104 from_mask(
105 *SMT_TOPOLOGY
106 .get(id)
107 .expect("ID must not exceed max cpus"),
108 )
109}
110
111#[inline]
113pub fn node_affinity(id: Id) -> impl Iterator<Item = Id> {
114 from_mask(
115 *NODE_TOPOLOGY
116 .get(id)
117 .expect("ID must not exceed max cpus"),
118 )
119}
120
121#[cfg(not(target_os = "openbsd"))]
124#[inline]
125#[must_use]
126pub fn available_parallelism() -> usize { cores_available().count() }
127
128#[cfg(target_os = "openbsd")]
129#[inline]
130#[must_use]
131pub fn available_parallelism() -> usize { num_cpus::get() }
132
133#[inline]
136#[must_use]
137pub fn nth_core_available(i: usize) -> Option<Id> { cores_available().nth(i) }
138
139#[inline]
141#[must_use]
142pub fn is_core_available(id: Id) -> bool { cores_available().any(is_equal_to!(id)) }
143
144#[inline]
146pub fn cores_available() -> impl Iterator<Item = Id> { from_mask(*CORES_AVAILABLE) }
147
148#[cfg(target_os = "linux")]
149#[inline]
150pub fn getcpu() -> Result<usize> {
151 use crate::{Error, utils::math};
152
153 let ret: i32 = unsafe { libc::sched_getcpu() };
158
159 #[cfg(target_os = "linux")]
160 unsafe {
163 std::hint::assert_unchecked(ret >= 0);
164 };
165
166 if ret == -1 {
167 return Err(Error::from_errno());
168 }
169
170 math::try_into(ret)
171}
172
173#[cfg(not(target_os = "linux"))]
174#[inline]
175pub fn getcpu() -> Result<usize> { Err(crate::Error::Io(std::io::ErrorKind::Unsupported.into())) }
176
177#[cfg(not(target_os = "openbsd"))]
178fn query_cores_available() -> impl Iterator<Item = Id> {
179 core_affinity::get_core_ids()
180 .unwrap_or_default()
181 .into_iter()
182 .map(|core_id| core_id.id)
183}
184
185#[cfg(target_os = "openbsd")]
186fn query_cores_available() -> impl Iterator<Item = Id> { 0..num_cpus::get() }
187
188fn init_smt_topology() -> [Mask; MASK_BITS] { [Mask::default(); MASK_BITS] }
189
190fn init_node_topology() -> [Mask; MASK_BITS] { [Mask::default(); MASK_BITS] }
191
192fn into_mask<I>(ids: I) -> Mask
193where
194 I: Iterator<Item = Id>,
195{
196 ids.inspect(|&id| {
197 debug_assert!(id < MASK_BITS, "Core ID must be < Mask::BITS at least for now");
198 })
199 .fold(Mask::default(), |mask, id| mask | (1 << id))
200}
201
202fn from_mask(v: Mask) -> impl Iterator<Item = Id> {
203 (0..MASK_BITS).filter(move |&i| (v & (1 << i)) != 0)
204}
205
206fn _sys_path(id: usize, suffix: &str) -> PathBuf {
207 format!("/sys/devices/system/cpu/cpu{id}/{suffix}").into()
208}