hermes_simd_core/numa/
locality.rs1pub fn current_numa_node() -> Option<u32> {
6 themis::try_current_numa_node().map(|node| node.get())
7}
8
9pub fn refresh_numa_node() -> Option<u32> {
11 current_numa_node()
12}
13
14#[cfg(feature = "std")]
15#[derive(Copy, Clone)]
16struct CacheEntry {
17 ptr_start: usize,
18 ptr_end: usize,
19 node: u32,
20 generation: u64,
21 local: bool,
22}
23
24#[cfg(feature = "std")]
25struct LocalityCache {
26 entries: [Option<CacheEntry>; 16],
27 next_idx: usize,
28}
29
30#[cfg(feature = "std")]
31impl LocalityCache {
32 const fn new() -> Self {
33 Self {
34 entries: [None; 16],
35 next_idx: 0,
36 }
37 }
38}
39
40#[cfg(feature = "std")]
41thread_local! {
42 #[allow(clippy::missing_const_for_thread_local)]
44 static LOCALITY_CACHE: core::cell::RefCell<LocalityCache> = const {
45 core::cell::RefCell::new(LocalityCache::new())
46 };
47}
48
49#[cfg(feature = "std")]
50#[repr(align(64))]
51struct CacheAlignedAtomicU64(core::sync::atomic::AtomicU64);
52
53#[cfg(feature = "std")]
54static ALLOC_GENERATION: CacheAlignedAtomicU64 =
55 CacheAlignedAtomicU64(core::sync::atomic::AtomicU64::new(0));
56
57#[inline]
66pub fn bump_alloc_generation() {
67 #[cfg(feature = "std")]
68 ALLOC_GENERATION
69 .0
70 .fetch_add(1, core::sync::atomic::Ordering::Release);
71}
72
73#[inline]
75pub fn get_alloc_generation() -> u64 {
76 #[cfg(feature = "std")]
77 {
78 ALLOC_GENERATION
79 .0
80 .load(core::sync::atomic::Ordering::Acquire)
81 }
82 #[cfg(not(feature = "std"))]
83 {
84 0
85 }
86}
87
88pub fn verify_numa_locality(ptr: *const u8, size: usize, expected_node: u32) -> bool {
90 #[cfg(feature = "std")]
91 {
92 let ptr_val = ptr as usize;
93 let gen = get_alloc_generation();
94
95 let cached = LOCALITY_CACHE.with(|cache| {
96 let cache_ref = cache.borrow();
97 let end_val = ptr_val.saturating_add(size);
98 for entry in cache_ref.entries.iter().flatten() {
99 if entry.node == expected_node
100 && entry.generation == gen
101 && ptr_val >= entry.ptr_start
102 && end_val <= entry.ptr_end
103 {
104 return Some(entry.local);
105 }
106 }
107 None
108 });
109
110 if let Some(local) = cached {
111 return local;
112 }
113
114 let local = verify_numa_locality_os(ptr, size, expected_node);
115
116 LOCALITY_CACHE.with(|cache| {
123 let mut cache_mut = cache.borrow_mut();
124 let idx = cache_mut.next_idx;
125 let end_val = ptr_val.saturating_add(size);
126 cache_mut.entries[idx] = Some(CacheEntry {
127 ptr_start: ptr_val,
128 ptr_end: end_val,
129 node: expected_node,
130 generation: gen,
131 local,
132 });
133 cache_mut.next_idx = (idx + 1) % 16;
134 });
135
136 local
137 }
138
139 #[cfg(not(feature = "std"))]
140 {
141 verify_numa_locality_os(ptr, size, expected_node)
142 }
143}
144
145fn verify_numa_locality_os(ptr: *const u8, size: usize, expected_node: u32) -> bool {
146 #[cfg(target_os = "windows")]
147 unsafe {
148 use core::ffi::c_void;
149 #[repr(C)]
150 #[derive(Copy, Clone)]
151 struct PsapiWorkingSetExInformation {
152 virtual_address: *mut c_void,
153 virtual_attributes: usize,
154 }
155 extern "system" {
156 fn GetCurrentProcess() -> *mut c_void;
157 fn K32QueryWorkingSetEx(hProcess: *mut c_void, pv: *mut c_void, cb: u32) -> i32;
158 }
159 let page_size = 4096;
160 let start_page = (ptr as usize) & !(page_size - 1);
161 let end_page = ((ptr as usize) + size + page_size - 1) & !(page_size - 1);
162 let pages_count = (end_page - start_page) / page_size;
163 if pages_count == 0 {
164 return true;
165 }
166
167 const CHUNK_SIZE: usize = 64;
168 let mut info_arr = [PsapiWorkingSetExInformation {
169 virtual_address: core::ptr::null_mut(),
170 virtual_attributes: 0,
171 }; CHUNK_SIZE];
172
173 let mut checked = 0;
174 while checked < pages_count {
175 let chunk_len = core::cmp::min(pages_count - checked, CHUNK_SIZE);
176 for i in 0..chunk_len {
177 info_arr[i].virtual_address =
178 (start_page + (checked + i) * page_size) as *mut c_void;
179 info_arr[i].virtual_attributes = 0;
180 }
181 let cb = (chunk_len * core::mem::size_of::<PsapiWorkingSetExInformation>()) as u32;
182 let res = K32QueryWorkingSetEx(
183 GetCurrentProcess(),
184 info_arr.as_mut_ptr() as *mut c_void,
185 cb,
186 );
187 if res != 0 {
188 for i in 0..chunk_len {
189 let flags = info_arr[i].virtual_attributes;
190 let valid = (flags & 1) != 0;
191 if valid {
192 let node = (flags >> 16) & 0x3F;
193 if node as u32 != expected_node {
194 return false;
195 }
196 }
197 }
198 } else {
199 return false;
200 }
201 checked += chunk_len;
202 }
203 true
204 }
205
206 #[cfg(all(target_os = "linux", feature = "libnuma"))]
207 unsafe {
208 #[link(name = "numa")]
209 extern "C" {
210 fn move_pages(
211 pid: i32,
212 count: usize,
213 pages: *const *mut core::ffi::c_void,
214 nodes: *const i32,
215 status: *mut i32,
216 flags: i32,
217 ) -> i32;
218 }
219 let page_size = 4096;
220 let start_page = (ptr as usize) & !(page_size - 1);
221 let end_page = ((ptr as usize) + size + page_size - 1) & !(page_size - 1);
222 let pages_count = (end_page - start_page) / page_size;
223 if pages_count == 0 {
224 return true;
225 }
226
227 const CHUNK_SIZE: usize = 64;
228 let mut pages_arr = [core::ptr::null_mut(); CHUNK_SIZE];
229 let mut status_arr = [0i32; CHUNK_SIZE];
230
231 let mut checked = 0;
232 while checked < pages_count {
233 let chunk_len = core::cmp::min(pages_count - checked, CHUNK_SIZE);
234 for i in 0..chunk_len {
235 pages_arr[i] = (start_page + (checked + i) * page_size) as *mut core::ffi::c_void;
236 }
237 let res = move_pages(
238 0,
239 chunk_len,
240 pages_arr.as_ptr(),
241 core::ptr::null(),
242 status_arr.as_mut_ptr(),
243 0,
244 );
245 if res >= 0 {
246 for i in 0..chunk_len {
247 let node = status_arr[i];
248 if node >= 0 && node as u32 != expected_node {
249 return false;
250 }
251 }
252 } else {
253 return false;
254 }
255 checked += chunk_len;
256 }
257 return true;
258 }
259
260 #[cfg(all(target_os = "linux", not(feature = "libnuma")))]
261 {
262 let _ = ptr;
263 let _ = size;
264 let _ = expected_node;
265 true
266 }
267
268 #[cfg(not(any(target_os = "windows", target_os = "linux")))]
269 {
270 let _ = ptr;
271 let _ = size;
272 let _ = expected_node;
273 true
274 }
275}