Rust binding with an easier to use API (#163)

Co-authored-by: Zhang "Echaozh" Yichao <does-not-exist@deepseek.com>
This commit is contained in:
Zhang "Echaozh" Yichao 2025-03-13 10:28:47 +08:00 committed by GitHub
parent b22f7153cc
commit 6c856acb4b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 512 additions and 21 deletions

209
Cargo.lock generated
View File

@ -139,9 +139,29 @@ dependencies = [
"proc-macro2",
"quote",
"regex",
"rustc-hash",
"rustc-hash 1.1.0",
"shlex",
"syn 2.0.90",
"syn 2.0.100",
]
[[package]]
name = "bindgen"
version = "0.71.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5f58bf3d7db68cfbac37cfc485a8d711e87e064c3d0fe0435b92f7a407f9d6b3"
dependencies = [
"bitflags 2.6.0",
"cexpr",
"clang-sys",
"itertools 0.12.1",
"log",
"prettyplease",
"proc-macro2",
"quote",
"regex",
"rustc-hash 2.1.1",
"shlex",
"syn 2.0.100",
]
[[package]]
@ -344,7 +364,7 @@ dependencies = [
"heck 0.5.0",
"proc-macro2",
"quote",
"syn 2.0.90",
"syn 2.0.100",
]
[[package]]
@ -485,7 +505,7 @@ dependencies = [
"proc-macro2",
"quote",
"scratch",
"syn 2.0.90",
"syn 2.0.100",
]
[[package]]
@ -498,7 +518,7 @@ dependencies = [
"codespan-reporting",
"proc-macro2",
"quote",
"syn 2.0.90",
"syn 2.0.100",
]
[[package]]
@ -516,7 +536,7 @@ dependencies = [
"proc-macro2",
"quote",
"rustversion",
"syn 2.0.90",
"syn 2.0.100",
]
[[package]]
@ -562,7 +582,7 @@ dependencies = [
"proc-macro-crate",
"proc-macro2",
"quote",
"syn 2.0.90",
"syn 2.0.100",
]
[[package]]
@ -607,7 +627,19 @@ checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7"
dependencies = [
"cfg-if",
"libc",
"wasi",
"wasi 0.11.0+wasi-snapshot-preview1",
]
[[package]]
name = "getrandom"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "43a49c392881ce6d5c3b8cb70f98717b7c07aabbdff06687b9030dbfbe2725f8"
dependencies = [
"cfg-if",
"libc",
"wasi 0.13.3+wasi-0.2.2",
"windows-targets",
]
[[package]]
@ -668,6 +700,15 @@ version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fbf6a919d6cf397374f7dfeeea91d974c7c0a7221d0d0f4f20d859d329e53fcc"
[[package]]
name = "hf3fs-usrbio-sys"
version = "0.1.0"
dependencies = [
"bindgen 0.71.1",
"shared_memory",
"uuid",
]
[[package]]
name = "iana-time-zone"
version = "0.1.61"
@ -795,7 +836,7 @@ version = "0.16.0+8.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ce3d60bc059831dc1c83903fb45c103f75db65c5a7bf22272764d9cc683e348c"
dependencies = [
"bindgen",
"bindgen 0.69.5",
"bzip2-sys",
"cc",
"glob",
@ -872,12 +913,34 @@ version = "2.7.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
[[package]]
name = "memoffset"
version = "0.6.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce"
dependencies = [
"autocfg",
]
[[package]]
name = "minimal-lexical"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a"
[[package]]
name = "nix"
version = "0.23.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f3790c00a0150112de0f4cd161e3d7fc4b2d8a5542ffc35f099a2562aecb35c"
dependencies = [
"bitflags 1.3.2",
"cc",
"cfg-if",
"libc",
"memoffset",
]
[[package]]
name = "nix"
version = "0.29.0"
@ -1011,6 +1074,16 @@ dependencies = [
"zerocopy",
]
[[package]]
name = "prettyplease"
version = "0.2.30"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f1ccf34da56fc294e7d4ccf69a85992b7dfb826b7cf57bac6a70bba3494cc08a"
dependencies = [
"proc-macro2",
"syn 2.0.100",
]
[[package]]
name = "proc-macro-crate"
version = "3.2.0"
@ -1089,7 +1162,7 @@ version = "0.6.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
dependencies = [
"getrandom",
"getrandom 0.2.15",
]
[[package]]
@ -1175,6 +1248,12 @@ version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2"
[[package]]
name = "rustc-hash"
version = "2.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d"
[[package]]
name = "rustc_version"
version = "0.4.1"
@ -1259,7 +1338,7 @@ checksum = "ad1e866f866923f252f05c889987993144fb74e722403468a4ebd70c3cd756c0"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.90",
"syn 2.0.100",
]
[[package]]
@ -1292,6 +1371,19 @@ dependencies = [
"lazy_static",
]
[[package]]
name = "shared_memory"
version = "0.12.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ba8593196da75d9dc4f69349682bd4c2099f8cde114257d1ef7ef1b33d1aba54"
dependencies = [
"cfg-if",
"libc",
"nix 0.23.2",
"rand",
"win-sys",
]
[[package]]
name = "shlex"
version = "1.3.0"
@ -1359,9 +1451,9 @@ dependencies = [
[[package]]
name = "syn"
version = "2.0.90"
version = "2.0.100"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "919d3b74a5dd0ccd15aeb8f93e7006bd9e14c295087c9896a110f490752bcf31"
checksum = "b09a44accad81e1ba1cd74a32461ba89dee89095ba17b32f5d03683b1b1fc2a0"
dependencies = [
"proc-macro2",
"quote",
@ -1416,7 +1508,7 @@ checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.90",
"syn 2.0.100",
]
[[package]]
@ -1550,7 +1642,7 @@ checksum = "395ae124c09f9e6918a2310af6038fba074bcf474ac352496d5910dd59a2226d"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.90",
"syn 2.0.100",
]
[[package]]
@ -1608,7 +1700,7 @@ version = "0.1.0"
dependencies = [
"chrono",
"libc",
"nix",
"nix 0.29.0",
"scopeguard 0.3.3",
"serde",
"serde_json",
@ -1643,6 +1735,15 @@ version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
[[package]]
name = "uuid"
version = "1.15.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e0f540e3240398cce6128b64ba83fdbdd86129c16a3aa1a3a252efd66eb3d587"
dependencies = [
"getrandom 0.3.1",
]
[[package]]
name = "valuable"
version = "0.1.0"
@ -1683,6 +1784,15 @@ version = "0.11.0+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
[[package]]
name = "wasi"
version = "0.13.3+wasi-0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "26816d2e1a4a36a2940b96c5296ce403917633dff8f3440e9b236ed6f6bacad2"
dependencies = [
"wit-bindgen-rt",
]
[[package]]
name = "wasm-bindgen"
version = "0.2.97"
@ -1705,7 +1815,7 @@ dependencies = [
"once_cell",
"proc-macro2",
"quote",
"syn 2.0.90",
"syn 2.0.100",
"wasm-bindgen-shared",
]
@ -1727,7 +1837,7 @@ checksum = "98c9ae5a76e46f4deecd0f0255cc223cfa18dc9b261213b8aa0c7b36f61b3f1d"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.90",
"syn 2.0.100",
"wasm-bindgen-backend",
"wasm-bindgen-shared",
]
@ -1748,6 +1858,15 @@ dependencies = [
"wasm-bindgen",
]
[[package]]
name = "win-sys"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5b7b128a98c1cfa201b09eb49ba285887deb3cbe7466a98850eb1adabb452be5"
dependencies = [
"windows",
]
[[package]]
name = "winapi"
version = "0.3.9"
@ -1779,6 +1898,19 @@ version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
[[package]]
name = "windows"
version = "0.34.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "45296b64204227616fdbf2614cefa4c236b98ee64dfaaaa435207ed99fe7829f"
dependencies = [
"windows_aarch64_msvc 0.34.0",
"windows_i686_gnu 0.34.0",
"windows_i686_msvc 0.34.0",
"windows_x86_64_gnu 0.34.0",
"windows_x86_64_msvc 0.34.0",
]
[[package]]
name = "windows-core"
version = "0.52.0"
@ -1849,6 +1981,12 @@ version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
[[package]]
name = "windows_aarch64_msvc"
version = "0.34.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "17cffbe740121affb56fad0fc0e421804adf0ae00891205213b5cecd30db881d"
[[package]]
name = "windows_aarch64_msvc"
version = "0.42.2"
@ -1861,6 +1999,12 @@ version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
[[package]]
name = "windows_i686_gnu"
version = "0.34.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2564fde759adb79129d9b4f54be42b32c89970c18ebf93124ca8870a498688ed"
[[package]]
name = "windows_i686_gnu"
version = "0.42.2"
@ -1879,6 +2023,12 @@ version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
[[package]]
name = "windows_i686_msvc"
version = "0.34.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9cd9d32ba70453522332c14d38814bceeb747d80b3958676007acadd7e166956"
[[package]]
name = "windows_i686_msvc"
version = "0.42.2"
@ -1891,6 +2041,12 @@ version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
[[package]]
name = "windows_x86_64_gnu"
version = "0.34.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cfce6deae227ee8d356d19effc141a509cc503dfd1f850622ec4b0f84428e1f4"
[[package]]
name = "windows_x86_64_gnu"
version = "0.42.2"
@ -1915,6 +2071,12 @@ version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
[[package]]
name = "windows_x86_64_msvc"
version = "0.34.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d19538ccc21819d01deaf88d6a17eae6596a12e9aafdbb97916fb49896d89de9"
[[package]]
name = "windows_x86_64_msvc"
version = "0.42.2"
@ -1936,6 +2098,15 @@ dependencies = [
"memchr",
]
[[package]]
name = "wit-bindgen-rt"
version = "0.33.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3268f3d866458b787f390cf61f4bbb563b922d091359f9608842999eaee3943c"
dependencies = [
"bitflags 2.6.0",
]
[[package]]
name = "zerocopy"
version = "0.7.35"
@ -1954,7 +2125,7 @@ checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.90",
"syn 2.0.100",
]
[[package]]

