1use super::{Access, RuntimeOwner};
2use crate::{Error, Result};
3use std::{
4 ops::{Deref, DerefMut, Range},
5 sync::{Arc, Mutex},
6};
7
8#[derive(Clone)]
10pub enum MemoryPlacement {
11 GpuLocal,
13 HostVisible,
16 #[cfg(feature = "npu")]
18 NpuLocal(super::NpuDevice),
19 #[cfg(feature = "npu")]
21 Shared(super::NpuDevice),
22}
23#[derive(Default)]
24pub(super) struct HostState {
25 pub readers: usize,
26 pub writer: bool,
27 pub external: bool,
28 pub poison: Option<String>,
29}
30#[derive(Clone, Copy, PartialEq, Eq)]
31pub(super) enum Engine {
32 Host,
33 Gpu,
34 #[cfg_attr(not(feature = "npu"), allow(dead_code))]
35 Npu,
36}
37pub(super) struct Visibility {
38 pub writer: Engine,
39 pub visible: [bool; 3],
40}
41impl Visibility {
42 pub fn new() -> Self {
43 Self {
44 writer: Engine::Host,
45 visible: [true, false, false],
46 }
47 }
48 pub fn sync_to_device(&self, consumer: Engine) -> bool {
49 self.writer == Engine::Host || consumer == Engine::Npu
50 }
51 pub fn wrote(&mut self, engine: Engine) {
52 self.writer = engine;
53 self.visible = [false; 3];
54 self.visible[engine as usize] = true;
55 }
56}
57pub(super) struct Storage {
58 pub accounted: bool,
59 pub native: Option<crate::fabric::Buffer>,
60 pub gpu: Option<crate::gpu::Buffer>,
61 pub pointer: *mut u8,
62 pub bytes: usize,
63 pub shared: bool,
64 pub host: Mutex<HostState>,
65 pub visibility: Mutex<Visibility>,
66 pub runtime: Arc<RuntimeOwner>,
67 #[cfg(test)]
68 pub _test_memory: Option<Arc<std::cell::UnsafeCell<[u8; 256]>>>,
69 pub _reservation: Option<crate::residency::MemoryReservation>,
71}
72unsafe impl Send for Storage {}
75unsafe impl Sync for Storage {}
76#[derive(Clone)]
78pub struct Buffer {
79 pub(super) storage: Arc<Storage>,
80}
81#[derive(Clone)]
83pub struct BufferView {
84 pub(super) buffer: Buffer,
85 pub(super) range: Range<usize>,
86 pub(super) retained: Vec<Arc<dyn Send + Sync>>,
87}
88impl Buffer {
89 pub fn len(&self) -> usize {
91 self.storage.bytes
92 }
93 pub fn is_empty(&self) -> bool {
95 self.len() == 0
96 }
97 pub fn view(&self) -> BufferView {
99 BufferView {
100 buffer: self.clone(),
101 range: 0..self.len(),
102 retained: Vec::new(),
103 }
104 }
105 pub fn slice(&self, range: Range<usize>) -> Result<BufferView> {
107 self.view().slice(range)
108 }
109 pub fn map_read(&self) -> Result<ReadGuard<'_>> {
112 self.acquire(false, true)?;
113 Ok(ReadGuard {
114 buffer: self,
115 range: 0..self.len(),
116 })
117 }
118 pub fn try_map_read(&self) -> Result<ReadGuard<'_>> {
120 self.acquire(false, false)?;
121 Ok(ReadGuard {
122 buffer: self,
123 range: 0..self.len(),
124 })
125 }
126 pub fn map_write(&self) -> Result<WriteGuard<'_>> {
128 self.acquire(true, true)?;
129 Ok(WriteGuard {
130 buffer: self,
131 range: 0..self.len(),
132 })
133 }
134 pub fn try_map_write(&self) -> Result<WriteGuard<'_>> {
136 self.acquire(true, false)?;
137 Ok(WriteGuard {
138 buffer: self,
139 range: 0..self.len(),
140 })
141 }
142 fn acquire(&self, write: bool, wait: bool) -> Result<()> {
143 self.acquire_with(write, wait, || self.storage.make_visible(Engine::Host))
144 }
145 pub(super) fn acquire_with(
147 &self,
148 write: bool,
149 wait: bool,
150 make_visible: impl FnOnce() -> Result<()>,
151 ) -> Result<()> {
152 if self.storage.pointer.is_null() {
153 return Err(Error::Unsupported(
154 "GPU-local memory is not host mapped; use an explicit graph copy to shared storage"
155 .into(),
156 ));
157 }
158 let core = &self.storage.runtime.core;
159 let view = self.view();
160 let mut scheduler = core.state.lock().unwrap_or_else(|e| e.into_inner());
161 loop {
162 {
163 let host = self.storage.host.lock().unwrap_or_else(|e| e.into_inner());
164 if let Some(error) = &host.poison {
165 return Err(Error::DeviceLost(error.clone()));
166 }
167 if host.external || host.writer || (write && host.readers != 0) {
168 return Err(Error::Busy("conflicting host mapping".into()));
169 }
170 }
171 if !scheduler.conflicts(
172 &view,
173 if write {
174 Access::ReadWrite
175 } else {
176 Access::Read
177 },
178 ) {
179 break;
180 }
181 if !wait {
182 return Err(Error::Busy("allocation is in device use".into()));
183 }
184 scheduler = core
185 .host_changed
186 .wait(scheduler)
187 .unwrap_or_else(|e| e.into_inner());
188 }
189 let mut host = self.storage.host.lock().unwrap_or_else(|e| e.into_inner());
190 if write {
191 host.writer = true;
192 } else {
193 host.readers += 1;
194 }
195 drop(host);
198 drop(scheduler);
199 if let Err(error) = make_visible() {
200 let mut host = self.storage.host.lock().unwrap_or_else(|e| e.into_inner());
201 if write {
202 host.writer = false;
203 } else {
204 host.readers -= 1;
205 }
206 drop(host);
207 core.host_changed.notify_all();
208 return Err(error);
209 }
210 Ok(())
211 }
212}
213impl BufferView {
214 pub fn is_host_visible(&self) -> bool {
216 !self.buffer.storage.pointer.is_null()
217 }
218 pub fn map_read(&self) -> Result<ReadGuard<'_>> {
221 self.buffer.acquire(false, true)?;
222 Ok(ReadGuard {
223 buffer: &self.buffer,
224 range: self.range.clone(),
225 })
226 }
227 pub fn map_write(&self) -> Result<WriteGuard<'_>> {
230 self.buffer.acquire(true, true)?;
231 Ok(WriteGuard {
232 buffer: &self.buffer,
233 range: self.range.clone(),
234 })
235 }
236 pub fn len(&self) -> usize {
238 self.range.len()
239 }
240 pub fn is_empty(&self) -> bool {
242 self.range.is_empty()
243 }
244 pub fn offset(&self) -> usize {
246 self.range.start
247 }
248 pub fn slice(&self, range: Range<usize>) -> Result<Self> {
250 if range.start > range.end || range.end > self.len() {
251 return Err(Error::Message("buffer view is out of bounds".into()));
252 }
253 Ok(Self {
254 buffer: self.buffer.clone(),
255 range: self.range.start + range.start..self.range.start + range.end,
256 retained: self.retained.clone(),
257 })
258 }
259 pub(crate) fn retain(mut self, owner: Arc<dyn Send + Sync>) -> Self {
260 self.retained.push(owner);
261 self
262 }
263 pub(crate) fn overlaps(&self, other: &Self) -> bool {
264 Arc::ptr_eq(&self.buffer.storage, &other.buffer.storage)
265 && self.range.start < other.range.end
266 && other.range.start < self.range.end
267 }
268 pub(crate) fn same_region(&self, other: &Self) -> bool {
269 Arc::ptr_eq(&self.buffer.storage, &other.buffer.storage) && self.range == other.range
270 }
271 pub(super) fn conflicts(&self, other: &Self) -> bool {
272 Arc::ptr_eq(&self.buffer.storage, &other.buffer.storage)
274 && (self.buffer.storage.shared || self.overlaps(other))
275 }
276 pub(super) fn gpu(&self) -> Result<crate::gpu::View<'_>> {
277 self.buffer
278 .storage
279 .gpu
280 .as_ref()
281 .ok_or_else(|| Error::Unsupported("NPU-local storage cannot bind to the GPU".into()))?
282 .try_slice(self.offset(), self.len())
283 }
284}
285impl Storage {
286 pub fn host_conflict(&self, access: Access) -> Result<()> {
287 let host = self.host.lock().unwrap_or_else(|e| e.into_inner());
288 if let Some(error) = &host.poison {
289 return Err(Error::DeviceLost(error.clone()));
290 }
291 if host.external || host.writer || (access.writes() && host.readers != 0) {
292 return Err(Error::Busy(
293 "device binding conflicts with a live host mapping".into(),
294 ));
295 }
296 Ok(())
297 }
298 pub fn make_visible(&self, engine: Engine) -> Result<()> {
299 let mut visibility = self.visibility.lock().unwrap_or_else(|e| e.into_inner());
300 if visibility.visible[engine as usize] {
301 return Ok(());
302 }
303 if let Some(native) = &self.native {
304 unsafe { native.cache_control(visibility.sync_to_device(engine), 0, self.bytes) }?;
309 self.runtime
310 .core
311 .counters
312 .cache_maintenance_bytes
313 .fetch_add(self.bytes as u64, std::sync::atomic::Ordering::Relaxed);
314 }
315 visibility.visible[engine as usize] = true;
316 Ok(())
317 }
318}
319pub struct ReadGuard<'a> {
329 buffer: &'a Buffer,
330 range: Range<usize>,
331}
332impl Deref for ReadGuard<'_> {
333 type Target = [u8];
334 fn deref(&self) -> &[u8] {
335 unsafe {
336 std::slice::from_raw_parts(
337 self.buffer.storage.pointer.add(self.range.start),
338 self.range.len(),
339 )
340 }
341 }
342}
343impl Drop for ReadGuard<'_> {
344 fn drop(&mut self) {
345 self.buffer
346 .storage
347 .host
348 .lock()
349 .unwrap_or_else(|e| e.into_inner())
350 .readers -= 1;
351 self.buffer.storage.runtime.core.host_changed.notify_all();
352 }
353}
354pub struct WriteGuard<'a> {
356 buffer: &'a Buffer,
357 range: Range<usize>,
358}
359impl Deref for WriteGuard<'_> {
360 type Target = [u8];
361 fn deref(&self) -> &[u8] {
362 unsafe {
363 std::slice::from_raw_parts(
364 self.buffer.storage.pointer.add(self.range.start),
365 self.range.len(),
366 )
367 }
368 }
369}
370impl DerefMut for WriteGuard<'_> {
371 fn deref_mut(&mut self) -> &mut [u8] {
372 unsafe {
373 std::slice::from_raw_parts_mut(
374 self.buffer.storage.pointer.add(self.range.start),
375 self.range.len(),
376 )
377 }
378 }
379}
380impl Drop for WriteGuard<'_> {
381 fn drop(&mut self) {
382 self.buffer
384 .storage
385 .visibility
386 .lock()
387 .unwrap_or_else(|e| e.into_inner())
388 .wrote(Engine::Host);
389 self.buffer
390 .storage
391 .host
392 .lock()
393 .unwrap_or_else(|e| e.into_inner())
394 .writer = false;
395 self.buffer.storage.runtime.core.host_changed.notify_all();
396 }
397}
398
399impl Drop for Storage {
400 fn drop(&mut self) {
401 if self
402 .visibility
403 .get_mut()
404 .unwrap_or_else(|e| e.into_inner())
405 .writer
406 == Engine::Host
407 && let Some(native) = &self.native
408 && unsafe { native.cache_control(true, 0, self.bytes) }.is_err()
409 {
410 std::mem::forget((
413 self.native.take(),
414 self.gpu.take(),
415 self._reservation.take(),
416 ));
417 return;
418 }
419
420 if self.accounted {
421 self.runtime
422 .core
423 .counters
424 .live_bytes
425 .fetch_sub(self.bytes as u64, std::sync::atomic::Ordering::Relaxed);
426 }
427 }
428}