From 8ec153793161c2920cb29c046d3e1cb334acc203 Mon Sep 17 00:00:00 2001 From: beviu Date: Sun, 16 Feb 2025 22:12:02 +0100 Subject: [PATCH 1/3] Fix pad renamed to min_wait_usec in io_uring_getevents_arg Since 6.12, the third field in io_uring_getevents_arg can be set to a non-zero value to enable a timeout to start only waiting for a single completion instead of the minimum specified in the io_uring_enter call. --- src/types.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/types.rs b/src/types.rs index fc67febf..419b985c 100644 --- a/src/types.rs +++ b/src/types.rs @@ -239,7 +239,7 @@ impl<'prev, 'now> SubmitArgs<'prev, 'now> { let args = sys::io_uring_getevents_arg { sigmask: 0, sigmask_sz: 0, - pad: 0, + min_wait_usec: 0, ts: 0, }; From f0f6a659a0817e919d3bd4d7dd1a60f79321397d Mon Sep 17 00:00:00 2001 From: beviu Date: Sun, 16 Feb 2025 22:17:05 +0100 Subject: [PATCH 2/3] Add SubmitArgs::min_wait_usec setter --- src/lib.rs | 8 ++++++++ src/types.rs | 16 ++++++++++++++++ 2 files changed, 24 insertions(+) diff --git a/src/lib.rs b/src/lib.rs index ed5a73ea..c525e833 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -597,6 +597,14 @@ impl Parameters { self.0.features & sys::IORING_FEAT_RECVSEND_BUNDLE != 0 } + /// If this flag is set, applications can use the + /// [`SubmitArgs::min_wait_usec`](types::SubmitArgs::min_wait_usec) method to specify a timeout + /// after which the kernel will return as soon as a single completion is received instead of + /// waiting for the minimum specified by the application. Available since kernel 6.12. + pub fn is_feature_min_timeout(&self) -> bool { + self.0.features & sys::IORING_FEAT_MIN_TIMEOUT != 0 + } + /// The number of submission queue entries allocated. pub fn sq_entries(&self) -> u32 { self.0.sq_entries diff --git a/src/types.rs b/src/types.rs index 419b985c..6bb778b3 100644 --- a/src/types.rs +++ b/src/types.rs @@ -262,6 +262,22 @@ impl<'prev, 'now> SubmitArgs<'prev, 'now> { } } + /// Sets a timeout in microseconds to start waiting for a minimum of a single completion. + /// + /// Once the timeout expires, the kernel will return when a single completion has been received + /// instead of waiting for the minimum amount of completions specified by the `want` parameter + /// in the call to [`Submitter::submit_and_wait`](crate::Submitter::submit_and_wait) or + /// [`Submitter::submit_with_args`](crate::Submitter::submit_with_args). + /// + /// Available since 6.12. Use the + /// [`Parameters::is_feature_min_timeout`](crate::Parameters::is_feature_min_timeout) method to + /// check for availability. + #[inline] + pub fn min_wait_usec(mut self, min_wait_usec: u32) -> Self { + self.args.min_wait_usec = min_wait_usec; + self + } + #[inline] pub fn timespec<'new>(mut self, timespec: &'new Timespec) -> SubmitArgs<'now, 'new> { self.args.ts = cast_ptr(timespec) as _; From f1f80f12f619e80226a9ee0266e9078b62b31973 Mon Sep 17 00:00:00 2001 From: beviu Date: Sun, 16 Feb 2025 23:17:16 +0100 Subject: [PATCH 3/3] Add test for SubmitArgs::min_wait_usec --- io-uring-test/src/main.rs | 1 + io-uring-test/src/tests/timeout.rs | 52 ++++++++++++++++++++++++++++++ 2 files changed, 53 insertions(+) diff --git a/io-uring-test/src/main.rs b/io-uring-test/src/main.rs index 088c774f..830307d8 100644 --- a/io-uring-test/src/main.rs +++ b/io-uring-test/src/main.rs @@ -115,6 +115,7 @@ fn test( tests::timeout::test_timeout_cancel(&mut ring, &test)?; tests::timeout::test_timeout_abs(&mut ring, &test)?; tests::timeout::test_timeout_submit_args(&mut ring, &test)?; + tests::timeout::test_timeout_submit_args_min_wait(&mut ring, &test)?; // net tests::net::test_tcp_write_read(&mut ring, &test)?; diff --git a/io-uring-test/src/tests/timeout.rs b/io-uring-test/src/tests/timeout.rs index 18ff34ea..13231a4e 100644 --- a/io-uring-test/src/tests/timeout.rs +++ b/io-uring-test/src/tests/timeout.rs @@ -381,3 +381,55 @@ pub fn test_timeout_submit_args( Ok(()) } + +pub fn test_timeout_submit_args_min_wait( + ring: &mut IoUring, + test: &Test, +) -> anyhow::Result<()> { + require! { + test; + ring.params().is_feature_ext_arg(); + ring.params().is_feature_min_timeout(); + }; + + println!("test timeout_submit_args_min_wait"); + + let ts = types::Timespec::new().sec(2); + let args = types::SubmitArgs::new() + .timespec(&ts) + .min_wait_usec(1_000_000); + + // timeout + + let start = Instant::now(); + match ring.submitter().submit_with_args(2, &args) { + Ok(_) => panic!(), + Err(ref err) if err.raw_os_error() == Some(libc::ETIME) => (), + Err(err) => return Err(err.into()), + } + assert_eq!(start.elapsed().as_secs(), 2); + + assert!(ring.completion().next().is_none()); + + // no timeout + + let nop_e = opcode::Nop::new(); + + unsafe { + ring.submission() + .push(&nop_e.build().user_data(0x1d).into()) + .expect("queue is full"); + } + + let start = Instant::now(); + ring.submitter().submit_with_args(2, &args)?; + assert_eq!(start.elapsed().as_secs(), 1); + + let cqes: Vec = ring.completion().map(Into::into).collect(); + + assert_eq!(cqes.len(), 1); + assert_eq!(cqes[0].user_data(), 0x1d); + assert_eq!(cqes[0].result(), 0); + + Ok(()) +}