1use super::*;
2use std::sync::Mutex;
3
4#[derive(Clone, Copy, Debug, Default)]
7pub struct AllocationOptions {
8 pub mapping_source: bool,
10 pub host_mapping_source: bool,
12 pub host_numa_node: Option<u32>,
14 pub coherent_gpu: bool,
16 pub minimum_alignment: u64,
18 pub required_backing_flags: u64,
21}
22impl AllocationOptions {
23 fn required_flags(self) -> u64 {
24 AMDF_MEMORY_FLAG_HOST_VISIBLE as u64
25 | self.required_backing_flags
26 | if self.host_mapping_source {
27 AMDF_MEMORY_FLAG_HOST_MAPPING_SOURCE as u64
28 } else {
29 0
30 }
31 }
32 fn placement(self) -> amdf_memory_placement_t {
33 self.host_numa_node
34 .map_or_else(Default::default, |node| amdf_memory_placement_t {
35 kind: AMDF_MEMORY_PLACEMENT_KIND_HOST_NUMA,
36 node,
37 })
38 }
39}
40
41#[derive(Clone)]
43pub struct Buffer(pub(super) Arc<Memory>);
44pub(super) struct Memory {
45 pub(super) instance: Arc<Instance>,
46 pub(super) devices: Vec<Device>,
47 pub(super) raw: *mut amdf_memory_t,
48 pub(super) mapping: *mut amdf_host_mapping_t,
49 pub(super) pointer: *mut u8,
50 bytes: usize,
51 needs_cache: Arc<std::sync::atomic::AtomicBool>,
52 pub(super) source: Option<Buffer>,
53 pub(super) reservation: Option<Arc<crate::residency::MemoryReservation>>,
54 host_storage: Option<Arc<memmap2::MmapMut>>,
55 pub(super) uses: Arc<Mutex<usize>>,
58}
59unsafe impl Send for Memory {}
62unsafe impl Sync for Memory {}
63impl Drop for Memory {
64 fn drop(&mut self) {
65 let api = self.instance.api.core;
66 unsafe {
67 let mapping_released =
68 self.mapping.is_null() || api.host_mapping_destroy.unwrap()(self.mapping) == 0;
69 if !mapping_released || api.memory_destroy.unwrap()(self.raw) != 0 {
70 std::mem::forget(self.instance.clone());
72 std::mem::forget(self.devices.clone());
73 std::mem::forget(self.source.take());
74 std::mem::forget(self.reservation.take());
75 std::mem::forget(self.host_storage.take());
76 }
77 }
78 }
79}
80impl Buffer {
81 pub(super) fn owns_registered_pages(&self) -> bool {
82 self.0.host_storage.is_some()
83 }
84
85 pub(crate) fn identity(&self) -> usize {
86 Arc::as_ptr(&self.0) as usize
87 }
88 pub(crate) fn same_backing(&self, other: &Self) -> bool {
89 Arc::ptr_eq(&self.0, &other.0)
90 }
91 pub(crate) fn exclusively_owned(&self) -> bool {
92 Arc::strong_count(&self.0) == 1
93 }
94 pub(crate) fn zero(&self) -> Result<()> {
95 let uses = self
96 .0
97 .uses
98 .lock()
99 .map_err(|_| Error::DeviceLost("memory ownership poisoned".into()))?;
100 if *uses != 0 {
101 return Err(Error::Busy("memory is executing".into()));
102 }
103 unsafe {
104 ptr::write_bytes(self.host_pointer(), 0, self.len());
105 self.cache_control(true, 0, self.len())
106 }
107 }
108
109 pub(super) fn retain_use(&self) -> Result<DeviceUse> {
110 let mut uses = self
111 .0
112 .uses
113 .lock()
114 .map_err(|_| Error::DeviceLost("memory ownership poisoned".into()))?;
115 *uses = uses
116 .checked_add(1)
117 .ok_or_else(|| Error::Busy("memory lease limit".into()))?;
118 Ok(DeviceUse(self.clone()))
119 }
120 pub(super) fn fence_value(&self) -> Result<u32> {
122 if self.len() < 4 {
123 return Err(Error::Message("completion storage is too small".into()));
124 }
125 unsafe {
126 if self
129 .0
130 .needs_cache
131 .load(std::sync::atomic::Ordering::Acquire)
132 {
133 return Err(Error::Message(
134 "completion fence must be GPU-coherent only".into(),
135 ));
136 }
137 Ok(self
138 .0
139 .pointer
140 .cast::<std::sync::atomic::AtomicU32>()
141 .as_ref()
142 .unwrap()
143 .load(std::sync::atomic::Ordering::Acquire))
144 }
145 }
146 pub(crate) fn host_pointer(&self) -> *mut u8 {
147 self.0.pointer
148 }
149 pub unsafe fn cache_control(&self, flush: bool, offset: usize, length: usize) -> Result<()> {
155 self.range(offset, length)?;
156 if let Some(source) = &self.0.source {
159 return unsafe { source.cache_control(flush, offset, length) };
160 }
161 if !self
162 .0
163 .needs_cache
164 .load(std::sync::atomic::Ordering::Acquire)
165 {
166 std::sync::atomic::fence(std::sync::atomic::Ordering::SeqCst);
167 return Ok(());
168 }
169 check("explicit mapped visibility", unsafe {
170 entry!(self.0.instance.api.core, host_mapping_cache_control)(
171 self.0.mapping,
172 if flush {
173 AMDF_HOST_CACHE_OPERATION_FLUSH
174 } else {
175 AMDF_HOST_CACHE_OPERATION_INVALIDATE
176 },
177 offset as u64,
178 length as u64,
179 )
180 })
181 }
182 pub fn len(&self) -> usize {
184 self.0.bytes
185 }
186 pub fn is_empty(&self) -> bool {
188 self.len() == 0
189 }
190 fn range(&self, offset: usize, length: usize) -> Result<()> {
191 if offset
192 .checked_add(length)
193 .is_none_or(|end| end > self.len())
194 {
195 return Err(Error::Message("buffer range is out of bounds".into()));
196 }
197 Ok(())
198 }
199 pub fn write(&self, offset: usize, bytes: &[u8]) -> Result<()> {
201 self.range(offset, bytes.len())?;
202 let uses = self
203 .0
204 .uses
205 .lock()
206 .map_err(|_| Error::DeviceLost("memory ownership poisoned".into()))?;
207 if *uses != 0 {
208 return Err(Error::Busy("buffer is in use by a device".into()));
209 }
210 unsafe {
211 self.cache_control(false, offset, bytes.len())?;
214 ptr::copy_nonoverlapping(bytes.as_ptr(), self.0.pointer.add(offset), bytes.len());
215 self.cache_control(true, offset, bytes.len())
216 }
217 }
218 pub fn read(&self, offset: usize, bytes: &mut [u8]) -> Result<()> {
220 self.range(offset, bytes.len())?;
221 let uses = self
222 .0
223 .uses
224 .lock()
225 .map_err(|_| Error::DeviceLost("memory ownership poisoned".into()))?;
226 if *uses != 0 {
227 return Err(Error::Busy("buffer is in use by a device".into()));
228 }
229 unsafe {
230 self.cache_control(false, offset, bytes.len())?;
231 ptr::copy_nonoverlapping(self.0.pointer.add(offset), bytes.as_mut_ptr(), bytes.len());
232 }
233 Ok(())
234 }
235 pub(super) fn with_host_bytes<T>(
238 &self,
239 action: impl FnOnce(&mut [u8]) -> Result<T>,
240 ) -> Result<T> {
241 let uses = self
242 .0
243 .uses
244 .lock()
245 .map_err(|_| Error::DeviceLost("memory ownership poisoned".into()))?;
246 if *uses != 0 {
247 return Err(Error::Busy("buffer is in use by a device".into()));
248 }
249 unsafe {
250 self.cache_control(false, 0, self.len())?;
251 let result = action(std::slice::from_raw_parts_mut(self.0.pointer, self.len()));
252 self.cache_control(true, 0, self.len())?;
253 result
254 }
255 }
256 pub fn device_address(&self, device: &Device) -> Result<u64> {
259 let index = self
260 .0
261 .devices
262 .iter()
263 .position(|d| Arc::ptr_eq(&d.0, &device.0));
264 let Some(index) = index else {
265 return self.0.source.as_ref().map_or_else(
266 || Err(Error::Message("device has no access to this buffer".into())),
267 |source| source.device_address(device),
268 );
269 };
270 let kind = match device.endpoint().engine() {
271 Engine::Gpu => AMDF_MEMORY_ADDRESS_GPU,
272 Engine::Xdna => AMDF_MEMORY_ADDRESS_XDNA_DMA,
273 };
274 let mut address = 0;
275 check("memory_query_address", unsafe {
276 entry!(self.0.instance.api.core, memory_query_address)(
277 self.0.raw,
278 index as u32,
279 kind,
280 &mut address,
281 )
282 })?;
283 Ok(address)
284 }
285}
286
287pub(super) struct DeviceUse(Buffer);
288impl Drop for DeviceUse {
289 fn drop(&mut self) {
290 let mut uses = self.0.0.uses.lock().unwrap_or_else(|e| e.into_inner());
291 *uses -= 1;
292 }
293}
294
295#[derive(Default)]
296struct MemoryOwners {
297 coherent: bool,
298 profile: Option<(*mut amdf_memory_scope_t, u32)>,
299 source: Option<Buffer>,
300 reservation: Option<Arc<crate::residency::MemoryReservation>>,
301 host_storage: Option<Arc<memmap2::MmapMut>>,
302 options: AllocationOptions,
303}
304struct ExternalMemory {
305 api: Arc<Api>,
306 value: amdf_external_memory_t,
307}
308impl Drop for ExternalMemory {
309 fn drop(&mut self) {
310 unsafe {
311 self.api.core.external_memory_release.unwrap()(&mut self.value);
312 }
313 }
314}
315impl Fabric {
316 pub fn allocate_with_options(
319 &self,
320 bytes: usize,
321 devices: &[Device],
322 options: AllocationOptions,
323 budget: Option<&crate::residency::MemoryBudget>,
324 ) -> Result<Buffer> {
325 self.create_memory(
326 bytes,
327 devices,
328 AMDF_MEMORY_ACCESS_READ | AMDF_MEMORY_ACCESS_WRITE,
329 options.minimum_alignment,
330 None,
331 MemoryOwners {
332 coherent: options.coherent_gpu,
333 reservation: budget.map(|b| b.reserve(bytes).map(Arc::new)).transpose()?,
334 options,
335 ..Default::default()
336 },
337 None,
338 )
339 }
340 pub fn allocate_registered(
346 &self,
347 bytes: usize,
348 devices: &[Device],
349 budget: Option<&crate::residency::MemoryBudget>,
350 ) -> Result<Buffer> {
351 let page = unsafe { libc::sysconf(libc::_SC_PAGESIZE) };
352 if page <= 0 || bytes == 0 {
353 return Err(Error::Message("invalid registered allocation size".into()));
354 }
355 let page = page as usize;
356 let bytes = bytes
357 .checked_add(page - 1)
358 .map(|v| v / page * page)
359 .ok_or_else(|| Error::Message("registered allocation overflow".into()))?;
360 let reservation = budget.map(|b| b.reserve(bytes).map(Arc::new)).transpose()?;
361 let mut mapping = memmap2::MmapOptions::new().len(bytes).map_anon()?;
362 let pointer = mapping.as_mut_ptr();
363 self.create_memory(
364 bytes,
365 devices,
366 AMDF_MEMORY_ACCESS_READ | AMDF_MEMORY_ACCESS_WRITE,
367 page as u64,
368 Some(pointer.cast()),
369 MemoryOwners {
370 coherent: true,
371 reservation,
372 host_storage: Some(Arc::new(mapping)),
373 ..Default::default()
374 },
375 None,
376 )
377 }
378 pub fn allocate_shared(&self, bytes: usize, devices: &[Device]) -> Result<Buffer> {
381 self.create_memory(
382 bytes,
383 devices,
384 AMDF_MEMORY_ACCESS_READ | AMDF_MEMORY_ACCESS_WRITE,
385 64,
386 None,
387 MemoryOwners {
388 coherent: true,
389 ..Default::default()
390 },
391 None,
392 )
393 }
394 pub fn allocate(&self, bytes: usize, devices: &[Device]) -> Result<Buffer> {
397 self.allocate_access(
398 bytes,
399 devices,
400 AMDF_MEMORY_ACCESS_READ | AMDF_MEMORY_ACCESS_WRITE,
401 64,
402 )
403 }
404 pub fn allocate_from(&self, data: &[u8], devices: &[Device]) -> Result<Buffer> {
407 self.create_memory(
408 data.len(),
409 devices,
410 AMDF_MEMORY_ACCESS_READ | AMDF_MEMORY_ACCESS_WRITE,
411 64,
412 None,
413 MemoryOwners::default(),
414 Some(data),
415 )
416 }
417 pub fn allocate_budgeted(
420 &self,
421 bytes: usize,
422 devices: &[Device],
423 budget: &crate::residency::MemoryBudget,
424 ) -> Result<Buffer> {
425 let reservation = Arc::new(budget.reserve(bytes)?);
426 self.create_memory(
427 bytes,
428 devices,
429 AMDF_MEMORY_ACCESS_READ | AMDF_MEMORY_ACCESS_WRITE,
430 64,
431 None,
432 MemoryOwners {
433 coherent: false,
434 profile: None,
435 source: None,
436 host_storage: None,
437 reservation: Some(reservation),
438 ..Default::default()
439 },
440 None,
441 )
442 }
443 pub(super) fn allocate_access(
444 &self,
445 bytes: usize,
446 devices: &[Device],
447 access: u32,
448 alignment: u64,
449 ) -> Result<Buffer> {
450 self.create_memory(
451 bytes,
452 devices,
453 access,
454 alignment,
455 None,
456 MemoryOwners::default(),
457 None,
458 )
459 }
460 pub(super) fn allocate_owned(
461 &self,
462 bytes: usize,
463 device: &Device,
464 access: u32,
465 alignment: u64,
466 coherent: bool,
467 budget: Option<&crate::residency::MemoryBudget>,
468 ) -> Result<Buffer> {
469 let reservation = budget.map(|b| b.reserve(bytes).map(Arc::new)).transpose()?;
470 self.create_memory(
471 bytes,
472 std::slice::from_ref(device),
473 access,
474 alignment,
475 None,
476 MemoryOwners {
477 coherent,
478 profile: None,
479 source: None,
480 reservation,
481 host_storage: None,
482 ..Default::default()
483 },
484 None,
485 )
486 }
487 #[cfg(feature = "npu")]
488 pub(crate) fn share_owned(&self, source: &Buffer, devices: &[Device]) -> Result<Buffer> {
489 let additional: Vec<_> = devices
490 .iter()
491 .filter(|device| source.device_address(device).is_err())
492 .cloned()
493 .collect();
494 if additional.is_empty() {
495 return Ok(source.clone());
496 }
497 self.create_memory(
498 source.len(),
499 &additional,
500 AMDF_MEMORY_ACCESS_READ | AMDF_MEMORY_ACCESS_WRITE,
501 4096,
502 None,
503 MemoryOwners {
504 coherent: false,
505 profile: None,
506 host_storage: None,
507 source: Some(source.clone()),
508 reservation: None,
509 ..Default::default()
510 },
511 None,
512 )
513 }
514
515 pub unsafe fn register_host(
522 &self,
523 pointer: *mut std::ffi::c_void,
524 bytes: usize,
525 devices: &[Device],
526 ) -> Result<Buffer> {
527 if pointer.is_null() {
528 return Err(Error::Message("host registration pointer is null".into()));
529 }
530 self.create_memory(
531 bytes,
532 devices,
533 AMDF_MEMORY_ACCESS_READ | AMDF_MEMORY_ACCESS_WRITE,
534 4096,
535 Some(pointer),
536 MemoryOwners {
537 coherent: true,
538 ..Default::default()
539 },
540 None,
541 )
542 }
543 #[allow(clippy::too_many_arguments)]
544 fn create_memory(
545 &self,
546 bytes: usize,
547 devices: &[Device],
548 access: u32,
549 alignment: u64,
550 host: Option<*mut std::ffi::c_void>,
551 owners: MemoryOwners,
552 initial_data: Option<&[u8]>,
553 ) -> Result<Buffer> {
554 if bytes == 0 {
555 return Err(Error::Message("allocation must be nonempty".into()));
556 }
557 if initial_data
558 .is_some_and(|data| data.len() != bytes || host.is_some() || owners.source.is_some())
559 {
560 return Err(Error::Message(
561 "initial data must cover exactly one new allocation".into(),
562 ));
563 }
564 for (index, device) in devices.iter().enumerate() {
565 if !Arc::ptr_eq(&self.0, &device.0.endpoint.0.instance)
566 || devices[..index]
567 .iter()
568 .any(|other| Arc::ptr_eq(&other.0, &device.0))
569 {
570 return Err(Error::Message(
571 "allocation devices must be unique and belong to this fabric".into(),
572 ));
573 }
574 }
575 let api = self.0.api.core;
576 let _ = entry!(api, memory_destroy);
577 let _ = entry!(api, host_mapping_destroy);
578 let _ = entry!(api, host_mapping_cache_control);
579 let mut external = if let Some(source) = &owners.source {
580 let _ = entry!(api, external_memory_release);
581 let mut external = ExternalMemory {
582 api: self.0.api.clone(),
583 value: Default::default(),
584 };
585 let options = amdf_memory_export_info_t {
586 type_: AMDF_STRUCTURE_TYPE_MEMORY_EXPORT_INFO,
587 structure_size: size_of::<amdf_memory_export_info_t>() as u32,
588 external_memory_type: AMDF_EXTERNAL_MEMORY_TYPE_DMA_BUF_FD,
589 byte_length: bytes as u64,
590 ..Default::default()
591 };
592 check("memory_export", unsafe {
593 entry!(api, memory_export)(source.0.raw, &options, &mut external.value)
594 })?;
595 Some(external)
596 } else {
597 None
598 };
599 let accesses: Vec<_> = devices
600 .iter()
601 .map(|device| amdf_memory_device_access_t {
602 device: device.0.raw,
603 requirements: amdf_memory_access_requirements_t {
604 access,
605 flags: AMDF_MEMORY_FLAG_DEVICE_ADDRESS as u64
606 | if owners.options.mapping_source {
607 AMDF_MEMORY_FLAG_MAPPING_SOURCE as u64
608 } else {
609 0
610 }
611 | if owners.coherent && device.endpoint().engine() == Engine::Gpu {
612 AMDF_MEMORY_FLAG_HOST_COHERENT as u64
613 } else {
614 0
615 },
616 address_kinds: 1
617 << match device.endpoint().engine() {
618 Engine::Gpu => AMDF_MEMORY_ADDRESS_GPU,
619 Engine::Xdna => AMDF_MEMORY_ADDRESS_XDNA_DMA,
620 },
621 ..Default::default()
622 },
623 })
624 .collect();
625 let mut count = 0;
626 let status = unsafe {
627 entry!(api, instance_enumerate_memory_scopes)(
628 self.0.raw,
629 0,
630 ptr::null_mut(),
631 &mut count,
632 )
633 };
634 if status != AMDF_STATUS_CODE_BUFFER_TOO_SMALL as u64 {
635 check("enumerate memory scopes", status)?;
636 }
637 let mut scopes = vec![ptr::null_mut(); count as usize];
638 check("enumerate memory scopes", unsafe {
639 entry!(api, instance_enumerate_memory_scopes)(
640 self.0.raw,
641 count,
642 scopes.as_mut_ptr(),
643 &mut count,
644 )
645 })?;
646 for scope in scopes {
647 let mut scope_info = amdf_memory_scope_info_t {
648 type_: AMDF_STRUCTURE_TYPE_MEMORY_SCOPE_INFO,
649 structure_size: size_of::<amdf_memory_scope_info_t>() as u32,
650 ..Default::default()
651 };
652 check("memory_scope_query_info", unsafe {
653 entry!(api, memory_scope_query_info)(scope, &mut scope_info)
654 })?;
655 if scope_info.kind != AMDF_MEMORY_SCOPE_KIND_SYSTEM {
656 continue;
657 }
658 for ordinal in 0..scope_info.memory_profile_count {
659 if owners
660 .profile
661 .is_some_and(|selected| selected != (scope, ordinal))
662 {
663 continue;
664 }
665 let mut profile = amdf_memory_profile_t {
666 type_: AMDF_STRUCTURE_TYPE_MEMORY_PROFILE,
667 structure_size: size_of::<amdf_memory_profile_t>() as u32,
668 ..Default::default()
669 };
670 let mut capabilities = vec![
671 amdf_memory_access_capabilities_t {
672 type_: AMDF_STRUCTURE_TYPE_MEMORY_ACCESS_CAPABILITIES,
673 structure_size: size_of::<amdf_memory_access_capabilities_t>() as u32,
674 ..Default::default()
675 };
676 devices.len()
677 ];
678 let status = unsafe {
679 entry!(api, memory_scope_query_device_profile)(
680 scope,
681 ordinal,
682 accesses.len() as u32,
683 accesses.as_ptr(),
684 &mut profile,
685 capabilities.as_mut_ptr(),
686 )
687 };
688 if status == AMDF_STATUS_CODE_UNSUPPORTED as u64 {
689 continue;
690 }
691 check("memory_scope_query_device_profile", status)?;
692 let role = if external.is_some() {
693 AMDF_MEMORY_PROFILE_ROLE_IMPORT
694 } else if host.is_some() {
695 AMDF_MEMORY_PROFILE_ROLE_REGISTER
696 } else {
697 AMDF_MEMORY_PROFILE_ROLE_CREATE
698 };
699 let construction = if external.is_some() {
700 &profile.import
701 } else if host.is_some() {
702 &profile.registration
703 } else {
704 &profile.allocation
705 };
706 if profile.roles & (role | AMDF_MEMORY_PROFILE_ROLE_HOST_MAP) as u64
707 != (role | AMDF_MEMORY_PROFILE_ROLE_HOST_MAP) as u64
708 || profile.supported_flags & owners.options.required_flags()
709 != owners.options.required_flags()
710 || (owners.options.host_numa_node.is_some()
711 && construction.placement_kinds
712 & (1 << AMDF_MEMORY_PLACEMENT_KIND_HOST_NUMA)
713 == 0)
714 || construction.maximum_byte_length < bytes as u64
715 {
716 continue;
717 }
718 let options = amdf_memory_create_info_t {
719 type_: AMDF_STRUCTURE_TYPE_MEMORY_CREATE_INFO,
720 structure_size: size_of::<amdf_memory_create_info_t>() as u32,
721 memory_profile_ordinal: ordinal,
722 access_count: accesses.len() as u32,
723 required_flags: owners.options.required_flags(),
724 placement: owners.options.placement(),
725 byte_length: bytes as u64,
726 minimum_alignment: alignment,
727 accesses: accesses.as_ptr(),
728 registered_host_pointer: host.unwrap_or(ptr::null_mut()),
729 registered_host_cacheability: if host.is_some() {
730 AMDF_HOST_CACHEABILITY_WRITE_BACK
731 } else {
732 0
733 },
734 ..Default::default()
735 };
736 let mut raw = ptr::null_mut();
737 if let Some(external) = &mut external {
738 let options = amdf_memory_import_info_t {
739 type_: AMDF_STRUCTURE_TYPE_MEMORY_IMPORT_INFO,
740 structure_size: size_of::<amdf_memory_import_info_t>() as u32,
741 memory_profile_ordinal: ordinal,
742 access_count: accesses.len() as u32,
743 required_flags: AMDF_MEMORY_FLAG_HOST_VISIBLE as u64,
744 minimum_alignment: alignment,
745 accesses: accesses.as_ptr(),
746 ..Default::default()
747 };
748 check("memory_import", unsafe {
749 entry!(api, memory_import)(scope, &options, &mut external.value, &mut raw)
750 })?;
751 } else {
752 check("memory_create", unsafe {
753 entry!(api, memory_create)(scope, &options, &mut raw)
754 })?;
755 }
756 if raw.is_null() {
757 return Err(missing("created memory"));
758 }
759 let needs_cache = owners.source.as_ref().map_or_else(
760 || Arc::new(std::sync::atomic::AtomicBool::new(false)),
761 |source| source.0.needs_cache.clone(),
762 );
763 if !owners.coherent
764 || devices.is_empty()
765 || devices
766 .iter()
767 .any(|device| device.endpoint().engine() != Engine::Gpu)
768 {
769 needs_cache.store(true, std::sync::atomic::Ordering::Release);
770 }
771 let mut memory = Memory {
772 instance: self.0.clone(),
773 devices: devices.to_vec(),
774 raw,
775 mapping: ptr::null_mut(),
776 pointer: ptr::null_mut(),
777 bytes,
778 needs_cache,
779 source: owners.source.clone(),
780 reservation: owners.reservation.clone(),
781 host_storage: owners.host_storage.clone(),
782 uses: owners
783 .source
784 .as_ref()
785 .map_or_else(|| Arc::new(Mutex::new(0)), |source| source.0.uses.clone()),
786 };
787 let options = amdf_memory_map_info_t {
788 type_: AMDF_STRUCTURE_TYPE_MEMORY_MAP_INFO,
789 structure_size: size_of::<amdf_memory_map_info_t>() as u32,
790 byte_length: bytes as u64,
791 flags: AMDF_MEMORY_MAP_FLAG_READ | AMDF_MEMORY_MAP_FLAG_WRITE,
792 ..Default::default()
793 };
794 check("memory_map", unsafe {
795 entry!(api, memory_map)(raw, &options, &mut memory.mapping)
796 })?;
797 let mut info = amdf_host_mapping_info_t {
798 type_: AMDF_STRUCTURE_TYPE_HOST_MAPPING_INFO,
799 structure_size: size_of::<amdf_host_mapping_info_t>() as u32,
800 ..Default::default()
801 };
802 check("host_mapping_query_info", unsafe {
803 entry!(api, host_mapping_query_info)(memory.mapping, &mut info)
804 })?;
805 if info.pointer.is_null() || info.byte_length < bytes as u64 {
806 return Err(missing("complete host mapping"));
807 }
808 memory.pointer = info.pointer.cast();
809 unsafe {
810 if host.is_none() && owners.source.is_none() {
811 if let Some(data) = initial_data {
812 ptr::copy_nonoverlapping(data.as_ptr(), memory.pointer, bytes);
816 } else {
817 ptr::write_bytes(memory.pointer, 0, bytes);
818 }
819 }
820 }
821 let buffer = Buffer(Arc::new(memory));
822 unsafe {
823 buffer.cache_control(true, 0, bytes)?;
824 }
825 return Ok(buffer);
826 }
827 }
828 Err(Error::Unsupported(
829 "no system memory profile admits all requested consumers".into(),
830 ))
831 }
832}
833
834#[cfg(all(test, feature = "npu"))]
835mod alias_tests {
836 use super::*;
837 #[test]
838 #[ignore = "requires native compiler, gfx1151 and NPU5"]
839 fn imported_aliases_share_host_leases_and_cache_requirements() -> Result<()> {
840 let gpu = Device::open(Engine::Gpu, 0)?;
841 let npu = Device::open(Engine::Xdna, 0)?;
842 let fabric = gpu.fabric();
843 let source = fabric.allocate(4096, std::slice::from_ref(&gpu))?;
844 let alias = fabric.share_owned(&source, &[gpu, npu.clone()])?;
845 assert!(
846 source
847 .0
848 .needs_cache
849 .load(std::sync::atomic::Ordering::Acquire)
850 );
851 let output = fabric.allocate(4096, std::slice::from_ref(&npu))?;
852 let artifact = crate::loom::Compiler::for_target(None, npu.target())?
853 .module(include_str!("../../tests/kernels/copy.xdna.loom"))
854 .compile(&crate::loom::Specialization::new("copy").with_config("copy.packets", "1"))?;
855 let program = unsafe {
856 npu.prepare_xdna(
857 &artifact,
858 1,
859 &[
860 XdnaBinding {
861 buffer: &alias,
862 offset: 0,
863 length: 4096,
864 },
865 XdnaBinding {
866 buffer: &output,
867 offset: 0,
868 length: 4096,
869 },
870 ],
871 )
872 }?;
873 for value in [0x53, 0x27, 0xb8] {
874 source.write(0, &[value; 4096])?;
875 let done = unsafe { program.dispatch() }?;
876 assert!(matches!(source.write(0, &[0]), Err(Error::Busy(_))));
877 assert!(matches!(alias.read(0, &mut [0]), Err(Error::Busy(_))));
878 done.wait()?;
879 let mut actual = [0; 4096];
880 output.read(0, &mut actual)?;
881 assert_eq!(actual, [value; 4096]);
882 }
883 drop(source);
884 alias.write(0, &[0x4b; 4096])?;
885 unsafe { program.dispatch() }?.wait()?;
886 let mut actual = [0; 4096];
887 output.read(0, &mut actual)?;
888 assert_eq!(actual, [0x4b; 4096]);
889 Ok(())
890 }
891}
892
893#[derive(Clone)]
898pub struct AllocationProfile {
899 fabric: Fabric,
900 devices: Vec<Device>,
901 scope: *mut amdf_memory_scope_t,
902 ordinal: u32,
903 coherent: bool,
904 limits: amdf_memory_construction_capabilities_t,
905}
906unsafe impl Send for AllocationProfile {}
908unsafe impl Sync for AllocationProfile {}
909impl Fabric {
910 pub fn allocation_profiles(
914 &self,
915 devices: &[Device],
916 coherent_gpu: bool,
917 ) -> Result<Vec<AllocationProfile>> {
918 for (i, device) in devices.iter().enumerate() {
919 if !Arc::ptr_eq(&self.0, &device.0.endpoint.0.instance)
920 || devices[..i]
921 .iter()
922 .any(|other| Arc::ptr_eq(&other.0, &device.0))
923 {
924 return Err(Error::Message(
925 "allocation devices must be unique and belong to this fabric".into(),
926 ));
927 }
928 }
929 let api = self.0.api.core;
930 let accesses = profile_accesses(devices, coherent_gpu);
931 let mut count = 0;
932 let status = unsafe {
933 entry!(api, instance_enumerate_memory_scopes)(
934 self.0.raw,
935 0,
936 ptr::null_mut(),
937 &mut count,
938 )
939 };
940 if status != AMDF_STATUS_CODE_BUFFER_TOO_SMALL as u64 {
941 check("enumerate memory scopes", status)?;
942 }
943 let mut scopes = vec![ptr::null_mut(); count as usize];
944 check("enumerate memory scopes", unsafe {
945 entry!(api, instance_enumerate_memory_scopes)(
946 self.0.raw,
947 count,
948 scopes.as_mut_ptr(),
949 &mut count,
950 )
951 })?;
952 scopes.truncate(count as usize);
953 let mut result = Vec::new();
954 for scope in scopes {
955 let mut info = amdf_memory_scope_info_t {
956 type_: AMDF_STRUCTURE_TYPE_MEMORY_SCOPE_INFO,
957 structure_size: size_of::<amdf_memory_scope_info_t>() as u32,
958 ..Default::default()
959 };
960 check("memory_scope_query_info", unsafe {
961 entry!(api, memory_scope_query_info)(scope, &mut info)
962 })?;
963 if info.kind != AMDF_MEMORY_SCOPE_KIND_SYSTEM {
964 continue;
965 }
966 for ordinal in 0..info.memory_profile_count {
967 let mut profile = amdf_memory_profile_t {
968 type_: AMDF_STRUCTURE_TYPE_MEMORY_PROFILE,
969 structure_size: size_of::<amdf_memory_profile_t>() as u32,
970 ..Default::default()
971 };
972 let mut capabilities = vec![
973 amdf_memory_access_capabilities_t {
974 type_: AMDF_STRUCTURE_TYPE_MEMORY_ACCESS_CAPABILITIES,
975 structure_size: size_of::<amdf_memory_access_capabilities_t>() as u32,
976 ..Default::default()
977 };
978 devices.len()
979 ];
980 let status = unsafe {
981 entry!(api, memory_scope_query_device_profile)(
982 scope,
983 ordinal,
984 accesses.len() as u32,
985 accesses.as_ptr(),
986 &mut profile,
987 capabilities.as_mut_ptr(),
988 )
989 };
990 if status == AMDF_STATUS_CODE_UNSUPPORTED as u64 {
991 continue;
992 }
993 check("memory_scope_query_device_profile", status)?;
994 let roles =
995 (AMDF_MEMORY_PROFILE_ROLE_CREATE | AMDF_MEMORY_PROFILE_ROLE_HOST_MAP) as u64;
996 if profile.roles & roles != roles
997 || profile.supported_flags & AMDF_MEMORY_FLAG_HOST_VISIBLE as u64 == 0
998 {
999 continue;
1000 }
1001 result.push(AllocationProfile {
1002 fabric: self.clone(),
1003 devices: devices.to_vec(),
1004 scope,
1005 ordinal,
1006 coherent: coherent_gpu,
1007 limits: profile.allocation,
1008 });
1009 }
1010 }
1011 Ok(result)
1012 }
1013}
1014fn profile_accesses(devices: &[Device], coherent: bool) -> Vec<amdf_memory_device_access_t> {
1015 devices
1016 .iter()
1017 .map(|device| amdf_memory_device_access_t {
1018 device: device.0.raw,
1019 requirements: amdf_memory_access_requirements_t {
1020 access: AMDF_MEMORY_ACCESS_READ | AMDF_MEMORY_ACCESS_WRITE,
1021 flags: AMDF_MEMORY_FLAG_DEVICE_ADDRESS as u64
1022 | if coherent && device.endpoint().engine() == Engine::Gpu {
1023 AMDF_MEMORY_FLAG_HOST_COHERENT as u64
1024 } else {
1025 0
1026 },
1027 address_kinds: 1
1028 << match device.endpoint().engine() {
1029 Engine::Gpu => AMDF_MEMORY_ADDRESS_GPU,
1030 Engine::Xdna => AMDF_MEMORY_ADDRESS_XDNA_DMA,
1031 },
1032 ..Default::default()
1033 },
1034 })
1035 .collect()
1036}
1037impl AllocationProfile {
1038 pub fn supports_host_numa(&self) -> bool {
1041 self.limits.placement_kinds & (1 << AMDF_MEMORY_PLACEMENT_KIND_HOST_NUMA) != 0
1042 }
1043 pub fn maximum_bytes(&self) -> u64 {
1045 self.limits.maximum_byte_length
1046 }
1047 pub fn byte_granularity(&self) -> u64 {
1049 self.limits.byte_length_granularity
1050 }
1051 pub fn allocate(
1053 &self,
1054 bytes: usize,
1055 budget: Option<&crate::residency::MemoryBudget>,
1056 ) -> Result<Buffer> {
1057 if bytes == 0
1058 || bytes as u64 > self.maximum_bytes()
1059 || !((bytes as u64).is_multiple_of(self.byte_granularity().max(1)))
1060 {
1061 return Err(Error::Message(
1062 "allocation does not satisfy the selected profile's size limits".into(),
1063 ));
1064 }
1065 self.fabric.create_memory(
1066 bytes,
1067 &self.devices,
1068 AMDF_MEMORY_ACCESS_READ | AMDF_MEMORY_ACCESS_WRITE,
1069 self.limits.minimum_alignment,
1070 None,
1071 MemoryOwners {
1072 coherent: self.coherent,
1073 profile: Some((self.scope, self.ordinal)),
1074 source: None,
1075 host_storage: None,
1076 reservation: budget.map(|b| b.reserve(bytes).map(Arc::new)).transpose()?,
1077 ..Default::default()
1078 },
1079 None,
1080 )
1081 }
1082 pub fn visibility(
1086 &self,
1087 producer: MemorySite<'_>,
1088 consumer: MemorySite<'_>,
1089 ) -> Result<ProspectiveVisibility> {
1090 let site = |site: MemorySite<'_>| -> Result<amdf_memory_profile_site_t> {
1091 let mut result = amdf_memory_profile_site_t::default();
1092 match site {
1093 MemorySite::Host => {
1094 result.kind = AMDF_MEMORY_SITE_KIND_HOST;
1095 result.value.host_access =
1096 AMDF_MEMORY_MAP_FLAG_READ | AMDF_MEMORY_MAP_FLAG_WRITE;
1097 }
1098 MemorySite::Device(device, family) => {
1099 let ordinal = self
1100 .devices
1101 .iter()
1102 .position(|d| Arc::ptr_eq(&d.0, &device.0))
1103 .ok_or_else(|| {
1104 Error::Message(
1105 "visibility device is absent from allocation profile".into(),
1106 )
1107 })?;
1108 result.kind = AMDF_MEMORY_SITE_KIND_DEVICE;
1109 result.value.device = amdf_memory_profile_site_t__bindgen_ty_1__bindgen_ty_1 {
1110 access_ordinal: ordinal as u32,
1111 queue_family_ordinal: family,
1112 };
1113 }
1114 }
1115 Ok(result)
1116 };
1117 let accesses = profile_accesses(&self.devices, self.coherent);
1118 let query = amdf_memory_profile_pair_query_t {
1119 type_: AMDF_STRUCTURE_TYPE_MEMORY_PROFILE_PAIR_QUERY,
1120 structure_size: size_of::<amdf_memory_profile_pair_query_t>() as u32,
1121 memory_profile_ordinal: self.ordinal,
1122 access_count: accesses.len() as u32,
1123 accesses: accesses.as_ptr(),
1124 required_flags: AMDF_MEMORY_FLAG_HOST_VISIBLE as u64,
1125 producer: site(producer)?,
1126 consumer: site(consumer)?,
1127 ..Default::default()
1128 };
1129 let mut info = amdf_memory_pair_info_t {
1130 type_: AMDF_STRUCTURE_TYPE_MEMORY_PAIR_INFO,
1131 structure_size: size_of::<amdf_memory_pair_info_t>() as u32,
1132 ..Default::default()
1133 };
1134 check("memory_scope_query_pair_info", unsafe {
1135 entry!(self.fabric.0.api.core, memory_scope_query_pair_info)(
1136 self.scope, &query, &mut info,
1137 )
1138 })?;
1139 Ok(ProspectiveVisibility {
1140 _profile: self.clone(),
1141 facts: visibility::VisibilityFacts::from_native(info)?,
1142 })
1143 }
1144}
1145pub struct ProspectiveVisibility {
1147 _profile: AllocationProfile,
1148 facts: visibility::VisibilityFacts,
1149}
1150impl std::ops::Deref for ProspectiveVisibility {
1151 type Target = visibility::VisibilityFacts;
1152 fn deref(&self) -> &Self::Target {
1153 &self.facts
1154 }
1155}