mod context;
mod loader;
mod registry;
#[cfg(target_family = "windows")]
mod veh;
pub use context::{create_temp_context, destroy_temp_context};
pub use registry::CudaAllocationRegistry;
use core::ffi::c_void;
use core::sync::atomic::Ordering;
use mnemosyne_core::MemoryBackend;
use registry::{
CUDA_ALLOCATIONS, CUDA_DEVICE_ALLOCATIONS, CUDA_HOST_PINNED_ALLOCATIONS, register_cuda_ptr_in,
unregister_cuda_ptr_in,
};
trait CudaAllocOps {
fn registry() -> &'static CudaAllocationRegistry;
fn alloc_sym() -> *mut c_void;
fn free_sym() -> *mut c_void;
unsafe fn raw_alloc(alloc_sym: *mut c_void, size: usize) -> *mut u8;
unsafe fn raw_free(free_sym: *mut c_void, ptr: *mut u8) -> core::ffi::c_int;
#[inline]
unsafe fn post_register(_ptr: *mut u8, _size: usize) {}
}
#[inline]
unsafe fn cuda_allocate<Ops: CudaAllocOps>(size: usize) -> *mut u8 {
unsafe { loader::init_cuda() };
let alloc_sym = Ops::alloc_sym();
let free_sym = Ops::free_sym();
if alloc_sym.is_null() || free_sym.is_null() {
return core::ptr::null_mut();
}
let ptr = unsafe { Ops::raw_alloc(alloc_sym, size) };
if ptr.is_null() {
return core::ptr::null_mut();
}
if register_cuda_ptr_in(Ops::registry(), ptr) {
unsafe { Ops::post_register(ptr, size) };
return ptr;
}
let _rollback_status = unsafe { Ops::raw_free(free_sym, ptr) };
core::ptr::null_mut()
}
#[inline]
unsafe fn cuda_deallocate<Ops: CudaAllocOps>(ptr: *mut u8) -> bool {
if !unregister_cuda_ptr_in(Ops::registry(), ptr) {
return false;
}
let free_sym = Ops::free_sym();
if free_sym.is_null() {
return false;
}
unsafe { Ops::raw_free(free_sym, ptr) == 0 }
}
unsafe fn managed_raw_alloc(alloc_sym: *mut c_void, size: usize) -> *mut u8 {
type CuMemAllocManagedFn = unsafe extern "system" fn(*mut u64, usize, u32) -> core::ffi::c_int;
let cu_mem_alloc_managed: CuMemAllocManagedFn = unsafe { core::mem::transmute(alloc_sym) };
let mut dptr: u64 = 0;
let res = unsafe { cu_mem_alloc_managed(&mut dptr, size, 0x01) };
if res == 0 && dptr != 0 {
dptr as *mut u8
} else {
core::ptr::null_mut()
}
}
unsafe fn managed_raw_free(free_sym: *mut c_void, ptr: *mut u8) -> core::ffi::c_int {
type CuMemFreeFn = unsafe extern "system" fn(u64) -> core::ffi::c_int;
let cu_mem_free: CuMemFreeFn = unsafe { core::mem::transmute(free_sym) };
unsafe { cu_mem_free(ptr as u64) }
}
pub struct CudaUnifiedBackend;
impl CudaAllocOps for CudaUnifiedBackend {
#[inline]
fn registry() -> &'static CudaAllocationRegistry {
&CUDA_ALLOCATIONS
}
#[inline]
fn alloc_sym() -> *mut c_void {
loader::CU_MEM_ALLOC_MANAGED.load(Ordering::Acquire)
}
#[inline]
fn free_sym() -> *mut c_void {
loader::CU_MEM_FREE.load(Ordering::Acquire)
}
#[inline]
unsafe fn raw_alloc(alloc_sym: *mut c_void, size: usize) -> *mut u8 {
unsafe { managed_raw_alloc(alloc_sym, size) }
}
#[inline]
unsafe fn raw_free(free_sym: *mut c_void, ptr: *mut u8) -> core::ffi::c_int {
unsafe { managed_raw_free(free_sym, ptr) }
}
}
impl MemoryBackend for CudaUnifiedBackend {
#[inline]
unsafe fn allocate(size: usize) -> *mut u8 {
unsafe { cuda_allocate::<Self>(size) }
}
#[inline]
unsafe fn deallocate(ptr: *mut u8, _size: usize) -> bool {
unsafe { cuda_deallocate::<Self>(ptr) }
}
}
pub struct CudaDeviceBackend;
impl CudaAllocOps for CudaDeviceBackend {
#[inline]
fn registry() -> &'static CudaAllocationRegistry {
&CUDA_DEVICE_ALLOCATIONS
}
#[inline]
fn alloc_sym() -> *mut c_void {
loader::CU_MEM_ALLOC_MANAGED.load(Ordering::Acquire)
}
#[inline]
fn free_sym() -> *mut c_void {
loader::CU_MEM_FREE.load(Ordering::Acquire)
}
#[inline]
unsafe fn raw_alloc(alloc_sym: *mut c_void, size: usize) -> *mut u8 {
unsafe { managed_raw_alloc(alloc_sym, size) }
}
#[inline]
unsafe fn raw_free(free_sym: *mut c_void, ptr: *mut u8) -> core::ffi::c_int {
unsafe { managed_raw_free(free_sym, ptr) }
}
#[inline]
unsafe fn post_register(ptr: *mut u8, size: usize) {
let advise_sym = loader::CU_MEM_ADVISE.load(Ordering::Acquire);
if advise_sym.is_null() {
return;
}
type CuMemAdviseFn = unsafe extern "system" fn(u64, usize, u32, i32) -> core::ffi::c_int;
let cu_mem_advise: CuMemAdviseFn = unsafe { core::mem::transmute(advise_sym) };
let _advise_status = unsafe { cu_mem_advise(ptr as u64, size, 3, 0) };
}
}
impl MemoryBackend for CudaDeviceBackend {
#[inline]
unsafe fn allocate(size: usize) -> *mut u8 {
unsafe { cuda_allocate::<Self>(size) }
}
#[inline]
unsafe fn deallocate(ptr: *mut u8, _size: usize) -> bool {
unsafe { cuda_deallocate::<Self>(ptr) }
}
}
#[inline(always)]
unsafe fn allocate_device_tier(size: usize) -> *mut u8 {
unsafe { CudaDeviceBackend::allocate(size) }
}
#[inline(always)]
unsafe fn deallocate_device_tier(ptr: *mut u8, size: usize) -> bool {
unsafe { CudaDeviceBackend::deallocate(ptr, size) }
}
pub struct CudaHbmBackend;
pub struct CudaGddrBackend;
const _: () = assert!(
core::mem::size_of::<CudaHbmBackend>() == 0
&& core::mem::size_of::<CudaGddrBackend>() == 0
&& core::mem::align_of::<CudaHbmBackend>() == 1
&& core::mem::align_of::<CudaGddrBackend>() == 1
);
macro_rules! impl_device_tier_backend {
($backend:ty) => {
impl MemoryBackend for $backend {
const SUPPORTS_PAGE_RESET: bool = CudaDeviceBackend::SUPPORTS_PAGE_RESET;
const SUPPORTS_MAKE_GUARD: bool = CudaDeviceBackend::SUPPORTS_MAKE_GUARD;
const SUPPORTS_DECOMMIT: bool = CudaDeviceBackend::SUPPORTS_DECOMMIT;
const ENABLE_CPU_CACHE: bool = CudaDeviceBackend::ENABLE_CPU_CACHE;
#[inline(always)]
unsafe fn allocate(size: usize) -> *mut u8 {
unsafe { allocate_device_tier(size) }
}
#[inline(always)]
unsafe fn deallocate(ptr: *mut u8, size: usize) -> bool {
unsafe { deallocate_device_tier(ptr, size) }
}
}
};
}
impl_device_tier_backend!(CudaHbmBackend);
impl_device_tier_backend!(CudaGddrBackend);
pub struct CudaHostPinnedBackend;
impl CudaAllocOps for CudaHostPinnedBackend {
#[inline]
fn registry() -> &'static CudaAllocationRegistry {
&CUDA_HOST_PINNED_ALLOCATIONS
}
#[inline]
fn alloc_sym() -> *mut c_void {
loader::CU_MEM_HOST_ALLOC.load(Ordering::Acquire)
}
#[inline]
fn free_sym() -> *mut c_void {
loader::CU_MEM_FREE_HOST.load(Ordering::Acquire)
}
#[inline]
unsafe fn raw_alloc(alloc_sym: *mut c_void, size: usize) -> *mut u8 {
type CuMemHostAllocFn =
unsafe extern "system" fn(*mut *mut c_void, usize, u32) -> core::ffi::c_int;
let cu_mem_host_alloc: CuMemHostAllocFn = unsafe { core::mem::transmute(alloc_sym) };
let mut host_ptr: *mut c_void = core::ptr::null_mut();
let res = unsafe { cu_mem_host_alloc(core::ptr::addr_of_mut!(host_ptr), size, 0x02) };
if res == 0 && !host_ptr.is_null() {
host_ptr as *mut u8
} else {
core::ptr::null_mut()
}
}
#[inline]
unsafe fn raw_free(free_sym: *mut c_void, ptr: *mut u8) -> core::ffi::c_int {
type CuMemFreeHostFn = unsafe extern "system" fn(*mut c_void) -> core::ffi::c_int;
let cu_mem_free_host: CuMemFreeHostFn = unsafe { core::mem::transmute(free_sym) };
unsafe { cu_mem_free_host(ptr as *mut c_void) }
}
}
impl MemoryBackend for CudaHostPinnedBackend {
#[inline]
unsafe fn allocate(size: usize) -> *mut u8 {
unsafe { cuda_allocate::<Self>(size) }
}
#[inline]
unsafe fn deallocate(ptr: *mut u8, _size: usize) -> bool {
unsafe { cuda_deallocate::<Self>(ptr) }
}
}
pub fn is_cuda_available() -> bool {
unsafe { loader::init_cuda() };
!loader::CU_MEM_ALLOC_MANAGED
.load(Ordering::Acquire)
.is_null()
}