pub fn is_uring_available() -> bool {
#[cfg(target_os = "linux")]
{
use std::sync::OnceLock;
static AVAILABLE: OnceLock<bool> = OnceLock::new();
*AVAILABLE.get_or_init(probe_uring_page_store_ops)
}
#[cfg(not(target_os = "linux"))]
{
false
}
}
#[cfg(target_os = "linux")]
fn probe_submit_one(
ring: &mut io_uring::IoUring,
entry: &io_uring::squeue::Entry,
what: &str,
) -> Option<i32> {
unsafe {
if ring.submission().push(entry).is_err() {
tracing::warn!(op = what, "io_uring probe: submission queue full");
return None;
}
}
if let Err(e) = ring.submit_and_wait(1) {
tracing::warn!(op = what, error = %e, "io_uring probe: submit_and_wait failed");
return None;
}
let mut cq = ring.completion();
match cq.next() {
Some(cqe) => Some(cqe.result()),
None => {
tracing::warn!(op = what, "io_uring probe: no CQE");
None
}
}
}
#[cfg(target_os = "linux")]
fn probe_uring_page_store_ops() -> bool {
use io_uring::{opcode, types, IoUring};
use std::ffi::CString;
use std::path::PathBuf;
let mut ring = match IoUring::new(8) {
Ok(r) => r,
Err(e) => {
tracing::warn!(
error = %e,
"io_uring not available; falling back to tokio::fs backend"
);
return false;
}
};
let probe_path: PathBuf = std::env::temp_dir().join(format!(
"gfs_uring_probe_{}_{}",
std::process::id(),
std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.map(|d| d.as_nanos())
.unwrap_or(0)
));
let path_cstr = match CString::new(probe_path.to_string_lossy().as_bytes()) {
Ok(p) => p,
Err(_) => return false,
};
struct ProbeFile(PathBuf);
impl Drop for ProbeFile {
fn drop(&mut self) {
let _ = std::fs::remove_file(&self.0);
}
}
let _cleanup = ProbeFile(probe_path.clone());
let open_e = opcode::OpenAt::new(types::Fd(libc::AT_FDCWD), path_cstr.as_ptr())
.flags(libc::O_RDWR | libc::O_CREAT | libc::O_TRUNC | libc::O_CLOEXEC)
.mode(0o644)
.build()
.user_data(1);
let fd = match probe_submit_one(&mut ring, &open_e, "openat") {
Some(r) if r >= 0 => r,
Some(r) => {
let err = std::io::Error::from_raw_os_error(-r);
tracing::warn!(
error = %err,
"io_uring OPENAT(create) probe failed (EPERM is common in sandboxes); \
falling back to tokio::fs backend"
);
return false;
}
None => return false,
};
let payload = b"gfs-uring-probe";
let mut readback = [0u8; 15];
debug_assert_eq!(payload.len(), readback.len());
let write_e = opcode::Write::new(types::Fd(fd), payload.as_ptr(), payload.len() as u32)
.offset(0)
.build()
.user_data(2);
match probe_submit_one(&mut ring, &write_e, "write") {
Some(r) if r == payload.len() as i32 => {}
Some(r) => {
if r < 0 {
let err = std::io::Error::from_raw_os_error(-r);
tracing::warn!(error = %err, "io_uring WRITE probe failed; falling back");
} else {
tracing::warn!(wrote = r, "io_uring WRITE probe short write; falling back");
}
unsafe { libc::close(fd) };
return false;
}
None => {
unsafe { libc::close(fd) };
return false;
}
}
let read_e = opcode::Read::new(types::Fd(fd), readback.as_mut_ptr(), readback.len() as u32)
.offset(0)
.build()
.user_data(3);
match probe_submit_one(&mut ring, &read_e, "read") {
Some(r) if r == payload.len() as i32 => {}
Some(r) => {
if r < 0 {
let err = std::io::Error::from_raw_os_error(-r);
tracing::warn!(
error = %err,
"io_uring READ probe failed while OPENAT succeeded — this environment \
allows io_uring per-opcode (GitHub Actions does exactly this); \
falling back to tokio::fs backend"
);
} else {
tracing::warn!(read = r, "io_uring READ probe short read; falling back");
}
unsafe { libc::close(fd) };
return false;
}
None => {
unsafe { libc::close(fd) };
return false;
}
}
if readback != *payload {
tracing::warn!("io_uring READ probe returned wrong bytes; falling back");
unsafe { libc::close(fd) };
return false;
}
let close_e = opcode::Close::new(types::Fd(fd)).build().user_data(4);
match probe_submit_one(&mut ring, &close_e, "close") {
Some(0) => {}
Some(r) => {
let err = std::io::Error::from_raw_os_error(-r);
tracing::warn!(
error = %err,
"io_uring CLOSE probe failed; the store falls back to libc::close, so \
continuing with the uring backend"
);
unsafe { libc::close(fd) };
}
None => {
unsafe { libc::close(fd) };
}
}
tracing::info!("io_uring is available (OPENAT / WRITE / READ / CLOSE probe ok)");
true
}