metrale_gpu_sys/
cufile.rs

1// SPDX-License-Identifier: MIT OR Apache-2.0
2
3//! 2026-09-26: cuFile (GPUDirect Storage) entry points, resolved from
4//! libcufile with `dlopen`, so a binary starts on a host without the library
5//! and `CuFile::load` reports the failure instead. metrale-storage's GDS probe
6//! and bench call them; docs/adr/0008-nvme-high-speed-swap.md records why the
7//! swap path does not use GDS on GB10.
8//!
9//! Owner: metrale-gpu-sys.
10//! Invariants: none beyond the types.
11
12#![deny(warnings)]
13#![deny(clippy::all)]
14#![allow(non_camel_case_types, non_snake_case)]
15
16use libloading::{Library, Symbol};
17use std::ffi::{c_int, c_void};
18use std::os::raw::c_long;
19
20pub const CUFILEOP_BASE_ERR: i32 = 5000;
21pub const CU_FILE_SUCCESS: i32 = 0;
22pub const CU_FILE_DRIVER_NOT_INITIALIZED: i32 = CUFILEOP_BASE_ERR + 1;
23pub const CU_FILE_PLATFORM_NOT_SUPPORTED: i32 = CUFILEOP_BASE_ERR + 7;
24pub const CU_FILE_IO_NOT_SUPPORTED: i32 = CUFILEOP_BASE_ERR + 8;
25pub const CU_FILE_DEVICE_NOT_SUPPORTED: i32 = CUFILEOP_BASE_ERR + 9;
26
27pub const CU_FILE_HANDLE_TYPE_OPAQUE_FD: c_int = 1;
28
29pub type CUfileHandle_t = *mut c_void;
30pub type CUresult = c_int;
31pub type CUfileOpError = c_int;
32
33#[repr(C)]
34#[derive(Copy, Clone, Debug)]
35pub struct CUfileError_t {
36    pub err: CUfileOpError,
37    pub cu_err: CUresult,
38}
39
40#[repr(C)]
41pub struct CUfileDescrHandle {
42    pub fd: c_int,
43    _pad: [u8; 8 - core::mem::size_of::<c_int>()],
44}
45
46impl CUfileDescrHandle {
47    pub fn from_fd(fd: c_int) -> Self {
48        Self {
49            fd,
50            _pad: [0; 8 - core::mem::size_of::<c_int>()],
51        }
52    }
53}
54
55#[repr(C)]
56pub struct CUfileDescr_t {
57    pub type_: c_int,
58    pub handle: CUfileDescrHandle,
59    pub fs_ops: *const c_void,
60}
61
62pub type FnDriverOpen = unsafe extern "C" fn() -> CUfileError_t;
63pub type FnDriverClose = unsafe extern "C" fn() -> CUfileError_t;
64pub type FnHandleRegister =
65    unsafe extern "C" fn(*mut CUfileHandle_t, *mut CUfileDescr_t) -> CUfileError_t;
66pub type FnHandleDeregister = unsafe extern "C" fn(CUfileHandle_t);
67pub type FnBufRegister = unsafe extern "C" fn(*const c_void, libc::size_t, c_int) -> CUfileError_t;
68pub type FnBufDeregister = unsafe extern "C" fn(*const c_void) -> CUfileError_t;
69pub type FnRead = unsafe extern "C" fn(
70    CUfileHandle_t,
71    *mut c_void,
72    libc::size_t,
73    c_long,
74    c_long,
75) -> libc::ssize_t;
76pub type FnWrite = unsafe extern "C" fn(
77    CUfileHandle_t,
78    *const c_void,
79    libc::size_t,
80    c_long,
81    c_long,
82) -> libc::ssize_t;
83pub type FnGetVersion = unsafe extern "C" fn(*mut c_int) -> CUfileError_t;
84
85pub struct CuFile {
86    _lib: Library,
87    pub driver_open: FnDriverOpen,
88    pub driver_close: FnDriverClose,
89    pub handle_register: FnHandleRegister,
90    pub handle_deregister: FnHandleDeregister,
91    pub buf_register: FnBufRegister,
92    pub buf_deregister: FnBufDeregister,
93    pub read: FnRead,
94    pub write: FnWrite,
95    pub get_version: FnGetVersion,
96}
97
98const SEARCH_PATHS: &[&str] = &[
99    "libcufile.so.0",
100    "libcufile.so",
101    "/usr/local/cuda/targets/sbsa-linux/lib/libcufile.so.0",
102    "/usr/local/cuda/targets/x86_64-linux/lib/libcufile.so.0",
103    "/usr/local/cuda-13.0/targets/sbsa-linux/lib/libcufile.so.0",
104];
105
106impl CuFile {
107    pub fn load() -> Result<Self, String> {
108        let mut last_err = String::new();
109        for path in SEARCH_PATHS {
110            match unsafe { Library::new(path) } {
111                Ok(lib) => return Self::resolve(lib).map_err(|e| format!("{path}: {e}")),
112                Err(e) => last_err = format!("{path}: {e}"),
113            }
114        }
115        Err(format!("libcufile not found ({last_err})"))
116    }
117
118    fn resolve(lib: Library) -> Result<Self, String> {
119        unsafe fn sym<'a, T: Copy + 'a>(lib: &'a Library, name: &[u8]) -> Result<T, String> {
120            unsafe {
121                let s: Symbol<'a, T> = lib
122                    .get(name)
123                    .map_err(|e| format!("symbol {}: {e}", String::from_utf8_lossy(name)))?;
124                Ok(*s)
125            }
126        }
127        unsafe {
128            let driver_open = sym::<FnDriverOpen>(&lib, b"cuFileDriverOpen\0")?;
129            let driver_close = sym::<FnDriverClose>(&lib, b"cuFileDriverClose\0")?;
130            let handle_register = sym::<FnHandleRegister>(&lib, b"cuFileHandleRegister\0")?;
131            let handle_deregister = sym::<FnHandleDeregister>(&lib, b"cuFileHandleDeregister\0")?;
132            let buf_register = sym::<FnBufRegister>(&lib, b"cuFileBufRegister\0")?;
133            let buf_deregister = sym::<FnBufDeregister>(&lib, b"cuFileBufDeregister\0")?;
134            let read = sym::<FnRead>(&lib, b"cuFileRead\0")?;
135            let write = sym::<FnWrite>(&lib, b"cuFileWrite\0")?;
136            let get_version = sym::<FnGetVersion>(&lib, b"cuFileGetVersion\0")?;
137            Ok(Self {
138                _lib: lib,
139                driver_open,
140                driver_close,
141                handle_register,
142                handle_deregister,
143                buf_register,
144                buf_deregister,
145                read,
146                write,
147                get_version,
148            })
149        }
150    }
151}
152
153pub fn err_to_str(err: CUfileOpError) -> &'static str {
154    match err {
155        0 => "success",
156        x if x == CU_FILE_DRIVER_NOT_INITIALIZED => "CU_FILE_DRIVER_NOT_INITIALIZED",
157        x if x == CU_FILE_PLATFORM_NOT_SUPPORTED => "CU_FILE_PLATFORM_NOT_SUPPORTED",
158        x if x == CU_FILE_IO_NOT_SUPPORTED => "CU_FILE_IO_NOT_SUPPORTED",
159        x if x == CU_FILE_DEVICE_NOT_SUPPORTED => "CU_FILE_DEVICE_NOT_SUPPORTED",
160        _ => "CU_FILE_OTHER_ERROR",
161    }
162}
163
164pub fn nvidia_fs_loaded() -> bool {
165    let modules = match std::fs::read_to_string("/proc/modules") {
166        Ok(s) => s,
167        Err(_) => return false,
168    };
169    modules
170        .lines()
171        .any(|l| l.starts_with("nvidia_fs ") || l.starts_with("nvidia-fs "))
172}