cufile_sys/
lib.rs

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