Skip to content

Commit fdb1509

Browse files
authored
fs: check for io-uring opcode support (#7815)
1 parent 426a562 commit fdb1509

File tree

6 files changed

+41
-15
lines changed

6 files changed

+41
-15
lines changed

spellcheck.dic

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
311
1+
312
22
&
33
+
44
<
@@ -195,6 +195,7 @@ ntasks
195195
NUMA
196196
ok
197197
oneshot
198+
opcode
198199
ORed
199200
os
200201
parker

tokio/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,7 @@ tracing = { version = "0.1.29", default-features = false, features = ["std"], op
110110
# Currently unstable. The API exposed by these features may be broken at any time.
111111
# Requires `--cfg tokio_unstable` to enable.
112112
[target.'cfg(all(tokio_unstable, target_os = "linux"))'.dependencies]
113-
io-uring = { version = "0.7.6", default-features = false, optional = true }
113+
io-uring = { version = "0.7.11", default-features = false, optional = true }
114114
libc = { version = "0.2.168", optional = true }
115115
mio = { version = "1.0.1", default-features = false, features = ["os-poll", "os-ext"], optional = true }
116116
slab = { version = "0.4.9", optional = true }

tokio/src/fs/open_options.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -531,7 +531,7 @@ impl OpenOptions {
531531
let handle = crate::runtime::Handle::current();
532532
let driver_handle = handle.inner.driver().io();
533533

534-
if driver_handle.check_and_init()? {
534+
if driver_handle.check_and_init(io_uring::opcode::OpenAt::CODE)? {
535535
Op::open(path.as_ref(), opts)?.await
536536
} else {
537537
let opts = opts.clone().into();

tokio/src/fs/read.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ pub async fn read(path: impl AsRef<Path>) -> io::Result<Vec<u8>> {
6868

6969
let handle = crate::runtime::Handle::current();
7070
let driver_handle = handle.inner.driver().io();
71-
if driver_handle.check_and_init()? {
71+
if driver_handle.check_and_init(io_uring::opcode::Read::CODE)? {
7272
return read_uring(&path).await;
7373
}
7474
}

tokio/src/fs/write.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ pub async fn write(path: impl AsRef<Path>, contents: impl AsRef<[u8]>) -> io::Re
3737
{
3838
let handle = crate::runtime::Handle::current();
3939
let driver_handle = handle.inner.driver().io();
40-
if driver_handle.check_and_init()? {
40+
if driver_handle.check_and_init(io_uring::opcode::Write::CODE)? {
4141
return write_uring(path, contents).await;
4242
}
4343
}

tokio/src/runtime/io/driver/uring.rs

Lines changed: 35 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use io_uring::{squeue::Entry, IoUring};
1+
use io_uring::{squeue::Entry, IoUring, Probe};
22
use mio::unix::SourceFd;
33
use slab::Slab;
44

@@ -38,6 +38,7 @@ impl State {
3838

3939
pub(crate) struct UringContext {
4040
pub(crate) uring: Option<io_uring::IoUring>,
41+
pub(crate) probe: io_uring::Probe,
4142
pub(crate) ops: slab::Slab<Lifecycle>,
4243
}
4344

@@ -46,6 +47,7 @@ impl UringContext {
4647
Self {
4748
ops: Slab::new(),
4849
uring: None,
50+
probe: Probe::new(),
4951
}
5052
}
5153

@@ -57,6 +59,10 @@ impl UringContext {
5759
self.uring.as_mut().expect("io_uring not initialized")
5860
}
5961

62+
pub(crate) fn is_opcode_supported(&self, opcode: u8) -> bool {
63+
self.probe.is_supported(opcode)
64+
}
65+
6066
/// Perform `io_uring_setup` system call, and Returns true if this
6167
/// actually initialized the io_uring.
6268
///
@@ -68,7 +74,18 @@ impl UringContext {
6874
return Ok(false);
6975
}
7076

71-
self.uring.replace(IoUring::new(DEFAULT_RING_SIZE)?);
77+
let uring = IoUring::new(DEFAULT_RING_SIZE)?;
78+
79+
match uring.submitter().register_probe(&mut self.probe) {
80+
Ok(_) => {}
81+
Err(e) if e.raw_os_error() == Some(libc::EINVAL) => {
82+
// The kernel does not support IORING_REGISTER_PROBE.
83+
return Err(io::Error::from_raw_os_error(libc::ENOSYS));
84+
}
85+
Err(e) => return Err(e),
86+
}
87+
88+
self.uring.replace(uring);
7289

7390
Ok(true)
7491
}
@@ -182,12 +199,19 @@ impl Handle {
182199
}
183200

184201
/// Check if the io_uring context is initialized. If not, it will try to initialize it.
185-
pub(crate) fn check_and_init(&self) -> io::Result<bool> {
202+
/// Then, check if the provided opcode is supported.
203+
///
204+
/// If both the context initialization succeeds and the opcode is supported,
205+
/// this returns `Ok(true)`.
206+
/// If either io_uring is unsupported or the opcode is unsupported,
207+
/// this returns `Ok(false)`.
208+
/// An error is returned if an io_uring syscall returns an unexpected error value.
209+
pub(crate) fn check_and_init(&self, opcode: u8) -> io::Result<bool> {
186210
match State::from_usize(self.uring_state.load(Ordering::Acquire)) {
187-
State::Uninitialized => match self.try_init() {
188-
Ok(()) => {
211+
State::Uninitialized => match self.try_init_and_check_opcode(opcode) {
212+
Ok(opcode_supported) => {
189213
self.set_uring_state(State::Initialized);
190-
Ok(true)
214+
Ok(opcode_supported)
191215
}
192216
// If the system doesn't support io_uring, we set the state to Unsupported.
193217
Err(e) if e.raw_os_error() == Some(libc::ENOSYS) => {
@@ -205,18 +229,19 @@ impl Handle {
205229
Err(e) => Err(e),
206230
},
207231
State::Unsupported => Ok(false),
208-
State::Initialized => Ok(true),
232+
State::Initialized => Ok(self.get_uring().lock().is_opcode_supported(opcode)),
209233
}
210234
}
211235

212236
/// Initialize the io_uring context if it hasn't been initialized yet.
213-
fn try_init(&self) -> io::Result<()> {
237+
/// Then, check whether the given opcode is supported.
238+
fn try_init_and_check_opcode(&self, opcode: u8) -> io::Result<bool> {
214239
let mut guard = self.get_uring().lock();
215240
if guard.try_init()? {
216241
self.add_uring_source(guard.ring().as_raw_fd())?;
217242
}
218243

219-
Ok(())
244+
Ok(guard.is_opcode_supported(opcode))
220245
}
221246

222247
/// Register an operation with the io_uring.
@@ -231,7 +256,7 @@ impl Handle {
231256
/// be valid for the entire duration of the operation, otherwise it may cause memory problems.
232257
pub(crate) unsafe fn register_op(&self, entry: Entry, waker: Waker) -> io::Result<usize> {
233258
// Note: Maybe this check can be removed if upstream callers consistently use `check_and_init`.
234-
if !self.check_and_init()? {
259+
if !self.check_and_init(entry.get_opcode() as u8)? {
235260
return Err(io::Error::from_raw_os_error(libc::ENOSYS));
236261
}
237262

0 commit comments

Comments
 (0)