View File

@ -1,7 +1,8 @@
[workspace]
members = [
"src/client/trash_cleaner",
"src/storage/chunk_engine"
"src/storage/chunk_engine",
"src/lib/rs/hf3fs-usrbio-sys"
]
resolver = "2"
@ -9,7 +10,7 @@ resolver = "2"
authors = ["dev <noreply@deepseek.com>"]
edition = "2021"
license = "MIT"
rust-version = "1.75.0" # MSRV
rust-version = "1.85.0" # MSRV
[profile.release-cmake]
debug = true

View File

@ -1,2 +1,9 @@
target_add_lib(hf3fs_api client-lib-common storage-client numa rt)
target_add_shared_lib(hf3fs_api_shared client-lib-common storage-client numa rt)
add_custom_command(
TARGET hf3fs_api_shared
POST_BUILD
COMMAND ${CMAKE_COMMAND} -E create_symlink "${CMAKE_CURRENT_BINARY_DIR}/libhf3fs_api_shared.so" "${CMAKE_SOURCE_DIR}/src/lib/rs/hf3fs-usrbio-sys/lib/libhf3fs_api_shared.so"
COMMENT "linking usrbio library to rust binding lib dir"
)

View File

@ -4,6 +4,7 @@
extern "C" {
#endif
#include <stdbool.h>
#include <stdint.h>
#include <sys/types.h>

View File

@ -0,0 +1,11 @@
[package]
name = "hf3fs-usrbio-sys"
version = "0.1.0"
edition = "2021"
[dependencies]
shared_memory = "0.12.4"
uuid = { version = "1.13.2", features = ["v4"] }
[build-dependencies]
bindgen = "0.71"

View File

@ -0,0 +1 @@
Make sure to build the CMake target `hf3fs_api_shared` (the usrbio library) before building this rust binding. The CMake build will link the built library to the `lib` directory, which is a prerequisite of the Cargo build.

View File

@ -0,0 +1,20 @@
use std::env;
use std::path::PathBuf;
fn main() {
let topdir = env::var("CARGO_MANIFEST_DIR").unwrap();
println!("cargo::rustc-link-search=native={}/lib", topdir);
println!("cargo::rustc-link-lib=hf3fs_api_shared");
let bindings = bindgen::Builder::default()
.header("include/hf3fs_usrbio.h")
.clang_arg("-std=c99")
.parse_callbacks(Box::new(bindgen::CargoCallbacks::new()))
.generate()
.expect("Unable to generate bindings");
let out_path = PathBuf::from(env::var("OUT_DIR").unwrap());
bindings
.write_to_file(out_path.join("bindings.rs"))
.expect("Couldn't write bindings!");
}

View File

@ -0,0 +1,279 @@
#![allow(non_upper_case_globals)]
#![allow(non_camel_case_types)]
#![allow(non_snake_case)]
include!(concat!(env!("OUT_DIR"), "/bindings.rs"));
use std::ffi::CString;
use std::mem::{size_of, transmute};
use std::ops::{Range, RangeInclusive};
use std::os::fd::{AsRawFd, FromRawFd, IntoRawFd, OwnedFd, RawFd};
use std::os::unix::fs;
use std::ptr;
use std::time::{Duration, SystemTime, UNIX_EPOCH};
use shared_memory::Shmem;
use uuid::Uuid;
impl Default for hf3fs_iov {
fn default() -> Self {
Self {
base: ptr::null_mut(),
iovh: ptr::null_mut(),
id: [0; 16],
mount_point: [0; 256],
size: 0,
block_size: 0,
numa: 0,
}
}
}
pub struct Iov(hf3fs_iov);
unsafe impl Send for Iov {}
unsafe impl Sync for Iov {}
impl Iov {
pub fn wrap(mountpoint: &str, shm: &Shmem, numa: i32) -> Result<Self, i32> {
let id = Uuid::new_v4();
let targ = format!("/dev/shm/{}", shm.get_os_id());
let link = format!(
"{}/3fs-virt/iovs/{}",
mountpoint,
id.as_hyphenated().to_string()
);
fs::symlink(&targ, &link).expect(&format!("failed to create symlink for shm {}", targ));
let mut iov = hf3fs_iov::default();
let mp = CString::new(mountpoint)
.expect("failed to alloc mountpoint string")
.into_raw();
let r = unsafe {
hf3fs_iovwrap(
&mut iov as *mut _,
shm.as_ptr() as *mut _,
id.as_bytes().as_ptr(),
mp,
shm.len(),
0,
numa,
)
};
if r == 0 {
Ok(Self(iov))
} else {
Err(r)
}
}
}
pub struct Ior {
ior: hf3fs_ior,
}
unsafe impl Send for Ior {}
impl Ior {
pub fn create(
mountpoint: &str,
for_read: bool,
entries: i32,
iodepth: i32,
timeout: i32,
numa: i32,
flags: u64,
) -> Result<Self, i32> {
let mut ior = hf3fs_ior {
iov: hf3fs_iov::default(),
iorh: ptr::null_mut(),
mount_point: [0; 256],
for_read: true,
io_depth: 0,
priority: 0,
timeout: 0,
flags: 0,
};
let mp = CString::new(mountpoint)
.expect("failed to alloc mountpoint string")
.into_raw();
let r = unsafe {
hf3fs_iorcreate4(
&mut ior as *mut _,
mp,
entries,
for_read,
iodepth,
timeout,
numa,
flags,
)
};
if r == 0 {
Ok(Self { ior })
} else {
Err(r)
}
}
pub fn prepare<T>(
&self,
iov: &Iov,
bufrng: Range<usize>,
fd: &RegisteredFd,
file_off: usize,
extra: T,
) -> Result<(), i32> {
let io = Box::new(PreparedIo {
buf: iov.0.base,
extra,
result: 0,
});
let r = unsafe {
hf3fs_prep_io(
&self.ior as *const _,
&iov.0 as *const _,
self.ior.for_read,
iov.0.base.add(bufrng.start) as *mut _,
fd.0.as_raw_fd(),
file_off,
(bufrng.end - bufrng.start) as _,
Box::into_raw(io) as *const _,
)
};
if r >= 0 {
Ok(())
} else {
Err(r)
}
}
pub fn submit(&self) {
unsafe {
hf3fs_submit_ios(&self.ior as *const _);
}
}
pub fn poll<T>(
&self,
wanted: RangeInclusive<usize>,
timeout_in_ms: u64,
) -> Vec<Box<PreparedIo<T>>> {
let dl = SystemTime::now() + Duration::from_millis(timeout_in_ms);
let ts = dl.duration_since(UNIX_EPOCH).unwrap();
let to = timespec {
tv_sec: ts.as_secs() as _,
tv_nsec: ts.subsec_nanos() as _,
};
let arr: __BindgenOpaqueArray<u8, { size_of::<timespec>() }> = unsafe { transmute(to) };
let pto = &arr as *const __BindgenOpaqueArray<u8, { size_of::<timespec>() }>;
let min = *wanted.start();
let max = *wanted.end();
let mut cqes = Vec::with_capacity(max);
cqes.resize(max, hf3fs_cqe::default());
let n = unsafe {
hf3fs_wait_for_ios(
&self.ior as *const _,
cqes.as_mut_ptr(),
max as _,
min as _,
pto as _,
)
};
(0..n)
.map(|i| {
let cqe: &hf3fs_cqe = &cqes[i as usize];
let mut io = unsafe { Box::from_raw(cqe.userdata as *mut PreparedIo<T>) };
io.result = cqe.result;
io
})
.collect()
}
}
impl Drop for Ior {
fn drop(&mut self) {
unsafe {
hf3fs_iordestroy(&mut self.ior as *mut _);
}
}
}
impl Default for hf3fs_cqe {
fn default() -> Self {
Self {
index: 0,
reserved: 0,
result: 0,
userdata: ptr::null_mut(),
}
}
}
pub struct RegisteredFd(OwnedFd);
impl RegisteredFd {
fn register(fd: RawFd) -> Self {
unsafe {
hf3fs_reg_fd(fd as _, 0);
Self(OwnedFd::from_raw_fd(fd))
}
}
pub fn open_and_register<P: AsRef<std::path::Path>>(path: P) -> std::io::Result<Self> {
std::fs::File::open(path).map(|f| Self::register(f.into_raw_fd()))
}
}
impl Drop for RegisteredFd {
fn drop(&mut self) {
unsafe {
hf3fs_dereg_fd(self.0.as_raw_fd());
}
}
}
pub struct PreparedIo<T> {
pub buf: *mut u8,
pub extra: T,
pub result: i64,
}
#[repr(C)]
struct timespec {
tv_sec: i64,
tv_nsec: i64,
}
#[cfg(test)]
mod tests {
use super::*;
use shared_memory::*;
#[test]
fn test_io() {
let shm = ShmemConf::new()
.os_id("/123")
.size(10 << 10)
.create()
.unwrap();
let iov = Iov::wrap("/3fs/test", &shm, 0).unwrap();
let ior = Ior::create(
"/3fs/test",
true,
1000,
0,
1000,
-1,
HF3FS_IOR_FORBID_READ_HOLES as _,
)
.unwrap();
let fp = "/3fs/test";
let fd = RegisteredFd::open_and_register(fp).unwrap();
ior.prepare(&iov, 0..52, &fd, 0, (1u64, 2usize, 3usize))
.unwrap();
ior.submit();
let rs = ior.poll::<(u64, usize, usize)>(1..=1, 1000);
assert_eq!(rs.len(), 1);
println!("{:?} {}", rs[0].extra, rs[0].result);
assert_eq!(rs[0].extra, (1u64, 2usize, 3usize));
assert_eq!(rs[0].result, 52);
}
}